File size: 7,044 Bytes
e4d2119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
"""
Multi-Agent Router for LoL Coach
Routes user queries to specialized agents based on intent classification.
"""

import logging
from typing import Literal, Optional
from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

# Setup logging
logger = logging.getLogger(__name__)


class RouteQuery(BaseModel):
    """Route a user query to the most relevant agent."""
    
    agent: Literal[
        "match_analyzer",
        "build_advisor", 
        "video_guide",
        "knowledge_base",
        "pregame_agent",
        "orchestrator"
    ] = Field(
        description="The agent that should handle this query"
    )
    
    reasoning: str = Field(
        description="Brief explanation of why this agent was chosen"
    )
    
    needs_multiple_agents: bool = Field(
        default=False,
        description="True if query requires multiple agents to collaborate"
    )


class QueryRouter:
    """
    Intelligent router that classifies user queries and directs them
    to the appropriate specialized agent.
    """
    
    def __init__(self, llm: ChatOpenAI):
        self.llm = llm
        self.structured_llm = llm.with_structured_output(RouteQuery)
        
        self.routing_prompt = ChatPromptTemplate.from_messages([
            ("system", """You are an expert at routing League of Legends coaching queries to specialized agents.

Available Agents:

1. **match_analyzer** - Handles:
   - Match history analysis
   - Performance statistics
   - Win/loss tracking
   - Recent game reviews
   - Enemy champion analysis
   - Build comparisons from past games
   - Examples: "Analyze my recent matches", "How did I do vs Zed?", "Show my match history"

2. **build_advisor** - Handles:
   - Optimal item builds
   - Rune recommendations
   - Champion matchups
   - Build optimization
   - Counter picks
   - Examples: "What items should I build on Ahri?", "Best runes for Jinx?", "Counters to Yasuo?"

3. **video_guide** - Handles:
   - YouTube video searches
   - Champion guides
   - Matchup videos
   - Educational content
   - Tutorial requests
   - Examples: "Find Ahri guides", "Videos about wave management", "Ahri vs Zed matchup videos"

4. **knowledge_base** - Handles:
   - Game concept explanations
   - Terminology definitions
   - Mechanics explanations
   - General LoL questions
   - Examples: "What is AP?", "Explain armor", "What does split pushing mean?"

5. **pregame_agent** - Handles:
   - Champion select strategy
   - Draft phase advice
   - Ban recommendations
   - Team composition analysis
   - Role and lane assignment
   - Counter-picking in draft
   - Examples: "Who should I ban?", "What champion should I pick?", "Help me with champion select", "Good team comp?"

6. **orchestrator** - Handles:
   - Complex queries needing multiple agents
   - Questions combining match analysis + builds
   - Comprehensive coaching requests
   - Multi-step inquiries
   - Examples: "Analyze my matches and suggest better builds", "Why am I losing and what should I do?"

Routing Guidelines:
- Choose the MOST SPECIFIC agent possible
- Only use orchestrator if query clearly needs 2+ agents
- Match keywords to agent expertise
- Consider user intent, not just keywords
- Default to orchestrator if very unclear

Analyze the query and route it to the best agent."""),
            ("human", "{query}")
        ])
    
    def route(self, query: str) -> RouteQuery:
        """
        Route a user query to the appropriate agent.
        
        Args:
            query: User's question or request
            
        Returns:
            RouteQuery with agent selection and reasoning
        """
        logger.info(f"Routing query: {query[:100]}...")
        
        try:
            chain = self.routing_prompt | self.structured_llm
            result = chain.invoke({"query": query})
            
            # Log routing decision
            logger.info(f"Routed to: {result.agent} - Reason: {result.reasoning}")
            logger.debug(f"Multiple agents needed: {result.needs_multiple_agents}")
            
            print(f"\n๐Ÿงญ Router Decision:")
            print(f"   Query: {query[:80]}...")
            print(f"   โ†’ Routed to: {result.agent}")
            print(f"   Reasoning: {result.reasoning}")
            if result.needs_multiple_agents:
                print(f"   ๐Ÿ”„ Multi-agent workflow required")
            
            return result
            
        except Exception as e:
            logger.error(f"Routing failed: {str(e)}", exc_info=True)
            # Fallback to orchestrator for complex queries
            logger.warning("Falling back to orchestrator")
            return RouteQuery(
                agent="orchestrator",
                reasoning=f"Routing error occurred, defaulting to orchestrator: {str(e)}",
                needs_multiple_agents=True
            )
    
    def get_agent_description(self, agent_name: str) -> str:
        """Get a human-readable description of what an agent does."""
        descriptions = {
            "match_analyzer": "๐ŸŽฏ Match Analyzer - Analyzes your game history and performance",
            "build_advisor": "๐Ÿ› ๏ธ Build Advisor - Recommends optimal items, runes, and champions",
            "video_guide": "๐ŸŽฌ Video Guide - Finds YouTube tutorials and gameplay videos",
            "knowledge_base": "๐Ÿ“š Knowledge Base - Explains game concepts and terminology",
            "pregame_agent": "๐ŸŽฏ Pregame Agent - Champion select, drafting, and ban strategy",
            "orchestrator": "๐ŸŽญ Orchestrator - Coordinates multiple agents for complex requests"
        }
        return descriptions.get(agent_name, agent_name)


def create_router(openai_api_key: str, temperature: float = 0.3) -> QueryRouter:
    """
    Create a configured query router.
    
    Args:
        openai_api_key: OpenAI API key
        temperature: LLM temperature (lower = more deterministic routing)
        
    Returns:
        Configured QueryRouter instance
    """
    llm = ChatOpenAI(
        api_key=openai_api_key,
        model="gpt-4o-mini",
        temperature=temperature
    )
    
    return QueryRouter(llm)


# Example usage and testing
if __name__ == "__main__":
    import os
    from dotenv import load_dotenv
    
    load_dotenv()
    
    router = create_router(os.getenv("OPENAI_API_KEY"))
    
    # Test queries
    test_queries = [
        "Analyze my recent matches",
        "What items should I build on Ahri?",
        "Find some Yasuo guides",
        "What does AP mean?",
        "I keep losing as Jinx, what builds should I use and show me some guides",
        "How did I do against Zed in my last game?",
        "Best runes for Master Yi jungle?",
        "Explain wave management",
        "Find matchup videos for Ahri vs Syndra"
    ]
    
    print("=" * 80)
    print("๐Ÿงช Testing Query Router")
    print("=" * 80)
    
    for query in test_queries:
        result = router.route(query)
        print()