fix(memory): add dimension-mismatch guard to EmbeddingRegistry::search_raw#3420
Merged
fix(memory): add dimension-mismatch guard to EmbeddingRegistry::search_raw#3420
Conversation
…h_raw Qdrant gRPC silently returns near-zero cosine scores (~0.022) when the query vector dimension does not match the stored collection dimension, unlike the REST API which returns a 400 error. This caused all skill candidates to be dropped below the min_injection_score threshold, making skill injection non-functional. Add a dimension guard in search_raw that: - Reads a cached_dim (Arc<tokio::sync::RwLock<Option<u64>>>) populated by ensure_collection at sync time - On cache miss, probes Qdrant once via get_collection_vector_size and populates the cache for subsequent calls - Returns DimensionProbe error with collection name, expected dim, and actual dim if a mismatch is detected, before issuing any gRPC call Make QdrantOps::get_collection_vector_size pub(crate) to support the cache-miss fallback path. Remove semantically incorrect From<TryFromIntError> impl and replace with safe as u64 casts. Closes #3418
c7df1c2 to
0f672a3
Compare
This was referenced Apr 25, 2026
Closed
bug-ops
added a commit
that referenced
this pull request
Apr 26, 2026
…te at Qdrant boundary Add EmbeddingVector<State> typestate newtype to zeph-common with Normalized and Unnormalized markers to prevent silent dimension/ normalization mismatches (recurring bug class: #3421, #3382, #3420). - EmbeddingVector::<Unnormalized>::new(v) wraps raw model output - .normalize() L2-normalizes and returns EmbeddingVector<Normalized> - EmbeddingVector::<Normalized>::new_normalized(v) trust-caller path - CodeStore::search now requires EmbeddingVector<Normalized> - Retriever normalizes before passing to store.search - agent_setup.rs: wraps Vec<f32> with .normalize() at embed call site
bug-ops
added a commit
that referenced
this pull request
Apr 26, 2026
…nticMemory bool→enum (#3436) * refactor(common,index): introduce EmbeddingVector<Normalized> typestate at Qdrant boundary Add EmbeddingVector<State> typestate newtype to zeph-common with Normalized and Unnormalized markers to prevent silent dimension/ normalization mismatches (recurring bug class: #3421, #3382, #3420). - EmbeddingVector::<Unnormalized>::new(v) wraps raw model output - .normalize() L2-normalizes and returns EmbeddingVector<Normalized> - EmbeddingVector::<Normalized>::new_normalized(v) trust-caller path - CodeStore::search now requires EmbeddingVector<Normalized> - Retriever normalizes before passing to store.search - agent_setup.rs: wraps Vec<f32> with .normalize() at embed call site * refactor(memory): replace 5 bool params in SemanticMemory with two-variant enums Replace bool flags in SemanticMemory search/recall API with named enums to enforce the project no-bool-params rule and eliminate call-site swap bugs (addresses TODO at semantic/mod.rs:129): TemporalDecay, MmrReranking, ImportanceScoring, QueryBiasCorrection, HebbianReinforcement Each enum has Enabled/Disabled variants, #[must_use] is_enabled(), and From<bool> for config bootstrap compatibility. All callers, builder methods, and test fixtures updated across the workspace.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #3418: skill injection non-functional due to Qdrant gRPC returning near-zero cosine scores (~0.022) on dimension mismatch.
Root cause:
EmbeddingRegistry::search_rawwas issuing gRPC searches without validating that the query vector dimension matches the collection's stored dimension. Qdrant gRPC silently returns near-zero cosine scores on mismatch (unlike REST which returns 400), so all skill candidates were dropped below themin_injection_score=0.20threshold.Fix:
cached_dim: Arc<tokio::sync::RwLock<Option<u64>>>toEmbeddingRegistry— populated byensure_collectionat sync timesearch_rawreads the cache and returnsErr(DimensionProbe)with a clear message (collection name, expected dim, actual dim) before issuing any gRPC callget_collection_vector_sizeand populates the cacheQdrantOps::get_collection_vector_sizepub(crate)to support the cache-miss pathFrom<TryFromIntError>impl; replaced with safeas u64castsTest plan
DimensionProbeerror, matching dimension passes guard, live integration test (#[ignore])cargo +nightly fmt --checkcleancargo clippy -p zeph-memory --lib -- -D warningscleanNotes
The same root cause pattern affects #3382 (ReasoningBank embed dim mismatch). That issue is tracked separately.