Multi-Provider Routing¶
SafeAI's multi-provider routing lets you register multiple LLM providers and automatically route requests based on priority, cost, latency, or availability. When a provider goes down, the circuit breaker trips and traffic fails over to the next healthy provider — no manual intervention, no dropped requests.
Works with the proxy
Multi-provider routing integrates directly with the Proxy / Sidecar. Any routing strategy you configure applies to all requests flowing through the proxy.
Quick Start¶
from safeai.core.router import ProviderRegistry, ProviderConfig
registry = ProviderRegistry(strategy="priority")
registry.register(ProviderConfig(
name="openai",
base_url="https://api.openai.com",
api_key_env="OPENAI_API_KEY",
models=["gpt-4o", "gpt-4o-mini"],
priority=1,
))
registry.register(ProviderConfig(
name="anthropic",
base_url="https://api.anthropic.com",
api_key_env="ANTHROPIC_API_KEY",
models=["claude-sonnet-4-20250514"],
priority=2,
))
decision = registry.route()
print(decision.provider) # "openai"
print(decision.reason) # "highest priority healthy provider"
Provider Configuration¶
Each provider is defined with a ProviderConfig:
from safeai.core.router import ProviderConfig
provider = ProviderConfig(
name="openai", # unique identifier
base_url="https://api.openai.com", # API endpoint
api_key_env="OPENAI_API_KEY", # env var holding the API key
models=["gpt-4o", "gpt-4o-mini"], # models available on this provider
priority=1, # lower number = higher priority
)
| Field | Type | Description |
|---|---|---|
name | str | Unique identifier for the provider |
base_url | str | Base URL of the provider's API |
api_key_env | str | Environment variable name containing the API key |
models | list[str] | Models available from this provider |
priority | int | Routing priority — lower number means higher preference |
Local providers
For self-hosted providers like Ollama, omit api_key_env:
Routing Strategies¶
Create a ProviderRegistry with one of four strategies:
from safeai.core.router import ProviderRegistry, ProviderConfig
for strategy in ["priority", "cost_optimized", "latency_optimized", "round_robin"]:
reg = ProviderRegistry(strategy=strategy)
reg.register(ProviderConfig(name="a", base_url="http://a", models=["m"], priority=1))
reg.register(ProviderConfig(name="b", base_url="http://b", models=["m"], priority=2))
d = reg.route()
print(f"{strategy}: {d.provider} ({d.reason})")
| Strategy | Behavior |
|---|---|
priority | Always picks the healthy provider with the lowest priority number |
cost_optimized | Routes to the cheapest available provider for the requested model |
latency_optimized | Routes to the provider with the lowest observed response latency |
round_robin | Distributes requests evenly across all healthy providers |
Strategy applies only to healthy providers
Regardless of strategy, providers with an open circuit breaker are excluded from selection. A round_robin registry with one unhealthy provider will skip it automatically.
Circuit Breaker¶
The circuit breaker tracks consecutive failures per provider. After a configurable threshold, the provider's circuit opens and all traffic fails over to the next candidate.
from safeai.core.router import ProviderRegistry, ProviderConfig
registry = ProviderRegistry(strategy="priority", circuit_breaker_threshold=3)
registry.register(ProviderConfig(name="primary", base_url="http://primary", models=["m"], priority=1))
registry.register(ProviderConfig(name="backup", base_url="http://backup", models=["m"], priority=2))
# Simulate 3 consecutive failures
for _ in range(3):
registry.report_failure("primary")
# Circuit is now open — traffic routes to backup
decision = registry.route()
assert decision.provider == "backup"
| Parameter | Default | Description |
|---|---|---|
circuit_breaker_threshold | 5 | Consecutive failures before the circuit opens |
Health Monitoring¶
Check the status of all registered providers at any time:
health = registry.health()
for status in health:
print(f"{status.provider}: circuit_open={status.circuit_open}")
# After failing over from primary
health = registry.health()
print(f"Primary circuit open: {health[0].circuit_open}") # True
print(f"Backup circuit open: {health[1].circuit_open}") # False
Periodic health checks
Call registry.health() on a schedule and feed the results into your observability stack. Combine with Audit Logging to track routing decisions over time.
Model-Specific Routing¶
When you need a specific model, pass it to route(). The registry selects only providers that serve that model:
from safeai.core.router import ProviderRegistry, ProviderConfig
registry = ProviderRegistry(strategy="priority")
registry.register(ProviderConfig(
name="openai",
base_url="https://api.openai.com",
api_key_env="OPENAI_API_KEY",
models=["gpt-4o", "gpt-4o-mini"],
priority=1,
))
registry.register(ProviderConfig(
name="anthropic",
base_url="https://api.anthropic.com",
api_key_env="ANTHROPIC_API_KEY",
models=["claude-sonnet-4-20250514"],
priority=2,
))
# Routes to anthropic — the only provider with this model
decision = registry.route(model="claude-sonnet-4-20250514")
print(f"Model route: {decision.provider}") # "anthropic"
No provider for model
If no healthy provider serves the requested model, route() raises an error. Always ensure at least one provider is registered for each model you plan to use.
YAML Configuration¶
Configure routing declaratively in safeai.yaml:
routing:
enabled: true
strategy: priority
circuit_breaker_threshold: 3
providers:
- name: openai
base_url: https://api.openai.com
api_key_env: OPENAI_API_KEY
models: [gpt-4o, gpt-4o-mini]
priority: 1
- name: anthropic
base_url: https://api.anthropic.com
api_key_env: ANTHROPIC_API_KEY
models: [claude-sonnet-4-20250514]
priority: 2
- name: ollama
base_url: http://localhost:11434
models: [llama3.2]
priority: 3
Load and verify the config programmatically:
from safeai.config.models import SafeAIConfig
cfg = SafeAIConfig()
print(f"Routing: enabled={cfg.routing.enabled}, strategy={cfg.routing.strategy}")
| Setting | Default | Description |
|---|---|---|
routing.enabled | false | Enable multi-provider routing |
routing.strategy | priority | One of priority, cost_optimized, latency_optimized, round_robin |
routing.circuit_breaker_threshold | 5 | Failures before circuit opens |
Proxy Integration¶
When routing is enabled, the Proxy / Sidecar automatically applies your routing strategy to every intercepted LLM request:
proxy:
enabled: true
listen: "0.0.0.0:8080"
routing:
enabled: true
strategy: latency_optimized
providers:
- name: openai
base_url: https://api.openai.com
api_key_env: OPENAI_API_KEY
models: [gpt-4o]
priority: 1
- name: anthropic
base_url: https://api.anthropic.com
api_key_env: ANTHROPIC_API_KEY
models: [claude-sonnet-4-20250514]
priority: 2
Point your application at the proxy and SafeAI handles provider selection, failover, and health monitoring transparently:
See Also¶
- Proxy / Sidecar integration for HTTP proxy setup
- Policy Engine guide for boundary enforcement on routed requests
- Audit Logging guide for tracking routing decisions