Skip to content

feat(runtime): EmbedderRegistry trait + pack-extension hook (#397)#421

Merged
ohdearquant merged 2 commits into
mainfrom
v023/embedder-registry
May 25, 2026
Merged

feat(runtime): EmbedderRegistry trait + pack-extension hook (#397)#421
ohdearquant merged 2 commits into
mainfrom
v023/embedder-registry

Conversation

@ohdearquant
Copy link
Copy Markdown
Owner

Summary

P4 of v023-operationalize. Trait-based embedder registry with pack-extension hook. Closes #397.

What's added

New module crates/khive-runtime/src/embedder_registry.rs:

#[async_trait]
pub trait EmbedderProvider: Send + Sync {
    fn name(&self) -> &str;
    fn dimensions(&self) -> usize;
    async fn build(&self) -> RuntimeResult<Arc<dyn EmbeddingService>>;
}

EmbedderRegistry struct manages providers (register, get, names).

LatticeEmbedderProvider adapter wraps lattice_embed::EmbeddingModel so existing dual-embedding behavior continues unchanged.

Pack extension hook

New default-no-op method on PackRuntime trait:

fn register_embedders(&self, _runtime: &KhiveRuntime) {}

Packs override this to register custom embedders. Default is no-op so existing packs remain unaffected.

Refactor

KhiveRuntime:

  • Replaced raw HashMap<String, EmbedderEntry> with EmbedderRegistry under the hood
  • All existing methods (embedder(name), resolve_embedding_model(name), registered_embedding_model_names()) route through the registry
  • New public method register_embedder(provider) for pack-level registration
  • Backwards-compat: KhiveRuntime::new(config) API unchanged

Tests (9 new)

Unit (5):

  • register_and_get_provider_round_trip
  • duplicate_name_last_wins (documented choice)
  • names_returns_all_registered
  • get_service_unknown_name_returns_error
  • get_service_calls_build_once (lazy init verified)

Integration (4):

  • register_embedder_and_retrieve_via_embedder_method
  • registered_names_includes_custom_provider
  • dual_embedding_regression_both_models_registered
  • embedder_unknown_name_returns_error

All workspace tests pass.

ADR amendment

docs/adr/ADR-031-multi-engine-retrieval.md addendum documents the pack-extension surface.

Closes

- New crates/khive-runtime/src/embedder_registry.rs: EmbedderProvider async trait, EmbedderRegistry struct with register/get/names/get_entry, LatticeEmbedderProvider adapter wrapping lattice_embed::EmbeddingModel
- KhiveRuntime refactored: embedder_registry: Arc<RwLock<EmbedderRegistry>> replaces private HashMap; register_embedder() public method; embedder()/resolve_embedding_model()/registered_embedding_model_names() routed through registry; RwLockGuard never held across await
- PackRuntime trait gains register_embedders(&self, runtime) default no-op hook for pack-level custom provider registration
- 5 new unit tests in embedder_registry.rs: register+get round-trip, duplicate last-wins, names() coverage, UnknownModel on missing, build-once caching
- 4 new integration tests in tests/integration.rs (embedder_registry_tests module): MockEmbedderProvider round-trip, registered_names includes custom, dual-embedding regression both lattice models reachable, unknown name returns UnknownModel
- ADR-031 amended with pack-extensible EmbedderRegistry addendum documenting the design, API surface, and file changes
- Closes #397

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…errors

- PackRuntime::register_embedders now actually called during pack registration
  via VerbRegistry::call_register_embedders, invoked in KhiveMcpServer::with_packs
  immediately after registry build and before first dispatch
- Lattice-specific vector paths refactored in vectors_for_model to handle custom
  providers: lattice names use the enum-backed path, non-lattice names fall through
  to a provider-dimension path using sanitize_key — no panic on custom provider names
- Provider build() errors propagated as RuntimeResult instead of panic:
  EmbedderEntry::resolve now uses get/set on OnceCell with explicit error mapping
  rather than .expect() — build failures return RuntimeError::Internal
- 2 new integration tests: pack_register_embedders_hook_makes_provider_reachable
  proves the hook fires end-to-end; failing_provider_build_returns_err_not_panic
  proves build() errors return Err, not abort the process

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ohdearquant ohdearquant merged commit 02690dc into main May 25, 2026
0 of 3 checks passed
ohdearquant added a commit that referenced this pull request May 25, 2026
…c + score normalization (#440)

Five Wave-1 fixes from /tmp/v023-usage-audit-consolidated.md UE3 + ADR-A:

1. remember(source_id=...) accepts 8-char short IDs (Critical) — same prefix
   resolution as get/link/update. Closes recon #291 and the chain
   create->remember workflow.

2. recall(help=true) + remember(help=true) HandlerDef params updated to
   include all PR #406/#421 args (top_k, score_floor, fusion_strategy,
   embedding_model + presentation for verbose breakdown).

3. decay_factor default doc corrected: 0.1 -> 0.01 (10x off).

4. compute_score normalizes RRF-fused scores by (k+1) to comparable range
   with weighted/union so score_floor is portable across fusion_strategy.

5. presentation="verbose" on recall includes per-component score breakdown
   without changing agent-mode response shape.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[adr-031] EmbedderRegistry implementation (integration codex MAJ-3)

1 participant