ModelPort implements an opt-in, policy-aware router for logical model aliases.
It adds adaptive candidate selection without changing the contract for explicit
provider:model, static aliases, exact models, or prefix routing.
offis the default. Smart aliases are not advertised, and a direct request using one stays on the first configured eligible candidate. This is the routing kill switch without turning an established alias into an outage.shadowscores every eligible candidate but sends the request through the first configured eligible candidate. This measures disagreements without changing production routing.activeapplies the scored order only to the configured percentage of requests. The remainder stays on the configured-order control route and is recorded ascanary_control.- An explicit Provider/model request remains deterministic. A routing-profile header never turns it into a smart route.
- Candidate policy and capability checks run before scoring. Attempt-scoped quota, budget, credential, transport, and Provider checks still run before every actual egress.
Smart groups are defined only in TOML. Environment variables can switch the mode, default profile, and activation percentage, but cannot create candidates.
[routing]
mode = "shadow"
default_profile = "balanced"
policy_version = "builtin-v1"
activation_percent = 0
[routing.groups.general]
aliases = ["auto", "auto-general"]
default_profile = "balanced"
[[routing.groups.general.candidates]]
provider = "local_qwen"
model = "qwen3.5-9b-q5km"
quality = 0.72
latency_hint_ms = 1200
enabled = true
[[routing.groups.general.candidates]]
provider = "deepseek"
model = "deepseek-v4-flash"
quality = 0.88
latency_hint_ms = 1800
enabled = trueCandidate quality is a bounded prior from 0 to 1, not a measured claim.
latency_hint_ms must be between 1 and 600000. Every candidate model must
be accepted by its Provider inventory/prefix/passthrough policy. Alias names
must be unique and cannot conflict with static aliases. A configuration may
contain at most 128 groups and 256 candidates per group.
The restart/reload-time overrides are:
MODELPORT_SMART_ROUTING_MODE=shadow
MODELPORT_SMART_ROUTING_PROFILE=balanced
MODELPORT_SMART_ROUTING_ACTIVATION_PERCENT=0Smart aliases work on both inference edges. Clients may optionally send:
X-ModelPort-Routing-Profile: quality
X-ModelPort-Session-Id: application-session-referenceProfiles are quality, balanced, economy, and latency. The session value
is limited to 128 ASCII non-control bytes. ModelPort hashes it together with
the authenticated principal before routing; raw session values are neither
logged nor stored. Reusing it keeps affinity and canary assignment stable for
that principal. Without it, active-mode bucketing uses a principal-scoped
request ID. Callers that need stable retry assignment should reuse their bounded
request ID; otherwise assignment is intentionally per request and does not
derive from prompt content.
The router first removes disabled, missing, capability-incompatible, policy-ineligible, and cooling candidates. If every otherwise eligible candidate is cooling, it keeps them as a last-resort plan so a stale cooldown cannot make the route permanently unavailable.
The built-in score combines:
| Profile | quality | estimated cost | latency | reliability |
|---|---|---|---|---|
quality |
0.60 | 0.10 | 0.15 | 0.15 |
balanced |
0.35 | 0.30 | 0.20 | 0.15 |
economy |
0.15 | 0.70 | 0.05 | 0.10 |
latency |
0.20 | 0.10 | 0.60 | 0.10 |
Quality is the configured prior. Estimated cost uses the request output limit, a bounded input approximation, and configured Provider pricing. Latency starts from the candidate hint and blends toward an in-process EWMA as the sample count grows, so one fast or slow observation cannot immediately replace the prior. Reliability uses a conservative prior plus current Provider-health outcomes. A small deterministic affinity term breaks close scores for a supplied session.
Every admitted request receives a rtd_* decision ID. PostgreSQL migration
0007_smart_routing_decisions.sql stores the selected and recommended route,
mode, profile, policy version, candidate count, selected/recommended scores,
bounded reason codes,
session-affinity flag, and shadow-disagreement flag. It does not store request
content or a raw session identifier. Decision rows follow the parent request's
retention lifecycle, so removing an expired request also removes its decision
and attached feedback instead of leaving orphaned storage.
Administrators can inspect current process state through
GET /admin/router/status. Prometheus exposes
modelport_routing_decisions_total{mode,profile,provider}. Request logs include
the decision ID and selected/recommended route in structured backend logs.
Recommended rollout:
- Configure candidate pricing, quality priors, latency hints, and policy.
- Start with
shadowandactivation_percent=0. - Compare shadow disagreement, errors, latency, cost, Tool Use success, and Provider cooldown by profile for at least one representative traffic cycle.
- Switch to
activeat a small percentage such as5, then increase through explicit reviewed changes. - Set
MODELPORT_SMART_ROUTING_MODE=shadoworoffand reload/restart to stop adaptive selection.offkeeps direct smart-alias calls on their configured baseline order; explicit routes remain available throughout.
Runtime latency EWMA, route counters, and data-plane Provider-health outcomes used by reliability scoring are process-local; they are not a distributed semantic-quality model. Configured prices and input-token estimates are planning inputs, not an upstream invoice. PostgreSQL includes a routing-feedback evidence table, but this release has no public feedback-ingestion or automatic weight-training endpoint. A future learning service should consume versioned, privacy-reviewed offline evaluations and publish a signed policy snapshot instead of mutating weights in the request path.