/**
 * SearchableEmbeddingCache — ADR-121 Phase 2.
 *
 * The legacy `PersistentEmbeddingCache` and the in-process `LRUCache`
 * only support exact-key lookup. That's fine for "I've seen this
 * exact text before" but useless for "I haven't computed this exact
 * text but I have something within ε" — which is the whole point of
 * a vector cache for embedding-heavy workloads.
 *
 * This module adds an HNSW-backed cache implementation that:
 *   - Implements the same key-value contract as the legacy cache
 *     (back-compat: existing callers don't change)
 *   - Adds `search(query, k)` and `searchByText(text, k, embed)` for
 *     ANN retrieval over the cached vectors
 *   - Lazy-loads `@ruvector/core` as an optional peer dep; falls back
 *     to in-memory exact-only when the dep isn't installed
 *
 * Phase 2 acceptance:
 *   - ≥0.95 near-duplicate recall (where exact key match would be 0)
 *   - ≥10k inserts/sec on the standard test machine
 *   - Legacy PersistentEmbeddingCache unchanged
 */
import type { EmbeddingResult } from './types.js';
/**
 * Search result row.
 *
 * `score` is a **similarity** score: higher = more similar, identical
 * vectors → 1.0. The cache normalizes the raw distance metric returned
 * by `@ruvector/core` (which returns cosine *distance* and Euclidean
 * *distance*) into the similarity convention used elsewhere in this
 * package (e.g. `cosineSimilarity()`).
 *
 * Conversion table:
 *   cosine     : similarity = 1 - distance
 *   dot-product: similarity = -distance  (already in dot-product space)
 *   euclidean  : similarity = 1 / (1 + distance)  (monotonic, bounded [0,1])
 *   manhattan  : similarity = 1 / (1 + distance)
 */
export interface CacheSearchHit {
    /** Stable opaque id assigned to the cached entry. */
    readonly id: string;
    /** The cached embedding vector. */
    readonly vector: Float32Array;
    /** The original text the vector embeds, when available. */
    readonly text?: string;
    /** Similarity score in [0, 1] (higher = more similar). */
    readonly score: number;
}
export interface SearchableCacheOptions {
    /** Embedding dimension. Required — must match the producer service. */
    readonly dimension: number;
    /** Distance metric (default 'cosine'). */
    readonly metric?: 'cosine' | 'euclidean' | 'dot-product' | 'manhattan';
    /** Max in-memory entries (default 10,000). */
    readonly maxSize?: number;
    /**
     * Quantization mode for cached vectors (ADR-121 Phase 3, alpha.21):
     *   'none' (default) — full float32 in memory
     *   'int8'           — per-vector int8 + scale, 4× memory reduction,
     *                      recall ≥0.98 on unit-normalized vectors
     * RaBitQ (1-bit, 32× reduction) is a separate batch API; see
     * `RabitqSnapshot` for that path — it doesn't fit the streaming
     * insert/get/delete contract this cache exposes.
     */
    readonly quantize?: 'none' | 'int8';
}
export declare class SearchableEmbeddingCache {
    private backing;
    private degraded;
    private readonly options;
    /** Map from cache key (text or content hash) to the assigned vector id. */
    private readonly keyToId;
    /** Reverse map from vector id back to the original text key. */
    private readonly idToKey;
    /**
     * Per-id int8 store. Only populated when `quantize === 'int8'`. The
     * cache continues to hand back float32 vectors to callers (dequantized
     * on demand) — quantization is a memory-layout concern transparent
     * to the IEmbeddingService contract.
     */
    private readonly int8Store;
    private nextId;
    private hits;
    private misses;
    constructor(options: SearchableCacheOptions);
    /** Lazy-initialize the HNSW backing. Idempotent. */
    initialize(): Promise<void>;
    /** Cache the embedding under the given text key. */
    set(key: string, vector: Float32Array, opts?: {
        id?: string;
    }): Promise<void>;
    /** Exact-key lookup. Returns the cached vector or undefined. */
    get(key: string): Promise<Float32Array | undefined>;
    /** Whether the exact key is cached. */
    has(key: string): Promise<boolean>;
    /** Remove a cached entry by key. */
    delete(key: string): Promise<boolean>;
    /**
     * ANN search over the cached embeddings. Returns top-k neighbors
     * by similarity, sorted descending by score (higher = more similar).
     */
    search(query: Float32Array, k?: number): Promise<CacheSearchHit[]>;
    /**
     * Normalize whatever the backing returns (Float32Array, number[],
     * or an object-as-array from a NAPI bridge) into a Float32Array.
     */
    private toFloat32Array;
    /**
     * Embed a text query (via the supplied embedder), then ANN search
     * for nearest cached neighbors. Convenience helper so callers
     * don't have to wire embed → search manually.
     */
    searchByText(text: string, k: number, embed: (text: string) => Promise<EmbeddingResult>): Promise<CacheSearchHit[]>;
    /**
     * Snapshot of cache stats — size, hit rate, backing status.
     *
     * Size is read from the local id-map (synchronous) instead of the
     * backing's `len()` because the real `@ruvector/core` returns a
     * Promise for that. The id-map stays in lockstep with the backing
     * through `set`/`delete`/`clear`/`maybeEvict`, so this is exact.
     */
    stats(): {
        size: number;
        hits: number;
        misses: number;
        hitRate: number;
        degraded: boolean;
        metric: string;
        dimension: number;
        quantize: 'none' | 'int8';
        estimatedBytes: number;
    };
    /** Clear all cached entries. */
    clear(): Promise<void>;
    /**
     * FIFO eviction when over maxSize. Async because the backing's
     * `delete` may be async (the real NAPI binding returns a Promise).
     */
    private maybeEvict;
}
/**
 * Factory — keeps the constructor a one-liner and matches the
 * createXxxService convention used elsewhere in the package.
 */
export declare function createSearchableEmbeddingCache(options: SearchableCacheOptions): SearchableEmbeddingCache;
//# sourceMappingURL=searchable-embedding-cache.d.ts.map