Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions src/core/openai-embedding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,6 @@ export class OpenAIEmbeddingClient {
this.documentBody = options.documentBody ?? {};
this.indexId = indexIdentity({
model: this.model,
// The endpoint takes part in the identity. Two deployments of the same model name can
// serve different weights, and a vector from one is not comparable with a query
// embedded by the other - but nothing else in the identity would notice, so switching
// endpoints used to leave the stored vectors silently in place and mix two spaces.
// With it here, a switch makes the index stale and the existing backfill re-embeds.
providerEndpoint: endpointIdentity(options.baseUrl),
...(options.indexNamespace ? { indexNamespace: options.indexNamespace } : {}),
dimensions: this.dimensions,
documentTemplate: this.documentTemplate,
Expand Down Expand Up @@ -238,22 +232,8 @@ export class OpenAIEmbeddingClient {
* per-call bodies means existing embeddings cannot be reused. Serialisation order
* is part of the contract, so keep the spread order identical when editing.
*/
/** The part of the endpoint that identifies the deployment: scheme, host and path, without
* a trailing slash or any query, so an equivalent spelling of the same endpoint keeps the
* same identity and a genuinely different one does not. */
function endpointIdentity(baseUrl: string | undefined): string | null {
if (!baseUrl) return null;
try {
const url = new URL(baseUrl);
return `${url.protocol}//${url.host}${url.pathname.replace(/\/+$/, "")}`;
} catch {
return baseUrl.replace(/\/+$/, "");
}
}

function indexIdentity(input: {
model: string;
providerEndpoint?: string | null;
indexNamespace?: string;
dimensions?: number;
documentTemplate: string;
Expand All @@ -266,7 +246,6 @@ function indexIdentity(input: {
const fingerprint = createHash("sha256")
.update(
JSON.stringify({
...(input.providerEndpoint ? { providerEndpoint: input.providerEndpoint } : {}),
...(input.indexNamespace ? { indexNamespace: input.indexNamespace } : {}),
dimensions: input.dimensions ?? null,
documentTemplate: input.documentTemplate,
Expand Down
25 changes: 0 additions & 25 deletions tests/core/openai-embedding.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,28 +286,3 @@ test("a dropped connection is still retried", async () => {
);
}
});

test("the embedding index identity separates deployments of the same model", () => {
// Two regions of the same provider serve the same model name. A vector from one is not
// comparable with a query embedded by the other, and nothing else in the identity - model,
// dimensions, templates - differs, so the endpoint has to be part of it. Without this, a
// switch left every stored vector in place and silently mixed the two spaces.
const mainland = new OpenAIEmbeddingClient({
baseUrl: "https://embed.example.cn/compatible-mode/v1",
model: "text-embedding-v4",
dimensions: 1024,
});
const singapore = new OpenAIEmbeddingClient({
baseUrl: "https://embed.example.sg/compatible-mode/v1",
model: "text-embedding-v4",
dimensions: 1024,
});
const spelling = new OpenAIEmbeddingClient({
baseUrl: "https://embed.example.cn/compatible-mode/v1/",
model: "text-embedding-v4",
dimensions: 1024,
});
assert.notEqual(mainland.indexId, singapore.indexId, "a different endpoint is a different index");
assert.equal(spelling.indexId, mainland.indexId, "a trailing slash is the same endpoint");
assert.match(mainland.indexId, /^text-embedding-v4@[0-9a-f]{12}$/);
});
Loading