agentskit.js
Recipes

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

PolicyBehavior
'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) => idCustom 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:

  1. classify(request) returns a string β†’ use that candidate id (if present).
  2. classify(request) returns tags β†’ filter candidates to those with all tags, then apply policy.
  3. Fall back to policy across all capability-matched candidates.

#Observe decisions

createRouter({
  onRoute: ({ id, reason, request }) => {
    console.log(`[router] -> ${id} (${reason})`)
  },
  candidates: [...],
})

#See also

Explore nearby

✎ Edit this page on GitHubΒ·Found a problem? Open an issue β†’Β·How to contribute β†’

On this page