createFingerprint
createFingerprint({ canaries, modelId })
Embeds a set of canary probes and builds a fingerprint that records which model produced them. Store it next to the index.
Swap the model behind an index and every vector already in it still came from the old one, while new query vectors come from the new one. Scores stay plausible and nothing errors -- search just quietly gets worse.
embedguard answers one question: is the embedding model that produced this vector still the one this index was built with?
Fingerprint the model once, when the index is built. Then re-check on every query -- or on a schedule -- by re-embedding the same canary probes and comparing.
A same-dimension model swap is invisible to a dimension check. Cosine similarity between the canaries catches it.
import { createFingerprint, checkDrift } from "embedguard";
const fingerprint = await createFingerprint({
modelId: "text-embedding-3-small",
canaries: ["return policy", "shipping times"],
});
const { passed } = await checkDrift(fingerprint, freshCanaries, {
threshold: 0.92,
});
if (!passed) {
throw new Error("Embedding model drift detected");
}
createFingerprint({ canaries, modelId })
Embeds a set of canary probes and builds a fingerprint that records which model produced them. Store it next to the index.
checkDimension(fingerprint, embedding)
A cheap dimension check for the query path. Catches model swaps that change the vector size.
checkDrift(fingerprint, freshCanaries, { threshold })
Re-embeds the same canary probes and compares cosine similarity. Catches same-dimension model swaps.
When you change the embedding model behind a vector index, nothing breaks -- that's the problem. Vectors already in the index were produced by the old model; new query vectors come from the new one. The two live in different vector spaces, so cosine similarity between them is noise.
No error is thrown. Scores still look like scores. Search just gets quietly, imperceptibly worse.
$ embedguard check
index built with model-a
query embedded by model-b -- same dimension
scores 0.81 · 0.79 · 0.78
plausible. meaningless. no error thrown.
embedguard checkDrift() → model swap detected