Skip to content
OpenSmartRoute

Roadmap

What is complete, what is open and the exit criteria for each milestone.

docs/ROADMAP.md

Status values: Shipped (in a tagged release), Complete (every item on main and the exit criterion measured and met; ships with the next tag), In progress (code on main, part of the exit criterion still open), Planned, Research (tracked, not scheduled). Where a criterion is open, the section names exactly what is missing and what would close it.

Versioning follows SemVer. Each release has an exit criterion that must be met before tagging. Items are ordered by dependency, not by preference. Dates are deliberately absent; the project ships when the criteria are met.

This document is rebuilt from two inputs: (a) an engineering audit of the v0.3.0 code base (what is implemented, what is heuristic, what is missing; section 0), and (b) a literature scan of LLM / agent / tool routing up to September 2026 (summarised as design principles in section 0.3, cited per item below, catalogued in RESEARCH.md). Every planned item names the paper or concept it operationalises so the why survives the what.

v0.4.0 shipped first implementations across the v0.4 to v0.9 themes and v0.5.0 the research track, the routing SLM and autopilot, estimates and the MCP server, the CLI sign-in flow and end-to-end tracing (see CHANGELOG.md). The research track that followed (verbalised difficulty, draft-response and hidden-state representations, elastic budgets, personalisation, self-escalation, hand-off, aggregation, protocol selection, schema-aware and cache-preserving discovery, skill graphs, MCP server recommendation, semantic-router import, energy models, edge/cloud and auctions) is implemented on main with tests; each row below names the module. The per-version sections keep their exit criteria; an item is only Shipped when its code is released, and a version is only closed when its exit criterion has been measured and published.


0. Where we were: engineering audit of v0.3.0#

0.1 What exists (and how it works)#

