Skip to main content

Routing

client.routing controls how the gateway selects a model and provider for each request. Artemis Task decides every model="auto" request, by model capability; a request that names a model uses no router. See Artemis Task for how it decides.

The router​

There is no router to choose, per request or in a setting. The chat call's router= keyword (RouterChoice) is optional: it takes only "artemis" or "v2", which change nothing, and any other value raises NuRouteError with status 400. When Artemis Task cannot decide a request, fallback-router:apollo decides it, and the request's routerVersion and routerFallback say so and why (see Which router decided). Your governance policy applies either way. More in Routing concepts.

Get the current config​

config = client.routing.get_config()
print(config["strategy"], config.get("speed"))

Update routing config​

update_config() replaces the whole config. A key you leave out goes back to its default: strategy to "balanced", the weights to 40/30/30 and the policy to its defaults. speed and the model aliases are kept when you leave them out; send None for speed off. Aliases in the config you send are ignored: change them with the alias methods. If the stored config cannot be read at that moment, the call fails with 503 and nothing is saved. Changing the config needs an owner's or admin's session: sign the client in with auth.login() (see Authentication). With an API key, get_config() works and update_config() and the alias changes fail with 403. Read the config, change what you need and send all of it back:

config = client.routing.get_config()
config["strategy"] = "balanced" # "balanced" | "cheapest" | "fastest" | "highest_quality" | "manual"
config["policy"]["fallbackBehavior"] = "allow_commercial"
client.routing.update_config(config)

See Fallback for what fallbackBehavior actually controls. It decides what happens when no provider passes your policy constraints, not whether the gateway retries on a provider error.

What the strategy means​

Artemis Task reads the strategy as a preset:

strategyArtemis Task preset
balancedBalanced
highest_qualityQuality-first
cheapestCost-first
fastestBalanced at speed Fastest on a capability version with a speed table, else Fastest
manualNone: your pinned provider serves it, recorded as routerFallback "manual_mode"

For a request that names a model, the strategy ranks that model's providers. The balanced weights rank the providers of a model you name, and the choice the fallback router makes inside its tier; Artemis Task does not read them. Cost, latency and quality controls sets out what the strategy, the weights, speed and the cost and latency caps in policy do, with what to change to make routing cheaper or faster.

Speed​

speed is Artemis Task's Speed setting: how much a quicker answer is worth, at the quality target the strategy's preset sets. "off", "faster", "fastest", or None for off; any other value raises NuRouteError with status 400. No chat keyword or header sets it, and a project's router_speed overrides the organization's for the project's requests. fallback-router:apollo ignores it.

from nuroute.types import RouterSpeed # Literal["off", "faster", "fastest"]

speed: RouterSpeed = "faster"
config = client.routing.get_config()
config["speed"] = speed # None: off
client.routing.update_config(config)

# A project: the wire's snake_case key; "" follows the organization
proj = client.project("proj_abc123")
cfg = proj.get_routing_config()
cfg.update(router_speed="fastest")
proj.set_routing_config(**cfg)

Model aliases​

Aliases map a name of your choice to a model. They are stored per organization (and per project), but the request path does not resolve them today: a chat request has to name the real model, or auto.

# Create or update an alias
client.routing.set_alias("smart", "gpt-4o")
client.routing.set_alias("fast", "gpt-4o-mini")
client.routing.set_alias("coding", "claude-sonnet-4-6")

# List all aliases
aliases = client.routing.list_aliases()

# Delete an alias
client.routing.delete_alias("coding")

Routing history​

result = client.routing.list_history(limit=20, provider="openai", strategy="balanced")
decision = client.routing.get_decision(result["decisions"][0]["id"])
print(decision["selectedProvider"], decision.get("routerVersion"), decision["candidates"])

Which router decided​

client.chat.complete() returns the response body only, so read the router from the request log. The completion id is chatcmpl- followed by the request id, which client.requests.list() can search for:

res = client.chat.complete(model="auto", messages=messages)

request_id = res["id"].removeprefix("chatcmpl-")
page = client.requests.list(search=request_id, limit=1)
detail = client.requests.get(page["requests"][0]["id"])

print(detail.get("routerVersion"), detail.get("capabilityVersion"), detail.get("routerFallback"))
# v2 cap-6 None, or v1 None no_estimates when fallback-router:apollo decided

RequestDetail (from client.requests.get()) and RoutingDecision (from client.routing.get_decision()) both include the RouterDecision keys:

KeyTypeMeaning
routerVersionRouterVersion | NoneThe router that decided: "v2" (Artemis Task) or "v1" (fallback-router:apollo); None for a request that named a model
capabilityVersionstr | NoneThe capability version Artemis Task decided with, e.g. "cap-6"; None when the fallback router decided
routerFallbackstr | NoneWhy the fallback router decided instead of Artemis Task; None otherwise

RequestDetail also breaks down the request's time: latencyMs (the provider call), routingDurationMs (routing), totalMs (end to end, as the gateway measured it; 0 for requests recorded before it was tracked) and overheadMs (the rest; None when totalMs is 0). There is no separate timeline call: requests.get_timeline() was removed, since the gateway has no such endpoint.

When the fallback router decides, the request is still served, and your governance policy applies to its choice too. The fallback reasons:

routerFallbackMeaning
no_estimatesArtemis Task had no estimates: router-model failed, was busy or ran out of time, or no capability version is active
unknown_capability_versionArtemis Task's estimates came from a capability version the deployment does not know
manual_modeRouting is manual (mode or strategy manual), or the request pins a provider
preset_unavailableThe capability version has no preset for the strategy (Fastest on cap-1)
no_servable_modelNone of Artemis Task's models could be served after your policy and enabled providers, or your cost and latency caps ruled them all out and your fallbackBehavior is not "fail" (reasons then include "policy_caps_exhausted"; under "fail" the request is refused with 422 instead)
v2_disabledArtemis Task is switched off by the operator. Self-hosted NuRoute does not include Artemis Task yet, so its auto requests all report this
invalid_estimatesArtemis Task's estimates failed a sanity check
v2_errorArtemis Task hit an error while deciding

Candidates​

candidates lists what was weighed, each with provider, model, costUsd, latencyMs, score (0–100) and winner. On an Artemis Task decision, each Candidate adds Artemis Task's figures. Under Quality-first, Balanced and Cost-first there is one row per pool model that can be served, on the provider it would be served by; under Fastest, one row per model and provider.

KeyTypeMeaning
qualityfloatThe model's expected score on this prompt (0–1)
utilityfloat | NoneThe expected score less the preset's price for extra cost; the highest wins. None under Fastest
inputTokensintThe prompt tokens it priced
outputTokensfloatThe answer length, in tokens, it expected
expectedLatencyMsfloatThe answer time it expected on this provider; Fastest picks by it

When the fallback router decided, candidates lists the models it weighed in its tier, score being each one's rank under the strategy (winner highest), and the keys above are absent.