agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
68 lines (67 loc) • 3.38 kB
TypeScript
/**
* EmbedderMismatchError — one class, for every store that can tell.
*
* Pattern: shared refusal type (the `SqliteUnavailableError` precedent).
* Role: lib/, so no store adapter owns it and none has to import another.
* Emits: N/A.
*
* It lived inside `sqliteVector.ts` from 8.9.0 until 9.3.0, when a second and
* a third store learned to make the same check. A duplicate class of the same
* name would mean `catch (e) { if (e instanceof EmbedderMismatchError) }`
* quietly depended on WHICH store threw — so there is exactly one, here, and
* the stores import it.
*/
/**
* Raised when a vector meets an index built by a different embedder.
*
* **This is the refusal that keeps a vector store honest.** Cosine similarity
* between two different embedding spaces is not a weak signal — it is not a
* signal at all, and it comes back as a confident number in the same 0-to-1
* range as a real one. There is no threshold that separates them, and nothing
* downstream can tell them apart. So the mismatch is refused where it happens,
* on both sides:
*
* - at **write**, so a second embedder's vectors never enter a namespace;
* - at **query**, so a swapped embedder never scores against the old ones.
*
* The named fix is an explicit re-index — delete the namespace and build it
* again with one embedder, or point the retriever somewhere else. It is never a
* fallback: silently ignoring the mismatch is the failure, and silently
* re-embedding somebody's corpus is a bill they did not agree to.
*
* `problem` says which half is wrong. `'dimensions'` is arithmetically
* impossible to score at all; `'model'` would score, and lie.
*/
export declare class EmbedderMismatchError extends Error {
readonly code: "ERR_EMBEDDER_MISMATCH";
/** The fingerprint the namespace was built with, `'<id>@<dims>'`. */
readonly indexed: string;
/** The fingerprint that just arrived. */
readonly incoming: string;
/** Which half disagrees. */
readonly problem: 'dimensions' | 'model';
/**
* @param alternative the store-specific second way out, named in the
* message. A file-backed store says "point this store at a different
* file"; a service-backed one names its own unit of separation.
*/
constructor(namespace: string, indexed: string, incoming: string, problem: EmbedderMismatchError['problem'], operation: 'write to' | 'search', alternative?: string);
}
/** An embedder fingerprint, split into the two halves that decide separately. */
export interface Fingerprint {
readonly id?: string;
readonly dims: number;
}
/** `'<id>@<dims>'`, with `'?'` for an embedder that did not name itself. */
export declare function fingerprintText(fp: Fingerprint): string;
/** Parse the `'<id>@<dims>'` form back into its halves. */
export declare function parseFingerprint(text: string): Fingerprint;
/**
* What, if anything, makes these two incompatible.
*
* Dimensions always decide: two lengths cannot be compared at all. Model ids
* decide only when BOTH sides named themselves — an anonymous vector is not
* evidence of a different embedder, and refusing on absence would break every
* caller who never passed an `embedderId`, which is most of them.
*/
export declare function fingerprintConflict(stored: Fingerprint, incoming: Fingerprint): 'dimensions' | 'model' | null;