FAQ
Does router require an LLM to function?
No. The default scoring path uses semantic (embedding) similarity plus keyword matching only. An LLM is only involved if you set RouterConfig.llm_model, and even then it's invoked solely as a fallback for low-confidence queries — see HybridScorer.score_all.
Do I need sentence-transformers?
Yes, for any normal usage — HierarchicalRouter.__init__ constructs an EmbeddingProvider, which raises ImportError immediately if sentence-transformers is missing. See Installation.
Can I use my own embedding model instead of sentence-transformers?
Based on the analyzed source, EmbeddingProvider is hard-coded to sentence_transformers.SentenceTransformer — there's no pluggable embedding backend abstraction exposed publicly. You could swap EmbeddingConfig.model_name for any model name loadable by SentenceTransformer(...), but a fully custom (non-sentence-transformers) embedding backend would require subclassing or reimplementing EmbeddingProvider yourself.
How do I make routing faster?
Use RouterConfig.fast() as a starting preset (top_k=1, ExecutionMode.SINGLE, caching on). Also ensure CacheConfig.enabled=True (the default) — repeated queries skip embedding and scoring entirely on a cache hit. See Architecture → Caching.
How do I make routing more accurate?
Use RouterConfig.accurate() as a starting preset, and consider setting llm_model/llm_api_key to enable the LLM fallback tier for ambiguous queries. Also invest in more/better examples per route — semantic scoring is directly driven by how well your examples represent real user phrasing.
Why did my route get selected over an apparently-better match?
Check result.trace.candidates and result.trace.notes — every decision includes a full scored candidate list. Common causes: (1) group-level selection picked a different group entirely (only the winning group's routes are considered — see RoutingPipeline._select_groups), (2) Route.priority broke a near-tie, (3) a learned score_bias from the feedback loop shifted things, (4) a keyword veto (required/excluded) zeroed out an otherwise-strong semantic match.
Can multiple groups be considered together for a single query?
Not currently — RoutingPipeline._select_groups selects exactly one winning group per query, even when the docstring/comments describe "top group(s)" as an extension point. If you need cross-domain comparison, put the relevant routes in the same RouteGroup. See the note in RoutingPipeline.
Does the async routing path retry failed handlers like the sync path does?
No — _execute_single_async/_execute_sequential_async/_execute_parallel_async call handlers directly without the _with_retry wrapper used by their synchronous counterparts. See the warning in ExecutionEngine.
How is feedback actually applied?
FeedbackEngine.record(...) updates an exponential moving average per route; FeedbackEngine.apply_biases(route_map) then writes a clipped [-0.3, +0.3] bias directly onto Route.score_bias, which is added into every future scoring pass. See Concepts → Feedback and adaptive bias.
Is router thread-safe?
Partially. CacheManager/_LRUTTLCache, FeedbackEngine, and MetricsCollector all use internal locks and are safe for concurrent access. Route/group registration (register, unregister, etc. on HierarchicalRouter) is not lock-protected in the analyzed source — avoid concurrent registration from multiple threads while queries are in flight, or add your own synchronization.
What happens if no route matches?
In priority order: (1) a configured fallback handler (router.set_fallback(...)) is invoked if set; (2) otherwise, if RouterConfig.raise_on_no_match=True, a LookupError is raised; (3) otherwise, a failed RoutingResult is returned. See HierarchicalRouter._handle_no_match.
Where can I find the version/changelog for this library?
No version metadata, changelog, or release notes were present in the analyzed source tree — this documentation cannot report version history. See API Reference → Version history.