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.

Related pages