/**
* In-database vector storage + brute-force search.
*
* Design:
* - Vectors are stored as BLOB columns holding a little-endian Float32 buffer.
* Encoding: `encodeVector(Float32Array) -> Buffer`.
* - Each row additionally stores its squared L2 norm² (cached) so cosine
* similarity can be computed without recomputing sqrt on every query.
* - Search is brute-force: `SELECT id, vec, norm2 FROM
WHERE vec IS NOT NULL`
* then we compute cosine(q, v) in JS and keep top-K with a small heap.
* - We *intentionally* don't rely on sqlite-wasm or vss. Pure JS brute is
* ~1 M × 384 in <50ms on a laptop, which is plenty for local plugin use.
*
* When usage grows past, say, 100K rows per table, this module is the single
* place to swap in an ANN index (e.g. hnswlib-node or faiss).
*/
import type { EmbeddingVector } from "../types.js";
import type { StorageDb } from "./types.js";
/** Float32Array → Buffer (little-endian, zero-copy when possible). */
export declare function encodeVector(vec: EmbeddingVector): Buffer;
/** Buffer → Float32Array. Copies so callers can't mutate the underlying DB blob. */
export declare function decodeVector(buf: Buffer | Uint8Array | null | undefined): EmbeddingVector | null;
export declare function dot(a: EmbeddingVector, b: EmbeddingVector): number;
export declare function norm2(a: EmbeddingVector): number;
export declare function cosine(a: EmbeddingVector, b: EmbeddingVector): number;
/**
* Cosine similarity using pre-computed norm² of `b`. Saves one sqrt + one pass
* per candidate when the query side is fixed.
*/
export declare function cosinePrenormed(a: EmbeddingVector, aNorm: number, b: EmbeddingVector, bNorm2: number): number;
export interface VectorRow {
id: TId;
vec: EmbeddingVector;
/** Pre-computed L2 norm². If absent we compute + cache. */
norm2?: number;
meta?: TMeta;
}
export interface VectorHit {
id: TId;
score: number;
meta?: TMeta;
}
/**
* Brute-force top-K cosine search over an in-memory array of rows. Stable
* (ties ordered by input order). Mutates `rows[i].norm2` if it was missing.
*/
export declare function topKCosine(query: EmbeddingVector, rows: Array>, k: number): Array>;
export interface VectorScanOptions {
/** Name of the BLOB column holding the vector. */
vecColumn: string;
/** Name of the REAL column caching norm². If absent we compute per-row. */
norm2Column?: string;
/** Optional WHERE clause (without the "WHERE"). */
where?: string;
/** Parameters for the WHERE clause. */
params?: Record;
/** Optional LIMIT to cap candidates fetched from SQLite. */
hardCap?: number;
}
export interface ScanRow {
id: string;
vec: Buffer | null;
norm2?: number | null;
[k: string]: unknown;
}
/**
* Default LIMIT applied when the caller doesn't pass an explicit
* `hardCap`. Historically 100_000, which — combined with the old
* `.all()` materialisation — meant one accidental "no cap" call
* could pull 100k multi-KB vector BLOBs into JS memory in a single
* synchronous step. On the reporter's DB in issue #2076 that
* translated to 4.2 GB RSS and a 100 % CPU main-thread stall.
*
* 5_000 is a much safer default: it still covers realistic per-agent
* corpora and it forces callers that legitimately need more to opt
* in explicitly.
*/
export declare const DEFAULT_SCAN_HARD_CAP = 5000;
/**
* Stream rows from `table`, decode vectors, and run top-K cosine against
* `query`. `selectExtra` lets callers bring along columns that will surface in
* `VectorHit.meta`.
*
* Streaming: we use `.iterate()` (not `.all()`) so at most one row's
* BLOB is decoded at a time. The top-K min-heap keeps only `k`
* vectors of state, so peak RSS is O(k * dim) regardless of how many
* rows the LIMIT allows. Fixes the "load 100k BLOBs synchronously"
* pathology in #2076.
*/
export declare function scanAndTopK(db: StorageDb, table: string, selectExtra: string[], query: EmbeddingVector, k: number, opts: VectorScanOptions): Array>;
//# sourceMappingURL=vector.d.ts.map