Hidden Assumptions in semantic-router
13 assumptions this code never checks · 3 critical · spanning Domain, Scale, Resource, Environment, Contract, Ordering, Temporal
Every codebase relies on things it never checks. Most of them are routine. CodeSea looked at vllm-project/semantic-router and picked out the few most likely to cause trouble — explained plainly, with what to do about each. The full list is just below.
Most of what this code assumes is routine. These 3 are the ones most likely to cause trouble here — in plain terms, with what to do about each. The rest are minor; they're under "Show everything".
The paper is explicit that the headline benefit numbers were calculated from assumptions, not observed from real traffic. The authors themselves say these figures still need to be confirmed against actual production data. If you enable the load-adaptive caching and expect the promised savings, the real outcome could be quite different, and the software will not tell you.
What to do: Treat the load-based savings numbers as unverified estimates and measure actual hit rates and model-traffic reduction on your own workload before relying on them.
“The traffic reduction figures (9–17%) are theoretical projections based on assumed linear relationships between threshold relaxation and hit rate improvements.”
Read it in the paper · Discussion ↗When a downstream model gets busy, the method keeps cached answers around longer to boost hit rates — but this means users can receive out-of-date answers, and for fast-changing data the staleness can climb very high. The paper accepts this trade only when slow responses would be worse than stale ones, which may not hold for your use case.
What to do: For any category where freshness matters (financial, news, real-time data), cap the maximum time-to-live and confirm that serving somewhat stale answers is genuinely acceptable under load.
“Extending to t 1 = 15 t_{1}=15 minutes ( β = 3 \beta=3 ): staleness increases to 60%.”
Read it in the paper · Discussion ↗The paper draws a size window where this design pays off: roughly tens of thousands up to about ten million cached entries. Below that, a simpler cache is better; well above it, the fast local-search timing that the economics depend on starts to slip. Outside this window the claimed advantages may not apply.
What to do: Check that your expected cache size falls within the range the paper validated and reconsider the approach if it is much smaller or far larger.
“Pure in-memory caches remain simpler for small datasets ( < < 10K entries).”
Read it in the paper · Discussion ↗Show everything (10 more)
The claim that low-hit-rate categories become worth caching rests on specific timing numbers the authors assumed for search speed and model response time. If your servers are faster, slower, or your models respond differently, the point at which caching pays off shifts, and a category the paper says is viable might not be in your setup.
What to do: Measure your actual search latency and model response times, then recompute whether your low-hit-rate categories still clear the break-even point before enabling caching for them.
“Total cost: approximately 30ms per query (hit or miss) plus 5ms document fetch on hit.”
Read it in the paper · Vector Database Limitations ↗break-even / miss-cost assumptions embedded in response-cache economics
The router assumes all three AI model folders (for intent, personal-info detection, and security scanning) exist on disk and contain the right files in the right format. Nothing checks this at startup. If a model folder is missing, renamed, or points to the wrong type of model, the entire routing system fails silently on the first request — there is no early warning during setup.
What to do: Add a startup health check that verifies each model folder exists and contains the expected config file before accepting any traffic, so a missing model is caught immediately rather than on the first user request.
candle-binding/src/ffi/instances/mod.rs:Options
The labels your AI model outputs and the labels you type into routing rules must match exactly — including spelling and capitalization. Nothing checks that they agree. If you swap in a newly trained model whose label names changed even slightly, or if a rule is typed with a small difference, that rule will quietly stop working and all traffic will be sent to the fallback model. Everything looks fine; the routing policy is just silently doing nothing.
What to do: After loading the model, compare its known label list against all labels referenced in active routing rules, and log a clear warning at startup for any rule that references a label the model doesn't produce.
candle-binding/src/classifiers/lora/intent_lora.rs:IntentLoRAClassifier::classify_intent
If you configure the system to use a GPU, it assumes the GPU exists, has the right drivers, and has enough free memory for all three AI models at once. Nothing checks this before trying to load the models. On a shared server where another program is using the GPU, or on a machine without a compatible graphics card, the failure message you get will be technical and hard to interpret.
What to do: Document the minimum GPU memory requirement clearly, and make the startup log print which device was selected and whether model loading succeeded before accepting traffic.
candle-binding/src/ffi/instances/mod.rs:Options
The system records where in a message personal information was found, using position numbers. Those numbers are likely measured in the AI model's internal word-piece units, not in the actual character positions in the original text. If anything later uses those positions to highlight or redact the original text — for example in the dashboard traces — it will mark the wrong places, especially for messages with emoji, accented characters, or non-English text.
What to do: Clarify in the struct documentation whether positions are token indices or character offsets, and if they are token indices, add a conversion step before exposing them to the dashboard or any redaction pipeline.
candle-binding/src/classifiers/lora/pii_lora.rs:PIILoRAClassifier
All requests share the same pool of worker threads for running the three AI classifiers in parallel. Under heavy simultaneous traffic, those threads get divided among all requests at once. A single request that normally classifies in parallel may end up running its three classifiers one-at-a-time when the server is busy, making everything slower with no clear signal about why.
What to do: Consider setting a concurrency limit on the router (maximum simultaneous requests being classified) that matches the available thread count, so latency stays predictable under load.
candle-binding/src/classifiers/lora/parallel_engine.rs:ParallelLoRAEngine::classify_parallel
Routing rules are saved as text expressions. If a signal name — such as an intent category or personal-info type — ever contains a quotation mark or backslash, the saved rule will be corrupted. When the system loads that rule back, it will silently ignore it, meaning that routing policy quietly disappears with no error shown to the user.
What to do: Add quote escaping when building the expression text, and add unescaping when reading it back, so unusual label names don't silently break saved rules.
dashboard/frontend/src/components/ExpressionBuilderSupport.ts:serializeNode
The list of how to connect to each AI provider — including things like required version headers that some providers enforce — is baked in when the software starts and never updated while it runs. If a provider like Anthropic stops accepting old version headers or changes their authentication format, every request to that provider starts failing, and it looks like the provider is down rather than the configuration being out of date.
What to do: Track provider API version requirements in a changelog and add a startup log line showing which API versions are configured for each provider, so a sudden failure can be quickly diagnosed as a catalog update needed.
config/catalog/resources/providers/anthropic.yaml:ProviderDefinition
Configuration values like which hardware device to use and what numeric precision to run at are passed as plain text strings. If there is a typo — even just wrong capitalization — the system silently falls back to a default instead of warning you. You might expect GPU acceleration but get CPU performance, or expect high-precision math but get lower-precision results, with nothing in the logs to explain why.
What to do: Add validation after deserializing the configuration that checks each string field against its known valid values and logs a clear error for unrecognized options before trying to use them.
candle-binding/src/ffi/instances/mod.rs:Options
All management of the loaded AI models goes through a single internal lock. While actual classification runs in parallel just fine, creating, listing, or removing a model instance blocks everything else trying to do the same thing at that moment. Under very high traffic this becomes a bottleneck that is hard to diagnose because normal request processing is unaffected.
What to do: Be aware that hot-reloading models during peak traffic will cause brief management stalls; plan model updates for low-traffic windows until a sharded registry is implemented.
candle-binding/src/ffi/instances/mod.rs:FFI Instance Registry
See the full structural analysis of semantic-router: the pipeline, data models, and system behavior that put these assumptions in context.
Full analysis of vllm-project/semantic-router →Frequently Asked Questions
What does semantic-router assume that could break in production?
The one most likely to cause trouble: The paper is explicit that the headline benefit numbers were calculated from assumptions, not observed from real traffic. The authors themselves say these figures still need to be confirmed against actual production data. If you enable the load-adaptive caching and expect the promised savings, the real outcome could be quite different, and the software will not tell you. What to do: Treat the load-based savings numbers as unverified estimates and measure actual hit rates and model-traffic reduction on your own workload before relying on them.
How many hidden assumptions does semantic-router have?
CodeSea found 13 assumptions semantic-router relies on but never validates, 3 of them critical, spanning Domain, Scale, Resource, Environment, Contract, Ordering, Temporal. Most are routine — the analysis flags the two or three most likely to actually bite.
What is a hidden assumption?
Something the code depends on but never checks: a data shape, an ordering, an environment condition, a scale limit, or a contract with another service. It holds until the world it runs in changes, then fails silently.