MixRoute Gateway plugin thumbnail

MixRoute Gateway

Registers MixRoute as three Agent Zero chat providers - Anthropic with thinking effort control, general models, and Smart Routing - each with its own API key slot. Adds a sidebar credit visor showing balance, runway, burn rate, cache hit rate, and cache savings, read from the MixRoute console API.

Author revworxai 0 stars Version 0.2.0 Updated

README

MixRoute Gateway

Registers MixRoute as three first-class Agent Zero chat providers, so Claude models served through the gateway get real thinking effort control without per-preset api_base or api_key overrides.

Configuration only - no Python, no UI, no background requests.

What you get

Provider Serves API key slot
MixRoute (Anthropic) Claude models with thinking effort control API_KEY_MIXROUTE_ANTHROPIC
MixRoute (General) All 200+ models, no Smart Routing; effort control on OpenAI models only API_KEY_MIXROUTE_OPENAI
MixRoute (Smart Routing) Smart Routing (model: auto) API_KEY_MIXROUTE_SMART

Plus a credit visor in the sidebar, on by default, showing balance, runway, burn rate, cache hit rate, and cache savings. See Credit visor.

Install

  1. Copy this directory to /a0/usr/plugins/mixroute_gateway/.
  2. Restart Agent Zero.
  3. Enable MixRoute Gateway in Settings -> Plugins.
  4. Add your keys (below), then pick a MixRoute provider in your model config.

API keys - three separate credentials

This is the part that most often causes confusion.

MixRoute issues the Smart Routing key separately from the non-Smart-Routing keys. All three look alike - they share the sk- prefix - but they carry different entitlements and are not interchangeable. That is why this plugin ships three providers rather than one: each provider id maps to its own key slot, so you can also segregate spend per key if you want to.

Add them to /a0/usr/.env:

API_KEY_MIXROUTE_ANTHROPIC=sk-xxxxxxxx-your-anthropic-scoped-key
API_KEY_MIXROUTE_OPENAI=sk-xxxxxxxx-your-general-key
API_KEY_MIXROUTE_SMART=sk-xxxxxxxx-your-smart-routing-key

Or paste them into the Settings API-Keys pane, which writes the same slots.

If you only use one MixRoute capability, fill only that slot. An unfilled slot simply means that provider will fail to authenticate if selected.

Key names are not arbitrary: Agent Zero derives them from the provider id as API_KEY_{PROVIDER_ID_UPPER}. Renaming a provider id orphans its key.

How to create each key in the MixRoute dashboard

The three keys are not interchangeable, and two of them need deliberate setup. Getting this wrong is the most common cause of confusing failures.

1. Smart Routing key — a separate pane

Smart Routing keys are created on a different pane of the MixRoute dashboard from ordinary keys. A Smart Routing key works only for Smart Routing and will be rejected in either of the other two slots. The reverse is also true: an ordinary key cannot drive Smart Routing.

Both directions are enforced by MixRoute with explicit errors:

Attempt MixRoute response
Smart Routing key with a named model Smart routing key can only use smart routing model names (e.g. auto)
Ordinary key with model: auto Model 'auto' requires a smart routing dedicated key

2. Anthropic key — filter it to Anthropic models

When creating the key for API_KEY_MIXROUTE_ANTHROPIC, use MixRoute's model filter to restrict it to Anthropic models — one, several, or all of them.

This is strongly recommended rather than strictly required, and the reason is practical. The model picker for a provider shows whatever that key can reach. An unfiltered key surfaces all 200+ MixRoute models under MixRoute (Anthropic), which makes it easy to select a non-Anthropic model and send it thinking-effort parameters it cannot honour. Filtering the key makes the wrong choice impossible rather than merely discouraged.

A filtered key rejects anything outside its filter, which is the intended behaviour:

This token has no access to model gpt-5.2

3. General key — leave it unfiltered

For API_KEY_MIXROUTE_OPENAI, create an ordinary key with no model filter. That gives access to all 200+ MixRoute models, Anthropic ones included.

Effort control on this route is model-dependent:

Model family via the general key reasoning_effort
OpenAI models (e.g. gpt-5.2) Works
Anthropic models Ignored — Claude runs at its default level

The reason is the handler. This provider uses LiteLLM's OpenAI handler, which passes reasoning_effort through natively to OpenAI models but has no way to translate it into Anthropic's own effort mechanism. For thinking effort on Claude, use the MixRoute (Anthropic) provider instead.

Measured on gpt-5.2 through this route, identical prompt, 2 reps per level:

Effort Reasoning tokens Completion tokens
low 265, 312 1071, 1126
high 556, 835 1314, 1573

Reasoning tokens roughly double. Useful detail: OpenAI models report reasoning_tokens in the usage object, so the effect is directly observable. Anthropic models return 0 there even when reasoning demonstrably occurs, so on that route you have to infer effort from completion-token counts instead.

Which key for which job

Goal Key Provider
Claude with thinking effort control Anthropic-filtered MixRoute (Anthropic)
Any model; effort control on OpenAI models only Unfiltered ordinary MixRoute (General)
Automatic model selection Smart Routing MixRoute (Smart Routing)

The practical split: reach for the Anthropic key when reasoning depth matters, and the general key for everything else.

The /v1 asymmetry is intentional

In conf/model_providers.yaml the Anthropic entry's api_base has no /v1 suffix, while the other two do:

Provider api_base LiteLLM appends Final URL
Anthropic https://api.mixroute.ai /v1/messages .../v1/messages
OpenAI, Smart https://api.mixroute.ai/v1 /chat/completions .../v1/chat/completions

Making the three consistent breaks one of them. Adding /v1 to the Anthropic entry yields /v1/v1/messages and a 404 on every Claude call. It looks like a typo; it is load-bearing.

Effort control in presets

Set effort in the preset's Additional Parameters (kwargs) field:

reasoning_effort="high"

The quoted form is correct and is what the UI itself writes when it renders an existing preset. Unquoted reasoning_effort=high also works: the field parser tries JSON.parse on the value and falls back to the raw string when that fails, so both forms produce the identical stored value. In presets.yaml it lands as reasoning_effort: high.

This field is parsed before the request reaches any provider, so the syntax is the same for every MixRoute provider. What differs is whether the target model acts on it.

Which preset should set it

Preset provider Model reasoning_effort
MixRoute (Anthropic) Claude Works - this is the reason the provider exists
MixRoute (General) OpenAI models Works
MixRoute (General) Claude Silently ignored
MixRoute (Smart Routing) auto Effort is the router's decision, not the preset's

The silent case is the trap: pointing the OpenAI provider at a Claude model and setting effort produces no error and no effect.

Anthropic presets - remove the old overrides

If you are migrating a hand-rolled preset that reached MixRoute by overriding the stock anthropic provider, it will carry three kwargs. Keep one, delete two:

api_base="https://api.mixroute.ai"   <- DELETE, the provider supplies it
api_key="sk-..."                     <- DELETE, comes from API_KEY_MIXROUTE_ANTHROPIC
reasoning_effort="high"              <- KEEP

Also clear the top-level API Base field; empty means "use the provider's".

Leaving a stale api_key behind is the dangerous one. It keeps using whatever credential was pasted there, which may bypass MixRoute billing entirely while appearing to work.

Verifying effort actually applied

The method differs by route:

Route How to check
OpenAI models reasoning_tokens in the usage object rises with effort
Anthropic models reasoning_tokens is always 0; compare completion-token counts across levels instead

Measured on claude-opus-5 through the Anthropic provider, identical prompt, 3 reps per level: low averaged 1539 completion tokens, high 6635, and max exceeded 15000. The levels are clearly distinct.

Do not judge effort by how thoughtful the reply reads, and do not conclude from an empty reasoning_tokens on the Anthropic route that no reasoning occurred.

Credit visor

A sidebar readout of your MixRoute account. On by default — installing the plugin gives the full visor immediately. Turn it off in the settings pane if you want the three chat providers alone; while off, the visor makes no requests to MixRoute at all, so your UI and your account are left untouched.

It shows five things:

Row Shows
Credits Dollars remaining
Runway How long that lasts at the measured burn rate
Bar Balance against your green threshold, coloured by state
Burn Spend per hour, and the period it was measured over
Cache hit / Saved est. Cache read share, and what caching saved over that period

It needs one extra credential in /a0/usr/.env:

API_KEY_MIXROUTE_SYSTEM=<your System API key>
MIXROUTE_ID=<your numeric account id>

The System API key is not one of the three inference keys above. It is a console-scoped credential created separately in the MixRoute dashboard. The two scopes do not overlap: an inference key returns invalid access token here, and the System key returns 401 on chat calls.

Indicator Meaning
Green Comfortable balance, above your green threshold
Yellow Getting low — roughly a few hours of headroom
Red Urgent. Below about $2, large requests fail outright
Grey No reading available; hover for why
* after the figure Reading is older than the last poll attempt

Thresholds and refresh interval are configurable. Defaults assume a busy session and are based on measured burn, so green means hours of headroom rather than minutes.

Not included yet

Per-model breakdown of cache performance, and pagination past the most recent 100 requests. Neither blocks anything today: every request on this account uses one model, so there is nothing to break down, and 100 rows covers hours of normal use.

One display state has never been seen live. Saved est. turns red and negative when caching costs more than it saves, which needs a cache hit rate below 52.6%. It is unit-tested and wired, but no session has yet been inefficient enough to trigger it.

Troubleshooting

Symptom Likely cause
Providers missing from the dropdown Plugin not enabled, or Agent Zero not restarted
404 on Claude calls /v1 was added to the Anthropic api_base
401 on one provider only That provider's key slot is empty or holds the wrong key type
Smart Routing 401s while others work Smart Routing key is separately issued - check you used the right one
403 + pre-consume quota failed, user remaining quota: ... Account is out of credits. Not a configuration fault - top up at MixRoute. The message names the shortfall, e.g. remaining 0.88 vs required 1.76
No available channel for model <name> under group default MixRoute has no capacity for the model it selected. Fix this in the MixRoute dashboard, not the plugin - repin the affected tier to a model with an available channel. Most often seen on Smart Routing
Visor says "check System API key" An inference key is in API_KEY_MIXROUTE_SYSTEM. The visor needs the console-scoped System key, created separately in the dashboard
Visor says "out of credits" but the console shows a balance The balance fell below the pre-consume requirement for a large request. Check the console figure against your red threshold
Visor shows grey with "MixRoute unreachable" MixRoute's API is not answering. Confirmed against its public status endpoint, so this is not a credential problem
Visor shows a figure with * after it The last poll failed, so the previous reading is being shown. Hover for its age
Visor never appears It is on by default, so check the toggle was not switched off, then that Agent Zero has been restarted since installing
Visor rows are blank after an update Agent Zero caches plugin helpers in memory. A browser refresh cannot reload them - restart Agent Zero
Saved est. is red and negative Caching is costing more than it saves, below roughly 52.6% hit rate. Check the 1-hour cache TTL is active, then look for prompt-prefix churn invalidating the cache
Widget shows -- No successful reading yet. Hover for the reason