Skip to content

feat(cognition,#1411): tool_embedding PR-2 — process cache + async embed/search + IPC handlers#1416

Merged
joelteply merged 1 commit into
canaryfrom
feat/oxidizer-tool-embedding-pr2
May 18, 2026
Merged

feat(cognition,#1411): tool_embedding PR-2 — process cache + async embed/search + IPC handlers#1416
joelteply merged 1 commit into
canaryfrom
feat/oxidizer-tool-embedding-pr2

Conversation

@joelteply
Copy link
Copy Markdown
Contributor

Summary

Stacks on PR-1 #1413 (MERGED). PR-2 wires the async path: process-wide LazyLock<Mutex<ToolEmbeddingCache>> + async embed_tools (replaces TS ToolRegistry.generateToolEmbeddings) + async semantic_search_tools (replaces TS ToolRegistry.semanticSearchTools) + cognition/embed-tools + cognition/semantic-search-tools IPC handlers.

What this ships

  • ToolEmbeddingCache (process-singleton) — Vec<ToolEmbedding> + parallel Vec<ToolDescription> + model. Replaces TS ToolRegistry.toolEmbeddings: Map<string, Float32Array> so cache lifecycle stays Rust-side.
  • ToolEmbeddingError — typed: NoAdapter, EmbeddingFailed, CacheEmpty, EmbeddingCountMismatch.
  • embed_tools(EmbedToolsRequest) -> Result<EmbedToolsResponse, _> — async. Routes via global_registryadapter.create_embedding, validates count parity, replaces (not merges) cache atomically under one Mutex acquire.
  • semantic_search_tools(SemanticSearchToolsRequest) -> Result<Vec<SemanticSearchResult>, _> — async. Reads cached embeddings (CacheEmpty if absent), embeds the query through the cache's model (no silent embedding-space mixing), computes cosine via PR-1 pure fn, filters by threshold, sorts descending, truncates to limit.
  • IPC command arms in CognitionModule:
    • cognition/embed-tools
    • cognition/semantic-search-tools
  • Test scaffolding (_clear_cache_for_tests + _install_cache_for_tests) for cache-state tests without a real adapter.

NOT in this PR

  • PR-3: TS shim — ToolRegistry.generateToolEmbeddings + ToolRegistry.semanticSearchTools delegate to client.cognitionEmbedTools + client.cognitionSemanticSearchTools.
  • PR-4: Delete dead TS — inline cosineSimilarity, toolEmbeddings Map, AIProviderDaemon.createEmbedding call sites.

Discipline

  • No silent default-on-error. Provider failure / count mismatch / empty cache surface as typed Result variants.
  • semantic_search_tools uses the cache's model unless explicitly overridden — never silently mixes embedding spaces (different models = meaningless cosine).
  • Cache replacement is atomic under one Mutex acquire — no read-modify-write window for partial state.
  • expect("mutex poisoned") panics rather than swallowing — by design (cache poisoning is a logic bug, not runtime).
  • generated_at_ms intentionally NOT retained on cache struct (no internal reader; EmbedToolsResponse carries it for caller observability; future cache-state IPC can re-add).

Tests (5 new — full module now 27 passing)

  • Error Display (3): NoAdapter carries provider+model; CacheEmpty gives actionable hint ("call embed_tools first"); EmbeddingCountMismatch includes both numbers.
  • semantic_search_empty_cache_errors — pins CacheEmpty before any adapter lookup.
  • cache_install_and_clear_for_tests — pins scaffolding contract for downstream test isolation.

Full cognition regression: 373/373 pass. Clippy held at 157 baseline.

Refs

Test plan

  • cargo test cognition::tool_embedding:: — 27/27 pass
  • cargo test cognition:: — 373/373 pass (no regressions)
  • cargo clippy — held at 157 baseline
  • Pre-push TS clean, ESLint baseline held
  • CI green

🤖 Generated with Claude Code

…bed/search + IPC handlers

Stacks on PR-1 #1413 (MERGED). Wires the async path: process-wide
LazyLock<Mutex<ToolEmbeddingCache>> + async embed_tools (replaces TS
ToolRegistry.generateToolEmbeddings) + async semantic_search_tools
(replaces TS ToolRegistry.semanticSearchTools) + cognition/embed-tools
+ cognition/semantic-search-tools IPC handlers.

## What this ships

- ToolEmbeddingCache (process-singleton) — Vec<ToolEmbedding> +
  parallel Vec<ToolDescription> + model. Replaces TS
  ToolRegistry.toolEmbeddings: Map<string, Float32Array>.
- ToolEmbeddingError — typed: NoAdapter, EmbeddingFailed, CacheEmpty,
  EmbeddingCountMismatch.
- embed_tools(EmbedToolsRequest) -> Result<EmbedToolsResponse, _> —
  async. Routes via global_registry → adapter.create_embedding,
  validates count parity, replaces (not merges) cache atomically
  under one lock acquire.
- semantic_search_tools(SemanticSearchToolsRequest) -> Result<Vec<SemanticSearchResult>, _>
  — async. Reads cached embeddings (CacheEmpty if absent), embeds the
  query through the cache's model (no silent space-mixing), computes
  cosine via PR-1 pure fn, filters by threshold, sorts descending,
  truncates to limit.
- IPC command arms: cognition/embed-tools + cognition/semantic-search-tools.
- Test scaffolding (_clear_cache_for_tests + _install_cache_for_tests)
  for cache-state tests without requiring a real adapter.

## NOT in this PR

- PR-3: TS shim — ToolRegistry.generateToolEmbeddings +
  semanticSearchTools delegate to client.cognitionEmbedTools +
  client.cognitionSemanticSearchTools.
- PR-4: Delete dead TS — inline cosineSimilarity, toolEmbeddings Map,
  AIProviderDaemon.createEmbedding call sites.

## Discipline

- No silent default-on-error. Provider failure / count mismatch /
  empty cache surface as typed Result.
- semantic_search_tools uses the cache's model unless explicitly
  overridden — never silently mixes embedding spaces (different
  models = meaningless cosine).
- Cache replacement atomic under one Mutex acquire — no
  read-modify-write window for partial state.
- expect("mutex poisoned") panics rather than swallowing — by design.
- generated_at_ms intentionally NOT retained on cache struct (no
  internal reader yet; EmbedToolsResponse already carries it for
  caller observability; a future cache-state IPC can re-add).

## Tests (5 new — full module now 27 passing)

- Error Display: NoAdapter carries provider+model, CacheEmpty gives
  actionable hint, EmbeddingCountMismatch includes both numbers.
- semantic_search_empty_cache_errors pins CacheEmpty before any
  adapter lookup.
- cache_install_and_clear_for_tests pins scaffolding contract.

Full cognition regression: 373/373 pass. Clippy held at 157 baseline.

Ref: #1411 PR-1 (MERGED #1413), #1248 umbrella.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@joelteply joelteply merged commit c46da02 into canary May 18, 2026
3 checks passed
@joelteply joelteply deleted the feat/oxidizer-tool-embedding-pr2 branch May 18, 2026 18:55
joelteply added a commit that referenced this pull request May 18, 2026
… (PR-4 folded) (#1418)

* feat(cognition,#1411): tool_embedding PR-2 — process cache + async embed/search + IPC handlers

Stacks on PR-1 #1413 (MERGED). Wires the async path: process-wide
LazyLock<Mutex<ToolEmbeddingCache>> + async embed_tools (replaces TS
ToolRegistry.generateToolEmbeddings) + async semantic_search_tools
(replaces TS ToolRegistry.semanticSearchTools) + cognition/embed-tools
+ cognition/semantic-search-tools IPC handlers.

## What this ships

- ToolEmbeddingCache (process-singleton) — Vec<ToolEmbedding> +
  parallel Vec<ToolDescription> + model. Replaces TS
  ToolRegistry.toolEmbeddings: Map<string, Float32Array>.
- ToolEmbeddingError — typed: NoAdapter, EmbeddingFailed, CacheEmpty,
  EmbeddingCountMismatch.
- embed_tools(EmbedToolsRequest) -> Result<EmbedToolsResponse, _> —
  async. Routes via global_registry → adapter.create_embedding,
  validates count parity, replaces (not merges) cache atomically
  under one lock acquire.
- semantic_search_tools(SemanticSearchToolsRequest) -> Result<Vec<SemanticSearchResult>, _>
  — async. Reads cached embeddings (CacheEmpty if absent), embeds the
  query through the cache's model (no silent space-mixing), computes
  cosine via PR-1 pure fn, filters by threshold, sorts descending,
  truncates to limit.
- IPC command arms: cognition/embed-tools + cognition/semantic-search-tools.
- Test scaffolding (_clear_cache_for_tests + _install_cache_for_tests)
  for cache-state tests without requiring a real adapter.

## NOT in this PR

- PR-3: TS shim — ToolRegistry.generateToolEmbeddings +
  semanticSearchTools delegate to client.cognitionEmbedTools +
  client.cognitionSemanticSearchTools.
- PR-4: Delete dead TS — inline cosineSimilarity, toolEmbeddings Map,
  AIProviderDaemon.createEmbedding call sites.

## Discipline

- No silent default-on-error. Provider failure / count mismatch /
  empty cache surface as typed Result.
- semantic_search_tools uses the cache's model unless explicitly
  overridden — never silently mixes embedding spaces (different
  models = meaningless cosine).
- Cache replacement atomic under one Mutex acquire — no
  read-modify-write window for partial state.
- expect("mutex poisoned") panics rather than swallowing — by design.
- generated_at_ms intentionally NOT retained on cache struct (no
  internal reader yet; EmbedToolsResponse already carries it for
  caller observability; a future cache-state IPC can re-add).

## Tests (5 new — full module now 27 passing)

- Error Display: NoAdapter carries provider+model, CacheEmpty gives
  actionable hint, EmbeddingCountMismatch includes both numbers.
- semantic_search_empty_cache_errors pins CacheEmpty before any
  adapter lookup.
- cache_install_and_clear_for_tests pins scaffolding contract.

Full cognition regression: 373/373 pass. Clippy held at 157 baseline.

Ref: #1411 PR-1 (MERGED #1413), #1248 umbrella.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(cognition,#1411): tool_embedding PR-3 — TS shim + delete dead TS (PR-4 folded)

Stacks on PR-2 #1416. ToolRegistry.generateToolEmbeddings (inline
AIProviderDaemon.createEmbedding) + semanticSearchTools (inline
cosineSimilarity + manual sort) now delegate to
RustCoreIPCClient.cognitionEmbedTools +
RustCoreIPCClient.cognitionSemanticSearchTools. Mirrors codex's
check_redundancy PR-3 #1383 shape (PR-4 dead-code delete folded in).

## What this ships

- `ToolRegistry.populateRustEmbeddingCache` — calls
  client.cognitionEmbedTools with all registered tools. Rust populates
  the process-wide cache.
- `ToolRegistry.ensureToolEmbeddings` simplified — one-shot guard
  + concurrent-call dedup. TTL gone (Rust cache persists for the
  process lifetime; a future "tools changed" event will re-trigger).
- `ToolRegistry.semanticSearchTools` thin shim — call
  client.cognitionSemanticSearchTools(query, limit), map descriptions
  through cleanDescription for chat UX presentation.
- TS cognition mixin adds `cognitionEmbedTools` +
  `cognitionSemanticSearchTools` binding methods.
- New ts-rs barrel re-exports: EmbedToolsRequest, EmbedToolsResponse,
  SemanticSearchToolsRequest, SemanticSearchResult.

## Dead TS deleted (PR-4 folded in)

- `private toolEmbeddings: Map<string, number[]>` cache state — Rust
  owns the cache now.
- `private embeddingsGeneratedAt: number` + `EMBEDDINGS_TTL_MS` — TTL
  belongs to Rust if reintroduced.
- `private cosineSimilarity(a, b)` — Rust's pure cosine_similarity
  (PR-1) is the source of truth.
- `import { AIProviderDaemon }` from
  '../../../daemons/ai-provider-daemon/shared/AIProviderDaemon' —
  unused after both call sites moved to IPC.
- Inline embedding request construction + Math.round +
  threshold-comparison loop — all in Rust now.

Net diff: -136 LOC TS, +51 LOC mixin (which lives in the bindings
layer next to other cognition delegates). Net cognition-TS deletion
in the ratchet-watched dirs.

## Discipline

- ensureToolEmbeddings cache flag scoped to TS singleton — no global
  state outside the registry instance.
- Concurrent-call dedup retained (multiple callers hitting
  semanticSearchTools at boot won't trigger N parallel embed_tools
  IPC calls — TS pipes them through one promise).
- cleanDescription stays TS — that's pure UI/presentation; Rust
  returns the raw description.
- Error handling: IPC failures throw (no fail-open default), matches
  the pattern in cognitionGenerateResponse + cognitionCheckRedundancy.

## Stack progress

- #1411 PR-1 (pure types + cosine + threshold): #1413 MERGED
- #1411 PR-2 (cache + async + IPC handlers): #1416 OPEN
- #1411 PR-3 (TS shim + dead-TS delete): **this PR**
- #1411 PR-4 (dead-TS delete): **folded into this PR**

After merge: `ToolRegistry.ts` semantic-search surface is a 40-LOC
shim. AIProviderDaemon dependency gone from this file.

## Refs

- #1411 sub-card
- #1413 PR-1 (MERGED)
- #1416 PR-2 (in flight)
- #1383 codex's check_redundancy PR-3 — same shape, folded-PR-4 pattern
- #1248 umbrella

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant