Implement intelligent model routing based on request characteristics. Use when optimizing for cost, speed, or quality per request. Trigger with phrases like 'openrouter routing', 'model selection', 'smart routing', 'dynamic model'.
Beyond simple task-based model selection, production systems need configurable routing rules that consider user tier, cost budget, time of day, model availability, and feature requirements. This skill covers building a rules engine for OpenRouter model selection with config-driven rules, dynamic conditions, and override capabilities.
Prerequisites
An OpenRouter API key (sk-or-v1-...) exported as OPENROUTER_API_KEY — see the openrouter-install-auth skill for setup
Python 3.8+ with the OpenAI SDK — the rules engine itself is stdlib (dataclasses, json, random) layered on top
Per-request metadata available in your app: user tier, task type, remaining budget, tool/vision needs, latency SLA (the RoutingContext fields)
Budget tracking wired up (see openrouter-cost-controls) if you use budget-conditioned rules like
low-budget
Instructions
Model each request's metadata as a RoutingContext (user tier, task type, budget remaining, tools/vision flags, latency SLA) per Rules Engine.
Define RoutingRule entries in priority order — free-tier first, then budget, capability (tools/vision), task type, latency, and always a priority=99 default catch-all.
Resolve the winning rule with evaluate_rules(ctx): first match by ascending priority wins; failing conditions return False instead of raising.
Execute through routed_completion() per Routed Completion — it applies the rule's model, fallback chain (models + route: "fallback"), and max_tokens.
To make rules hot-reloadable, express them as JSON per Config-Driven Rules and match with match_config_rule() instead of lambdas.
Validate any rule change on a slice of traffic with ab_test_routing() per A/B Testing Rules before full rollout.
import random
def ab_test_routing(ctx: RoutingContext, test_name: str, variant_b_pct: float = 0.10):
"""Route a percentage of traffic to variant B for comparison."""
rule = evaluate_rules(ctx)
if random.random() < variant_b_pct:
# Variant B: try a different model
return RoutingRule(
name=f"{rule.name}:variant-b",
priority=rule.priority,
condition=rule.condition,
model="openai/gpt-4o", # Test against a different model
fallbacks=rule.fallbacks,
max_tokens=rule.max_tokens,
)
return rule
Output
A resolved RoutingRule per request — name, model, fallbacks, max_tokens — from evaluate_rules()
A completion result dict from routed_completion(): {content, model, rule, tokens}; the rule field makes every routing decision auditable
A JSON rules config (Config-Driven Rules) that can be hot-reloaded without redeployment
A/B variant assignments (<rule-name>:variant-b) for a configurable percentage of traffic
Examples
A pro-tier code request falls through the free-tier, budget, tools, and vision rules and matches code-tasks:
The same context with user_tier="free" matches the priority-1 free-tier rule instead, landing on google/gemma-2-9b-it:free capped at 512 tokens. More worked examples: references/examples.md.
Error Handling
Error
Cause
Fix
No rule matched
Missing default catch-all
Always include a priority=99 default rule
Rule condition error
Dynamic check raised exception
Wrap condition in try/catch; return False on error
Wrong model selected
Rule priority incorrect
Log matching rule name; review priority ordering
Config parse error
Invalid JSON rule definition
Validate config at startup; fail fast
Enterprise Considerations
Store rules in a config file or database for hot-reloading without redeployment
Log every routing decision (rule name, model, context) for analytics and debugging
Use A/B testing to validate rule changes before full rollout
Always include a default catch-all rule with a reliable, affordable model
Version your rule configurations and track changes alongside code deployments
Combine routing rules with budget enforcement (see openrouter-cost-controls)