Spaces:
Running
Running
| """ | |
| Intelligent Query Router | |
| ========================= | |
| Detects user intent from query and routes to appropriate: | |
| - Response type: text, chart, table, graph visualization | |
| - Mode: RAG, GraphRAG, Hybrid, Vision, Prediction | |
| - Model: DeepSeek, Claude, GPT-4V based on complexity | |
| No hardcoding - all pattern-based detection. | |
| """ | |
| import re | |
| from typing import Dict, Any, List, Tuple, Optional | |
| from dataclasses import dataclass | |
| from enum import Enum | |
| class ResponseType(Enum): | |
| TEXT = "text" | |
| CHART = "chart" | |
| TABLE = "table" | |
| GRAPH = "graph" # Knowledge graph visualization | |
| MIXED = "mixed" # Text + visuals | |
| class QueryIntent(Enum): | |
| LOOKUP = "lookup" # Simple fact retrieval | |
| ANALYSIS = "analysis" # Deeper analysis | |
| COMPARISON = "comparison" # Compare entities | |
| TREND = "trend" # Time-based trends | |
| BREAKDOWN = "breakdown" # Category breakdown | |
| PREDICTION = "prediction" # Future forecasting | |
| VISUALIZATION = "visualization" # Explicit chart request | |
| RELATIONSHIP = "relationship" # Entity relationships | |
| SUMMARY = "summary" # Overview/summary | |
| class QueryAnalysis: | |
| """Result of query analysis""" | |
| intent: QueryIntent | |
| response_type: ResponseType | |
| suggested_mode: str | |
| suggested_model: str | |
| chart_type: Optional[str] | |
| entities: List[str] | |
| metrics: List[str] | |
| time_reference: Optional[str] | |
| confidence: float | |
| # Intent detection patterns (no hardcoding - pattern-based) | |
| INTENT_PATTERNS = { | |
| QueryIntent.COMPARISON: [ | |
| r'\bcompare\b', r'\bvs\b', r'\bversus\b', r'\bdifference\b', | |
| r'\bhigher\b.*\bthan\b', r'\blower\b.*\bthan\b', r'\bbetter\b.*\bthan\b', | |
| r'\bwhich\b.*\b(more|less|higher|lower|better)\b' | |
| ], | |
| QueryIntent.TREND: [ | |
| r'\btrend\b', r'\bover time\b', r'\bhistory\b', r'\bgrowth\b', | |
| r'\bchange\b.*\b(over|since|from)\b', r'\bmonthly\b', r'\byearly\b', | |
| r'\bquarterly\b', r'\blast\b.*\b(months?|years?|weeks?)\b' | |
| ], | |
| QueryIntent.BREAKDOWN: [ | |
| r'\bby\b.*\b(department|category|type|region|product)\b', | |
| r'\bbreakdown\b', r'\bdistribution\b', r'\bsplit\b', | |
| r'\bper\b', r'\beach\b', r'\bgroup\b.*\bby\b' | |
| ], | |
| QueryIntent.PREDICTION: [ | |
| r'\bpredict\b', r'\bforecast\b', r'\bestimate\b', r'\bproject\b', | |
| r'\bwill\b.*\bbe\b', r'\bnext\b.*\b(month|year|quarter)\b', | |
| r'\bfuture\b', r'\bexpect\b' | |
| ], | |
| QueryIntent.VISUALIZATION: [ | |
| r'\bchart\b', r'\bgraph\b', r'\bvisualize\b', r'\bplot\b', | |
| r'\bshow\b.*\b(me|chart|graph)\b', r'\bdisplay\b.*\b(chart|graph)\b', | |
| r'\bbar\b.*\bchart\b', r'\bpie\b.*\bchart\b', r'\bline\b.*\bchart\b' | |
| ], | |
| QueryIntent.RELATIONSHIP: [ | |
| r'\brelate\b', r'\bconnect\b', r'\blink\b', r'\bdepend\b', | |
| r'\brelationship\b', r'\bnetwork\b', r'\bwho\b.*\b(works|reports|manages)\b' | |
| ], | |
| QueryIntent.SUMMARY: [ | |
| r'\bsummary\b', r'\boverview\b', r'\bsummarize\b', r'\bhighlight\b', | |
| r'\bkey\b.*\b(points|metrics|insights)\b', r'\btop\b.*\b\d+\b' | |
| ], | |
| QueryIntent.ANALYSIS: [ | |
| r'\banalyze\b', r'\banalysis\b', r'\bexplain\b', r'\bwhy\b', | |
| r'\bunderstand\b', r'\binsight\b', r'\bpattern\b' | |
| ], | |
| } | |
| # Chart type detection patterns | |
| CHART_PATTERNS = { | |
| 'bar': [r'\bbar\b', r'\bcompar', r'\brank', r'\btop\b', r'\bby\b.*\b(department|category)\b'], | |
| 'line': [r'\bline\b', r'\btrend\b', r'\bover time\b', r'\bgrowth\b', r'\bmonthly\b', r'\byearly\b'], | |
| 'pie': [r'\bpie\b', r'\bdistribution\b', r'\bbreakdown\b', r'\bshare\b', r'\bproportion\b'], | |
| 'scatter': [r'\bscatter\b', r'\bcorrelation\b', r'\brelation\b.*\bbetween\b'], | |
| 'heatmap': [r'\bheatmap\b', r'\bmatrix\b', r'\bdensity\b'], | |
| } | |
| def detect_intent(query: str) -> QueryIntent: | |
| """Detect the primary intent of a query""" | |
| query_lower = query.lower() | |
| # Check each intent pattern | |
| for intent, patterns in INTENT_PATTERNS.items(): | |
| for pattern in patterns: | |
| if re.search(pattern, query_lower): | |
| return intent | |
| # Default to lookup for simple queries | |
| if len(query.split()) <= 5: | |
| return QueryIntent.LOOKUP | |
| return QueryIntent.ANALYSIS | |
| def detect_response_type(query: str, intent: QueryIntent) -> ResponseType: | |
| """Determine what type of response the user wants""" | |
| query_lower = query.lower() | |
| # Explicit visualization request | |
| if intent == QueryIntent.VISUALIZATION: | |
| return ResponseType.CHART | |
| # Relationship queries β graph | |
| if intent == QueryIntent.RELATIONSHIP: | |
| return ResponseType.GRAPH | |
| # Trend/comparison/breakdown β chart | |
| if intent in [QueryIntent.TREND, QueryIntent.COMPARISON, QueryIntent.BREAKDOWN]: | |
| return ResponseType.CHART | |
| # Check for explicit table request | |
| if re.search(r'\btable\b|\blist\b.*\ball\b', query_lower): | |
| return ResponseType.TABLE | |
| # Check for "show me" pattern | |
| if re.search(r'\bshow\b.*\b(chart|graph|visual)\b', query_lower): | |
| return ResponseType.CHART | |
| # Summary/analysis with data β mixed | |
| if intent in [QueryIntent.SUMMARY, QueryIntent.ANALYSIS]: | |
| return ResponseType.MIXED | |
| return ResponseType.TEXT | |
| def detect_chart_type(query: str, intent: QueryIntent) -> Optional[str]: | |
| """Detect which chart type is most appropriate""" | |
| query_lower = query.lower() | |
| for chart_type, patterns in CHART_PATTERNS.items(): | |
| for pattern in patterns: | |
| if re.search(pattern, query_lower): | |
| return chart_type | |
| # Default chart types based on intent | |
| intent_chart_map = { | |
| QueryIntent.TREND: 'line', | |
| QueryIntent.COMPARISON: 'bar', | |
| QueryIntent.BREAKDOWN: 'pie', | |
| QueryIntent.PREDICTION: 'line', | |
| } | |
| return intent_chart_map.get(intent) | |
| def extract_entities(query: str) -> List[str]: | |
| """Extract entity names from query (departments, products, etc.)""" | |
| entities = [] | |
| # Look for quoted terms | |
| quoted = re.findall(r'"([^"]+)"', query) | |
| entities.extend(quoted) | |
| # Look for capitalized words (likely proper nouns) | |
| words = query.split() | |
| for i, word in enumerate(words): | |
| # Skip first word and common words | |
| if i > 0 and word[0].isupper() and word.lower() not in ['the', 'a', 'an', 'and', 'or']: | |
| entities.append(word) | |
| return list(set(entities)) | |
| def extract_metrics(query: str) -> List[str]: | |
| """Extract metric/measure names from query""" | |
| metrics = [] | |
| metric_patterns = [ | |
| r'\b(salary|revenue|sales|cost|profit|expense|budget)\b', | |
| r'\b(count|total|average|sum|max|min|mean)\b', | |
| r'\b(performance|rating|score|percentage|rate)\b', | |
| ] | |
| query_lower = query.lower() | |
| for pattern in metric_patterns: | |
| matches = re.findall(pattern, query_lower) | |
| metrics.extend(matches) | |
| return list(set(metrics)) | |
| def extract_time_reference(query: str) -> Optional[str]: | |
| """Extract time reference from query""" | |
| query_lower = query.lower() | |
| time_patterns = [ | |
| (r'last\s+(\d+)\s+(day|week|month|year)s?', 'relative'), | |
| (r'this\s+(week|month|quarter|year)', 'current'), | |
| (r'(january|february|march|april|may|june|july|august|september|october|november|december)', 'month'), | |
| (r'(\d{4})', 'year'), | |
| (r'(q[1-4]|quarter\s*\d)', 'quarter'), | |
| ] | |
| for pattern, time_type in time_patterns: | |
| match = re.search(pattern, query_lower) | |
| if match: | |
| return f"{time_type}:{match.group(0)}" | |
| return None | |
| def suggest_mode(intent: QueryIntent, response_type: ResponseType, has_image: bool = False) -> str: | |
| """Suggest the best mode for this query""" | |
| # Image attached β Vision mode | |
| if has_image: | |
| return "vision" | |
| # Prediction intent β Prediction mode | |
| if intent == QueryIntent.PREDICTION: | |
| return "prediction" | |
| # Relationship/network β GraphRAG | |
| if intent == QueryIntent.RELATIONSHIP or response_type == ResponseType.GRAPH: | |
| return "graphrag" | |
| # Complex analysis β Hybrid | |
| if intent in [QueryIntent.ANALYSIS, QueryIntent.COMPARISON] and response_type == ResponseType.MIXED: | |
| return "hybrid" | |
| # Default to RAG | |
| return "rag" | |
| def suggest_model(intent: QueryIntent, query: str, has_image: bool = False) -> str: | |
| """Suggest the best model for this query""" | |
| # Vision required | |
| if has_image: | |
| return "gpt4v" | |
| # Complex reasoning β Claude | |
| if intent in [QueryIntent.ANALYSIS, QueryIntent.PREDICTION]: | |
| if len(query.split()) > 15: # Long, complex query | |
| return "claude-3.5" | |
| # Default to DeepSeek (fast, accurate) | |
| return "deepseek" | |
| def analyze_query(query: str, has_image: bool = False) -> QueryAnalysis: | |
| """ | |
| Full query analysis - determines intent, response type, mode, and model. | |
| This is the main entry point. | |
| """ | |
| intent = detect_intent(query) | |
| response_type = detect_response_type(query, intent) | |
| chart_type = detect_chart_type(query, intent) if response_type == ResponseType.CHART else None | |
| return QueryAnalysis( | |
| intent=intent, | |
| response_type=response_type, | |
| suggested_mode=suggest_mode(intent, response_type, has_image), | |
| suggested_model=suggest_model(intent, query, has_image), | |
| chart_type=chart_type, | |
| entities=extract_entities(query), | |
| metrics=extract_metrics(query), | |
| time_reference=extract_time_reference(query), | |
| confidence=0.8 if intent != QueryIntent.LOOKUP else 0.9 | |
| ) | |
| def get_routing_decision(query: str, has_image: bool = False) -> Dict[str, Any]: | |
| """ | |
| Get routing decision for the query. | |
| Returns dict with mode, model, response_type for use by send_message. | |
| """ | |
| analysis = analyze_query(query, has_image) | |
| return { | |
| "mode": analysis.suggested_mode, | |
| "model": analysis.suggested_model, | |
| "response_type": analysis.response_type.value, | |
| "intent": analysis.intent.value, | |
| "chart_type": analysis.chart_type, | |
| "entities": analysis.entities, | |
| "metrics": analysis.metrics, | |
| "generate_chart": analysis.response_type in [ResponseType.CHART, ResponseType.MIXED], | |
| } | |
| # Test | |
| if __name__ == "__main__": | |
| test_queries = [ | |
| "What is total salary?", | |
| "Compare Engineering and Sales salary", | |
| "Show me revenue trend over the last 6 months", | |
| "Create a pie chart of department distribution", | |
| "Who reports to the Engineering manager?", | |
| "Predict next quarter's revenue", | |
| "Give me a summary of key metrics", | |
| ] | |
| print("Query Intent Detection Test") | |
| print("=" * 60) | |
| for q in test_queries: | |
| result = get_routing_decision(q) | |
| print(f"\nQuery: {q}") | |
| print(f" Intent: {result['intent']}") | |
| print(f" Response: {result['response_type']}") | |
| print(f" Mode: {result['mode']}") | |
| print(f" Model: {result['model']}") | |
| print(f" Chart: {result['chart_type']}") | |