#20147: feat(routing): add L1.5 semantic router for task classification 🤖
docs
channel: discord
gateway
agents
size: XL
Cluster:
Plugin and Hook Enhancements
## Summary
Add a semantic router (L1.5) between L1 keyword matching and L2 LLM classification for intelligent task-type resolution using local embeddings.
- **SemanticRouter class** (`semantic-router.ts`): cosine similarity matching against pre-computed utterance embeddings, with configurable threshold (default 0.68) and confidence gap check (default 0.05)
- **Utterance library** (`utterances.ts`): 300+ example utterances across 23 TaskTypes (Chinese/English), FALLBACK excluded from matching
- **Task resolver async upgrade** (`task-resolver.ts`): L1 keyword match (sync) → L1.5 semantic match → fallback, with debug logging for hit tracking (`[routing] L1/L1.5/fallback`)
- **Short-window context enrichment**: prepends last 2 `InboundHistory` messages to semantic query for terse inputs ("做", "好", "继续")
- **LRU embedding cache**: 100-entry query cache to avoid redundant embedding computation
- **Routing instance integration**: `setEmbeddingProvider()` on `RoutingInstance` for lazy background init
- **Routing instance integration**: opt-in activation via `routing.semantic_router.enabled: true`; no-op when disabled or unconfigured (existing deployments unaffected)
- **Zod schema**: `routing.semantic_router` config validation (`enabled`, `threshold`, `min_gap`, `custom_utterances`)
### Design decisions
- Zero new dependencies — reuses existing `EmbeddingProvider` (node-llama-cpp / embeddinggemma-300m-qat-Q8_0)
- L1 keyword matching intentionally ignores `recentContext` to prevent keyword leakage from history
- Confidence gap prevents ambiguous classifications (top-1 vs top-2 score delta < min_gap → fallback)
- FALLBACK has no utterances — unmatched inputs naturally fall through
- **Opt-in by default** — semantic router only activates when `routing.semantic_router.enabled: true` is explicitly set; existing deployments are unaffected. Custom utterances can be merged per-TaskType via `custom_utterances` to extend or override built-in examples without forking the utterance library.
## Testing
- **241 tests**, all passing across 11 test files
- Unit tests: cosine similarity, threshold, gap check, cache (hit/miss/eviction/clear), edge cases (empty input, long input, concurrent resolve, re-init)
- Integration tests: L1→L1.5→fallback chain, recentContext forwarding, mock SemanticRouter
- E2E tests: full `resolveTaskType` + mock EmbeddingProvider, threshold/min_gap config wiring, `setEmbeddingProvider` lifecycle
- Schema regression tests: `semantic_router` field validation (full/minimal/strict)
- Manager wiring test: verifies `setEmbeddingProvider` called with correct args
```bash
npx vitest run src/gateway/routing/ src/config/config.schema-regressions src/auto-reply/reply/get-reply-directives.antiflap src/memory/__tests__/manager-routing-wiring
# 11 test files, 241 tests passed
npx oxlint src/gateway/routing/ # 0 errors
```
Most Similar PRs
#16677: feat(routing): intelligent model routing via pre-route hook
by gonesurfing · 2026-02-15
67.7%
#7770: feat(routing): Smart Router V2 - Configuration-driven model dispatc...
by zzjj7000 · 2026-02-03
66.2%
#15264: feat: Dynamic thinking level pre-routing based on message complexity
by phani-D · 2026-02-13
62.8%
#16529: fix(fallback): treat OpenRouter routing errors as failover-eligible
by zirubak · 2026-02-14
62.4%
#23630: feat : implement task-based model routing based on prompt keywords ...
by jayy-77 · 2026-02-22
60.0%
#20053: feat(voicewake): trigger-based routing to agent/session
by longbiaochen · 2026-02-18
59.6%
#17392: Add testing infrastructure and expand gateway OAuth scopes
by jordanhubbard · 2026-02-15
59.2%
#15571: feat: infrastructure foundation — hooks, model failover, sessions, ...
by tangcruz · 2026-02-13
58.2%
#9123: Feat/smart router backport and custom model provider
by JuliusYang3311 · 2026-02-04
57.9%
#16815: feat: add Kilo provider and OpenRouter routing params
by hoysama · 2026-02-15
57.8%