Adapter router
Auto-pick an adapter per request based on cost, latency, capabilities, or a custom classifier.
You don't want every call to hit your most expensive model. You also
don't want to split the agent in two just to use Haiku for easy
questions and Sonnet for hard ones. createRouter from
@agentskit/adapters builds a single AdapterFactory that picks
among N candidates on every createSource().
#Install
Comes with @agentskit/adapters.
#Pick the cheapest capable candidate
import { createRouter, anthropic, openai } from '@agentskit/adapters'
const router = createRouter({
candidates: [
{ id: 'haiku', adapter: anthropic({ model: 'claude-haiku-4-5' }), cost: 0.25 },
{ id: 'sonnet', adapter: anthropic({ model: 'claude-sonnet-4-6' }), cost: 3, capabilities: { tools: true } },
{ id: 'gpt-mini', adapter: openai({ model: 'gpt-4o-mini' }), cost: 0.15 },
],
// policy: 'cheapest' is the default
})Candidates are filtered against the request's requirements first
(e.g. requests with tools filter out capabilities: { tools: false })
and then ranked by the policy.
#Policies
| Policy | Behavior |
|---|---|
'cheapest' (default) | Minimum cost |
'fastest' | Minimum latencyMs |
'greenest' | Minimum gCO2PerKtok (carbon intensity) |
'green-cost' | Composite normalised carbon Γ cost score |
'capability-match' | First candidate that satisfies requirements |
(input) => id | Custom function, sync or async |
#Carbon-aware + cost-aware routing
Each candidate can carry a gCO2PerKtok (grams CO2eq per 1k tokens) signal β populate manually or via applyCarbonTable() against DEFAULT_CARBON_TABLE (or a custom regional table).
import { createRouter, applyCarbonTable, DEFAULT_CARBON_TABLE, anthropic, openai } from '@agentskit/adapters'
const candidates = applyCarbonTable(
[
{ id: 'haiku', adapter: anthropic({ model: 'claude-haiku-4-5' }), cost: 0.25, region: 'us-east' },
{ id: 'gpt-mini', adapter: openai({ model: 'gpt-4o-mini' }), cost: 0.15, region: 'eu-west' },
],
DEFAULT_CARBON_TABLE,
)
const router = createRouter({ candidates, policy: 'green-cost' })'greenest' minimises grid carbon intensity. 'green-cost' normalises both signals and minimises the sum β picks the model that's cheap and on a clean grid. Candidates missing either signal are treated as median (neutral) so partial telemetry doesn't bias the ranking.
#Classify-then-route
Skip the policy entirely when the classifier can pick a specific candidate, or narrow the pool by tags.
const router = createRouter({
classify: request => {
const text = request.messages[request.messages.length - 1]?.content ?? ''
if (/code|typescript|refactor/i.test(text)) return ['coding']
if (/image|photo|picture/i.test(text)) return 'sonnet' // id wins
return undefined
},
candidates: [
{ id: 'haiku', adapter: anthropic({ model: 'claude-haiku-4-5' }), cost: 0.25, tags: ['fast'] },
{ id: 'sonnet', adapter: anthropic({ model: 'claude-sonnet-4-6' }), cost: 3 },
{ id: 'coder', adapter: openai({ model: 'gpt-5-codex' }), cost: 2, tags: ['coding'] },
],
})Resolution order per request:
classify(request)returns a string β use that candidate id (if present).classify(request)returns tags β filter candidates to those with all tags, then applypolicy.- Fall back to
policyacross all capability-matched candidates.
#Observe decisions
createRouter({
onRoute: ({ id, reason, request }) => {
console.log(`[router] -> ${id} (${reason})`)
},
candidates: [...],
})#See also
- Speculative execution β run several candidates in parallel
- Custom adapter
Explore nearby
- PeerRecipes
Copy-paste solutions grouped by theme. Every recipe end-to-end, runs as written.
- PeerCustom adapter
Wrap any LLM API as an AgentsKit adapter. Plug-and-play with the rest of the kit in 30 lines.
- PeerAdapter contract tests
Verify any adapter against the ADR 0001 invariants A1βA10 with the shared test harness.