Agentic Architecture Pattern: Model Router

Written by

in


    """
Pattern 200: Model Router Pattern

Intelligently route queries to the most appropriate model based on task type,
complexity, cost, latency requirements, or other criteria.
"""

from typing import Dict, Any, List, Optional, Callable
from dataclasses import dataclass
from enum import Enum
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI


class TaskType(Enum):
    """Types of tasks for routing."""
    SIMPLE_QA = "simple_qa"
    COMPLEX_REASONING = "complex_reasoning"
    CODE_GENERATION = "code_generation"
    CREATIVE_WRITING = "creative_writing"
    TRANSLATION = "translation"
    SUMMARIZATION = "summarization"
    CLASSIFICATION = "classification"


@dataclass
class Model:
    """Model configuration for routing."""
    name: str
    model_id: str
    strengths: List[TaskType]
    cost_per_token: float
    avg_latency_ms: float
    max_tokens: int
    llm: Optional[ChatOpenAI] = None
    
    def __post_init__(self):
        if self.llm is None:
            # Set temperature based on task strengths
            temp = 0.7 if TaskType.CREATIVE_WRITING in self.strengths else 0.3
            self.llm = ChatOpenAI(model=self.model_id, temperature=temp)


class ModelRouterPattern:
    """
    Model Router Pattern - Route to optimal model.
    """
    
    def __init__(self):
        self.models: List[Model] = []
        self.router_llm = ChatOpenAI(model="gpt-4", temperature=0)
        self.routing_history: List[Dict[str, Any]] = []
    
    def register_model(self, model: Model):
        """Register a model for routing."""
        self.models.append(model)
    
    def classify_task(self, query: str) -> TaskType:
        """Classify the task type using LLM."""
        task_types = ", ".join([t.value for t in TaskType])
        
        prompt = ChatPromptTemplate.from_messages([
            ("system", f"Classify the task type. Options: {task_types}. Respond with only the task type."),
            ("human", "{query}")
        ])
        
        chain = prompt | self.router_llm
        response = chain.invoke({"query": query})
        
        # Parse response to TaskType
        response_text = str(response.content).lower().strip()
        for task_type in TaskType:
            if task_type.value in response_text:
                return task_type
        
        return TaskType.SIMPLE_QA  # Default
    
    def select_model(self, task_type: TaskType, preferences: Optional[Dict[str, Any]] = None) -> Model:
        """Select best model for task type based on preferences."""
        preferences = preferences or {}
        
        # Filter models by task type strength
        suitable_models = [m for m in self.models if task_type in m.strengths]
        
        if not suitable_models:
            suitable_models = self.models  # Fallback to all models
        
        if not suitable_models:
            raise ValueError("No models registered")
        
        # Select based on preferences
        optimize_for = preferences.get("optimize_for", "quality")
        
        if optimize_for == "cost":
            return min(suitable_models, key=lambda m: m.cost_per_token)
        elif optimize_for == "latency":
            return min(suitable_models, key=lambda m: m.avg_latency_ms)
        elif optimize_for == "quality":
            # Assume first model in strengths list is highest quality
            return suitable_models[0]
        else:
            return suitable_models[0]
    
    def run(self, query: str, preferences: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """Route query to appropriate model and execute."""
        # Classify task
        task_type = self.classify_task(query)
        print(f"Task classified as: {task_type.value}")
        
        # Select model
        selected_model = self.select_model(task_type, preferences)
        print(f"Routing to model: {selected_model.name}\n")
        
        # Execute with selected model
        prompt = ChatPromptTemplate.from_messages([
            ("system", "You are a helpful AI assistant."),
            ("human", "{query}")
        ])
        
        chain = prompt | selected_model.llm
        response = chain.invoke({"query": query})
        
        # Track routing decision
        routing_record = {
            "query": query,
            "task_type": task_type.value,
            "selected_model": selected_model.name,
            "model_cost": selected_model.cost_per_token,
            "model_latency": selected_model.avg_latency_ms
        }
        self.routing_history.append(routing_record)
        
        return {
            "query": query,
            "task_type": task_type.value,
            "routed_to": selected_model.name,
            "result": response.content,
            "routing_metadata": {
                "cost_per_token": selected_model.cost_per_token,
                "avg_latency_ms": selected_model.avg_latency_ms
            }
        }
    
    def get_statistics(self) -> Dict[str, Any]:
        """Get routing statistics."""
        model_usage = {}
        for record in self.routing_history:
            model = record["selected_model"]
            model_usage[model] = model_usage.get(model, 0) + 1
        
        return {
            "total_queries": len(self.routing_history),
            "model_usage": model_usage,
            "registered_models": len(self.models)
        }


# Example usage
if __name__ == "__main__":
    print("=== Model Router Pattern Demo ===\n")
    
    router = ModelRouterPattern()
    
    # Register models with different capabilities
    router.register_model(Model(
        name="GPT-4",
        model_id="gpt-4",
        strengths=[TaskType.COMPLEX_REASONING, TaskType.CODE_GENERATION],
        cost_per_token=0.00003,
        avg_latency_ms=2000,
        max_tokens=8000
    ))
    
    router.register_model(Model(
        name="GPT-3.5-Turbo",
        model_id="gpt-3.5-turbo",
        strengths=[TaskType.SIMPLE_QA, TaskType.SUMMARIZATION, TaskType.CLASSIFICATION],
        cost_per_token=0.000002,
        avg_latency_ms=800,
        max_tokens=4000
    ))
    
    router.register_model(Model(
        name="GPT-4-Creative",
        model_id="gpt-4",
        strengths=[TaskType.CREATIVE_WRITING, TaskType.TRANSLATION],
        cost_per_token=0.00003,
        avg_latency_ms=2000,
        max_tokens=8000
    ))
    
    # Test different queries
    queries = [
        "What is 2 + 2?",
        "Write a Python function to implement quicksort with detailed comments",
        "Write a creative short story about a robot discovering emotions"
    ]
    
    for i, query in enumerate(queries, 1):
        print(f"\nQuery {i}: {query}")
        print("-" * 60)
        result = router.run(query, preferences={"optimize_for": "quality"})
        print(f"Routed to: {result['routed_to']}")
        print(f"Result: {result['result'][:150]}...")
        print()
    
    # Statistics
    print("\n=== Routing Statistics ===")
    stats = router.get_statistics()
    print(f"Total queries: {stats['total_queries']}")
    print(f"Model usage: {stats['model_usage']}")

    

Agentic Architecture Pattern: Model Router

Pattern 200: 200 Model Router Pattern – Line-by-Line Explanation

Overview

Pattern 200: Model Router Pattern

Intelligently route queries to the most appropriate model based on task type, complexity, cost, latency requirements, or other criteria.


Code Structure Analysis

File Information

  • Pattern Number: 200
  • Pattern Name: 200 Model Router Pattern
  • File Path: patterns\llm_specific\200_model_router_pattern.py
  • Total Lines: 212

Detailed Code Explanation

Module Docstring

"""
Pattern 200: Model Router Pattern

Intelligently route queries to the most appropriate model based on task type,
complexity, cost, latency requirements, or other criteria.
"""

Explanation: This module docstring describes the purpose and key features of the 200 Model Router Pattern pattern.


Imports Section

from typing import Dict, Any, List, Optional, Callable
from dataclasses import dataclass
from enum import Enum
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI  # First 20 imports shown

Explanation: The module imports necessary dependencies: – from typing import Dict, Any, List, Optional, Callable: Type hints for better code documentation – from dataclasses import dataclass: Data class decorators for clean data structures – from enum import Enum: Enumeration support for fixed sets of values – from langchain_core.prompts import ChatPromptTemplate: LangChain framework integration – from langchain_openai import ChatOpenAI: LangChain framework integration


Enumerations

TaskType (Line 15)

class TaskType(Enum):
    SIMPLE_QA
    COMPLEX_REASONING
    CODE_GENERATION
    CREATIVE_WRITING
    TRANSLATION
    SUMMARIZATION
    CLASSIFICATION

Purpose: Types of tasks for routing.

Values: – SIMPLE_QA: Represents simple qa – COMPLEX_REASONING: Represents complex reasoning – CODE_GENERATION: Represents code generation – CREATIVE_WRITING: Represents creative writing – TRANSLATION: Represents translation – SUMMARIZATION: Represents summarization – CLASSIFICATION: Represents classification


Classes

TaskType (Line 15)

Description: Types of tasks for routing.

Model (Line 27)

Description: Model configuration for routing.

Methods:

__post_init__ (Line 37)
def __post_init__(self):
    """No method docstring."""

Purpose: No method docstring.

Parameters: – self: Instance reference

ModelRouterPattern (Line 44)

Description: Model Router Pattern – Route to optimal model.

Methods:

__init__ (Line 49)
def __init__(self):
    """No method docstring."""

Purpose: No method docstring.

Parameters: – self: Instance reference

register_model (Line 54)
def register_model(self, model):
    """Register a model for routing."""

Purpose: Register a model for routing.

Parameters: – self: Instance reference – model: Parameter for model

classify_task (Line 58)
def classify_task(self, query):
    """Classify the task type using LLM."""

Purpose: Classify the task type using LLM.

Parameters: – self: Instance reference – query: Parameter for query

select_model (Line 78)
def select_model(self, task_type, preferences):
    """Select best model for task type based on preferences."""

Purpose: Select best model for task type based on preferences.

Parameters: – self: Instance reference – task_type: Parameter for task_type – preferences: Parameter for preferences

run (Line 104)
def run(self, query, preferences):
    """Route query to appropriate model and execute."""

Purpose: Route query to appropriate model and execute.

Parameters: – self: Instance reference – query: Parameter for query – preferences: Parameter for preferences

get_statistics (Line 144)
def get_statistics(self):
    """Get routing statistics."""

Purpose: Get routing statistics.

Parameters: – self: Instance reference


Design Patterns Used

  • Strategy Pattern: Different execution strategies for various scenarios
  • State Pattern: Managing different states in the workflow
  • Chain of Responsibility: Sequential processing through workflow nodes
  • Observer Pattern: Tracking and monitoring pattern execution
  • Factory Pattern: Creating instances with proper configuration

Usage Example

# Initialize the pattern
pattern = TaskType(name="example", model_name="gpt-4")

# Configure and execute
result = pattern.execute(data={"key": "value"})

# Check results
print(result)

Key Concepts

  • LLM Integration: Uses LangChain/OpenAI for intelligent decision-making
  • Workflow Orchestration: LangGraph manages execution flow through nodes
  • State Management: TypedDict defines workflow state structure
  • Error Handling: Comprehensive error handling and fallback mechanisms
  • Extensibility: Designed for easy customization and extension

Extension Points

This pattern can be extended by: 1. Adding custom workflow nodes 2. Implementing additional validation logic 3. Integrating with external services 4. Customizing LLM prompts and responses 5. Adding monitoring and logging capabilities

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *