Skip to content

Commit fb5cd57

Browse files
committed
feat: Implement keyword-based routing fallback and enhance error handling in routing service
1 parent 52ff0bb commit fb5cd57

2 files changed

Lines changed: 197 additions & 8 deletions

File tree

src/services/routing_service.py

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ def determine_agent(
7575
return result.output
7676

7777
except Exception as e:
78-
# Fallback to simple keyword-based routing
79-
return self._fallback_routing(user_query, csv_loaded)
78+
print(f"Routing failed with error: {e}")
79+
# Use simple keyword-based routing as fallback
80+
return self._keyword_based_routing(user_query, csv_loaded)
8081

8182
def handle_conversation_query(self, user_query: str) -> str:
8283
"""Handle conversational queries."""
@@ -131,7 +132,7 @@ def handle_csv_query(
131132

132133
except Exception as e:
133134
# If LLM fails, provide a graceful response without showing errors
134-
return WORST_CASE_SCENARIO
135+
return WORST_CASE_SCENARIO
135136

136137
def _execute_csv_analysis(
137138
self, python_code: str, csv_info: Dict[str, Any], explanation: str
@@ -224,15 +225,15 @@ def install_package(package):
224225
current_code = fixed_code
225226
continue # Try again with fixed code
226227

227-
return WORST_CASE_SCENARIO
228+
return WORST_CASE_SCENARIO
228229

229230
except Exception as e:
230-
return WORST_CASE_SCENARIO
231+
return WORST_CASE_SCENARIO
231232

232233
except Exception as e:
233-
return WORST_CASE_SCENARIO
234+
return WORST_CASE_SCENARIO
234235

235-
return WORST_CASE_SCENARIO
236+
return WORST_CASE_SCENARIO
236237

237238
def _fix_python_code(
238239
self, original_code: str, error_message: str, csv_info: Dict[str, Any]
@@ -375,6 +376,68 @@ def _format_sql_response(self, sql_response) -> str:
375376

376377
return "\n\n".join(response_parts)
377378

379+
def _keyword_based_routing(self, user_query: str, csv_loaded: bool) -> RoutingDecision:
380+
"""Keyword-based routing when LLM routing fails."""
381+
query_lower = user_query.lower()
382+
383+
# CSV Agent keywords
384+
csv_keywords = [
385+
"csv", "analyze", "chart", "plot", "graph", "average", "mean", "sum",
386+
"count", "max", "min", "statistics", "data", "visualization", "top",
387+
"bottom", "highest", "lowest", "distribution", "correlation"
388+
]
389+
390+
# SQL Agent keywords
391+
sql_keywords = [
392+
"select", "insert", "update", "delete", "sql", "query", "table",
393+
"database", "users", "customers", "orders", "products", "where",
394+
"join", "group by", "order by", "from"
395+
]
396+
397+
# Conversation Agent keywords
398+
conversation_keywords = [
399+
"hello", "hi", "hey", "how are you", "what can you do", "help",
400+
"thanks", "thank you", "goodbye", "bye", "good morning", "good evening"
401+
]
402+
403+
# Check for CSV analysis (prioritize if CSV is loaded)
404+
if csv_loaded and any(keyword in query_lower for keyword in csv_keywords):
405+
return RoutingDecision(
406+
agent="CSV_AGENT",
407+
confidence=0.8,
408+
reasoning="Keyword-based routing detected CSV analysis request"
409+
)
410+
411+
# Check for SQL keywords
412+
if any(keyword in query_lower for keyword in sql_keywords):
413+
return RoutingDecision(
414+
agent="SQL_AGENT",
415+
confidence=0.8,
416+
reasoning="Keyword-based routing detected SQL request"
417+
)
418+
419+
# Check for conversation keywords
420+
if any(keyword in query_lower for keyword in conversation_keywords):
421+
return RoutingDecision(
422+
agent="CONVERSATION_AGENT",
423+
confidence=0.9,
424+
reasoning="Keyword-based routing detected conversation request"
425+
)
426+
427+
# Default based on context
428+
if csv_loaded:
429+
return RoutingDecision(
430+
agent="CSV_AGENT",
431+
confidence=0.6,
432+
reasoning="CSV loaded, defaulting to CSV analysis"
433+
)
434+
else:
435+
return RoutingDecision(
436+
agent="CONVERSATION_AGENT",
437+
confidence=0.5,
438+
reasoning="No clear intent detected, defaulting to conversation"
439+
)
440+
378441
def _fallback_routing(self, user_query: str, csv_loaded: bool) -> RoutingDecision:
379442
"""Fallback routing when LLM routing fails - let LLM decide, not hardcoded keywords."""
380443
# Default to conversation - let the LLM handle all decisions
@@ -386,4 +449,4 @@ def _fallback_routing(self, user_query: str, csv_loaded: bool) -> RoutingDecisio
386449

387450
def _get_fallback_conversation_response(self, user_query: str) -> str:
388451
"""Get fallback conversation response when LLM fails."""
389-
return WORST_CASE_SCENARIO
452+
return WORST_CASE_SCENARIO

test_routing_fix.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/env python3
2+
"""Test script to verify the routing fix is working."""
3+
4+
import sys
5+
import os
6+
import tempfile
7+
import pandas as pd
8+
9+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
10+
11+
from src.services.routing_service import IntelligentRoutingService
12+
from src.backend.orchestrator import BackendOrchestrator
13+
from src.schemas.requests import NewChatRequest
14+
15+
16+
def test_routing_only():
17+
"""Test just the routing mechanism."""
18+
print("🧠 Testing Routing Mechanism")
19+
print("=" * 40)
20+
21+
routing_service = IntelligentRoutingService()
22+
23+
test_cases = [
24+
("Hello", False, "CONVERSATION_AGENT"),
25+
("What is the average salary?", True, "CSV_AGENT"),
26+
("Show me all users", False, "SQL_AGENT"),
27+
("Create a chart", True, "CSV_AGENT"),
28+
("SELECT * FROM users", False, "SQL_AGENT"),
29+
]
30+
31+
for query, csv_loaded, expected in test_cases:
32+
print(f"\nQuery: '{query}' (CSV loaded: {csv_loaded})")
33+
decision = routing_service.determine_agent(query, [], csv_loaded=csv_loaded)
34+
print(f"Expected: {expected}")
35+
print(f"Actual: {decision.agent}")
36+
print(f"Confidence: {decision.confidence}")
37+
print(f"Reasoning: {decision.reasoning}")
38+
39+
status = "✅ PASS" if decision.agent == expected else "❌ FAIL"
40+
print(f"Status: {status}")
41+
42+
43+
def test_csv_analysis_with_real_data():
44+
"""Test CSV analysis with actual CSV data."""
45+
print("\n📊 Testing CSV Analysis with Real Data")
46+
print("=" * 40)
47+
48+
# Create a temporary CSV file
49+
data = {
50+
'name': ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve'],
51+
'age': [25, 30, 35, 28, 32],
52+
'salary': [50000, 60000, 70000, 55000, 65000],
53+
'department': ['IT', 'HR', 'IT', 'Finance', 'HR']
54+
}
55+
56+
df = pd.DataFrame(data)
57+
58+
# Create temporary CSV file
59+
with tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False) as f:
60+
df.to_csv(f.name, index=False)
61+
csv_path = f.name
62+
63+
print(f"Created test CSV: {csv_path}")
64+
print("CSV Content:")
65+
print(df.to_string(index=False))
66+
67+
# Create orchestrator and test CSV analysis
68+
orchestrator = BackendOrchestrator()
69+
70+
# Create session
71+
session_info = orchestrator.create_new_session(
72+
NewChatRequest(session_name="Test Session")
73+
)
74+
session_id = session_info.session_id
75+
print(f"\nCreated session: {session_id}")
76+
77+
# Load CSV data
78+
with open(csv_path, 'r') as f:
79+
csv_content = f.read()
80+
81+
result = orchestrator.load_csv_data(session_id, csv_content)
82+
print(f"CSV Load Result: {result['status']}")
83+
84+
# Test CSV analysis queries
85+
test_queries = [
86+
"What is the average salary?",
87+
"How many people are in each department?",
88+
"Who has the highest salary?",
89+
]
90+
91+
for query in test_queries:
92+
print(f"\n--- Testing Query: '{query}' ---")
93+
try:
94+
response = orchestrator.generate_intelligent_response(session_id, query)
95+
print(f"Response: {response.content}")
96+
print(f"Response Type: {type(response.content)}")
97+
98+
# Check if this is raw Python code (the old problem)
99+
if "import" in response.content or "pd.read_csv" in response.content:
100+
print("❌ ISSUE: Response contains raw Python code!")
101+
else:
102+
print("✅ SUCCESS: Response is clean human-readable text!")
103+
104+
except Exception as e:
105+
print(f"❌ ERROR: {str(e)}")
106+
107+
# Cleanup
108+
os.unlink(csv_path)
109+
110+
111+
def main():
112+
"""Run all tests."""
113+
print("🚀 Testing Routing Fix")
114+
print("=" * 50)
115+
116+
# Test 1: Routing mechanism
117+
test_routing_only()
118+
119+
# Test 2: CSV analysis with real data
120+
test_csv_analysis_with_real_data()
121+
122+
print("\n🎉 Testing completed!")
123+
124+
125+
if __name__ == "__main__":
126+
main()

0 commit comments

Comments
 (0)