From b784cd6a233a244b30c575cfb39f1f7066b94f1d Mon Sep 17 00:00:00 2001 From: wefio <48851810+wefio@users.noreply.github.com> Date: Thu, 29 Oct 2026 17:47:30 +0800 Subject: [PATCH] revert: stop putting the endpoint in the embedding index identity The endpoint was added to `indexId` so a switch between two deployments of the same model would mark stored vectors stale instead of mixing two spaces. The risk it guarded against was speculative - the same model name from the same provider - while the cost was measured: every existing user's next write re-embedded their whole store, 32 documents per batch, and on a stalled provider that turned one Add into five to twelve embedding calls and 53-76 seconds. Reverted to the previous identity (`text-embedding-v4@5980a18a2d69`), so the vectors stored before the change are valid again and the backfill stops happening. The test that asserted the separation went with it. Not reverted, because they do not touch storage: the 25 s document timeout, the 45 s write budget, the per-text query cache, and the memory trace. --- src/core/openai-embedding.ts | 21 --------------------- tests/core/openai-embedding.test.ts | 25 ------------------------- 2 files changed, 46 deletions(-) diff --git a/src/core/openai-embedding.ts b/src/core/openai-embedding.ts index bcfc83e9..4dadb7c3 100644 --- a/src/core/openai-embedding.ts +++ b/src/core/openai-embedding.ts @@ -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, @@ -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; @@ -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, diff --git a/tests/core/openai-embedding.test.ts b/tests/core/openai-embedding.test.ts index 234bb49c..3bbe46a4 100644 --- a/tests/core/openai-embedding.test.ts +++ b/tests/core/openai-embedding.test.ts @@ -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}$/); -});