diff --git a/agent.py b/agent.py index 3db9ace..dd30c32 100644 --- a/agent.py +++ b/agent.py @@ -24,7 +24,7 @@ from langchain_core.tools import Tool from vram_manager import VRAMManager from router import Router from agent_factory import build_medium_agent, build_complex_agent -from fast_tools import FastToolRunner, RealTimeSearchTool +from fast_tools import FastToolRunner, WeatherTool import channels # Bifrost gateway — all LLM inference goes through here @@ -121,7 +121,7 @@ _memory_search_tool = None # Fast tools run before the LLM — classifier + context enricher _fast_tool_runner = FastToolRunner([ - RealTimeSearchTool(searxng_url=SEARXNG_URL), + WeatherTool(searxng_url=SEARXNG_URL), ]) # GPU mutex: one LLM inference at a time diff --git a/fast_tools.py b/fast_tools.py index b4ac2e4..a31d091 100644 --- a/fast_tools.py +++ b/fast_tools.py @@ -35,55 +35,61 @@ class FastTool(ABC): async def run(self, message: str) -> str: ... -class RealTimeSearchTool(FastTool): +class WeatherTool(FastTool): """ - Injects live SearXNG search snippets for queries that require real-time data: - weather, news, prices, scores, business hours. + Fetches current weather for the user's location (Balashikha, Moscow region) + by querying SearXNG, which has external internet access. - Matched queries are also forced to medium tier by the Router so the richer - model handles the injected context. + Triggered by any weather-related query. The Router also forces medium tier + when this tool matches so the richer model handles the injected data. """ _PATTERN = re.compile( r"\b(weather|forecast|temperature|rain(ing)?|snow(ing)?|humidity|wind\s*speed" - r"|today.?s news|breaking news|latest news|news today|current events" - r"|bitcoin price|crypto price|stock price|exchange rate" - r"|right now|currently|at the moment|live score|score now|score today" - r"|open now|hours today|is .+ open)\b", + r"|холодно|тепло|погода|прогноз погоды" + r"|how (hot|cold|warm) is it|what.?s the (weather|temp)|dress for the weather)\b", re.IGNORECASE, ) + # Fixed query — always fetch home location weather + _SEARCH_QUERY = "weather Balashikha Moscow now" + def __init__(self, searxng_url: str): self._searxng_url = searxng_url @property def name(self) -> str: - return "real_time_search" + return "weather" def matches(self, message: str) -> bool: return bool(self._PATTERN.search(message)) async def run(self, message: str) -> str: - """Search SearXNG and return top snippets as a context block.""" + """Query SearXNG for Balashikha weather and return current conditions snippet.""" try: async with httpx.AsyncClient(timeout=15) as client: r = await client.get( f"{self._searxng_url}/search", - params={"q": message, "format": "json"}, + params={"q": self._SEARCH_QUERY, "format": "json"}, ) r.raise_for_status() - items = r.json().get("results", [])[:4] + items = r.json().get("results", [])[:5] except Exception as e: - return f"[real_time_search error: {e}]" + return f"[weather error: {e}]" + if not items: return "" - lines = [f"Live web search results for: {message}\n"] - for i, item in enumerate(items, 1): + + # Prefer results whose snippets contain actual current conditions + lines = ["Current weather data for Balashikha, Moscow region:\n"] + for item in items: + snippet = item.get("content", "") title = item.get("title", "") url = item.get("url", "") - snippet = item.get("content", "")[:400] - lines.append(f"[{i}] {title}\nURL: {url}\n{snippet}\n") - return "\n".join(lines) + if snippet: + lines.append(f"[{title}]\n{snippet}\nSource: {url}\n") + + return "\n".join(lines) if len(lines) > 1 else "" class FastToolRunner: