| """ |
| Tool Executor β Routes tool calls to the right implementation |
| Supports: code, shell, file, browser, github, memory, search, test, none |
| """ |
|
|
| import asyncio |
| import os |
| import subprocess |
| import tempfile |
| import time |
| from typing import Any, List, Optional |
|
|
| import structlog |
|
|
| from api.websocket_manager import WebSocketManager |
|
|
| log = structlog.get_logger() |
|
|
|
|
| class ToolExecutor: |
| def __init__(self, ws_manager: WebSocketManager): |
| self.ws = ws_manager |
|
|
| async def run( |
| self, |
| tool: str, |
| task: str, |
| goal: str = "", |
| previous: List = [], |
| task_id: str = "", |
| session_id: str = "", |
| ) -> str: |
| tool = (tool or "none").lower().strip() |
|
|
| dispatch = { |
| "code": self._tool_code, |
| "shell": self._tool_shell, |
| "file": self._tool_file, |
| "github": self._tool_github, |
| "memory": self._tool_memory, |
| "search": self._tool_search, |
| "test": self._tool_test, |
| "browser": self._tool_browser, |
| "none": self._tool_none, |
| } |
|
|
| fn = dispatch.get(tool, self._tool_none) |
| return await fn(task=task, goal=goal, previous=previous, task_id=task_id, session_id=session_id) |
|
|
| |
| async def _tool_code(self, task, goal, previous, task_id, session_id) -> str: |
| """Generate code using LLM.""" |
| from core.agent import AgentCore |
| agent = AgentCore(self.ws) |
| messages = [ |
| {"role": "system", "content": "You are an expert software engineer. Write clean, production-quality code. Return only the code with minimal explanation."}, |
| {"role": "user", "content": f"Task: {task}\nGoal: {goal}\n\nWrite the code to accomplish this."}, |
| ] |
| result = await agent.llm_stream(messages, task_id=task_id, session_id=session_id) |
| return result or f"# Code for: {task}" |
|
|
| |
| async def _tool_shell(self, task, goal, previous, task_id, session_id) -> str: |
| """Execute shell commands safely in a temp workspace.""" |
| |
| from core.agent import AgentCore |
| agent = AgentCore(self.ws) |
| messages = [ |
| {"role": "system", "content": "Extract the shell command to run. Return ONLY the command, nothing else."}, |
| {"role": "user", "content": f"Task: {task}"}, |
| ] |
| cmd = await agent.llm_stream(messages, task_id=task_id, session_id=session_id) |
| cmd = cmd.strip().strip("`").strip() |
|
|
| |
| blocked = ["rm -rf /", ":(){ :|:& };:", "mkfs", "dd if=", "shutdown", "reboot", "halt"] |
| for b in blocked: |
| if b in cmd: |
| return f"β Blocked dangerous command: {cmd}" |
|
|
| try: |
| await self.ws.emit(task_id, "step_progress", { |
| "action": "shell_exec", |
| "command": cmd[:200], |
| }, session_id=session_id) |
| proc = await asyncio.create_subprocess_shell( |
| cmd, |
| stdout=asyncio.subprocess.PIPE, |
| stderr=asyncio.subprocess.PIPE, |
| cwd="/tmp", |
| ) |
| stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=30) |
| output = stdout.decode()[:2000] + (stderr.decode()[:500] if stderr else "") |
| return output or "Command executed (no output)" |
| except asyncio.TimeoutError: |
| return "β οΈ Command timed out after 30s" |
| except Exception as e: |
| return f"β Shell error: {str(e)}" |
|
|
| |
| async def _tool_file(self, task, goal, previous, task_id, session_id) -> str: |
| """Create or modify files.""" |
| from core.agent import AgentCore |
| agent = AgentCore(self.ws) |
| messages = [ |
| {"role": "system", "content": "Generate file content. Respond with JSON: {\"filename\": \"...\", \"content\": \"...\"}"}, |
| {"role": "user", "content": f"Task: {task}\nGoal: {goal}"}, |
| ] |
| raw = await agent.llm_stream(messages, task_id=task_id, session_id=session_id) |
| try: |
| import json |
| start = raw.find("{") |
| end = raw.rfind("}") + 1 |
| data = json.loads(raw[start:end]) |
| filename = data.get("filename", "output.txt") |
| content = data.get("content", raw) |
| path = f"/tmp/workspace/{filename}" |
| os.makedirs(os.path.dirname(path), exist_ok=True) |
| with open(path, "w") as f: |
| f.write(content) |
| await self.ws.emit(task_id, "step_progress", { |
| "action": "file_written", |
| "filename": filename, |
| "size": len(content), |
| }, session_id=session_id) |
| return f"β
File written: {filename} ({len(content)} chars)" |
| except Exception as e: |
| return f"File task result: {raw[:500]}" |
|
|
| |
| async def _tool_github(self, task, goal, previous, task_id, session_id) -> str: |
| """Perform GitHub operations.""" |
| return f"GitHub: {task}\n(Set GITHUB_TOKEN to enable real GitHub operations)" |
|
|
| |
| async def _tool_memory(self, task, goal, previous, task_id, session_id) -> str: |
| """Save/retrieve from memory.""" |
| from memory.db import save_memory, search_memory |
| results = await search_memory(task[:50], session_id=session_id) |
| if results: |
| return "\n".join([r["content"][:300] for r in results[:3]]) |
| return "No relevant memories found" |
|
|
| |
| async def _tool_search(self, task, goal, previous, task_id, session_id) -> str: |
| """Web search using available APIs.""" |
| return f"Search result for: {task}\n(Integrate search API for real results)" |
|
|
| |
| async def _tool_test(self, task, goal, previous, task_id, session_id) -> str: |
| """Generate and run tests.""" |
| from core.agent import AgentCore |
| agent = AgentCore(self.ws) |
| messages = [ |
| {"role": "system", "content": "Write test cases for the given task. Use pytest format."}, |
| {"role": "user", "content": f"Write tests for: {task}\nContext: {goal}"}, |
| ] |
| result = await agent.llm_stream(messages, task_id=task_id, session_id=session_id) |
| return result or f"# Tests for: {task}" |
|
|
| |
| async def _tool_browser(self, task, goal, previous, task_id, session_id) -> str: |
| """Browser automation (stub β extend with playwright).""" |
| return f"Browser task: {task}\n(Install playwright for real browser automation)" |
|
|
| |
| async def _tool_none(self, task, goal, previous, task_id, session_id) -> str: |
| """Use LLM directly without tools.""" |
| from core.agent import AgentCore |
| agent = AgentCore(self.ws) |
| messages = [ |
| {"role": "system", "content": "You are an expert engineer. Complete the task thoroughly."}, |
| {"role": "user", "content": f"Task: {task}\nGoal context: {goal}"}, |
| ] |
| result = await agent.llm_stream(messages, task_id=task_id, session_id=session_id) |
| return result or f"Completed: {task}" |
|
|