fix: centralize embedding model config to prevent query/ingest mismatch - #912
fix: centralize embedding model config to prevent query/ingest mismatch#912OmkarKirpan wants to merge 6 commits into
Conversation
Single source of truth for embedding model resolution. Resolves from collection metadata, falls back to MiniLM for legacy palaces. New palaces default to all-mpnet-base-v2 (768-dim). Part of MemPalace#903
Resolves from config.json or MEMPALACE_EMBEDDING_MODEL env var. Used for new palace creation only; existing palaces read from collection metadata. Part of MemPalace#903
get_collection() and get_or_create_collection() now accept optional embedding_function and embedding_model_name params. Model name is stamped into collection metadata on create. Fully backwards compatible. Part of MemPalace#903
On create: stamps new_palace_model() (mpnet) into collection metadata. On read: resolves model from metadata, falls back to MiniLM for legacy. All collection access now uses the correct embedding function. Also fixes tests that opened bare PersistentClient instances without the correct embedding function, causing dimension mismatches (768 vs 384). Part of MemPalace#903
_get_collection() resolves the model from collection metadata and passes the correct embedding_function to ChromaDB. tool_status() reports the active embedding_model. Closes MemPalace#903
- ChromaBackend.create_collection() now accepts embedding_function and embedding_model_name params - cli.py repair, repair.py rebuild_index: read embedding model from existing collection before delete/recreate, preserve it - migrate.py: stamp new_palace_model() on migrated palaces - palace.get_collection(): accept optional config param so CLI mining respects config.json embedding_model setting - Update test_rebuild_index_success to verify new embedding args Addresses code review findings MemPalace#4, MemPalace#5, MemPalace#7 for MemPalace#903
Three Phase 2 fixes: 1. Embedding model guard — palace_meta.json (MemPalace#903/MemPalace#912): - Added embedding_model property to MempalaceConfig (default: all-MiniLM-L6-v2; env MEMPALACE_EMBEDDING_MODEL overrides). - write_palace_meta() in palace.py writes model name + timestamp to <palace>/palace_meta.json at the end of every mine run. - read_palace_meta() in palace.py reads it back at search time. - search_memories() in searcher.py compares the stored ingest model against the current config model; if they differ, a "warning" key is added to the search result and logged to stderr. - Non-fatal: old palaces without palace_meta.json get no warning. - Prevents silent garbage results when users switch embedding models. 2. silent_save config respected in hooks_cli.py (MemPalace#854): - hook_stop() now gates on MEMPAL_VERBOSE env var, matching the existing behavior in hooks/mempal_save_hook.sh. - Default (MEMPAL_VERBOSE unset): mine transcript in background, never block the AI or interrupt the conversation. - MEMPAL_VERBOSE=true: block with diary reason (developer mode). - Updated tests to reflect new default behavior. 3. sanitize_name() Unicode — confirmed already working; skipped (MemPalace#637). Python 3's re.UNICODE default makes [^\W_] match all Unicode letters. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@OmkarKirpan — heads-up that #442 just got rebased onto
#442 effectively contains your fix as a subset. If maintainers prefer the broader scope, #912 would be superseded. If they prefer the narrower minimal fix first, I can wait for #912 to merge and rebase #442 on top — happy to coordinate either way. @igorls @bensig — would appreciate guidance on which sequencing you want. |
|
Hi, thanks for the contribution. This PR has merge conflicts with Could you rebase onto If this change is no longer relevant, feel free to close the PR. (This message is part of a periodic backlog pass, sent to all open PRs that match this state.) |
|
Closing in favor of #442. The narrow The remaining concern (per-palace embedding model so the MCP path uses the same model ingest used, closes #903) is better served by @NickShtefan's #442, which contains this PR's intent as a subset and adds init-time model binding, mismatch detection, and a recovery path. No point shipping a narrower competing version. Thanks @bensig @igorls @milla-jovovich for the time on this thread. |
Summary
Closes #903
Adds centralized embedding model configuration so the MCP server, CLI search, and all ingest paths use the same model — fixing silent query failures when models mismatch.
Design Decisions
Storage approach: Embedding model name stored in ChromaDB collection metadata (not a separate file). Atomic with the collection, can't desync. Absence of the key = legacy palace.
Default for new palaces:
all-mpnet-base-v2(768-dim) — better search quality (+3.5pp on LoCoMo R@10 benchmarks over MiniLM).Default for existing palaces:
all-MiniLM-L6-v2(384-dim) — backwards compatible, no re-mining required. Detected by absence ofembedding_modelkey in collection metadata.Resolution chain: Collection metadata (authoritative) > config file / env var (new palaces only) > built-in default. This means once a palace is created, its model is locked in and self-describing.
No migration tool in this PR: Re-embedding existing palaces from MiniLM to mpnet is a separate concern. This PR prevents the mismatch; migrating existing palaces is a follow-up.
All create paths stamp the model: Repair, rebuild, and migrate operations preserve the original model through the delete/recreate cycle.
Resolution chain
Files changed
mempalace/embedding.py— model registry, resolution, embedding function factorymempalace/config.py—embedding_modelproperty on MempalaceConfigmempalace/backends/chroma.py—get_collection(),get_or_create_collection(),create_collection()acceptembedding_function+embedding_model_namemempalace/palace.py— resolves model from metadata on read, stamps on createmempalace/mcp_server.py—_get_collection()uses correct embedding function,tool_status()reports active modelmempalace/cli.py,mempalace/repair.py,mempalace/migrate.py— all collection-create paths now stamp embedding modelKnown follow-ups (not in scope)
get_collectioncall on read paths for metadata resolution (~ms overhead, within budget)Test plan
mempalace_statusreports active embedding modelruff checkandruff formatclean