VecturaKit

repository·main·Indexed 43 days ago

https://github.com/rryam/vecturakit

A Swift-based vector database for on-device applications providing local vector storage, indexing, and hybrid search (combining vector similarity with BM25 text search). It supports pluggable embeddings via VecturaEmbedder, including integrations for Apple's NaturalLanguage framework, OpenAI-compatible endpoints, and MLX for GPU acceleration. Optimized for Apple Silicon on macOS 14.0+, it supports macOS 15.0+, iOS 18.0+, tvOS 18.0+, visionOS 2.0+, and watchOS 11.0+.

Tokens
13.1K
Snippets
38
Records
57
Agent score
56%

What's inside VecturaKit

  1. Overview of Performance Test Suites

    main

    VecturaKit provides several specialized test suites to help developers evaluate performance, scalability, memory usage, and accuracy across different configurations.

    Available Suites

    1. BenchmarkSuite: Establishes performance baselines for different strategies (e.g., fullMemory, indexed, automatic) and dataset sizes.
    2. ScalabilitySuite: Analyzes how performance and memory usage scale as datasets grow from small to large.
    3. MemoryProfilerSuite: Provides deep analysis of memory usage throughout the lifecycle (initialization, adding documents, and searching).
    4. ParameterTuningSuite: Helps find optimal configurations for indexed mode by sweeping parameters like candidateMultiplier, batchSize, and maxConcurrentBatches.
    5. AccuracyTests: Validates search quality (Recall@10) in indexed mode compared to fullMemory mode.
    6. RealisticWorkloadSuite (Opt-in): Stress tests the system with production-like characteristics, including cold-starts, multi-client pressure, and mixed read/write traffic.
  2. Overview of VecturaKit

    main

    VecturaKit is a Swift-based vector database designed for on-device applications. It provides local vector storage, indexing, and hybrid search (combining vector similarity with BM25 text search).

    Key features include:

    • Pluggable Embeddings: Use any provider conforming to VecturaEmbedder (e.g., OpenAICompatibleEmbedder, NLContextualEmbedder, SwiftEmbedder, or MLXEmbedder).
    • Hybrid Search: Combines vector and text search.
    • On-Device Storage: Local management of embeddings with persistent storage.
    • Auto-Dimension Detection: Automatically detects dimensions from the configured embedder.
    • Memory Management: Supports automatic, full-memory, or indexed modes for varying dataset sizes.
    • Batch Processing: Parallel indexing for faster ingestion.
  3. Choose a memory strategy for VecturaKit

    main

    VecturaKit provides different memory strategies via VecturaConfig to balance performance and memory usage based on your dataset size.

    • automatic (Default): Intelligently selects between fullMemory and indexed strategies. Use this for most variable workloads.
    • fullMemory: Loads all documents into RAM at initialization. This provides zero I/O during search and extremely low latency (<15ms), but memory usage scales linearly (~187 KB per document). Recommended for datasets up to 10,000 documents.
    • indexed: Designed for larger datasets to keep memory usage low. Note that the current FileStorageProvider implementation falls back to fullMemory. For true indexed behavior, a custom IndexedVecturaStorage provider is required.
    // Use the automatic strategy (default)
    VecturaConfig(name: "my-db")
    
    // For known small datasets (< 10K docs) to ensure maximum speed
    VecturaConfig(name: "my-db", memoryStrategy: .fullMemory)
    
    // For large datasets (requires IndexedVecturaStorage implementation)
    VecturaConfig(
        name: "large-db",
        memoryStrategy: .indexed(
            candidateMultiplier: 10,
            batchSize: 100,
            maxConcurrentBatches: 4
        )
    )
  4. Understand VecturaKit performance baselines

    main

    For small datasets (approximately 1,000 documents) using the fullMemory strategy, VecturaKit provides the following performance characteristics:

    • Latency: ~10.43 ms average, ~11.37 ms P99.
    • Memory Usage: ~182.80 MB peak (~187.18 KB per document).
    • Throughput: ~178 documents per second.
    • Initialization: ~0.54 ms.

    Usage Recommendation:

    • The current implementation is considered Production Ready for datasets containing fewer than 10,000 documents.
    • For larger datasets, implementation of IndexedVecturaStorage (SQLite-based) is required to handle scale efficiently.
  5. Understand Accuracy Trade-offs in Indexed Mode

    main

    In indexed mode, search accuracy (Recall) is traded for speed. While current tests use MockIndexedStorage (which achieves 100% recall by computing exact similarities), production Approximate Nearest Neighbor (ANN) algorithms like HNSW or IVF will have lower recall.

    Estimated Recall vs Latency for Production ANN:

    MultiplierRecall@10Avg Latency
    560-70%~30 ms
    1080-90%~40 ms
    1590-95%~50 ms
    2095-99%~60 ms
  6. Implement a Custom Storage Provider with VecturaStorage

    main

    To integrate with external storage systems like SQLite, Core Data, or cloud storage, implement the VecturaStorage protocol. You must provide implementations for creating the storage directory, loading, saving, deleting, and updating documents.

    import Foundation
    import VecturaKit
    
    final class MyCustomStorageProvider: VecturaStorage {
        private var documents: [UUID: VecturaDocument] = [:]
    
        func createStorageDirectoryIfNeeded() async throws {
            // Initialize your storage system
        }
    
        func loadDocuments() async throws -> [VecturaDocument] {
            return Array(documents.values)
        }
    
        func saveDocument(_ document: VecturaDocument) async throws {
            documents[document.id] = document
        }
    
        func deleteDocument(withID id: UUID) async throws {
            documents.removeValue(forKey: id)
        }
    
        func updateDocument(_ document: VecturaDocument) async throws {
            documents[document.id] = document
        }
    
        func getTotalDocumentCount() async throws -> Int {
            return documents.count
        }
    }
    
    // Usage
    let config = VecturaConfig(name: "my-db")
    let customStorage = MyCustomStorageProvider()
    let vectorDB = try await VecturaKit(config: config, storageProvider: customStorage)
  7. Choose a memory management strategy in VecturaKit

    main

    VecturaKit provides three memory management strategies via VecturaConfig.memoryStrategy to balance search speed and memory usage:

    1. Automatic Mode (Recommended): Automatically switches strategies based on dataset size.
      • < 10,000 documents: Uses fullMemory mode.
      • ≥ 10,000 documents: Uses indexed mode (requires a compatible storage provider).
    2. Full Memory Mode: Loads all documents into RAM. Best for small to medium datasets (< 100,000 documents) where sub-10ms latency is required and memory is available.
    3. Indexed Mode: Uses on-demand loading and storage-layer indexing. Best for very large datasets (> 100,000 documents) or memory-constrained environments.
    // Automatic strategy (default)
    let config = VecturaConfig(name: "my-database")
    let vectura = try await VecturaKit(config: config)
    
    // Explicit Full Memory Mode
    let configFull = VecturaConfig(name: "my-database", memoryStrategy: .fullMemory)
    let vecturaFull = try await VecturaKit(config: configFull)
    
    // Explicit Indexed Mode
    let configIndexed = VecturaConfig(name: "my-database", memoryStrategy: .indexed(candidateMultiplier: 4))
    let vecturaIndexed = try await VecturaKit(config: configIndexed)
  8. Understand VecturaKit memory management strategies

    main

    VecturaKit offers different memory management strategies to balance performance and resource usage. You can choose between:

    • automatic: The default strategy managed by the system.
    • fullMemory: Keeps all data in memory for maximum speed. Note that this uses approximately 180-200 KB per document.
    • indexed: Optimized for larger datasets by using indexed storage.

    For detailed implementation details on the IndexedVecturaStorage protocol and how to choose the right strategy for datasets larger than 10K documents, refer to the INDEXED_STORAGE_GUIDE.md.

  9. Implement a Custom Search Engine with VecturaSearchEngine

    main

    If you need specialized search algorithms (e.g., pure vector, pure text, or custom ranking), implement the VecturaSearchEngine protocol. The search method receives the SearchQuery, the VecturaStorage instance, and SearchOptions to perform the lookup.

    import Foundation
    import VecturaKit
    
    struct MyCustomSearchEngine: VecturaSearchEngine {
        func search(
            query: SearchQuery,
            storage: VecturaStorage,
            options: SearchOptions
        ) async throws -> [VecturaSearchResult] {
            let documents = try await storage.loadDocuments()
    
            guard case .text(let queryText) = query else {
                return []
            }
    
            let results = documents.filter { doc in
                doc.text.lowercased().contains(queryText.lowercased())
            }.map { doc in
                VecturaSearchResult(
                    id: doc.id,
                    text: doc.text,
                    score: 1.0,
                    createdAt: doc.createdAt
                )
            }
    
            return Array(results.prefix(options.numResults))
        }
    
        func indexDocument(_ document: VecturaDocument) async throws {}
        func removeDocument(id: UUID) async throws {}
    }
    
    // Usage
    let vectorDB = try await VecturaKit(
        config: config, 
        embedder: embedder, 
        searchEngine: MyCustomSearchEngine()
    )
  10. Understand Accuracy Test Recall Behavior

    main

    If AccuracyTests report 100% recall, this is expected behavior when using MockIndexedStorage.

    Why? MockIndexedStorage computes exact cosine similarity for all documents and returns the top-N most similar, which results in perfect accuracy but does not represent real Approximate Nearest Neighbor (ANN) algorithms (like HNSW or IVF) used in production. Real ANN implementations typically have recall ranges of 85-98%.

    To test production accuracy: Implement a real IndexedVecturaStorage using an ANN library like FAISS, Annoy, or hnswlib.

  11. Integrate Apple's NaturalLanguage framework with VecturaKit

    main

    VecturaKit can use Apple's NaturalLanguage framework via NLContextualEmbedder for on-device, privacy-first contextual embeddings. This approach requires no external dependencies and supports multiple languages (English, Spanish, French, German, Italian, Portuguese, etc.).

    To use this integration, import both VecturaKit and VecturaNLKit.

    import VecturaKit
    import VecturaNLKit
  12. Use OpenAI-Compatible Embeddings

    main

    You can use OpenAICompatibleEmbedder to connect to local or hosted servers (like Ollama, LM Studio, or vLLM) that expose a /v1/embeddings endpoint. This requires importing VecturaOAIKit.

    import VecturaKit
    import VecturaOAIKit
    
    let config = VecturaConfig(name: "my-oai-vector-db", dimension: nil)
    
    let embedder = OpenAICompatibleEmbedder(
      baseURL: "http://localhost:1234/v1",
      model: "text-embedding-model",
      apiKey: nil,
      timeoutInterval: 120,
      retryAttempts: 2,
      retryBaseDelaySeconds: 1
    )
    
    let vectorDB = try await VecturaKit(config: config, embedder: embedder)