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 option (RouterChoice) is
optional: it takes only 'artemis' or 'v2', which change nothing, and any other value is rejected with a
NuRouteError whose status is 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
const config = await client.routing.getConfig();
console.log(config.strategy, config.speed, config.policy?.fallbackBehavior);
Update routing config
updateConfig() replaces the whole config. A field 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 null 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, getConfig() works and updateConfig() and the alias
changes fail with 403. Read the config, change what you need and send all of it back:
const config = await client.routing.getConfig();
await client.routing.updateConfig({
...config,
strategy: 'balanced', // 'balanced' | 'cheapest' | 'fastest' | 'highest_quality' | 'manual'
policy: { ...config.policy, fallbackBehavior: 'allow_commercial' },
});
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:
strategy | Artemis Task preset |
|---|---|
balanced | Balanced |
highest_quality | Quality-first |
cheapest | Cost-first |
fastest | Balanced at speed Fastest on a capability version with a speed table, else Fastest |
manual | None: 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 null for off; any other value is
rejected with a NuRouteError whose status is 400. No chat option or header sets it, and a project's speed
overrides the organization's for the project's requests. fallback-router:apollo ignores it.
import type { RouterSpeed } from '@aicp/sdk'; // 'off' | 'faster' | 'fastest'
const speed: RouterSpeed = 'faster';
const config = await client.routing.getConfig();
await client.routing.updateConfig({ ...config, speed }); // null: off
// A project: null follows the organization (sent as router_speed: "")
const proj = client.project('proj_abc123');
const current = await proj.routing.getConfig();
await proj.routing.setConfig({ ...current, speed: 'fastest' });
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
await client.routing.setAlias('smart', 'gpt-4o');
await client.routing.setAlias('fast', 'gpt-4o-mini');
await client.routing.setAlias('coding', 'claude-sonnet-4-6');
// List all aliases
const aliases = await client.routing.listAliases();
// [{ alias: 'smart', model: 'gpt-4o' }, ...]
// Delete an alias
await client.routing.deleteAlias('coding');
Routing history
Inspect how recent requests were routed:
const { decisions, total } = await client.routing.listHistory({
limit: 20,
offset: 0,
provider: 'openai', // optional filter
strategy: 'balanced', // optional filter
status: 'success', // optional filter
});
const detail = await client.routing.getDecision(decisions[0].id);
console.log(detail.selectedProvider, detail.routerVersion, detail.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:
const res = await client.chat.complete({ model: 'auto', messages });
const requestId = res.id.replace(/^chatcmpl-/, '');
const { requests } = await client.requests.list({ search: requestId, limit: 1 });
const detail = await client.requests.get(requests[0].id);
console.log(detail.routerVersion, detail.capabilityVersion, detail.routerFallback);
// 'v2' 'cap-6' null, or 'v1' null 'no_estimates' when fallback-router:apollo decided
RequestDetail (from client.requests.get()) and RoutingDecision (from client.routing.getDecision()) both
extend RouterDecision:
| Field | Type | Meaning |
|---|---|---|
routerVersion | RouterVersion | null | The router that decided: 'v2' (Artemis Task) or 'v1' (fallback-router:apollo); null for a request that named a model |
capabilityVersion | string | null | The capability version Artemis Task decided with, e.g. 'cap-6'; null when the fallback router decided |
routerFallback | string | null | Why the fallback router decided instead of Artemis Task; null 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; null when totalMs is 0). There is no separate timeline call: requests.getTimeline() 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:
routerFallback | Meaning |
|---|---|
no_estimates | Artemis Task had no estimates: router-model failed, was busy or ran out of time, or no capability version is active |
unknown_capability_version | Artemis Task's estimates came from a capability version the deployment does not know |
manual_mode | Routing is manual (mode or strategy manual), or the request pins a provider |
preset_unavailable | The capability version has no preset for the strategy (Fastest on cap-1) |
no_servable_model | None 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_disabled | Artemis Task is switched off by the operator. Self-hosted NuRoute does not include Artemis Task yet, so its auto requests all report this |
invalid_estimates | Artemis Task's estimates failed a sanity check |
v2_error | Artemis 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.
| Field | Type | Meaning |
|---|---|---|
quality | number | The model's expected score on this prompt (0–1) |
utility | number | null | The expected score less the preset's price for extra cost; the highest wins. null under Fastest |
inputTokens | number | The prompt tokens it priced |
outputTokens | number | The answer length, in tokens, it expected |
expectedLatencyMs | number | The 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 fields above are absent.