Repository: Dripfarm/SVDB Branch: master Commit: 7bb4c6e9d547 Files: 25 Total size: 38.8 KB Directory structure: gitextract_5o4a4uc6/ ├── .gitignore ├── .swiftpm/ │ └── xcode/ │ └── package.xcworkspace/ │ └── xcshareddata/ │ └── IDEWorkspaceChecks.plist ├── LICENSE ├── Package.swift ├── README.md ├── SVDBDemo/ │ ├── SVDBDemo/ │ │ ├── Assets.xcassets/ │ │ │ ├── AccentColor.colorset/ │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── ContentView.swift │ │ ├── Preview Content/ │ │ │ └── Preview Assets.xcassets/ │ │ │ └── Contents.json │ │ ├── SVDBDemoApp.swift │ │ └── WORDS.swift │ └── SVDBDemo.xcodeproj/ │ ├── project.pbxproj │ └── project.xcworkspace/ │ ├── contents.xcworkspacedata │ └── xcshareddata/ │ ├── IDEWorkspaceChecks.plist │ └── swiftpm/ │ └── Package.resolved ├── Sources/ │ └── SVDB/ │ ├── API/ │ │ ├── Collection.swift │ │ └── SVDB.swift │ └── Internals/ │ ├── Models/ │ │ ├── Document.swift │ │ ├── Errors.swift │ │ └── SearchResult.swift │ └── TheMathFile.swift └── Tests/ └── SVDBTests/ ├── CollectionTests.swift ├── SVDBTests.swift └── SearchTests.swift ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .DS_Store /.build /Packages /*.xcodeproj xcuserdata/ DerivedData/ .swiftpm/config/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: .swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2025 Dripfarm Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Package.swift ================================================ // swift-tools-version: 5.8 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "SVDB", products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "SVDB", targets: ["SVDB"]), ], dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "SVDB", dependencies: []), .testTarget( name: "SVDBTests", dependencies: ["SVDB"]), ] ) ================================================ FILE: README.md ================================================ # Swift Vector Database (SVDB) A new fast local on-device vector database for Swift Apps. Built for those building the next-generation of user experiences only possible with on-device intelligence. Local on-device vector databases are just the beginning. ## Installation To install it using the Swift Package Manager, either directly add it to your project using Xcode 11, or specify it as dependency in the Package.swift file: ``` // ... dependencies: [ .package(url: "https://github.com/Dripfarm/SVDB.git", from: "1.0.0"), ], //... ``` ## Usage ### 1. Create Embeddings ``` let document = "cat" ``` **ChatGPT:** I find [This Swift OpenAI package to be the best](https://github.com/MacPaw/OpenAI) ``` import OpenAI func embed(text: String) async -> [Double]? { let query = EmbeddingsQuery(model: .textEmbeddingAda, input: text) let result = try! await openAI.embeddings(query: query) return result.data.first?.embedding } let wordEmbedding = embed(text: document) ``` **NLEmbeddings** ``` import NaturalLanguage let embedding: NLEmbedding? = NLEmbedding.wordEmbedding(for: .english) let wordEmedding = embedding?.vector(for: document) //returns double array ``` ### 2. Add Documents ``` let animalCollection = SVDB.shared.collection("animals") SVDB.shared.addDocument(text: document, embedding: wordEmbedding) ``` ### 3. Search ``` let dogEmedding = embedding?.vector(for: "dog") let results = animalCollection.search(query: dogEmedding) ``` ## Demo Check out the demo [Demo](https://github.com/Dripfarm/SVDB/tree/master/SVDBDemo) ## Todo Not sure. I want to make it easier to add documents and take care of the embeddings for you. Any suggestions? ================================================ FILE: SVDBDemo/SVDBDemo/Assets.xcassets/AccentColor.colorset/Contents.json ================================================ { "colors" : [ { "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: SVDBDemo/SVDBDemo/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: SVDBDemo/SVDBDemo/Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: SVDBDemo/SVDBDemo/ContentView.swift ================================================ // // ContentView.swift // SVDBDemo // // Created by Jordan Howlett on 8/4/23. // import Accelerate import CoreML import NaturalLanguage import SVDB import SwiftUI struct EmbeddingEntry: Codable { let id: UUID let text: String let embedding: [Double] let magnitude: Double } func generateRandomSentence() -> String { var sentence = "" for _ in 1...5 { if let randomWord = words.randomElement() { sentence += randomWord + " " } } return sentence.trimmingCharacters(in: .whitespaces) } struct ContentView: View { let collectionName: String = "testCollection" @State private var collection: Collection? @State private var query: String = "emotions" @State private var newEntry: String = "" @State private var neighbors: [(String, Double)] = [] var body: some View { VStack { TextField("Enter query", text: $query) .padding() .textFieldStyle(RoundedBorderTextFieldStyle()) HStack { TextField("New Entry", text: $newEntry) .textFieldStyle(RoundedBorderTextFieldStyle()) Button("Add Entry") { Task { await addEntry(newEntry) } } } .padding() Button("Find Neighbors") { self.neighbors.removeAll() Task { await findNeighbors() } } Button("Generate Random Embeddings") { Task { await generateRandomEmbeddings() } } .padding() List(neighbors, id: \.0) { neighbor in Text("\(neighbor.0) - \(neighbor.1)") } } .padding() .onAppear { Task { await loadCollection() } } } func loadCollection() async { do { collection = try SVDB.shared.collection(collectionName) } catch { print("Failed to load collection:", error) } } func generateRandomEmbeddings() async { var randomSentences: [String] = [] for _ in 1...100 { let sentence = generateRandomSentence() randomSentences.append(sentence) } for sentence in randomSentences { await addEntry(sentence) } print("Done creating") } func addEntry(_ entry: String) async { guard let collection = collection else { return } guard let embedding = generateEmbedding(for: entry) else { return } collection.addDocument(text: entry, embedding: embedding) } func generateEmbedding(for sentence: String) -> [Double]? { guard let embedding = NLEmbedding.wordEmbedding(for: .english) else { return nil } let words = sentence.lowercased().split(separator: " ") guard let firstVector = embedding.vector(for: String(words.first!)) else { return nil } var vectorSum = [Double](firstVector) for word in words.dropFirst() { if let vector = embedding.vector(for: String(word)) { vDSP_vaddD(vectorSum, 1, vector, 1, &vectorSum, 1, vDSP_Length(vectorSum.count)) } } var vectorAverage = [Double](repeating: 0, count: vectorSum.count) var divisor = Double(words.count) vDSP_vsdivD(vectorSum, 1, &divisor, &vectorAverage, 1, vDSP_Length(vectorAverage.count)) return vectorAverage } func findNeighbors() async { guard let collection = collection else { return } guard let queryEmbedding = generateEmbedding(for: query) else { return } let results = collection.search(query: queryEmbedding) neighbors = results.map { ($0.text, $0.score) } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } ================================================ FILE: SVDBDemo/SVDBDemo/Preview Content/Preview Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: SVDBDemo/SVDBDemo/SVDBDemoApp.swift ================================================ // // SVDBDemoApp.swift // SVDBDemo // // Created by Jordan Howlett on 8/4/23. // import SwiftUI @main struct SVDBDemoApp: App { var body: some Scene { WindowGroup { ContentView() } } } ================================================ FILE: SVDBDemo/SVDBDemo/WORDS.swift ================================================ // // WORDS.swift // SVDBDemo // // Created by Jordan Howlett on 8/4/23. // let words = [ "apple", "banana", "cherry", "date", "elderberry", "fig", "grape", "honeydew", "kiwi", "lemon", "mango", "nectarine", "orange", "papaya", "quince", "raspberry", "strawberry", "tangerine", "watermelon", "blueberry", "dog", "cat", "mouse", "bird", "fish", "elephant", "giraffe", "tiger", "lion", "bear", "happy", "sad", "excited", "angry", "bored", "confused", "calm", "elated", "frustrated", "nervous", "love", "hate", "trust", "fear", "joy", "surprise", "anticipation", "disgust", "sadness", "acceptance", "run", "walk", "jump", "dance", "sing", "read", "write", "draw", "play", "swim", "sun", "moon", "star", "sky", "cloud", "rain", "snow", "wind", "storm", "thunder", "red", "blue", "green", "yellow", "purple", "pink", "brown", "black", "white", "gray", "spring", "summer", "fall", "winter", "morning", "noon", "evening", "night", "dawn", "dusk", "car", "truck", "boat", "plane", "train", "bicycle", "skateboard", "rollerblades", "scooter", "unicycle", "tree", "flower", "bush", "cactus", "fern", "moss", "mushroom", "vine", "grass", "weed", "shirt", "pants", "skirt", "dress", "socks", "shoes", "hat", "scarf", "gloves", "coat", "eat", "drink", "sleep", "wake", "talk", "listen", "learn", "teach", "laugh", "cry", "big", "small", "fast", "slow", "old", "young", "tall", "short", "loud", "quiet", "ocean", "lake", "river", "stream", "pond", "puddle", "fountain", "waterfall", "wave", "ripple", "rock", "stone", "pebble", "boulder", "gravel", "sand", "dirt", "mud", "clay", "silt", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "breakfast", "lunch", "dinner", "snack", "appetizer", "dessert", "meal", "feast", "picnic", "buffet" ] ================================================ FILE: SVDBDemo/SVDBDemo.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 56; objects = { /* Begin PBXBuildFile section */ 177872D12A7DF0FE00D52548 /* SVDBDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 177872D02A7DF0FE00D52548 /* SVDBDemoApp.swift */; }; 177872D32A7DF0FE00D52548 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 177872D22A7DF0FE00D52548 /* ContentView.swift */; }; 177872D52A7DF10000D52548 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 177872D42A7DF10000D52548 /* Assets.xcassets */; }; 177872D82A7DF10000D52548 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 177872D72A7DF10000D52548 /* Preview Assets.xcassets */; }; 17C2FE882A7DF61B00A3D246 /* WORDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17C2FE872A7DF61B00A3D246 /* WORDS.swift */; }; 17C2FE8B2A7DF79300A3D246 /* SVDB in Frameworks */ = {isa = PBXBuildFile; productRef = 17C2FE8A2A7DF79300A3D246 /* SVDB */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 177872CD2A7DF0FE00D52548 /* SVDBDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SVDBDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 177872D02A7DF0FE00D52548 /* SVDBDemoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SVDBDemoApp.swift; sourceTree = ""; }; 177872D22A7DF0FE00D52548 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 177872D42A7DF10000D52548 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 177872D72A7DF10000D52548 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 17C2FE872A7DF61B00A3D246 /* WORDS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WORDS.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 177872CA2A7DF0FE00D52548 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 17C2FE8B2A7DF79300A3D246 /* SVDB in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 177872C42A7DF0FE00D52548 = { isa = PBXGroup; children = ( 177872CF2A7DF0FE00D52548 /* SVDBDemo */, 177872CE2A7DF0FE00D52548 /* Products */, ); sourceTree = ""; }; 177872CE2A7DF0FE00D52548 /* Products */ = { isa = PBXGroup; children = ( 177872CD2A7DF0FE00D52548 /* SVDBDemo.app */, ); name = Products; sourceTree = ""; }; 177872CF2A7DF0FE00D52548 /* SVDBDemo */ = { isa = PBXGroup; children = ( 177872D02A7DF0FE00D52548 /* SVDBDemoApp.swift */, 177872D22A7DF0FE00D52548 /* ContentView.swift */, 177872D42A7DF10000D52548 /* Assets.xcassets */, 177872D62A7DF10000D52548 /* Preview Content */, 17C2FE872A7DF61B00A3D246 /* WORDS.swift */, ); path = SVDBDemo; sourceTree = ""; }; 177872D62A7DF10000D52548 /* Preview Content */ = { isa = PBXGroup; children = ( 177872D72A7DF10000D52548 /* Preview Assets.xcassets */, ); path = "Preview Content"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 177872CC2A7DF0FE00D52548 /* SVDBDemo */ = { isa = PBXNativeTarget; buildConfigurationList = 177872DB2A7DF10000D52548 /* Build configuration list for PBXNativeTarget "SVDBDemo" */; buildPhases = ( 177872C92A7DF0FE00D52548 /* Sources */, 177872CA2A7DF0FE00D52548 /* Frameworks */, 177872CB2A7DF0FE00D52548 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = SVDBDemo; packageProductDependencies = ( 17C2FE8A2A7DF79300A3D246 /* SVDB */, ); productName = SVDBDemo; productReference = 177872CD2A7DF0FE00D52548 /* SVDBDemo.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 177872C52A7DF0FE00D52548 /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1430; LastUpgradeCheck = 1430; TargetAttributes = { 177872CC2A7DF0FE00D52548 = { CreatedOnToolsVersion = 14.3.1; }; }; }; buildConfigurationList = 177872C82A7DF0FE00D52548 /* Build configuration list for PBXProject "SVDBDemo" */; compatibilityVersion = "Xcode 14.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 177872C42A7DF0FE00D52548; packageReferences = ( 17C2FE892A7DF79300A3D246 /* XCRemoteSwiftPackageReference "SVDB" */, ); productRefGroup = 177872CE2A7DF0FE00D52548 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 177872CC2A7DF0FE00D52548 /* SVDBDemo */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 177872CB2A7DF0FE00D52548 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 177872D82A7DF10000D52548 /* Preview Assets.xcassets in Resources */, 177872D52A7DF10000D52548 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 177872C92A7DF0FE00D52548 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 177872D32A7DF0FE00D52548 /* ContentView.swift in Sources */, 17C2FE882A7DF61B00A3D246 /* WORDS.swift in Sources */, 177872D12A7DF0FE00D52548 /* SVDBDemoApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 177872D92A7DF10000D52548 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 16.4; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 177872DA2A7DF10000D52548 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 16.4; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; }; 177872DC2A7DF10000D52548 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"SVDBDemo/Preview Content\""; DEVELOPMENT_TEAM = PPVB2USUND; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = co.beginagain.SVDBDemo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 177872DD2A7DF10000D52548 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"SVDBDemo/Preview Content\""; DEVELOPMENT_TEAM = PPVB2USUND; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = co.beginagain.SVDBDemo; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 177872C82A7DF0FE00D52548 /* Build configuration list for PBXProject "SVDBDemo" */ = { isa = XCConfigurationList; buildConfigurations = ( 177872D92A7DF10000D52548 /* Debug */, 177872DA2A7DF10000D52548 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 177872DB2A7DF10000D52548 /* Build configuration list for PBXNativeTarget "SVDBDemo" */ = { isa = XCConfigurationList; buildConfigurations = ( 177872DC2A7DF10000D52548 /* Debug */, 177872DD2A7DF10000D52548 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ 17C2FE892A7DF79300A3D246 /* XCRemoteSwiftPackageReference "SVDB" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "git@github.com:Dripfarm/SVDB.git"; requirement = { kind = upToNextMajorVersion; minimumVersion = 1.0.0; }; }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ 17C2FE8A2A7DF79300A3D246 /* SVDB */ = { isa = XCSwiftPackageProductDependency; package = 17C2FE892A7DF79300A3D246 /* XCRemoteSwiftPackageReference "SVDB" */; productName = SVDB; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 177872C52A7DF0FE00D52548 /* Project object */; } ================================================ FILE: SVDBDemo/SVDBDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: SVDBDemo/SVDBDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: SVDBDemo/SVDBDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved ================================================ { "pins" : [ { "identity" : "svdb", "kind" : "remoteSourceControl", "location" : "git@github.com:Dripfarm/SVDB.git", "state" : { "revision" : "cd44e9db1786b811277db1b151825e714b1995ac", "version" : "1.0.1" } } ], "version" : 2 } ================================================ FILE: Sources/SVDB/API/Collection.swift ================================================ // // File.swift // // // Created by Jordan Howlett on 8/4/23. // import Accelerate import CoreML import NaturalLanguage @available(macOS 10.15, *) @available(iOS 13.0, *) public class Collection { private var documents: [UUID: Document] = [:] private let name: String init(name: String) { self.name = name } public func addDocument(id: UUID? = nil, text: String, embedding: [Double]) { let document = Document( id: id ?? UUID(), text: text, embedding: embedding ) documents[document.id] = document save() } public func addDocuments(_ docs: [Document]) { docs.forEach { documents[$0.id] = $0 } save() } public func removeDocument(byId id: UUID) { documents[id] = nil save() } public func search( query: [Double], num_results: Int = 10, threshold: Double? = nil ) -> [SearchResult] { let queryMagnitude = sqrt(query.reduce(0) { $0 + $1 * $1 }) var similarities: [SearchResult] = [] for document in documents.values { let id = document.id let text = document.text let vector = document.embedding let magnitude = sqrt(vector.reduce(0) { $0 + $1 * $1 }) let similarity = MathFunctions.cosineSimilarity(query, vector, magnitudeA: queryMagnitude, magnitudeB: magnitude) if let thresholdValue = threshold, similarity < thresholdValue { continue } similarities.append(SearchResult(id: id, text: text, score: similarity)) } return Array(similarities.sorted(by: { $0.score > $1.score }).prefix(num_results)) } private func save() { let svdbDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("SVDB") try? FileManager.default.createDirectory(at: svdbDirectory, withIntermediateDirectories: true, attributes: nil) let fileURL = svdbDirectory.appendingPathComponent("\(name).json") do { let encodedDocuments = try JSONEncoder().encode(documents) let compressedData = try (encodedDocuments as NSData).compressed(using: .zlib) try compressedData.write(to: fileURL) } catch { print("Failed to save documents: \(error.localizedDescription)") } } public func load() throws { let svdbDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("SVDB") let fileURL = svdbDirectory.appendingPathComponent("\(name).json") // Check if file exists guard FileManager.default.fileExists(atPath: fileURL.path) else { print("File does not exist for collection \(name), initializing with empty documents.") documents = [:] return } do { let compressedData = try Data(contentsOf: fileURL) let decompressedData = try (compressedData as NSData).decompressed(using: .zlib) documents = try JSONDecoder().decode([UUID: Document].self, from: decompressedData as Data) print("Successfully loaded collection: \(name)") } catch { print("Failed to load collection \(name): \(error.localizedDescription)") throw CollectionError.loadFailed(error.localizedDescription) } } public func clear() { documents.removeAll() save() } } ================================================ FILE: Sources/SVDB/API/SVDB.swift ================================================ import Accelerate import CoreML import NaturalLanguage @available(macOS 10.15, *) @available(iOS 13.0, *) public class SVDB { public static let shared = SVDB() private var collections: [String: Collection] = [:] private init() {} public func collection(_ name: String) throws -> Collection { if collections[name] != nil { throw SVDBError.collectionAlreadyExists } let collection = Collection(name: name) collections[name] = collection try collection.load() return collection } public func getCollection(_ name: String) -> Collection? { return collections[name] } public func releaseCollection(_ name: String) { collections[name] = nil } public func reset() { for (_, collection) in collections { collection.clear() } collections.removeAll() } } ================================================ FILE: Sources/SVDB/Internals/Models/Document.swift ================================================ // // File.swift // // // Created by Jordan Howlett on 8/4/23. // import Foundation public struct Document: Codable, Identifiable { public let id: UUID public let text: String public let embedding: [Double] public let magnitude: Double public init(id: UUID? = nil, text: String, embedding: [Double]) { self.id = id ?? UUID() self.text = text self.embedding = embedding self.magnitude = sqrt(embedding.reduce(0) { $0 + $1 * $1 }) } } ================================================ FILE: Sources/SVDB/Internals/Models/Errors.swift ================================================ // // File.swift // // // Created by Jordan Howlett on 8/4/23. // import Foundation public enum SVDBError: Error { case collectionAlreadyExists } public enum CollectionError: Error { case fileNotFound case loadFailed(String) } ================================================ FILE: Sources/SVDB/Internals/Models/SearchResult.swift ================================================ // // File.swift // // // Created by Jordan Howlett on 8/4/23. // import Foundation public struct SearchResult { public let id: UUID public let text: String public let score: Double } ================================================ FILE: Sources/SVDB/Internals/TheMathFile.swift ================================================ // // MathFunctions.swift // import Accelerate enum MathFunctions { static func cosineSimilarity(_ a: [Double], _ b: [Double], magnitudeA: Double, magnitudeB: Double) -> Double { var result = 0.0 vDSP_dotprD(a, 1, b, 1, &result, vDSP_Length(a.count)) return result / (magnitudeA * magnitudeB) } static func euclideanDistance(_ a: [Double], _ b: [Double]) -> Double { var differences = [Double](repeating: 0.0, count: a.count) vDSP_vsubD(a, 1, b, 1, &differences, 1, vDSP_Length(a.count)) var squaredDifferences = [Double](repeating: 0.0, count: a.count) vDSP_vsqD(differences, 1, &squaredDifferences, 1, vDSP_Length(a.count)) var sumOfSquaredDifferences = 0.0 vDSP_sveD(squaredDifferences, 1, &sumOfSquaredDifferences, vDSP_Length(a.count)) return sqrt(sumOfSquaredDifferences) } } ================================================ FILE: Tests/SVDBTests/CollectionTests.swift ================================================ // // SwiftUIView.swift // // // Created by Jordan Howlett on 8/4/23. // @testable import SVDB import XCTest class SVDBCollectionTests: XCTestCase { override func setUp() { super.setUp() SVDB.shared.reset() } override func tearDown() { super.tearDown() SVDB.shared.reset() try! SVDB.shared.collection("test").clear() } func testAddDocument_WithProvidedID() { let collection = Collection(name: "test") let id = UUID() let text = "Test text awesome 2" let embedding = [1.0, 2.0, 3.0] collection.addDocument(id: id, text: text, embedding: embedding) let results = collection.search(query: embedding, num_results: 5) XCTAssertEqual(results.count, 1) XCTAssertEqual(results.first?.text, text) } func testAddDocument_Duplicate() { let collection = Collection(name: "test") let id = UUID() let text = "Test text awesome 2" let expected = "Test text we expect" let embedding = [1.0, 2.0, 3.0] collection.addDocument(id: id, text: text, embedding: embedding) collection.addDocument(id: id, text: expected, embedding: embedding) let results = collection.search(query: embedding, num_results: 5) XCTAssertEqual(results.count, 1, "Documents with duplicate ids should be overwritten") XCTAssertEqual(results.first?.text, expected) } func testAddDocument_WithoutProvidedID() { let collection = Collection(name: "test") let text = "Test text Awesome" let embedding = [1.0, 2.0, 3.0] collection.addDocument(id: nil, text: text, embedding: embedding) let results = collection.search(query: embedding, num_results: 5) XCTAssertEqual(results.count, 1) XCTAssertEqual(results.first?.text, text) } func testAddDocument_MagnitudeCalculation() { let collection = Collection(name: "test") let embedding = [3.0, 4.0] collection.addDocument(id: nil, text: "text", embedding: embedding) let results = collection.search(query: embedding, num_results: 5) XCTAssertEqual(results.count, 1) XCTAssertEqual(results.first?.text, "text") } func testAddDocuments() { let svdb = SVDB.shared SVDB.shared.reset() let collectionName = "test" let collection = try! svdb.collection(collectionName) let document1 = Document(id: UUID(), text: "test1", embedding: [1.0, 2.0, 3.0]) let document2 = Document(id: UUID(), text: "test2", embedding: [4.0, 5.0, 6.0]) let query = [2.5, 3.5, 4.5] collection.addDocuments([document1, document2]) let searchResults = collection.search(query: query, num_results: 5) let resultTexts = searchResults.map { $0.text } XCTAssertTrue(resultTexts.contains(document1.text)) XCTAssertTrue(resultTexts.contains(document2.text)) XCTAssertTrue(resultTexts.count == 2) } func testRemoveDocument_Existing() { let collection = Collection(name: "test") let id1 = UUID() let id2 = UUID() let document1 = Document(id: id1, text: "test1", embedding: [1.0, 2.0, 3.0]) let document2 = Document(id: id2, text: "test2", embedding: [4.0, 5.0, 6.0]) collection.addDocuments([document1, document2]) XCTAssertFalse(collection.search(query: []).isEmpty, "There should be a hit for this query") collection.removeDocument(byId: id1) XCTAssertEqual(collection.search(query: []).count, 1) collection.removeDocument(byId: id2) XCTAssertEqual(collection.search(query: []).count, 0) } } ================================================ FILE: Tests/SVDBTests/SVDBTests.swift ================================================ @testable import SVDB import XCTest final class SVDBTests: XCTestCase { override func setUp() { super.setUp() SVDB.shared.reset() } func testCreateCollection_Success() { let name = "uniqueCollectionName" do { _ = try SVDB.shared.collection(name) } catch { XCTFail("Unexpected error: \(error)") } } func testCreateCollection_CollectionAlreadyExists() { let name = "existingCollectionName" do { _ = try SVDB.shared.collection(name) print("created") } catch { XCTFail("Unexpected error: \(error)") } do { _ = try SVDB.shared.collection(name) XCTFail("Expected collectionAlreadyExists error, but no error was thrown.") } catch let error as SVDBError { XCTAssertEqual(error, SVDBError.collectionAlreadyExists) } catch { XCTFail("Unexpected error: \(error)") } } func testRemoveDocument() throws { let svdb = SVDB.shared let collectionName = "test" let collection = try svdb.collection(collectionName) let document1 = Document(id: UUID(), text: "test1", embedding: [1.0, 2.0, 3.0]) let document2 = Document(id: UUID(), text: "test2", embedding: [4.0, 5.0, 6.0]) collection.addDocuments([document1, document2]) collection.removeDocument(byId: document1.id) let query = [2.5, 3.5, 4.5] let searchResults = collection.search(query: query, num_results: 2) let resultIds = searchResults.map { $0.id } XCTAssertFalse(resultIds.contains(document1.id)) XCTAssertTrue(resultIds.contains(document2.id)) } } ================================================ FILE: Tests/SVDBTests/SearchTests.swift ================================================ // // SwiftUIView.swift // // // Created by Jordan Howlett on 8/4/23. // @testable import SVDB import XCTest class SVDBSearchTests: XCTestCase { override func setUp() { super.setUp() SVDB.shared.reset() } func testSearchWithDefaultParameters() throws { let svdb = SVDB.shared let collectionName = "test" let collection = try svdb.collection(collectionName) let documents = [ Document(id: UUID(), text: "test1", embedding: [1.0, 2.0, 3.0]), Document(id: UUID(), text: "test2", embedding: [4.0, 5.0, 6.0]) ] collection.addDocuments(documents) let query = [2.5, 3.5, 4.5] let results = collection.search(query: query) XCTAssertEqual(results.count, 2) } func testSearchWithNumResults() throws { let svdb = SVDB.shared let collectionName = "test" let collection = try svdb.collection(collectionName) let documents = [ Document(id: UUID(), text: "test1", embedding: [1.0, 2.0, 3.0]), Document(id: UUID(), text: "test2", embedding: [4.0, 5.0, 6.0]) ] collection.addDocuments(documents) let query = [2.5, 3.5, 4.5] let results = collection.search(query: query, num_results: 1) XCTAssertEqual(results.count, 1) } func testSearchWithThreshold() throws { let svdb = SVDB.shared let collectionName = "test" let collection = try svdb.collection(collectionName) let documents = [ Document(id: UUID(), text: "test1", embedding: [900.0, 5000.0, 13.0]), Document(id: UUID(), text: "test2", embedding: [4.0, 5.0, 6.0]) ] collection.addDocuments(documents) let query = [4.0, 5.0, 6.0] let results = collection.search(query: query, threshold: 0.9) XCTAssertEqual(results.count, 1) } }