Repository navigation
feat(embedding): support embedding.dimensions for openai-compatible Matryoshka models (e.g. Qwen3-Embedding-8B 768/1024/4096) #405
Description
Short description
feat(embedding): support embedding.dimensions for openai-compatible Matryoshka models (e.g. Qwen3-Embedding-8B 768/1024/4096)
What problem does this solve?
Qwen3-Embedding-8B (and other Matryoshka models) supports variable output dimensions via the OpenAI-compatible dimensions request field. The model can return 768, 1024, 2048, or 4096 truncated vectors from the same weights via MRL.
Magic Context 0.41.1 has no embedding.dimensions config. The openai-compatible provider always omits dimensions, so the endpoint's default is used:
POST /v1/embeddings {model: Qwen3-Embedding-8B, input: "hello world"}→ 768 dimsPOST /v1/embeddings {model: Qwen3-Embedding-8B, input: "测试中文"}→ 1024 dims (same endpoint, same model, different input language)POST /v1/embeddings {model: Qwen3-Embedding-8B, dimensions: 4096}→ 4096 dims (native)
We use Magic Context with a mixed Chinese/English codebase (Pi). local Xenova/all-MiniLM-L6-v2 (384 dims) has poor Chinese→English recall, and the default 768/1024 from the remote endpoint is also unstable across languages. For a small 17-case bilingual retrieval test (same as #259 style), 4096 preserved the best recall. There's currently no way to pin the store to the native 4096 without forking the plugin.
Proposed solution
Add an optional embedding.dimensions for the openai-compatible provider, plumbed end-to-end:
{
"embedding": {
"provider": "openai-compatible",
"endpoint": "https://api.example.com/v1",
"model": "Qwen3-Embedding-8B",
"api_key": "sk-...",
"dimensions": 4096 // 768 | 1024 | 2048 | 4096 | omitted = provider default
}
}Implementation (mirrors truncate/input_type pattern):
-
Schema
packages/plugin/src/config/schema/magic-context.ts:
BaseEmbeddingConfigSchema.dimensions: z.number().int().positive().max(8192).optional()+ include inEmbeddingConfigSchema.transformforopenai-compatible(andsynapsefor completeness). -
Identity
embedding-identity.ts:
getEmbeddingProviderIdentityincludes...dimensions?{dimensions}:{}
(changing dimensions must re-embed; omitted keeps byte-identical identity for existing configs). -
Runtime
embedding.ts/project-embedding-registry.ts:
resolveEmbeddingConfigpreservesdimensions;getChunkEmbeddingModelIdincludesdimensionsinchunkIdentity;EMBEDDING_AFFECTING_KEYSincludesembedding.dimensions. -
Transport
embedding-openai.ts:
OpenAICompatibleEmbeddingProviderOptions.dimensions,this.dimensions,modelIdincludes dimensions,embedBatchadds...this.dimensions?{dimensions}:{}toPOST /v1/embeddingsbody.
Verified locally on pi-magic-context@0.41.1 (Pi 0.84.4, Qwen3-Embedding-8B via https://api.example.com/v1):
| Request | dims | bytes | model_id |
|---|---|---|---|
no dimensions (en) |
768 | 3072 | b2b64c6a1044797a0a3f8f26672942a5 |
dimensions:1024 (zh) |
1024 | 4096 | — |
dimensions:4096 (zh) |
4096 | 16384 | 0f39523135a2dfd6c3a58933ede73966:chunk:2b0c... |
After dimensions:4096 + /reload, memory 2 → 16384 bytes / 4096 dims correctly.
Omitted dimensions keeps current behavior (no re-embed for existing users).
Alternatives considered
- Keep provider default (768/1024): unstable across languages, lower recall for bilingual store; same limitation that motivated Make the local embedding dtype configurable for practical multilingual recall #259
local_dtype. - Deploy a separate model alias that defaults to 4096: requires server-side change, not client-configurable.
- Switch to
localmultilingual model withq8: solves recall but ~858 MB RSS vs remote 0 MB, and still fixed dims.
Area
Memory / historian / dreamer
Additional context
- Endpoint is
api.example.com/v1Qwen3-Embedding-8B(Matryoshka MRL). Verifieddimensionsis standard OpenAI-compatible field (also used bytext-embedding-3-*). - Willing to PR the 8-site patch (schema + identity + 2× resolveEmbeddingConfig + 2× createProvider + provider + chunk) with tests if approach is approved.
- Related: Make the local embedding dtype configurable for practical multilingual recall #259 (local_dtype), Support query/passsage input_type for asymmetric embedding models #155 (input_type), [Enhancement] Add Nvidia NIM embedding support (openai-compatible) #127 (NIM truncate) — same fold pattern.
Activity
coleleavitt commentedon Sep 13, 2026
Current-baseline audit against
2d6a8af1/ upstream5bf26e65: keep this issue open. Current master still has noembedding.dimensionsconfig/request field. PR #406 remains the direct implementation attempt, but its current head is conflicting and still changes the omitted/default chunk identity, lacks returned-width enforcement and dimensions-specific regressions, and fails the generated configuration-doc guard. Existing endpoint probes only report returned width; they do not satisfy the request to pin/send a dimension.magic-alfonso commentedon Oct 17, 2026
Accepted. We'll add an optional
embedding.dimensionssetting for OpenAI-compatible providers. It's sent as the request'sdimensionsparameter, and changing it is treated like changing the embedding model, so existing vectors are re-embedded. A pull request is welcome; if none arrives, we'll build it.