Skip to content
embedguard

Catches silent embedding-model swaps under a vector index before they corrupt similarity search.

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?

$ npm install embedguard

Guard every query in three lines

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.

src/guard.ts
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");
}

Three functions, one job

01

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.

02

checkDimension

checkDimension(fingerprint, embedding)

A cheap dimension check for the query path. Catches model swaps that change the vector size.

03

checkDrift

checkDrift(fingerprint, freshCanaries, { threshold })

Re-embeds the same canary probes and compares cosine similarity. Catches same-dimension model swaps.

Why this exists

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

What embedguard doesn't do

  • Doesn't call any embedding provider itself.
    You produce the embeddings; embedguard verifies them.
  • Not a full ML observability platform.
    It answers one question and stays out of the way.
  • Doesn't reindex anything for you.
    When drift is detected, re-embedding is your call.

Add it before the next model swap

Free and open source under the MIT license.

$ npm install embedguard