LayerImplementedMechanism
SignalsLengthSignal, LanguageSignal, ModalitySignal, ComplexitySignal, DomainActionSignal, PII / jailbreak regexesLexicon + regex heuristics, <1 ms, zero deps. Nothing is learned.
PolicyPolicy.check/filterHard constraints before scoring: enable, allow/deny, kind, region, boundary, PII, tenant, cost/latency/token SLO, modality, language, jailbreak ≥ 0.5 → safety/human only
StrategiesRules (pin/prefer/avoid), Capability, Similarity (hashing embedder, optional sentence-transformers), Bandit (Beta–Bernoulli TS per domain), LLMJudge, Cascade (executor, not a scorer)Ensemble $\hat q=\sum_k w_k\kappa_k s_k / \sum_k w_k\kappa_k$; utility $U=w_q\hat q-w_c\tilde c-w_\ell\tilde\ell$ with log-min-max normalisation and a hard quality floor; confidence = softmax margin at fixed $\tau=0.1$
LearningIRTStrategy (2PL, online SGA), PreferenceStrategy (Bradley–Terry / Elo per domain), LinUCBStrategy (disjoint LinUCB on a 8+domains
MathsThompsonBeta (with decay), UCB1, LinUCB, EpsilonGreedy, CostAwareBandit (Lagrangian dual ascent), IRTModel, BradleyTerry, Elo, MarkovChain, RoutingMDP, EWMA, Welford, PageHinkley, WindowDrift, Wilson, ECE, Pareto, TOPSIS, Erlang‑C, Little, KingmanPure Python, deterministic, serialisable
Real-timeCircuitBreaker, TokenBucket, Budget, HealthRegistry/Policy/StrategyNon-consuming checks in policy; one token reserved for the chosen target only
EnterpriseRouterBuilder, EnterpriseRouter, middleware (Guard, Tenant, Cache, Timeout), StateStore (memory, file), Telemetry (logging, metrics), AuditSink (hash-chained JSONL)Ports are ABCs; only in-process / file adapters ship
SecurityInputGuard (size, token estimate, head/tail naturalness gadget score), sanitize_for_prompt, Redactor, GuardMiddleware, load_secretThreat model T1–T10 in SECURITY.md
AdaptersOpenAICompatClient (stdlib, retries, typed errors), judge_fn, embedder, chat_handler, agent harnesses (CallableHarness, HTTPHarness, SubprocessHarnessHarnessResult), SKILL.md loader (load_skills, agentskills.io frontmatter → RouteTarget), sentence-transformers, OpenTelemetryNo connection pooling
Executionexecution.execute/aexecute, Router.run, AsyncRouter.run, EnterpriseRouter.runPlan-aware: skill pre-processor → persona/skill/primary instructions composed into context["system"] → primary handler; one Outcome per participant with role and shared task_id; slots gated by slot_quality_floor
Evalevaluate, cost_quality_frontier, area_under_frontier, expected_calibration_errorOffline, single-draw labels, no counterfactual estimation
SurfaceRouter, AsyncRouter, FastAPI create_app, osr CLI, config.load_targets/load_rules, FeedbackStore (+ RouteLLM preference export)

0.2 What was missing (from the audit) and where it landed#

GapWhere it bitRoadmap homeStatus
All signals are lexicon heuristics; no learned difficulty / task-type / reasoning-need classifiersignals/__init__.pyv0.4Shipped in 0.4.0 (signals.models, signals.ontology)
No static task-type table baseline or "always-strongest" baseline in osr evaleval/__init__.pyv0.4Shipped in 0.4.0 (eval.baselines, TaskTableStrategy)
Single-draw oracle in eval; no paraphrase-robustness or pool-diversity metricseval/__init__.pyv0.4Shipped in 0.4.0 (multi_sample_oracle, eval.robustness)
Confidence uses fixed $\tau$; ECE computed but never used to recalibrate; no abstention / set-valued outputrouter.py::_softmax_marginv0.4, v0.8Shipped in 0.4.0 (math.calibration, RouteDecision.candidate_set)
Target = one model; reasoning effort / think-mode is not a routable dimensioncore/types.pyv0.5Shipped in 0.4.0 (RouteTarget.effort, EffortStrategy)
No personalisation: user / tenant profile does not condition scorescore/types.py, strategiesv0.5Shipped in 0.4.0 (RouteRequest.profile, ProfileSignal)
Cascade runs outside the ensemble and its steps are not fed back to learners; no "stop" action; fixed orderstrategies/cascade.pyv0.6Shipped in 0.4.0 (CascadePlanner, BeliefTracker)
No trajectory step index; learners treat each outcome as immediate, so delayed, task-level reward is not joinedcore/types.py::Outcome, learning/v0.6Shipped in 0.4.0 (Outcome.step, TaskCredit, TaskPins)
Plan slots emit outcomes but strategies do not condition on rolestrategies/, learning/v0.6Shipped in 0.4.0 (signals.extra["role"], role-keyed learner memory: strategies.base.role_of / memory_key)
No MCP / A2A import; no retrieve-then-rank narrowing for large catalogues; SimilarityStrategy is flat top-kadapters/v0.7Shipped in 0.4.0 (adapters.mcp, adapters.a2a, retrieval)
Only in-memory / file StateStore; per-outcome writes; no schema version in saved stateenterprise/__init__.py, learning/__init__.pyv0.7, v0.8Shipped in 0.4.0 (enterprise.stores)
IRT / BT / LinUCB / Markov have no forgetting; only ThompsonBeta decaysmath/irt.py, math/preference.py, math/bandits.py, math/markov.pyv0.8Shipped in 0.4.0 (decay= / discount= on every learner)
CostAwareBandit handles one budget; no multi-resource knapsack, no shadow-audit streammath/bandits.pyv0.8Shipped in 0.4.0 (MultiKnapsackBandit)
Latency is a static latency_ms; queue state / TTFT is not modelled although Erlang-C existsrealtime/, math/decision.pyv0.8Shipped in 0.4.0 (enterprise.ops.QueueAwareStrategy)
No off-policy evaluation (IPS / DR), no shadow mode, no A/B harnesseval/v0.8Shipped in 0.4.0 (eval.ope, enterprise.ops.ShadowMiddleware)
No learning-to-defer objective for TargetKind.HUMANstrategiesv0.8Shipped in 0.4.0 (DeferStrategy)
bandits._inverse is a naive Gauss-Jordan inverse (can go singular); posterior variance not surfaced in StrategyScoremath/bandits.pyv0.8Shipped in 0.4.0 (Sherman-Morrison updates, variance)
Energy / carbon not a cost dimensioncore/types.py::costv0.8Shipped in 0.4.0 (unit_energy, unit_carbon, Objective(energy=, carbon=))
Gadget detector is heuristic; no origin / provenance check on tool parameters in plans; no federated learning of router weightssecurity/v0.9Shipped in 0.4.0 (GadgetDetector, OriginPolicy, merge() on every learner + learning.merge_learners)
Untested: cascade path, audit-chain verification, judge JSON failure modes, state-corruption recovery, multi-replica consistencytests/every releaseShipped in 0.4.0 (tests/test_resilience.py, test_cascade_*; FileAuditSink.verify, AutoLearner.quarantined / refresh)

0.3 Evidence-driven principles (literature, 2025–2026)#

These findings change defaults, not just features. Each is cited in RESEARCH.md.

  1. Beat the boring baselines first. Under unified evaluation many routers, including commercial ones, fail to reliably outperform simple baselines, and a large router-to-oracle gap remains (LLMRouterBench, Jan 2026). A static task-type → model table captured 21 of 29 routable questions in one study ("Most of the LLM Routing Gap Is Task Type", Aug 2026). ⇒ osr eval must always report always-strongest, always-cheapest and static task table alongside any learned strategy, and a TaskTableStrategy becomes a first-class, cheapest-possible learner.
  2. The oracle is noisy. 12–36 % of the reported oracle gap on open-model pools is single-draw label noise that no single-commit router can recover ("How Much of the Routing Gap Is Real?", Jul 2026). ⇒ evaluation adopts a multi-sample oracle protocol and reports a reproducible headroom figure.
  3. Curate, don't pile. Larger ensembles show diminishing returns versus careful curation (LLMRouterBench); hierarchic social entropy saturates below ~10 behaviourally distinct actors ("When is Routing Meaningful?", Jul 2026). ⇒ ship a diversity / coreset report for catalogues.
  4. Robustness is a metric. Surface-form paraphrases should route identically; kNN-style routers collapse under perturbation while prompted routers stay stable (same paper). ⇒ a paraphrase robustness score in osr eval.
  5. Route the task, not the call. Agentic workflows have one delayed, task-level outcome; per-call routers mis-attribute feedback (TRACE-Router, Jul 2026; MTRouter, ACL 2026; ProgRouter, EMNLP 2026). ⇒ Outcome gains task_id; admission-time routing with pinned backend; terminal reward.
  6. Escalation is an optimal-stopping problem, not a threshold (Bayesian self-escalation, Aug 2026; TACIT-Switch, Aug 2026; RLCascadeRouter, Aug 2026). ⇒ cascade becomes an MDP with a stop action.
  7. Control risk, not just cost. Conformal / distribution-free calibration gives set-valued routing with abstention and provable mis-routing bounds (RACER, Feb 2026; RouteNLP, ACL 2026 industry; CR², May 2026). ⇒ conformal thresholds replace hand-tuned escalate_below.
  8. Budgets are knapsacks and the world drifts. Multi-resource constraints, shadow-audit streams, pessimistic reward / optimistic cost, hard meters before commitment (Drift-Aware Sparse Routing, Sep 2026). ⇒ generalise CostAwareBandit; add forgetting to every learner.
  9. Latency is queue state, not a constant (Latency-aware routing with a TTFT estimator, Jul 2026; HW-Router, DAC 2026). ⇒ wire math.decision queueing into HealthStrategy.
  10. Discovery at scale is retrieve-then-rank. In-context selection collapses from Match@1 0.85 → 0.12 as registries grow to thousands of models / agents / tools / skills; retrieve-then-rank crosses over at N≈500 (Enrich-Retrieve-Rank, Aug 2026); hybrid BM25 + dense with RRF cut MCP tool tokens by 99 % in production (SCOUT, Aug 2026). ⇒ two-stage catalogue narrowing before scoring.
  11. Skill sets are submodular knapsacks (Best Prefix Selection, Aug 2026). ⇒ RoutePlan skill slots are selected as a set under a token budget, not top‑k.
  12. Reasoning effort is a routable dimension (Think When Needed, SIGIR 2026; SCX Router predicts task type, difficulty, reasoning mode and output length before generation, Sep 2026). ⇒ targets carry effort variants; signals predict whether to think.
  13. Personalisation matters exactly when it changes the answer (GMTRouter, EMNLP 2026; SkillFeed profile-conditioned skill routing, Aug 2026; xRouteBench personalised tasks). ⇒ profile-conditioned scoring with counterfactual tests.
  14. Routing data is fragmented and private. Federated router training improves the accuracy–cost frontier over client-local routers ("Federate the Router", Jan 2026). ⇒ mergeable learner state.
  15. The control plane is an attack surface. Beyond confounder gadgets: indirect prompt injection via tool content (ROPE, Aug 2026), unsigned MCP manifests, and recognition-without-enforcement gaps. ⇒ origin checks on state-changing tool parameters inside routed plans.

v0.3: Real integrations (Shipped)#

Goal: route to real providers with the same code that runs in tests.

ItemStatusNotes
OpenAI-compatible HTTP client (stdlib, retries, typed errors)Shippedadapters.OpenAICompatClient; works with OpenAI, Azure, vLLM, Ollama, LiteLLM, OpenRouter, Groq
Judge / embedder / handler glue for the clientShippedjudge_fn, embedder, chat_handler
sentence-transformers embedderShippedadapters.sentence_transformers_embedder (extra: embeddings)
OpenTelemetry telemetry adapterShippedadapters.OpenTelemetryTelemetry
Public config loaders with validation errorsShippedconfig.load_targets / load_rules
Rate-limit tokens consumed only for the chosen targetShippedbug fix in HealthPolicy / EnterpriseRouter

Exit criterion: an end-to-end test routes, escalates to an LLM judge, and executes against an in-process OpenAI-compatible server with zero mocks of the transport. Met.


v0.4: Learned signals and honest evaluation (Shipped)#

Goal: replace lexicon heuristics with trained, still-tiny models, without adding torch to the core, and make osr eval hard to fool.

Ordering rationale: principle 1 (baselines) and 2 (noisy oracle) come first because they decide whether anything in v0.5+ is worth building.

0.4a Evaluation harness#

ItemStatusNotes
Mandatory baselines in osr eval: always-strongest, always-cheapest, random, static task-type table, oracleShippedosr eval --baselines; eval.baselines reports gap-to-oracle and the label-noise floor (LLMRouterBench 2601.07206; "Most of the routing gap is task type" 2608.23023)
Multi-sample oracle protocolShippedmulti_sample_oracle() and noise_floor() decompose the gap into label-noise floor vs recoverable (2607.03436)
Run-to-run varianceShippedrepeat_flip_rate() in osr eval --robustness (5 % run-to-run flips observed in 2608.23023)
Paraphrase-robustness scoreShippedparaphrase_robustness(): deterministic surface-form rewrites, % identical decisions (2607.09197)
Pool-diversity reportShippedeval.robustness.diversity() and greedy k-center coreset()
Latency-aware frontierShippedeval.frontier.frontier3() and hypervolume(); osr eval --frontier3
Dataset adaptersShippedeval.datasets presets: routerbench, llmrouterbench, routereval, xroutebench, routerxbench; osr eval --preset
Component-level ablation reportShippedablation_report() drops one strategy or extractor at a time; osr eval --ablation

0.4b Learned signals (zero core deps)#

ItemStatusNotes
TaskTableStrategyShippedper-(task type x target) success table with Wilson intervals; fit_task_table() from outcomes
Task-type classifierShippedTaskTypeSignal: multinomial logistic regression on hashed n-grams over signals.ontology.TaskOntology (SCX Router 2609.02292); weights ship as JSON; falls back to DOMAIN_LEXICON
Difficulty classifierShippedLearnedDifficultySignal; training on RouterBench / LLMRouterBench outcomes via osr train --from
Reasoning-need signalShippedReasoningNeedSignal (Think When Needed 2601.18146); consumed by EffortStrategy
Expected-output-length signalShippedOutputLengthSignal regression head
Verbalised-difficulty retrievalShippedsignals.VerbalisedDifficultySignal: reads a small model's verbalised difficulty (number, 7/10, or words) from context["difficulty"] or a callable, blends it into complexity and raises reasoning_need; parse_difficulty() (VDAR-Router 2607.18098)
Training CLI osr train --from feedback.jsonlShippedproduces the SignalModelBundle JSON; --gadget also trains the gadget detector
Cold-start synthetic supervisionShippedsynthesize_dataset() generates labelled prompts per ontology node (TRouter 2604.09377; SCX); osr train --synth

0.4c Calibration#

ItemStatusNotes
Temperature / isotonic fitting of confidenceShippedTemperatureScaler, IsotonicCalibrator; osr eval --calibration reports ECE, Brier and reliability bins
Judge-score calibrationShippedLLMJudgeStrategy(calibrate=True, min_fit=20) keeps an IsotonicCalibrator per judge instance, pairs each raw judge score with the served target's observed quality/success and re-fits on every outcome; raw scores are kept in the rationale (raw=) once the mapping is active; calibrator state is persisted with state()/load()
Confidence exposed per strategyShippedposterior variance surfaced for LinUCB and IRT

Exit criterion: on examples/eval_dataset.jsonl and one public suite, learned signals beat the static task table by a margin larger than the measured label-noise floor; osr eval reports ECE <= 0.10; paraphrase robustness >= 0.9; core dependency count remains zero.

Measurement (published with 0.5.0, partially met). Command: osr -t <targets> [--slm slm.json] eval <suite> --baselines --calibration --robustness --limit 1000. The public suites are the human-preference battles relabelled onto three tiers (llm-small, llm-mid, llm-frontier) by osr collect; the SLM bundle was trained on Arena-55k + RouteLLM battles and the suites below are held out.

SuiteRouter (learned)DeclarativeStatic task tableRandomCheapestParaphraseECE rawECE held-out isotonic
examples/eval_dataset.jsonl (n=30, 17 targets)0.8330.8330.2330.1330.0670.8670.2450.261 (n=15)
MT-Bench human (n=1000)0.5190.4210.5410.4410.3720.9730.3160.050
PPE human (n=1000)0.5610.4180.6510.4450.1740.8980.2790.036
WebDev Arena (n=1000)0.533-0.6700.3910.0000.8720.2530.060
  • Met: learned signals beat the declarative router by +10 to +14 pp on every held-out suite and beat random / cheapest / best-prior everywhere; zero core dependencies; paraphrase robustness >= 0.9 on two of three public suites. With a calibrator fitted on half the suite, held-out ECE is 0.04-0.06 (isotonic) and 0.05-0.13 (temperature), i.e. the <= 0.10 target is met after fitting.
  • Not met: on the public suites the static task table (which, with no task-type labels, degenerates to "always the best single tier") still wins by 2-14 pp, because a pairwise preference between two models is a weak label for a three-tier decision; the label-noise floor is not measurable on these rows (one sample per prompt, noise_floor() needs samples); raw, unfitted ECE is 0.25-0.32.
  • What this means: ship the router with a fitted TemperatureScaler/IsotonicCalibrator (see calibration_report()["held_out"]), and use multi-sample suites (RouterBench presets) to beat the task table by more than the noise floor - that measurement is tracked under v1.0 leaderboard runs.

Re-measurement (0.5.x SLM recipe, leave-one-source-out). The routing SLM now trains on ten public sources, so each suite below was held out in turn and the SLM trained on the other nine (same command, same targets, --limit 1000). The recipe change that matters for mixed catalogues - a corpus of model battles no longer pushes down tools, skills and agents it never compared, and the strategy abstains on them - takes examples/eval_dataset.jsonl with the SLM from 0.43 to 0.80 (0.83 without it; the old recipe made any SLM harmful there). On the tier suites the numbers are unchanged within noise: PPE 0.561, MT-Bench 0.508, WebDev Arena 0.506 (declarative 0.42 / 0.42 / -, static table 0.651 / 0.541 / 0.670); held-out isotonic ECE 0.03 / 0.07 / 0.07. The conclusion stands: learned signals beat the declarative router by +9 to +14 pp, and a pairwise battle remains too weak a label to beat "always the best tier".


v0.5: Target representations, effort and personalisation (Complete)#

Goal: learn what a target is good at from its own outcomes, so examples and quality_prior are derived, not hand-written, and widen "target" to cover how hard the model should think and for whom.

0.5a Representations#

ItemStatusNotes
Target embeddings from outcome logsShippedlearning.target_embedding(): centroid of description, capability lexicon and examples; nearest_targets() routes to unseen targets via neighbours (UniRoute / EmbedLLM)
Offline reward-matrix warm startShippedlearning.warm_start_from_matrix(rows, strategies, weight=): replays a full-information (prompt, {target: reward}) matrix into every learner before going online (LinUCB ridge per arm, IRT ability, Thompson posteriors, task table, Markov rewards); weight < 1 shrinks the offline evidence toward the prior (OrcaRouter 2605.30736)
Automatic examples miningShippedlearning.ExampleMiner promotes high-quality outcomes into target.examples, de-duplicated by cosine
Cold-start via IRT warm-upShippedlearning.warm_start() copies shrunk IRT ability, Thompson posteriors and Bradley-Terry strengths from neighbours (IRT-Router)
Target similarity to fallback orderingShippedlearning.SimilarityFallback
Query-response mixed representationShippedsignals.DraftResponseSignal: hedges, self-corrections, query overlap and length ratio of a cheap draft (context["draft"] or a drafter callable) become extra["draft_uncertainty"] and lift complexity (JiSi 2601.01330)
Hidden-state Dirichlet routerShippedmath.DirichletProbe (evidential Dirichlet head, digamma loss, KL regulariser, pure Python SGD) and strategies.HiddenStateStrategy(state_fn, targets, dim); epistemic uncertainty K/S becomes the strategy confidence so an unfamiliar state defers to the other strategies (ProbeDirichlet, RouterXBench 2602.11877)

0.5b Effort as a routable dimension#

ItemStatusNotes
Target.effort variantsShippedRouteTarget.effort in {none, low, medium, high} with RouteTarget.family grouping siblings for breaker / budget purposes
Think-vs-non-think decisionShippedEffortStrategy / decide_effort() match ReasoningNeedSignal to effort level (Think When Needed 2601.18146; When to Think Deeply 2606.06745)
Token-budget-aware routing to elastic modelsShippedstrategies.expand_elastic(parent, [BudgetVariant(...)]) turns one elastic model into <id>@<budget> siblings sharing family and handler; TokenBudgetStrategy scores the fit between token_budget and the tokens the answer needs (truncation risk vs unused capacity) (Nemotron Elastic 2511.16664, Star 2605.07182)

0.5c Personalisation#

ItemStatusNotes
RouteRequest.profileShippedopaque user / tenant profile features; ProfileSignal hashes them into the LinUCB context
Few-shot user adaptationShippedlearning.UserAdaptiveStrategy: per-user x target Beta posteriors shrunk toward neighbour users (cosine over hashed profile vectors) and the global posterior, w = n / (n + kappa); observe_user() warm-starts from logged interactions; state()/load() (GMTRouter 2511.08590)
Counterfactual personalisation testShippedeval.robustness.profile_swap_fairness() measures how often the decision changes with the profile (SkillFeed methodology 2608.28241)

Exit criterion: adding a new target with only id, kind and cost reaches >= 90 % of its hand-configured routing accuracy after 200 outcomes; effort routing reduces tokens >= 30 % at equal quality on a reasoning benchmark. First half met: eval.criteria.cold_start_ratio() strips the legal expert down to id/kind/cost next to seven configured experts and a generalist, streams mixed traffic through the real Router with explore_rate=0.1, and measures 1.000 x the hand-configured accuracy after 200 outcomes for the newcomer (0.000 before learning; 1 643 requests, 180 of them exploratory). Making this pass required cold-start exploration in the router (Router(explore_rate=, explore_min_samples=)), because a target with no description or examples is never ranked first and so never earns an outcome. Reproduce with python scripts/exit_criteria.py.

Effort-token half met in eval.criteria.effort_token_savings(): one reasoning model exposed through expand_elastic() as reasoner@fast (effort="low") and reasoner@think (effort="high"), 2 000 rows carrying per-sibling scores and tokens (60 % plain lookups / rewrites where both siblings score 0.90, 40 % proof / analysis prompts where the fast sibling drops to 0.50 - the pattern Think When Needed and When to Think Deeply report for live thinking / non-thinking pairs). The real Router with default_strategies() + EffortStrategy() + TokenBudgetStrategy() sends 59 % of the rows to the fast sibling and spends 61.6 % of the always-think tokens (1 234 vs 2 003 per row; oracle 1 231) at quality 0.900 vs 0.900. EvalRow.tokens is the new dataset field and osr eval DATASET --effort runs the same comparison on rows collected from live thinking / non-thinking pairs; the synthetic figure is the roadmap claim until a live pair is published from the hosted platform.


v0.6: Multi-step, multi-turn and agentic routing (Complete)#

Goal: treat routing as a sequence, not a single pick; align the unit of learning with the unit of supervision (the task).

0.6a Data model#

ItemStatusNotes
Outcome.task_id, Outcome.step, Outcome.roleShippedpopulated by Router.run(); learning.TaskCredit joins a terminal task-level reward to every call in the trajectory (TRACE-Router 2607.22465)
Delayed-feedback bandit updatesShippedTaskCredit buffers outcomes per task and credits on completion with uniform / discounted / last / blended schemes (Joulani et al. 2013)
Session history in RouteRequest.historyShippedHistorySignal turn-level features; learning.HistoryTargetStrategy scores each target by a logistic model over the history-target joint embedding h * e_t (recency-weighted hashed history, catalogue target embedding, shared weights + per-target bias, online SGD), with an incumbent bonus that flips to a penalty when context["last_failed"] is set; state()/load()/merge() (MTRouter 2604.23530)

0.6b Strategies#

ItemStatusNotes
Admission-time task routingShippedlearning.TaskPins pins a task to its first target and releases on failure or after max_steps (TRACE-Router; MTRouter)
Cascade as MDP with a stop actionShippedCascadePlanner(mode="mdp"): finite-horizon expected value over candidate orderings (RLCascadeRouter 2608.15817; Dekoninck 2024). CascadeResult.outcomes() / Router.learn_cascade() turn every executed step into a per-step Outcome (rejected = failure with the gate score, accepted = success) that every learner consumes
POMDP self-verification cascade (AutoMix)ShippedCascadePlanner(mode="pomdp") with BeliefTracker
Bayesian self-escalationShippedstrategies.SelfEscalation keeps a Beta competence posterior updated per streamed chunk (hedges, refusals, self-corrections, repetition, overrun) and stops when loss(stop) < loss(continue); wrap_stream(chunks, monitor) cuts any text iterator at the escalation point; SelfEscalation.for_target() seeds the prior from quality_prior (2608.24087)
Permanent-handoff policy from censored teacher signalsShippedlearning.MixtureCureModel (Weibull mixture-cure on cumulative risk, grid MLE with censoring) and learning.HandoffPolicy(threshold, hazard_threshold, horizon_steps, fallback_target, pins): hands a task over once and releases its TaskPins pin (TACIT-Switch 2608.27911)
Progress-guided step routingShippedProgressRouter: step index, failures, budget spent and last target condition each step's route (ProgRouter 2608.25992; 2511.06190)
Routing / aggregation switchShippedstrategies.MixtureOfAgents(k, confidence_below, budget_usd, aggregator): calls the top-k alternatives when the decision is unsure and the budget allows, aggregates by semantic majority vote (majority_vote) or a supplied synthesiser, and emits one Outcome per participant (JiSi 2601.01330; Mixture-of-Agents)
Multi-round executor (Router-R1)ShippedMultiRoundExecutor: route, execute, judge, refine loop with bounded rounds
Plan executionShippedRouter.run() / execution.py: skill pre-processes, persona and skill instructions set the system prompt, primary answers; per-participant outcomes; slot_quality_floor
Conversation-state MDP from real sessionsShippedMarkovStrategy(state_from="auto") keys transitions and rewards on the learned task type (ontology leaf from the task-type classifier) joined with the domain, "task_type" / "domain" force one axis; per-request state memory so the outcome credits the state that was scored; decay= forgets stale sessions
Collaboration-protocol selectionShippedstrategies.ProtocolPolicy: failure_risk() from decision confidence, signals and response uncertainty; an ordered ProtocolRule table (risk, budget, task type) picks single / cascade / aggregate / debate / handoff; per-protocol ledger reports which protocol actually paid (2608.14927)

Exit criterion: on tau-bench style agentic tasks, task-level routing beats the best single target on the accuracy-latency frontier; on multi-hop QA the executor beats the best single target at <= 60 % of its cost, reproducibly via osr eval --multi-round; cascade outcomes are consumed by every learner. Multi-round half met on the synthetic scored dataset in eval.criteria.multi_round_vs_best_single() (1 000 rows, small / medium / large targets with per-row scores): MultiRoundExecutor with failures_before_switch=1 reaches quality 0.911 (best single target large: 0.900, oracle 0.912) at 49.8 % of the best single target's cost, 1.8 rounds on average. osr eval DATASET --multi-round runs the same comparison on any dataset whose rows carry scores. Cascade outcomes are consumed by every learner (Router.learn_cascade(), tested).

tau-bench half met in eval.agentic.task_routing_frontier(): 1 000 synthetic tasks of 3-5 steps (customer support, coding, travel, finance; 30 % reasoning-heavy steps) with a per-step success / latency table for three agents - fast-agent (400 ms, 0.97 on plain steps, 0.45 on hard ones), balanced-agent (900 ms, 0.96 / 0.72) and strong-agent (2 000 ms, 0.94 / 0.90) - so no single agent dominates per step, the shape tau-bench reports across model tiers. Each agent is a CallableHarness behind harness_handler(); the real ProgressRouter (escalate on failure, switch after two) routes every step with EffortStrategy and the auto-learning quartet, TaskCredit credits the task result back, and both policies get one retry per step on common random numbers. Routed accuracy 0.987 vs 0.977 for the best single agent (strong-agent) at 45.4 % of its latency (3.9 s vs 8.5 s per task; 67 % of steps go to fast-agent, 33 % to strong-agent). AgentTask / load_agentic_tasks() define the JSONL format and osr eval TASKS.jsonl --agentic runs the same comparison on a table collected from live harnesses; the synthetic frontier is the roadmap claim until a live one is published.


v0.7: Catalogue interop and discovery at scale (Complete)#

Goal: stop hand-writing targets.yaml for things that already describe themselves, and keep routing accurate when the catalogue has thousands of entries.

0.7a Import#

ItemStatusNotes
MCP tool catalogue import to TargetKind.TOOLShippedadapters.mcp.tools_from_mcp() (offline tools/list), connect_mcp() (stdio JSON-RPC), enrich_description() at import time (Enrich-Retrieve-Rank 2608.22695)
MCP server recommendationShippedadapters.recommend_servers(task, servers, k, constraints, allowed_auth): BM25 + hashing-cosine fusion over ServerCard text (name, description, tool names, tags) with hard filters on latency, region, data boundary and auth kind; rationale names the matched tools (Task2MCP / T2MRec 2604.17234; ComplexMCP 2605.10787)
A2A agent cards to TargetKind.AGENTShippedadapters.a2a.fetch_agent_card(), agent_from_card(), skills_from_card(), a2a_handler()
Agent-harness adapters to TargetKind.AGENTShippedCallableHarness, HTTPHarness, SubprocessHarness with a common HarnessResult and task_id-carrying outcomes (2607.11399; SWE-Router 2607.00053)
Skill packages to TargetKind.SKILLShippedload_skills() reads agentskills.io SKILL.md folders
Persona catalogue importShippedadapters.personas.load_personas() from Markdown, JSON, CSV and Copilot agent / chatmode files; persona choice learned from plan-level outcomes (MasRouter)
OpenAI-compatible proxy modeShippedPOST /v1/chat/completions with model: "auto" routes, executes and learns; GET /v1/models
LangGraph / Microsoft Agent Framework nodeShippedadapters.frameworks.langgraph_node(), langgraph_condition(), maf_router_executor()
vLLM Semantic Router / gateway parityShippedadapters.load_semantic_router_config(path_or_dict) imports a semantic-router config.yaml (categories, model_scores, use_reasoning, model_config.pricing, system_prompt) as RouteTargets plus category Rules, so an existing gateway config routes through OpenSmartRoute unchanged (2603.04444)

0.7b Two-stage selection#

ItemStatusNotes
Retrieve-then-rank narrowingShippedretrieval.Retriever: BM25 + hashing-dense retrieval with reciprocal-rank fusion, on above narrow_above (default 500) targets (Enrich-Retrieve-Rank; SCOUT 2608.23992)
tool_search / execute_tool meta-toolsShippedretrieval.tool_search_target() and execute_tool_target() expose the router as two MCP tools (SCOUT)
Field-aware / schema-aware tool matchingShippeddiscovery.schema_match(request, target) extracts typed entities (emails, URLs, dates, times, paths, ids, numbers, currency, quoted strings) and scores coverage of the tool's required input_schema properties; discovery.SchemaAwareStrategy fuses it with the other strategies (SchemaRouter 2608.21375)
Cache-preserving tool routingShippeddiscovery.CachePreservingSelector(prefix_size, evict_after) keeps a per-session ordered tool prefix byte-stable across turns, appends new tools and evicts only after inactivity; prefix_hit_ratio() reports cache stability (CacheRouter 2608.22708)
Submodular skill-set selectionShippedretrieval.select_skill_set(): greedy benefit minus redundancy under a token budget (Best Prefix Selection 2608.19993)
Profile-conditioned skill routingShippedlearning.SkillAffinity: Beta posterior per (profile bucket, skill); relevance(profile, skills) plugs into select_skill_set(relevance=) (SkillFeed 2608.28241)
Skill graphs / compositionShippeddiscovery.SkillGraph: requires / conflicts / composes edges from SKILL.md frontmatter (osr-requires, osr-conflicts, osr-composes) plus learned co-usage; compose(seeds) closes under dependencies, resolves conflicts by priority and returns a dependency order (2606.18051; CaSKG 2608.25500)

0.7c Shared state#

ItemStatusNotes
Redis / Postgres StateStoreShippedenterprise.stores.RedisStateStore, SQLStateStore (DB-API 2.0), BatchedStateStore for write coalescing, NamespacedStateStore
State schema version and migrationShippedVersionedStateStore stamps {"_schema": n} and runs forward migrations; refuses to load newer state

Exit criterion: a fresh install routes to an MCP server's tools with no YAML written; with 5 000 synthetic tools Match@1 stays within 5 points of the 50-tool figure. Met. eval.criteria.match_at_1_at_scale() builds 50 and 5 000 verb-object-qualifier tools (synthetic_tool_catalogue()), routes 200 paraphrased prompts through RouterBuilder.with_retrieval() and measures Match@1 1.000 -> 1.000 (drop 0.000).


v0.8: Operations, risk control and economics (Complete)#

Goal: make the learners safe to leave running: bounded risk, bounded budgets, honest under drift, measurable without live traffic.

0.8a Risk control and abstention#

ItemStatusNotes
Conformal escalation thresholdsShippedmath.calibration.ConformalCalibrator (split conformal) behind RouterBuilder.with_calibration() (RouteNLP 2604.23577)
Set-valued routing with abstentionShippedRouteDecision.candidate_set = smallest nested set with mis-routing risk <= alpha (RACER 2603.06616; CR2 2605.12001)
Learning-to-defer for TargetKind.HUMANShippedDeferStrategy (Madras 2018; Mozannar and Sontag 2020; Verma 2023)
Uncertainty features from the responseShippedsignals.semantic_entropy() / response_uncertainty() (meaning clusters over sampled answers, hedging, optional P(True) judge); signals.UncertaintyGate is a drop-in Cascade quality gate and MixtureOfAgents trigger; signals.EventTrigger fires named actions from threshold rules (Kuhn 2023, Kadavath 2022; 2607.13048)

0.8b Non-stationarity and budgets#

ItemStatusNotes
Forgetting in every learnerShippeddecay < 1 for ThompsonBeta, IRTModel, BradleyTerry, MarkovChain (row counts) and RoutingMDP (exponentially weighted rewards); discount < 1 for LinUCB with an undiscounted ridge floor (D-UCB, Garivier and Moulines 2011); MarkovStrategy(decay=) wires both Markov knobs
Multi-knapsack CostAwareBanditShippedmath.bandits.MultiKnapsackBandit: several resources (USD, tokens, GPU-seconds, energy), one shadow price per resource by dual ascent, hard sliding-window meters checked before commitment, learned optimistic costs, pessimistic rewards, bounded audit stream of every pruned arm (Drift-Aware Sparse Routing 2609.00662; Badanidiyuru 2013)
Tenant fairness under shared budgetsShippedenterprise.ops.FairShareMiddleware: dominant-resource fairness over a sliding window
Numerically stable LinUCBShippedSherman-Morrison rank-1 updates; surfaced posterior variance

0.8c Latency, hardware and energy#

ItemStatusNotes
Queue-aware latency estimateShippedenterprise.ops.InflightTracker + QueueAwareStrategy (Erlang-C / Kingman) (Latency-aware routing 2607.18253)
Energy / carbon as cost dimensionsShippedRouteTarget.cost["wh_per_1k_tokens"] / ["gco2_per_1k_tokens"] (or gco2_per_wh grid intensity) read by unit_energy / unit_carbon; Objective(energy=, carbon=) weights them in the utility, log-normalised like money; MultiKnapsackBandit meters them as resources
Hardware-aware costShippedmath.EnergyModel fits Wh = e0 + e_in * prompt + e_out * output per target by ridge regression from measured samples and converts to gCO2 with a grid factor; math.HardwareProfile / hardware_profile() give priors for A100, H100, L4, RTX 4090, CPU and NPU when no meter exists; feeds wh_per_1k_tokens / gco2_per_1k_tokens (2608.28044; HW-Router 2608.14575)
Edge-cloud token-aware routingShippedstrategies.EdgeCloudStrategy: targets tagged `metadata["tier"] = edge

0.8d Offline and online evaluation#

ItemStatusNotes
Off-policy evaluationShippedeval.ope: IPS / SNIPS / doubly-robust with weight clipping and effective sample size; osr ope (Dudik et al. 2011); RouteDecision.propensities logs the softmax routing policy so LoggedDecision.from_decision needs no extra plumbing
Shadow mode and A/B harnessShippedenterprise.ops.ShadowMiddleware and ABTest with Wald's SPRT for promotion
Federated learner mergeShippedmerge() on ThompsonBeta, LinUCB, IRTModel, BradleyTerry, MarkovChain / RoutingMDP, BanditStrategy and TaskTableStrategy combine sufficient statistics without sharing prompts; learning.merge_learners(local, remote) pairs same-named strategies across replicas ("Federate the Router" 2601.22318). AutoLearner.refresh() is the writer / reader pattern for replicas that share one StateStore
Market / auction strategyShippedstrategies.AuctionStrategy: targets bid (claimed success, price); claims are corrected by each bidder's observed bias and realised rate, the highest corrected surplus wins and pays the second price (EA-RAM 2608.12719)

Exit criterion: conformal thresholds hit their nominal mis-routing rate +/- 2 pp on held-out data; the knapsack bandit never exceeds a hard budget in a 10^6-step simulation with drift; IPS estimates of a held-out strategy are within the bootstrap CI of its live result. Met (python scripts/exit_criteria.py --full):

  • conformal_coverage(): ConformalCalibrator(alpha=0.1) fitted on 1 000 router propensities over graded mixed-domain prompts covers 0.909 of 1 000 held-out labels (gap 0.9 pp, mean set size 1.2).
  • knapsack_never_exceeds_cap(): MultiKnapsackBandit(on_capped="abstain") over 10^6 steps with reward means reshuffled every 10^5 steps; peak sliding-window spend / cap = 0.9998 (USD) and 0.9996 (tokens), zero abstentions, mean reward 0.75. Getting there fixed two real defects: the "release the cheapest arm" fallback could breach the cap (peak 1.0165), and shadow prices exploded for token-scale budgets.
  • ope_within_live_ci(): 2 000 decisions logged under a cost-heavy softmax policy; IPS 0.812, SNIPS 0.816 and DR 0.816 all fall inside the live value's 95 % bootstrap CI [0.798, 0.832] (live 0.815, n_eff 992).

v0.9: Security hardening of the control plane (Complete)#

Goal: close the gaps listed in SECURITY.md that heuristics cannot.

ItemStatusNotes
Learned gadget / confounder detectorShippedsecurity.gadget.GadgetDetector; InputGuard(learned=True); trained via osr train --gadget (Rerouting LLM Routers)
Origin policy for tool parametersShippedsecurity.provenance.OriginPolicy: sensitive parameters of state-changing tools must originate from the user turn (ROPE 2608.27496)
Signed MCP manifestsShippedadapters.mcp.sign_manifest() / verify_manifest() (HMAC-SHA256 or Ed25519); osr mcp-manifest (2601.23132)
Resource-amplification limitsShippedsecurity.limits.ResourceLimiter: per-task caps on steps, tool calls, depth, tokens, cost and wall-clock (Beyond Max Tokens 2601.10955)
Safety-routing regression suiteShippedsecurity.safety.run_safety_suite(); osr safety --learned-guard gates CI (When Safety Routing Breaks 2609.01455)
Encrypted state at restShippedenterprise.stores.EncryptedStateStore (AES-256-GCM, key rotation)
External review packCompleteSECURITY_REVIEW.md: scope, trust boundaries, evidence table with reproduction commands, reviewer questions, known gaps, review log

Exit criterion: red-team suite passes in CI (met); published threat-model delta (met: SECURITY.md, "Threat-model delta: 0.3 -> 0.4"); a published review pack that lets an independent party run the review without a briefing (met: SECURITY_REVIEW.md).

The original wording also required the independent report itself. That cannot be produced from inside the repository, so it is now the adoption item it really is: tracked in the v1.0 readiness table below and closed when a third-party report is added to the review log in SECURITY_REVIEW.md.


v1.0: Stable API (Complete)#

Goal: freeze the public surface.

  • All opensmartroute.* public symbols frozen; the snapshot test (tests/public_api.json) already fails on any added or removed name. Deprecation policy: shipped in CONTRIBUTING.md, "Public API and deprecation policy" with errors.deprecated() / OpenSmartRouteDeprecationWarning as the mechanism.
  • Documented performance envelope: shipped in ARCHITECTURE.md (p50 / p95 / p99 for 16 / 64 / 256 / 1024 targets, with and without two-stage narrowing; python scripts/bench.py --scale reproduces it).
  • Security review of the control plane against docs/SECURITY.md threat model: the review pack is shipped (SECURITY_REVIEW.md); the independent report is an adoption item (table below).
  • Reference deployments: shipped as library (pip install), sidecar (deploy/Dockerfile, osr serve) and central control plane (deploy/helm/opensmartroute; hardened pod security, HPA, PDB, NetworkPolicy, ConfigMap-driven catalogue) - see deploy/README.md; hosted platform on Azure Container Apps with Azure OpenAI (platform/, infra/, azd up; community and enterprise editions, API keys, plans, metering, tenants, audit) - see platform/README.md.
  • Public leaderboard runs with reproducible configs: shipped - examples/leaderboard/results holds the 0.5.0 run (learned router vs declarative, static task table and baselines on MT-Bench human, PPE human and WebDev Arena, with robustness and held-out calibration; suite-level hold-out, seed 0, SLM SHA-256 recorded) produced by the recipe in examples/leaderboard. Listing on RouterArena, LLMRouterBench and xRouteBench is a submission to those projects and is tracked below.

Exit criterion (settled with 1.0.0). 1.0 means what the repository can prove: a frozen public API with a deprecation path, a published performance envelope, reference deployments, the security controls with a red-team suite in CI and a review pack, a published leaderboard run, and two consecutive releases without a breaking change. Adoption evidence - an independent security report, accepted leaderboard listings, two production users - needs a third party, so it is tracked and reported but does not gate the version. Nothing is typed by hand: python scripts/release.py readiness computes the table below from the repository (snapshot and tests present, envelope section, deployment files, review pack and CI safety suite, published run, non-breaking release steps in CHANGELOG.md verified against the tagged tests/public_api.json, review-log rows in SECURITY_REVIEW.md, accepted rows under Listings in the results page, rows in ADOPTERS.md); release.py check refuses any 1.x version while a release row is open, and the Release workflow prints the whole table in its summary.

Readiness itemKindHow it is verifiedStatus
Public API frozen with a deprecation pathreleasetests/test_public_api.py fails on any removed or renamed name; errors.deprecated()Met
Performance envelope publishedreleaseARCHITECTURE.md, scripts/bench.py --scaleMet
Reference deployments (library, sidecar, control plane, hosted)releasedeploy/, platform/, live at opensmartroute.aiMet
Security controls, red-team suite in CI, threat-model delta, review packreleasev0.9 aboveMet
Leaderboard run published with reproducible configreleaseexamples/leaderboard/resultsMet
Two consecutive releases with no breaking changerelease0.4.0 -> 0.5.0 and 0.5.0 -> 1.0.0, additions only against the tagged tests/public_api.jsonMet
Independent security reportadoptionA row in SECURITY_REVIEW.md, "Review log"Open
Leaderboard listings acceptedadoptionAn accepted row under results, "Listings" linking the published runOpen
Two independent production usersadoptionTwo rows in ADOPTERS.md (named, or anonymised with consent)0 of 2

What closes each adoption row:

  • Security report - a third party runs SECURITY_REVIEW.md and adds a row to its review log with the report link; findings are fixed or accepted first.
  • Listings - submit the 0.5.0 run (commit, SLM SHA-256, results page) to RouterArena, LLMRouterBench and xRouteBench; record the entry URL and accepted in the results page.
  • Production users - two organisations agree to be listed in ADOPTERS.md.

After 1.0 the public API changes only through the deprecation policy: a removal needs a deprecated() warning for at least one minor and a ### Removed entry, which the readiness check reads as a breaking step and the snapshot test refuses without an explicit snapshot update.


Research track#

Every question below now has a runnable answer in the tree; the table records where, so the question can be re-asked against a real catalogue and dataset rather than argued about.

QuestionAnswer in the codeRelated work
Is the remaining router-to-oracle gap real or label noise on our pools?eval.headroom.routing_headroom(rows, targets): oracle vs best single target, split into the label-noise floor (noise_floor) and the measurable, recoverable headroom2607.03436; LLMRouterBench 2601.07206
Does a static task table already capture most of the value for typical enterprise catalogues?eval.headroom.learnability_by_difficulty buckets rows by oracle-minus-best-single gap; TaskTableStrategy is a mandatory osr eval --baselines row, so the table's share of the headroom is reported per run2608.23023
How few, and how different, must targets be before routing beats the best single model?eval.headroom.target_diversity (mean pairwise disagreement, winner entropy / share) and min_catalogue(rows, targets, fraction) (smallest subset keeping 95 % of the oracle)2607.09197
Contrastive router training on (query, target) pairslearning.ContrastiveRouter (softmax cross-entropy over q·e_t / τ against the acceptable set of a row, acceptable_set(row, slack)) and ContrastiveStrategy with online updates from outcomesRouterDC (NeurIPS 2024); RouterDC-style objectives in LLMRouter
Reward-model-distilled routing labelslearning.soft_labels(scores, temperature) and ContrastiveRouter.fit(objective="distilled"): train against the softened reward distribution instead of one-hot labelsZooter
Can the whole ensemble be compressed into one small routing model?learning.RouterSLM (hashed dual encoder + temperature calibration + target snapshots in one JSON file) and distill_router(router, texts), which trains it on the router's own propensities; SLMStrategy puts the student back in the ensemble and keeps learning online; osr slm train/eval/predictRouteLLM matrix-factorisation router; Zooter; RouterDC
Does a router keep improving on its own from live data, without regressing?learning.SelfImprover: champion/challenger cycle over feedback rows (eval.rows_from_feedback), Hub datasets (eval.DatasetCollector, RouterBench / RouterEval) and synthetic rows; challenger is calibrated on the holdout and promoted only above OSR_SLM_MIN_GAIN; JSONL history; osr improveRouterBench 2403.12031; RouterEval 2503.10657
Where do current prices and quality priors come from at scale?adapters.ModelCatalogue (OpenRouter prices, Hugging Face model-index benchmarks and popularity → quality_prior, Pareto frontier(), cost_benchmark()) and adapters.WebKnowledge (Hugging Face / DuckDuckGo / Brave discovery, risk-scored page text); osr catalogueLLMRouterBench 2601.07206 cost-quality frontier
Model-level scaling: does accuracy keep rising with more targets?eval.headroom.scaling_curve(rows, targets, sizes): oracle / best-single / headroom for random sub-catalogues of each size, exposing diminishing returnsRouterEval; LLMRouterBench diminishing returns
Intra-generation escalation vs pre-generation routingstrategies.SelfEscalation + wrap_stream (stop mid-stream when loss(stop) < loss(continue)); compare against Cascade on the same dataset2608.24087
Routing as an RL problem end-to-end (Router-R1, RLCascadeRouter) vs predict-then-optimiselearning.PolicyGradientStrategy (REINFORCE on hashed request features, EWMA baseline, entropy bonus; reward is the decision utility decision_reward) and learning.decision_regret(rows, targets, choose, predict) which reports decision regret next to prediction MAE so the two losses can be compared directly2608.15817
Is routing learnable where it is most valuable (web / SWE agents)?eval.headroom.learnability_by_difficulty(rows, targets, buckets): per-difficulty-bucket oracle, best single, headroom and noise floor; the hardest bucket answers the question for a given pool2608.06171; SWE-Router 2607.00053; Agent-as-a-Router 2606.22902
Memory-tier routing for agentsstrategies.MemoryRouter: tiers with capacity, write/read cost and latency; learned ImportanceGate decides what to write, value decays per tick, lowest-value residents are demoted, recall(query, budget_tokens) fills a token budget by relevanceBudgetMem 2602.06025; Gated-Memory Routing 2609.00237
Multimodal / modality escalationstrategies.ModalityStrategy (penalises text-only targets when the request carries images / audio / video / files that only a text surrogate describes) and ModalityEscalation (text-first with a Beta posterior on "surrogate sufficed", escalates to the multimodal target when confidence is low)LatentRouter 2605.11301; modality escalation 2607.05438; CUA routing 2603.12823
Mechanism design when providers are strategicstrategies.AuctionStrategy: bias-corrected claims, second-price paymentEA-RAM 2608.12719; strategy auctions 2602.02751; coalition pricing 2608.07532
Semantic caching as a targetstrategies.SemanticCache.target() is a DESTINATION whose handler serves near-duplicate answers; SemanticCacheStrategy scores it from the hit similarity and learns per-band hit quality from outcomesGPTCache; vLLM semantic router 2603.04444
Routing among human annotators / expertsstrategies.AnnotatorPool (Dawid-Skene EM over per-domain annotator accuracy), select_quorum(domain, target_accuracy, budget) (greedy majority-vote quorum via quorum_accuracy) and HumanRoutingStrategy for HUMAN targetsQUORUM 2608.27974
Speculative / draft-based cascadesstrategies.SpeculativeCascade: runs draft and strong in parallel when the learned acceptance posterior says speculation is cheaper than either alone (expected_mode_costs), cancels the strong call when the draft is acceptedspeculative cascades; Differential Reasoning Router 2608.30224

Open follow-ups (not scheduled): learned energy/carbon priors per provider region, and replacing the hashed encoders in ContrastiveRouter / PolicyGradientStrategy with a real embedding model behind Embedder when a dependency is acceptable.


Out of scope#

  • Model serving, quantisation, or inference optimisation (use vLLM / TGI).
  • Protocol translation between provider APIs (use LiteLLM / aisix in front).
  • Token-level / layer-level routing inside a model (MoE, early exit, hybrid attention).
  • Prompt management / versioning.
  • Being a chat UI.