from __future__ import annotations import re from src.schemas import RouteDecision COMPLEX_TERMS = ( "build", "create", "modify", "debug", "script", "api", "repository", "repo", "scrape", "automate", "test", "programming", "code", "deploy", "generate files", "investigate", "fix", "browser automation", "monitor", "implement", "write code", "make an app", "generate a file", "run tests", "check this repo", ) COMPLEX_ACTION_TERMS = ( "build", "create", "modify", "debug", "script", "scrape", "automate", "implement", "write code", "make an app", "generate a file", "run tests", "fix", "monitor", ) DIRECT_SEARCH_TERMS = ( "find", "search", "look up", "lookup", "google", "web search", "internet", "latest", "current", "today", "news", "summarize", "compare", "list", "recommend", "docs", "documentation", "sources", "tools", "options", ) LOCAL_TERMS = ( "mm1", "memory", "route", "privacy boundary", "what do you know", "what's my name", "what is my name", "whats my name", "do you know my name", "who am i", "about me", "remember about me", ) CONVERSATION_TERMS = ( "hi", "hello", "hey", "thanks", "thank you", "how are you", "good morning", "good evening", "good night", "ok", "okay", "yes", "no", ) QUESTION_STARTERS = ("what", "who", "when", "where", "why", "how", "which", "can", "should", "could", "would") def _contains_any(text: str, terms: tuple[str, ...]) -> bool: return any(term in text for term in terms) def _starts_like_question(text: str) -> bool: return text.endswith("?") or bool(re.match(r"^\s*(what|who|when|where|why|how|which|can|should|could|would)\b", text)) def decide_route(message: str) -> RouteDecision: lower = message.lower() compact = lower.strip() normalized = compact.strip(" ?!.") if _contains_any(compact, LOCAL_TERMS): return RouteDecision("local_answer", 0.92, ["local_memory: can be answered from local state or MM1 context."]) if _contains_any(compact, COMPLEX_ACTION_TERMS): return RouteDecision("codex_big_agent", 0.88, ["complex_task: needs programming, automation, API, repository, or multi-step technical work."]) if _contains_any(compact, DIRECT_SEARCH_TERMS): return RouteDecision("direct_search", 0.82, ["explicit_web_task: asks for search, current information, comparison, docs, or recommendations."]) if _contains_any(compact, COMPLEX_TERMS): return RouteDecision("codex_big_agent", 0.78, ["complex_task: likely needs technical analysis or tool work."]) if normalized in CONVERSATION_TERMS or len(compact.split()) <= 4 and not _starts_like_question(compact): return RouteDecision("local_answer", 0.76, ["conversation: normal chat does not need outside tools."]) if _starts_like_question(compact) or compact.split()[0:1] and compact.split()[0] in QUESTION_STARTERS: return RouteDecision("local_answer", 0.55, ["needs_clarification: unclear whether to answer locally or use live search."]) return RouteDecision("local_answer", 0.66, ["conversation: no clear outside-tool requirement detected."])