/** * The semantic embedding pass. * * Embeds one vector per file so the index can answer questions phrased in the * problem's vocabulary rather than the code's — "where do we back off after a * 429" instead of `retryAfterMsFromHeaders`. * * ## What gets embedded, and why it is the file * * A bare declaration is poor material for an embedding: `function resolve(id: * string): Widget` carries almost nothing a lexical index does not already * have, and BM25 over FTS5 already matches it better. What carries meaning is * the concept layer's description of what a file is *for*, so that is the text * this pass embeds, falling back to the file's declaration names when no * summary exists yet. * * That also makes it eight times cheaper than a symbol-level pass on this * repository — eight thousand files against sixty-six thousand symbols. * * ## Why it is a pass and not part of indexing * * Model inference is asynchronous and batched. The indexer's write path is a * synchronous SQLite transaction; running inference inside it would hold a * write lock open across thousands of inferences. The older char-trigram * embedding could live there precisely because it was neither of those things * — and it was also not semantic, which is why it is being superseded rather * than extended. * * ## Why the vectors never silently mix * * Two models produce incomparable spaces. The provider id is stored with every * row and checked before a pass; a change wipes the table and re-embeds rather * than leaving half the index in one space and half in another. */ import { type IndexStore } from './writer.js'; /** * The host-supplied embedding model. * * Structurally identical to the `EmbeddingProvider` that * `packages/vector-memory` already defines, so its transformers-backed * implementation satisfies this without an adapter. Injected rather than * imported for the same reason the summariser is: `packages/tools` must not * depend on an optional native model runtime. */ export interface EmbeddingPort { /** Stable id including the model and quantisation. Changing it re-embeds. */ readonly id: string; /** Vector length. Must not change for a given `id`. */ readonly dimensions: number; /** Embed a batch. Must return exactly one vector per input, in order. */ embed(texts: string[]): Promise; } /** Texts sent per inference call. */ export declare const DEFAULT_BATCH_SIZE = 16; /** Ceiling on embedded text. Beyond this a summary is not a summary. */ export declare const MAX_EMBED_CHARS = 2000; export interface EmbedOptions { /** Stop after this many files. */ maxFiles?: number | undefined; batchSize?: number | undefined; /** Re-embed even when the stored text hash still matches. */ force?: boolean | undefined; signal?: AbortSignal | undefined; onProgress?: ((done: number, total: number) => void) | undefined; } export interface EmbedResult { embedded: number; /** Files whose embedded text was unchanged. */ cached: number; /** Vectors dropped because their file left the index. */ pruned: number; /** True when a provider change forced a full re-embed. */ providerChanged: boolean; /** Files with no summary, embedded from their declaration names instead. */ fromDeclarations: number; durationMs: number; errors: string[]; } /** * Run one embedding pass. Never throws: an inference failure costs that batch * its vectors, is recorded in `errors`, and the walk continues. */ export declare function embedFiles(store: IndexStore, port: EmbeddingPort, relativeOf: (file: string) => string, options?: EmbedOptions): Promise; /** Reported when a project has no index to embed. */ export type EmbedIndexMissing = { indexed: false; }; /** * Embed a project's files, owning the store lifetime so callers outside this * package never touch `indexStorePool`. Refuses to run without an index. */ export declare function embedProjectFiles(projectRoot: string, port: EmbeddingPort, options?: EmbedOptions & { indexDir?: string | undefined; }): Promise; //# sourceMappingURL=embedding-pass.d.ts.map