feat(embedding): support dimensions for openai-compatible Matryoshka models (#405) - #406
feat(embedding): support dimensions for openai-compatible Matryoshka models (#405)#406GuJi08233 wants to merge 2 commits into
Conversation
…models (cortexkit#405) Add optional embedding.dimensions for openai-compatible provider (e.g. Qwen3-Embedding-8B 768/1024/4096). Plumbs through schema transform, resolveEmbeddingConfig, provider identity (conditional fold), chunk identity, provider transport (POST {dimensions}), and EMBEDDING_AFFECTING_KEYS. Omitted keeps byte-identical identity for existing configs; setting triggers re-embed via per-model coexistence. Fixes cortexkit#405
There was a problem hiding this comment.
1 issue found across 7 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/plugin/src/config/schema/magic-context.ts">
<violation number="1" location="packages/plugin/src/config/schema/magic-context.ts:695">
P1: When a `synapse` embedding uses its OpenAI-compatible fallback, preserve `data.dimensions` while constructing `fallbackConfig`; otherwise fallback requests use the provider-default vector width and produce vectors that do not match the configured embedding identity.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| ...(inputType ? { input_type: inputType } : {}), | ||
| ...(queryInputType ? { query_input_type: queryInputType } : {}), | ||
| ...(truncate ? { truncate } : {}), | ||
| ...(data.dimensions ? { dimensions: data.dimensions } : {}), |
There was a problem hiding this comment.
P1: When a synapse embedding uses its OpenAI-compatible fallback, preserve data.dimensions while constructing fallbackConfig; otherwise fallback requests use the provider-default vector width and produce vectors that do not match the configured embedding identity.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/plugin/src/config/schema/magic-context.ts, line 695:
<comment>When a `synapse` embedding uses its OpenAI-compatible fallback, preserve `data.dimensions` while constructing `fallbackConfig`; otherwise fallback requests use the provider-default vector width and produce vectors that do not match the configured embedding identity.</comment>
<file context>
@@ -683,6 +692,7 @@ export const EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data)
...(inputType ? { input_type: inputType } : {}),
...(queryInputType ? { query_input_type: queryInputType } : {}),
...(truncate ? { truncate } : {}),
+ ...(data.dimensions ? { dimensions: data.dimensions } : {}),
...(data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}),
};
</file context>
When provider is synapse with dimensions and it falls back to openai-compatible, fallbackConfig must carry dimensions into the fallback request and identity, otherwise the fallback uses the provider default (768) instead of the configured 4096. Co-authored-by: greptile-apps[bot]
There was a problem hiding this comment.
Thanks for this — the plumbing is the #127 / #155 / #259 fold, and the synapse-fallback follow-up (9f15915d) is the right fix for the greptile note. Omitted dimensions on the POST body is correct, and assets/magic-context.schema.json matches the generator.
Requesting changes before merge:
-
Omitted
dimensionsmust not changechunkModelId.getChunkEmbeddingModelIdalways insertsdimensions: ""for openai-compatible configs (project-embedding-registry.ts:590). That rewrites every existing remote chunk identity on upgrade and forces a full history-chunk backfill, which contradicts the PR's byte-identical claim. Spread conditionally, the same waygetEmbeddingProviderIdentityalready does for this field and fortruncate. Add a golden: default openai config (nodimensions) keeps today's chunk id; only a set value moves it. -
Refuse vectors whose returned length disagrees with the request. The served-model guard still only looks at
body.model. A provider that ignoresdimensionswill store native-width vectors under the requested-width identity; if it later honors the parameter, cosine scans mix widths and score 0 (cosine-similarity.ts:2-3). Synapse already pins width from the first embed and rejects drift (embedding-synapse.ts:846-862). openai-compatible needs the same: whendimensionsis set, drop the batch ifvector.length !== dimensions; pin the first observed length on the provider instance so a later drift cannot share the identity. Identity may include the requested number (so a config change rotates the space) but the gate that accepts a vector has to be the returned length. -
Tests, red-first, on those two properties. Extend
embedding-openai.test.ts"provider modelId matches canonical identity" with adimensionscase (and passdimensionsinto the constructor). Add the#259-style pair: omitted keeps the golden openai identity;4096vs1024vs omitted are distinct; after a dims change,loadAllEmbeddings/ chunk search under the new id do not return the old-width rows. Add a POST-body test: unset omits the key, set includes it. Add a 200-with-wrong-length test that expects nulls. -
Regenerate the docs reference with
bun packages/plugin/scripts/build-config-docs.ts.packages/plugin/scripts/build-config-docs.test.tsfails on this head. -
Plumb
dimensionsthroughprobeEmbeddingEndpointand the dashboard Test Connection, the same wayinput_type/truncatealready are, so doctor does not probe a different vector space than the live path.
Nits, not merge blockers once 1–4 land: drop the as unknown as { dimensions?: number } casts (config.dimensions already typechecks after an openai-compatible narrow); fix the "on either" comment in EMBEDDING_AFFECTING_KEYS. Dashboard parity can stay on the embedding prefix (no new OMITTED_BY_DESIGN row). Project config may keep dimensions as a tuning field next to model / truncate; destination stripping does not need to grow.
I am not asking this PR to invent an AFT semantic.dimensions key. AFT has none; it fingerprints discovered length. Send the OpenAI field when the user set it, and believe the length that came back.
Fixes #405
Summary
Add optional
embedding.dimensionsfor theopenai-compatibleprovider to support Matryoshka models likeQwen3-Embedding-8B(768/1024/2048/4096 viaPOST {dimensions}).Omitted keeps current behavior (byte-identical identity, no re-embed). Setting triggers per-model coexistence re-embed.
Changes
packages/plugin/src/config/schema/magic-context.ts:BaseEmbeddingConfigSchema.dimensions(max 8192) + include inEmbeddingConfigSchema.transformforopenai-compatible/synapseembedding-identity.ts:getEmbeddingProviderIdentityfolds...dimensions?{dimensions}:{}embedding.ts/project-embedding-registry.ts:resolveEmbeddingConfigpreservesdimensions;getChunkEmbeddingModelIdincludesdimensions;EMBEDDING_AFFECTING_KEYSaddsembedding.dimensionsembedding-openai.ts:OpenAICompatibleEmbeddingProviderOptions.dimensions,this.dimensions,modelIdincludes dimensions,embedBatchadds...this.dimensions?{dimensions}:{}toPOST /v1/embeddingsassets/magic-context.schema.jsonregenerated viabun build-schema.tspattern (manual edit matching generator output)Mirrors existing
truncate/input_type/local_dtypeconditional-fold pattern (#127, #155, #259).Testing
pi-magic-context@0.41.1(Pi 0.84.4) withQwen3-Embedding-8Bvia openai-compatible:no dimensions→ 768 (en) / 1024 (zh) dimsdimensions:4096→ 16384 bytes / 4096 dims (memory 2 → 0f395...), verified viamemory_embeddingsandembedding_registrationsdoctorpasses;build-schemaguard would pass after regenerationChecklist
truncatefold pattern (conditional spread, no global re-embed when omitted)EMBEDDING_AFFECTING_KEYSfor untrusted-load GC guardassets/magic-context.schema.jsonNeed help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Fixes #405 by adding optional
embedding.dimensionsfor theopenai-compatibleprovider so Matryoshka models likeQwen3-Embedding-8Bcan be pinned to 768/1024/2048/4096. Omitting it keeps the current byte-identical identity; setting it sendsdimensionsin thePOST /v1/embeddingsbody and folds it into the chunk identity, so changing it re-embeds existing vectors. The synapse fallback also carries dimensions so fallback requests and identities use the configured value instead of the provider default.dimensionsin the config schema with a max of 8192.embedding.dimensionstoEMBEDDING_AFFECTING_KEYSso config changes trip the GC guard on untrusted loads.assets/magic-context.schema.json.Written for commit 9f15915. Summary will update on new commits.
Greptile Summary
The PR adds configurable output dimensions for OpenAI-compatible Matryoshka embedding models while preserving the existing provider-default behavior when omitted.
embedding.dimensions.Confidence Score: 5/5
The PR appears safe to merge.
No blocking failure remains.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart LR Config[Embedding configuration] --> Schema[Schema validation] Schema --> Routing{Provider routing} Routing -->|OpenAI-compatible| Resolve[Resolved embedding config] Routing -->|Synapse fallback| Fallback[OpenAI-compatible fallback config] Fallback --> Resolve Resolve --> Identity[Provider and chunk identities] Resolve --> Provider[OpenAI-compatible provider] Provider --> Request[POST /v1/embeddings with optional dimensions]Reviews (2): Last reviewed commit: "fix(embedding): propagate dimensions thr..." | Re-trigger Greptile
Context used: