feat(embedding): optionally truncate oversized embeddings to configured dims#917
feat(embedding): optionally truncate oversized embeddings to configured dims#917JustinBacher wants to merge 1 commit into
Conversation
…ed dims Matryoshka (MRL) embedding models served behind OpenAI-compatible endpoints (llama.cpp, some vLLM builds) ignore the `dimensions=` request parameter and always return their native dimension. Honcho then hard-fails on the dimension mismatch, making these models unusable even though truncating an MRL vector to a smaller size is valid. Add an opt-in `EMBEDDING_TRUNCATE_TO_DIMENSIONS` flag. When enabled and the provider returns a vector larger than `EMBEDDING_VECTOR_DIMENSIONS`, keep the leading components and L2-renormalize so cosine/inner-product similarity stays well-defined. Off by default, so a genuinely misconfigured model still fails loudly. Also keeps embeddings at or below pgvector's 2000-dimension HNSW index cap when the native model dimension exceeds it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
WalkthroughAdds an opt-in embedding configuration that truncates oversized provider vectors to the configured dimension, L2-renormalizes them, and preserves strict dimension validation when disabled. Single and batch OpenAI paths use the new behavior, with corresponding unit and integration tests. ChangesEmbedding dimension handling
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant OpenAIEmbeddingAPI
participant _EmbeddingClient
participant _l2_truncate
OpenAIEmbeddingAPI->>_EmbeddingClient: return oversized embedding
_EmbeddingClient->>_l2_truncate: truncate leading components
_l2_truncate-->>_EmbeddingClient: return L2-renormalized vector
_EmbeddingClient-->>OpenAIEmbeddingAPI: return fitted embedding
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Finn-loop review of 02edbe0 CI: not configured (CodeRabbit review passed; no required checks on this branch) ReviewAdds an opt-in 1. Must fix before merge[CI] No required CI configured on the 2. Should fix soonNone. 3. Safe to mergeCode is clean — the change is minimal, correctly scoped, uses a safe default (off), has coverage for the truncation path, the zero-norm edge case, and the backward-compatible rejection path. The CI infrastructure gap is the only blocker. Labels needed: |
Problem
Matryoshka (MRL) embedding models served behind OpenAI-compatible endpoints (llama.cpp, some vLLM builds) ignore the
dimensions=request parameter and always return their native dimension. Honcho then hard-fails in_validate_embedding_dimensionson the mismatch, so these models are unusable — even though truncating an MRL vector to a smaller size is a valid, designed-in operation.Concretely: a model like
llama-nemotron-embed-1b-v2emits 2048 dims. If you configureEMBEDDING_VECTOR_DIMENSIONS=1536, honcho sendsdimensions=1536, the server ignores it and returns 2048, and every embedding raisesEmbedding dimension mismatch.This also collides with pgvector's 2000-dimension HNSW index cap: native dims above 2000 can't be indexed at all, so truncating to a supported MRL size (e.g. 1024/1536) is the only way to keep an ANN index.
Change
Add an opt-in
EMBEDDING_TRUNCATE_TO_DIMENSIONSflag (defaultfalse). When enabled and the provider returns a vector larger thanEMBEDDING_VECTOR_DIMENSIONS, keep the leading components and L2-renormalize so cosine / inner-product similarity stays well-defined, then validate. When disabled (default), behavior is unchanged — a genuinely misconfigured model still fails loudly.src/config.py— newEmbeddingSettings.TRUNCATE_TO_DIMENSIONSfield, documented.src/embedding_client.py—_l2_truncate()helper +_fit_embedding_dimensions(), applied at both OpenAI call sites (single + batch); threaded through the singleton and its cache signature. Gemini path unchanged (it already honorsoutput_dimensionality)..env.template— document the flag.tests/llm/test_embedding_client.py— 4 new tests (helper slice+renorm, zero-norm guard, client truncates when enabled, client still rejects when disabled).Testing
uv run pytest tests/llm/test_embedding_client.py— 20 passed.uv run basedpyright src/embedding_client.py src/config.py— 0 errors.uv run ruff check/ruff format— clean.llama-nemotron-embed-1b-v2(native 2048): with the flag on andEMBEDDING_VECTOR_DIMENSIONS=1536, embeddings come back 1536-dim, unit-norm, and store/search in avector(1536)pgvector column with an HNSW index.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests