Module 5: OpenRouter - Introduction

Fallback Strategies with OpenRouter

Overview

You'll implement robust fallback for high availability: if one model fails, it automatically tries another.

Time: 20 minutes
Difficulty: Medium


🎯 Objectives

  • ✅ Fallback chain (multiple models)
  • ✅ Retry with exponential backoff
  • ✅ Model health check
  • ✅ Failure logging

🔄 Basic Fallback

from openai import OpenAI
import os

client = OpenAI(
    base_url="https://openrouter.ai/api/v1",
    api_key=os.getenv("OPENROUTER_API_KEY")
)

def chat_with_fallback(prompt: str) -> str:
    """Fallback chain: if one fails, try the next."""
    
    models = [
        "openai/gpt-3.5-turbo",          # Primary
        "anthropic/claude-3-haiku",      # Fallback 1
        "mistralai/mixtral-8x7b-instruct"  # Fallback 2
    ]
    
    for model in models:
        try:
            response = client.chat.completions.create(
                model=model,
                messages=[{"role": "user", "content": prompt}],
                timeout=10.0
            )
            print(f"✅ Success with: {model}")
            return response.choices[0].message.content
        
        except Exception as e:
            print(f"❌ {model} failed: {e}")
            continue
    
    return "Error: All models failed"

# Test
print(chat_with_fallback("Hi"))

⚡ Fallback with Exponential Backoff

import time

def chat_with_smart_fallback(prompt: str, max_retries: int = 3) -> str:
    """Fallback + retry with backoff."""
    
    models = [
        "openai/gpt-3.5-turbo",
        "anthropic/claude-3-haiku",
        "mistralai/mixtral-8x7b-instruct"
    ]
    
    for model in models:
        for attempt in range(max_retries):
            try:
                response = client.chat.completions.create(
                    model=model,
                    messages=[{"role": "user", "content": prompt}]
                )
                print(f"✅ {model} (attempt {attempt + 1})")
                return response.choices[0].message.content
            
            except Exception as e:
                wait = 2 ** attempt  # 1s, 2s, 4s
                print(f"⚠️ {model} attempt {attempt + 1} failed, waiting {wait}s...")
                if attempt < max_retries - 1:
                    time.sleep(wait)
        
        print(f"❌ {model} failed after {max_retries} attempts")
    
    return "Error: All models exhausted"

🎯 Smart Fallback (Cheap → Expensive)

def smart_fallback(prompt: str) -> dict:
    """Fallback chain ordered by cost."""
    
    # Ordered by price (cheap → expensive)
    models = [
        {"name": "mistralai/mixtral-8x7b-instruct", "cost": 0.24},
        {"name": "anthropic/claude-3-haiku", "cost": 0.25},
        {"name": "openai/gpt-3.5-turbo", "cost": 0.50},
        {"name": "openai/gpt-4-turbo", "cost": 10.00}
    ]
    
    for model_info in models:
        model = model_info["name"]
        try:
            response = client.chat.completions.create(
                model=model,
                messages=[{"role": "user", "content": prompt}]
            )
            
            return {
                "response": response.choices[0].message.content,
                "model_used": model,
                "cost_per_million": model_info["cost"],
                "tokens": response.usage.total_tokens
            }
        
        except Exception as e:
            print(f"⚠️ {model} (${model_info['cost']}/1M) failed")
            continue
    
    return {"error": "All models failed"}

# Test
result = smart_fallback("Hi")
print(f"Response: {result['response']}")
print(f"Model: {result['model_used']}")

📊 Fallback with Logging

import logging
from datetime import datetime

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class FallbackChat:
    """Chat with fallback and full logging."""
    
    def __init__(self):
        self.client = OpenAI(
            base_url="https://openrouter.ai/api/v1",
            api_key=os.getenv("OPENROUTER_API_KEY")
        )
        self.models = [
            "openai/gpt-3.5-turbo",
            "anthropic/claude-3-haiku",
            "mistralai/mixtral-8x7b-instruct"
        ]
    
    def chat(self, prompt: str) -> str:
        """Chat with robust fallback."""
        
        for i, model in enumerate(self.models):
            try:
                logger.info(f"Trying model {i+1}/{len(self.models)}: {model}")
                
                response = self.client.chat.completions.create(
                    model=model,
                    messages=[{"role": "user", "content": prompt}]
                )
                
                logger.info(f"✅ Success with {model}")
                return response.choices[0].message.content
            
            except Exception as e:
                logger.error(f"❌ {model} failed: {type(e).__name__} - {e}")
                
                if i == len(self.models) - 1:
                    logger.critical("All models exhausted!")
                    raise
        
        return None

# Usage
bot = FallbackChat()
try:
    response = bot.chat("Hi")
    print(response)
except Exception as e:
    print(f"Fatal error: {e}")

🔍 Model Health Check

def check_model_health(model: str) -> bool:
    """Check whether a model is available."""
    
    try:
        response = client.chat.completions.create(
            model=model,
            messages=[{"role": "user", "content": "test"}],
            max_tokens=5,
            timeout=5.0
        )
        return True
    except Exception:
        return False

def get_healthy_models() -> list:
    """Return the available models."""
    
    all_models = [
        "openai/gpt-3.5-turbo",
        "anthropic/claude-3-haiku",
        "mistralai/mixtral-8x7b-instruct",
        "google/gemini-pro"
    ]
    
    healthy = []
    for model in all_models:
        if check_model_health(model):
            healthy.append(model)
            print(f"✅ {model}")
        else:
            print(f"❌ {model}")
    
    return healthy

# Pre-check before using
healthy_models = get_healthy_models()
# Use only healthy models

📊 Metrics and Monitoring

class MonitoredFallbackChat:
    """Chat with metrics."""
    
    def __init__(self):
        self.client = OpenAI(
            base_url="https://openrouter.ai/api/v1",
            api_key=os.getenv("OPENROUTER_API_KEY")
        )
        self.stats = {
            "total_requests": 0,
            "successful": 0,
            "failed": 0,
            "by_model": {}
        }
    
    def chat(self, prompt: str, models: list) -> str:
        """Chat with metrics tracking."""
        
        self.stats["total_requests"] += 1
        
        for model in models:
            try:
                response = self.client.chat.completions.create(
                    model=model,
                    messages=[{"role": "user", "content": prompt}]
                )
                
                # Track success
                self.stats["successful"] += 1
                if model not in self.stats["by_model"]:
                    self.stats["by_model"][model] = {"success": 0, "failed": 0}
                self.stats["by_model"][model]["success"] += 1
                
                return response.choices[0].message.content
            
            except Exception as e:
                # Track failure
                if model not in self.stats["by_model"]:
                    self.stats["by_model"][model] = {"success": 0, "failed": 0}
                self.stats["by_model"][model]["failed"] += 1
        
        self.stats["failed"] += 1
        return None
    
    def print_stats(self):
        """Show the statistics."""
        print("\n=== Metrics ===")
        print(f"Total requests: {self.stats['total_requests']}")
        print(f"Successful: {self.stats['successful']}")
        print(f"Failed: {self.stats['failed']}")
        print("\nBy model:")
        for model, stats in self.stats["by_model"].items():
            total = stats["success"] + stats["failed"]
            success_rate = (stats["success"] / total * 100) if total > 0 else 0
            print(f"  {model}: {success_rate:.1f}% success ({stats['success']}/{total})")

# Usage
bot = MonitoredFallbackChat()
models = ["openai/gpt-3.5-turbo", "anthropic/claude-3-haiku"]

for _ in range(10):
    bot.chat("Test query", models)

bot.print_stats()

✅ Summary

Fallback strategies:

  1. Chain (try multiple models)
  2. Exponential backoff (retry with delays)
  3. Cheap-to-expensive ordering
  4. Health checks pre-flight
  5. Comprehensive logging
  6. Metrics tracking

High availability: 99.9%+ with 3+ models


Next: 06-model-selection-auto.md

You'll implement intelligent model auto-selection based on query type.

Time: 25 min