Add models-router: multidimensional model selection - #110
Merged
Conversation
Chooses which model answers a request across in-process ModelJars
artifacts and whatever hosted providers an application holds keys for.
The router selects and never calls, so applications keep their own
provider clients and this module carries no provider SDK. A decision
names a model id the application maps back to its own client.
Scoring is a weighted sum over normalized dimensions rather than a
difficulty classifier picking a tier. Difficulty alone cannot express
"this is easy but the cheap model is failing right now" or "this is hard
but the prompt does not fit the cheaper sibling's context", and routing
has to balance cost, latency, specialization and reliability at once.
Two design points came out of building it rather than planning it:
Reliability multiplies, it does not add. With availability as an
additive term, a model failing four calls in five still won on cost and
latency. Any additive weight small enough to be reasonable lets a cheap,
fast, broken model win. Availability weight now sets how sharply
failures are punished via successRate^(weight*10).
Tags filter, they do not merely inform. A model tagged {chat, code} was
being selected for a math query on price. Tags declare what a model is
for; an untagged model stays general purpose.
Presets are pre-tuned so nobody has to hand-weight five dimensions:
BALANCED, CHEAPEST, FASTEST, BEST_QUALITY, LOCAL_FIRST, PRIVACY_STRICT,
plus costQualityTradeoff(0..10) for a single dial. BEST_QUALITY carries
zero cost weight deliberately: a token weight left it picking a cheaper
model whenever two were within a hair on quality, which is exactly the
surprise that preset must not deliver.
Hard constraints filter before scoring, and a refusal names every reason
rather than whichever happened to be evaluated last. PRIVACY_STRICT on a
500k-token prompt refuses rather than silently choosing a model that
cannot hold it.
Sessions pin to their first choice while it stays eligible, because
switching models mid-conversation discards the provider's prompt cache.
TaskClassifier is an interface so an embedding-similarity classifier
over vectors-router drops in once that module is on Central.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Routes a request across in-process ModelJars artifacts and whatever hosted providers an application holds keys for.
The router selects and never calls. A decision names a model id; the application maps it back to its own client. That is why this module carries no OpenAI/Anthropic/DeepSeek SDK.
Behaviour on a realistic fleet
PRIVACY_STRICTrefusing the math query is correct: no local model declaresmath, and silently using one that cannot do it would be worse than failing.The single dial moves monotonically:
Two design points that came out of building it
Reliability multiplies, it does not add. With availability as an additive term a model failing four calls in five still won on cost and latency. Any additive weight small enough to be reasonable lets a cheap, fast, broken model win. Availability weight now sets how sharply failures are punished, via
successRate^(weight*10).Tags filter, they do not merely inform. A model tagged
{chat, code}was selected for a math query on price. Tags declare what a model is for; an untagged model stays general purpose.BEST_QUALITYcarries zero cost weight, deliberately. A token weight left it choosing the cheaper model whenever two were within a hair on quality — exactly the surprise a preset with that name must not deliver. There is a regression test for it.Other properties
deepseek/deepseek-v3 for math (score 0.891, availability 1.000, cost 0.333, latency 0.167, locality 0.000, quality 0.391). A router nobody can interrogate is a router nobody can tune.TaskClassifieris an interface so an embedding-similarity classifier overvectors-routerdrops in once that module reaches Central (Publish vectors-router to Maven Central vectors#57).19 tests;
buildandcomplianceCheckpass.🤖 Generated with Claude Code