vibekit-switchback
v0.1.1
Published
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.
Maintainers
Readme
Switchback
Per-turn cascade model router for AI agents. Starts cheap, climbs the tiers when the cheap one fumbles.
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-switchbackQuick 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 yourusage_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
autoinstead. - 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.
