Skip to content

About

Switchback — a per-turn cascade model router for AI agents. Classifies each request, starts on the cheapest model that can handle it, and escalates mid-turn if the cheap one fumbles. Brand-locked, framework-agnostic.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Switchback

Per-turn cascade model router for AI agents. Starts cheap, climbs the tiers when the cheap one fumbles.

npm Powers VibeKit

Don't want to wire it yourself? Switchback ships fully hosted inside VibeKit — every "Auto" turn runs through this cascade with brand-locked ladders, telemetry, and billing built in. Or hit the hosted classifier directly: POST https://vibekit.bot/api/v1/switchback/route.

Most AI agents pick one model per app and use it for every turn. A "fix this typo" turn and a "refactor the auth flow" turn both go to the same flagship — wasted spend on the trivial half, no escalation safety on the complex half.

Switchback classifies each user turn (trivial / standard / complex), starts the agent on the cheapest model in your ladder that can plausibly handle it, then escalates mid-turn — swapping to a more capable model on the next agent round — if it detects the cheap one is fumbling.

Born inside VibeKit. Extracted as a standalone library because the routing pattern is useful well beyond one product.

Install

npm install vibekit-switchback

Quick start

import {
  createCascade,
  createOpenRouterClassifier,
} from 'vibekit-switchback';

const cascade = createCascade({
  // Cheap → flagship. Brand-locked is your call: if your user has an
  // Anthropic key, hand Switchback an all-Anthropic ladder; if they're
  // on OpenAI, an all-OpenAI ladder. Switchback stays inside whatever
  // ladder you give it.
  ladder: [
    'openai/gpt-5.4-mini',
    'openai/gpt-5.4',
    'openai/gpt-5.5',
  ],
  classify: createOpenRouterClassifier({
    apiKey: process.env.OPENROUTER_API_KEY!,
  }),
});

const state = await cascade.init({
  userMessage: 'add a dark-mode toggle to the header',
  recentHistory: [], // optional: last 1-2 assistant turns for context
});

// state.initialModel === 'openai/gpt-5.4' (classifier picked tier 1)
// state.classifier.why === 'small UI feature'

// Run your agent's first round with state.initialModel:
const round0 = await runAgentRound({ model: state.initialModel, ... });
cascade.recordTokens(state, round0.totalTokens);

// After each round, check for escalation:
const decision = cascade.evaluate(state, {
  hasNoText: round0.text.trim() === '',
  endedOnToolUse: round0.endedOnToolUse,
  steps: round0.steps,           // optional — enables tool_error_spike trigger
  joinedText: round0.text,       // optional — enables refusal_signal trigger
}, /* round = */ 0);

if (decision.escalate) {
  const nextModel = cascade.escalate(state, decision.reason!, 0, round0.totalTokens);
  // ...use nextModel for the next round
}

// At end-of-turn, persist telemetry:
const telemetry = cascade.telemetry(state);
//  { initial_model, final_model, escalations, signals[], per_tier_tokens }

What makes Switchback different

  • Mid-turn escalation. Other routers (NotDiamond, Martian) pick a model at request start and that's that. Switchback can hand off mid-conversation if the cheap tier gives up — requires deep agent-loop integration but catches failures other routers can't.
  • Brand-locked by design. Switchback doesn't decide "use Claude or GPT" — the caller hands it one ladder per user, all within their BYOK provider. Your user's key stays your user's key, every tier.
  • Agent-loop signals. Five escalation triggers built around actual agent execution: empty-tool exits, tool-error spikes, continuation exhaustion, explicit retry-sentinels, refusal phrases. Not just chat-completion patterns.
  • Telemetry baked in. Every cascade.telemetry(state) returns a clean JSON record ready to write to your usage_logs / metrics sink. Five columns: initial model, final model, escalation count, signal log, per-tier tokens.

API

createCascade(config)

field type description
ladder readonly string[] Ordered list of model ids, cheap first.
classify (userMessage, recentHistory) => Promise<ClassifierResult> Your classifier function. See below.

Returns: { init, evaluate, escalate, recordTokens, telemetry }.

createOpenRouterClassifier(opts)

Drop-in classifier using OpenRouter. Fail-soft (returns tier 1 on any error so the cascade never blocks on classifier issues).

option type default description
apiKey string (required) OpenRouter API key.
model string openai/gpt-5.4-mini Model slug for classification.
baseUrl string https://openrouter.ai/api/v1 Override for OR-compatible proxies.
timeoutMs number 8000 Per-call timeout.

Escalation triggers (in cascade.evaluate)

trigger fires when
empty_tool_exit 2 consecutive rounds end on tool_use with no text output.
tool_error_spike ≥3 tool-error entries in this round's steps[].
continuation_exhaust Round index ≥5 with still no text accumulated.
sentinel_after_retry Your agent loop hit its empty-response retry and the retry was also empty.
refusal_signal First ~200 chars of the assistant reply start with "I can't" etc.

Each escalation bumps tier by 1 (capped at ladder top).

When NOT to use this

  • You only have one model. Save yourself the classifier latency. Switchback is for cascades.
  • You don't run an agent loop. The mid-turn escalation only helps if you have multiple rounds per user turn. For one-shot chat-completion routing, you want NotDiamond / Martian / OpenRouter's auto instead.
  • Latency-critical chat. The classifier adds ~200-500ms before the first token of round 0. If your UX needs sub-200ms first-token-latency, skip it.

License

MIT — see LICENSE.

Credits

Built by VibeKit. The approach is heavily inspired by Factory.ai's Factory Router — same thesis, different implementation. Switchback's contributions are the brand-locked ladders, mid-turn escalation, and agent-loop-signal triggers.

About

Switchback — a per-turn cascade model router for AI agents. Classifies each request, starts on the cheapest model that can handle it, and escalates mid-turn if the cheap one fumbles. Brand-locked, framework-agnostic.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages