File size: 7,053 Bytes
6afc01a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# """
# Stage 1: Query Router - Intelligent Server Selection
# """

# import json
# from typing import Dict, Any
# from openai import OpenAI


# class QueryRouter:
#     """Stage 1: Routes queries to appropriate MCP servers"""

#     def __init__(self, client: OpenAI, registry: Dict[str, Any]):
#         self.client = client
#         self.registry = registry

#     def route(self, query: str, location: Dict[str, Any]) -> Dict[str, Any]:
#         """
#         Analyze query and determine which MCP servers are needed
        
#         Returns:
#             {
#                 "intent": str,
#                 "required_servers": List[str],
#                 "reasoning": str
#             }
#         """
#         # Create registry summary
#         registry_text = "Available MCP Servers:\n"
#         for server_id, info in self.registry.items():
#             registry_text += f"\n{server_id}:\n"
#             registry_text += f"  Description: {info['description']}\n"
#             registry_text += f"  Use for: {', '.join(info['use_for'][:5])}\n"

#         system_prompt = f"""You are a query router for Farmer.chat agricultural system.

# Your task: Analyze the farmer's query and select which MCP servers are needed.

# {registry_text}

# Location: {location['name']} ({location['lat']}°N, {location['lon']}°E)

# CRITICAL RULES:
# 1. Select ALL servers that provide data relevant to answering the query completely
# 2. Consider IMPLICIT needs - look for context clues in the query
# 3. Keywords that trigger elevation: "elevation", "slope", "terrain", "my land", "my field", "drainage", "waterlogged", "frost risk", "wind exposure"
# 4. For crop decisions: ALWAYS include soil_properties + water + weather (comprehensive assessment)
# 5. For weather risk questions (wind, frost, flood): Include weather + elevation (terrain affects risk)
# 6. For pest questions with weather context: Include pests + weather
# 7. Be generous - better to have extra data than miss critical information
# 8. When farmer mentions location characteristics (height, slope, elevation), ALWAYS include elevation

# FEW-SHOT EXAMPLES:

# Example 1:
# Query: "Are strong winds expected at my land elevation?"
# Required: ["weather", "elevation"]
# Reasoning: Wind forecast from weather, but elevation affects wind exposure and risk. Farmer explicitly mentions elevation.

# Example 2:
# Query: "Should I plant rice today?"
# Required: ["weather", "soil_properties", "water"]
# Reasoning: Planting decisions need weather conditions, soil suitability, and water availability for comprehensive assessment.

# Example 3:
# Query: "Is there risk of frost tonight?"
# Required: ["weather", "elevation"]
# Reasoning: Frost risk depends on temperature from weather AND elevation (cold air sinks to lower areas).

# Example 4:
# Query: "What's my soil composition?"
# Required: ["soil_properties"]
# Reasoning: Direct soil query, only soil data needed. No implicit needs.

# Example 5:
# Query: "Can I grow tomatoes here?"
# Required: ["soil_properties", "water", "weather"]
# Reasoning: Crop suitability requires soil type, water availability, and climate conditions.

# Example 6:
# Query: "My field gets waterlogged after rain"
# Required: ["elevation", "soil_properties", "weather"]
# Reasoning: Waterlogging relates to drainage (elevation/slope), soil permeability, and rainfall patterns.

# Example 7:
# Query: "Should I spray pesticides now?"
# Required: ["pests", "weather"]
# Reasoning: Need to know pest presence AND weather conditions for optimal application timing.

# Example 8:
# Query: "How's the weather?"
# Required: ["weather"]
# Reasoning: Direct weather query, no implicit needs.

# Example 9:
# Query: "Give me complete farm status"
# Required: ["weather", "soil_properties", "water", "elevation", "pests"]
# Reasoning: Comprehensive assessment requires all available data sources.

# Example 10:
# Query: "Will it be too windy on my elevated farm?"
# Required: ["weather", "elevation"]
# Reasoning: Wind from weather, elevation affects exposure. "Elevated" is explicit context clue.

# Response format (JSON only):
# {{
#   "intent": "brief description of farmer's need",
#   "required_servers": ["server_id1", "server_id2"],
#   "reasoning": "why these servers"
# }}
# """

#         try:
#             response = self.client.chat.completions.create(
#                 model="gpt-4o",
#                 messages=[
#                     {"role": "system", "content": system_prompt},
#                     {"role": "user", "content": query}
#                 ],
#                 temperature=0.3
#             )

#             result_text = response.choices[0].message.content.strip()
#             result_text = result_text.replace("```json", "").replace("```", "").strip()

#             routing_decision = json.loads(result_text)
#             return routing_decision

#         except Exception as e:
#             print(f"❌ Routing error: {e}")
#             # Fallback - include common servers
#             return {
#                 "intent": "general_inquiry",
#                 "required_servers": ["weather", "soil_properties", "water"],
#                 "reasoning": "Fallback routing due to error"
#             }

"""
Query Router - Stage 1
Simple router that always queries all MCP servers for alert generation.
The intelligence is in the compiler, not the router.
"""

from typing import Dict, Any


class QueryRouter:
    """
    Router for alert generation system.
    
    For alert generation, ALWAYS queries all MCP servers since we need
    comprehensive data to identify potential issues. The compiler handles
    the intelligence of extracting only alerting/concerning information.
    """
    
    def __init__(self):
        """Initialize router - no LLM needed for simple all-server routing"""
        pass
    
    def route_alert_query(self, location: Dict[str, float]) -> Dict[str, bool]:
        """
        Route for alert generation - always query ALL servers.
        
        Args:
            location: Dict with 'latitude' and 'longitude' keys
            
        Returns:
            Dict with all servers set to True
        """
        return {
            "weather": True,
            "soil": True,
            "water": True,
            "elevation": True,
            "pests": True
        }
    
    def route_query(self, query: str, location: Dict[str, float]) -> Dict[str, bool]:
        """
        Route a general query - for now, also queries all servers.
        
        In the future, could use LLM to determine which servers are relevant
        to the specific query. But for alert generation, we always want all data.
        
        Args:
            query: User's query
            location: Dict with 'latitude' and 'longitude' keys
            
        Returns:
            Dict indicating which servers to query
        """
        # For now, query all servers for any query
        # The compiler will extract relevant information
        return self.route_alert_query(location)