feat(cognition,#1411): tool_embedding PR-1 — pure types + cosine_similarity + threshold#1413
Merged
Merged
Conversation
…larity + threshold Oxidizer for ToolRegistry.generateToolEmbeddings + ToolRegistry.semanticSearchTools (TS, see src/system/tools/server/ToolRegistry.ts:421-511). Sibling to closed oxidizers #1375 check_redundancy + #1385 generate_response. Part of #1248 "TS-as-thin-glue" arc. ## What this ships (PR-1 scope — pure, atomic) - Wire types (ts-rs): - `ToolDescription { name, description }` - `ToolEmbedding { tool_name, vector }` - `EmbedToolsRequest { tools, model? }` - `EmbedToolsResponse { embeddings, model, generated_at_ms }` - `SemanticSearchToolsRequest { query, model?, limit?, threshold? }` - `SemanticSearchResult { name, description, category, similarity }` - `cosine_similarity(a, b) -> f32` — pure. Mirrors TS impl with defensive 0.0 returns on length mismatch + zero magnitude. - `extract_category(tool_name) -> &str` — pure. First slash-segment or "root" (matches TS ternary). - `round_similarity(sim) -> f32` — pure. 3-decimal-place rounding (mirrors TS `Math.round(s * 1000) / 1000`). - Constants (all match TS literal values): - `SIMILARITY_THRESHOLD: f32 = 0.3` - `TOOL_EMBEDDING_MODEL: &str = "nomic-embed-text"` - `DEFAULT_SEARCH_LIMIT: u32 = 10` ## NOT in this PR - **PR-2**: cache (LazyLock<Mutex<ToolEmbeddingCache>>) + async embed_tools + semantic_search_tools + IPC handlers tools/embed + tools/semantic-search. - **PR-3**: TS shim — ToolRegistry calls client.toolsEmbed / client.toolsSemanticSearch. - **PR-4**: Delete dead TS (inline cosineSimilarity, toolEmbeddings Map, AIProviderDaemon.createEmbedding call sites). ## Discipline - f64 accumulation in cosine_similarity prevents catastrophic cancellation on long vectors; final cast to f32 matches wire shape. - Mismatched vector lengths -> 0.0 (TS parity). - Zero-magnitude -> 0.0 (avoids NaN from divide-by-zero). - All defaults match TS literals so PR-3 shim is byte-equivalent. ## Tests (22 — 16 logic + 6 ts-rs export) cosine_similarity (8): - identical vectors -> ~1.0 - orthogonal -> 0.0 - opposite -> ~-1.0 - mismatched lengths -> 0.0 - zero magnitude -> 0.0 (both sides + both) - empty vectors -> 0.0 - known pythagorean case (3,4) . (4,3) = 0.96 - long vector (1000-dim) precision preserved extract_category (3): - no slash -> "root" - standard category/tool -> first segment - leading slash boundary round_similarity (2): - 3-decimal rounding - negative values constants (3): - threshold, model, limit all match TS Full cognition regression: 368/368 pass. Ref: #1411 oxidizer card, #1248 umbrella. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 18, 2026
joelteply
added a commit
that referenced
this pull request
May 18, 2026
…bed/search + IPC handlers (#1416) 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: Test <test@test.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
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>
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
Oxidizer for
ToolRegistry.generateToolEmbeddings+ToolRegistry.semanticSearchTools(TS, seesrc/system/tools/server/ToolRegistry.ts:421-511). Sibling to closed oxidizers #1375 check_redundancy + #1385 generate_response. Part of #1248 "TS-as-thin-glue" arc.What this ships (PR-1)
ToolDescription,ToolEmbedding,EmbedToolsRequest,EmbedToolsResponse,SemanticSearchToolsRequest,SemanticSearchResultcosine_similarity(a, b) -> f32— pure. Defensive0.0on length mismatch + zero magnitude.extract_category(tool_name) -> &str— pure. First slash-segment or"root".round_similarity(sim) -> f32— pure. 3-decimal rounding (mirrors TSMath.round(s * 1000) / 1000).SIMILARITY_THRESHOLD=0.3,TOOL_EMBEDDING_MODEL="nomic-embed-text",DEFAULT_SEARCH_LIMIT=10.NOT in this PR
LazyLock<Mutex<...>>) + asyncembed_tools/semantic_search_tools+ IPC handlerstools/embed+tools/semantic-search.ToolRegistry→client.toolsEmbed/client.toolsSemanticSearch.cosineSimilarity,toolEmbeddingsMap,AIProviderDaemon.createEmbeddingcall sites).Discipline
f64accumulation incosine_similarityprevents catastrophic cancellation on long vectors; final cast tof32matches wire shape.0.0(TS parity).0.0(avoids NaN from divide-by-zero).Tests (22 — 16 logic + 6 ts-rs export)
cosine_similarity (8): identical→
1.0, orthogonal→0.0, opposite→-1.0, mismatched lengths→0.0, zero-magnitude→0.0 (both sides + both), empty→0.0, known pythagorean (3,4)·(4,3)=0.96, 1000-dim precision preserved.extract_category (3): no slash→"root", standard category/tool→first segment, leading-slash boundary.
round_similarity (2): 3-decimal positive + negative.
constants (3): threshold, model, limit all match TS literal.
ts-rs exports (6).
Full cognition regression: 368/368 pass.
Refs
🤖 Generated with Claude Code