Yash030/claude-code-proxy
2
1"""Task detection - analyze requests to determine required capabilities."""2 3from __future__ import annotations4 5import re6from dataclasses import dataclass7from typing import Any8 9from loguru import logger10 11from core.anthropic.content import get_block_attr12 13 14# Keywords that indicate specific task types15CODING_KEYWORDS = {16 "python",17 "javascript",18 "typescript",19 "java",20 "c++",21 "cpp",22 "golang",23 "rust",24 "ruby",25 "php",26 "swift",27 "kotlin",28 "sql",29 "html",30 "css",31 "react",32 "vue",33 "angular",34 "node",35 "django",36 "flask",37 "fastapi",38 "spring",39 "function",40 "class",41 "method",42 "api",43 "endpoint",44 "database",45 "query",46 "algorithm",47 "debug",48 "error",49 "fix",50 "implement",51 "create",52 "write",53 "code",54 "programming",55 "script",56 "module",57 "import",58 "export",59 "def ",60 "const ",61 "let ",62 "var ",63 "function ",64 "async ",65 "await ",66}67 68REASONING_KEYWORDS = {69 "analyze",70 "analysis",71 "reason",72 "why",73 "how",74 "explain",75 "compare",76 "contrast",77 "evaluate",78 "assess",79 "conclude",80 "deduce",81 "infer",82 "logic",83 "proof",84 "theorem",85 "hypothesis",86 "synthesize",87 "strategy",88 "think",89 "solve",90 "derive",91 "calculate",92 "compute",93 "math",94 "equation",95 "formula",96 "solution",97 "optimal",98 "best",99 "improve",100 "optimize",101 "design",102 "architecture",103 "system",104 "plan",105 "decision",106 "recommend",107}108 109VISION_KEYWORDS = {110 "image",111 "picture",112 "photo",113 "screenshot",114 "diagram",115 "chart",116 "graph",117 "visual",118 "see",119 "look at",120 "describe what",121 "what's in",122 "identify",123 "recognize",124 "detect",125 "object",126 "scene",127 "face",128 "text in image",129}130 131 132@dataclass(frozen=True, slots=True)133class TaskRequirements:134 """Detected requirements for a request."""135 136 requires_vision: bool = False137 requires_coding: bool = False138 requires_reasoning: bool = False139 requires_general_text: bool = True140 confidence: float = 0.0 # 0-1 confidence in detection141 142 @property143 def required_capabilities(self) -> set[str]:144 caps = set()145 if self.requires_vision:146 caps.add("vision")147 if self.requires_coding:148 caps.add("coding")149 if self.requires_reasoning:150 caps.add("reasoning")151 if self.requires_general_text:152 caps.add("general_text")153 return caps154 155 156class TaskDetector:157 """Analyze request messages to detect required capabilities."""158 159 def detect_requirements(self, messages: list[Any]) -> TaskRequirements:160 """Analyze messages and return required capabilities."""161 has_vision = False162 has_coding = False163 has_reasoning = False164 total_text = ""165 166 for msg in messages:167 # Handle both dict and object message formats168 if isinstance(msg, dict):169 content = msg.get("content")170 elif hasattr(msg, "content"):171 content = msg.content172 else:173 continue174 175 if isinstance(content, str):176 total_text += content.lower() + " "177 elif isinstance(content, list):178 for block in content:179 b_type = get_block_attr(block, "type") or ""180 181 # Check for image content182 if b_type == "image":183 has_vision = True184 logger.debug("TaskDetector: Found image in message")185 186 # Get text content187 if b_type == "text":188 text = get_block_attr(block, "text", "") or ""189 total_text += text.lower() + " "190 191 # Analyze text for keywords192 if total_text:193 has_coding = self._detect_coding(total_text)194 has_reasoning = self._detect_reasoning(total_text)195 196 # Calculate confidence197 confidence = self._calculate_confidence(198 has_vision, has_coding, has_reasoning, total_text199 )200 201 # Default to general text if nothing detected202 if not has_vision and not has_coding and not has_reasoning:203 has_general = True204 205 result = TaskRequirements(206 requires_vision=has_vision,207 requires_coding=has_coding,208 requires_reasoning=has_reasoning,209 requires_general_text=True,210 confidence=confidence,211 )212 213 logger.info(214 "TaskDetector: detected caps={} confidence={:.2f}",215 result.required_capabilities,216 confidence,217 )218 219 return result220 221 def _detect_coding(self, text: str) -> bool:222 """Detect if request requires coding capabilities."""223 # Check exact word matches first224 words = set(re.findall(r"\b\w+\b", text))225 coding_matches = words & CODING_KEYWORDS226 if len(coding_matches) >= 2:227 return True228 229 # Also check for substring matches (e.g., "python" in "write python code")230 for keyword in CODING_KEYWORDS:231 if keyword in text:232 # Found one keyword as substring, check for another233 remaining = text.replace(keyword, "")234 for kw2 in CODING_KEYWORDS:235 if kw2 in remaining and kw2 != keyword:236 return True237 # Also check for programming patterns238 if any(239 pat in text240 for pat in [241 "def ",242 "function ",243 "class ",244 "import ",245 "const ",246 "let ",247 "var ",248 "()",249 "=>",250 ]251 ):252 return True253 254 return False255 256 def _detect_reasoning(self, text: str) -> bool:257 """Detect if request requires reasoning capabilities."""258 words = set(re.findall(r"\b\w+\b", text))259 reasoning_matches = words & REASONING_KEYWORDS260 if len(reasoning_matches) >= 1:261 return True262 # Also check substring263 for keyword in REASONING_KEYWORDS:264 if keyword in text:265 return True266 return False267 268 def _calculate_confidence(269 self,270 has_vision: bool,271 has_coding: bool,272 has_reasoning: bool,273 text: str,274 ) -> float:275 """Calculate confidence in the detection."""276 if has_vision:277 return 0.95 # Image detection is reliable278 if has_coding or has_reasoning:279 # More text = more confident280 word_count = len(text.split())281 base = 0.7282 if word_count > 50:283 base = 0.8284 if word_count > 100:285 base = 0.85286 return base287 return 0.5 # Default confidence for general text288 289 def get_priority_hint(self, requirements: TaskRequirements) -> str:290 """Get a hint for model priority based on requirements."""291 if requirements.requires_vision:292 return "vision"293 if requirements.requires_coding:294 return "coding"295 if requirements.requires_reasoning:296 return "reasoning"297 return "balanced"298 