from __future__ import annotations import logging from typing import Any from duck_core.model_client import ModelClient from duck_core.tools.base import ToolResult logger = logging.getLogger(__name__) class CoderTool: """Tool that delegates code generation to the coder-role LLM. The coder model receives the task description and relevant context, then returns code or technical analysis. """ name = "coder" risk_level = "low" def __init__( self, model_client: ModelClient | None = None, role: str = "coder", max_output_tokens: int = 16384, ): self._model_client = model_client self._role = role self._max_output_tokens = max_output_tokens async def run(self, args: dict[str, Any]) -> ToolResult: task_description = str(args.get("task_description", "")).strip() if not task_description: return ToolResult(ok=False, error="task_description is required for coder tool") context = str(args.get("context", "")).strip() language = str(args.get("language", "python")).strip() prompt_parts = [f"Task: {task_description}"] if language: prompt_parts.append(f"Language: {language}") if context: prompt_parts.append(f"Context:\n{context}") messages = [{"role": "user", "content": "\n\n".join(prompt_parts)}] try: if self._model_client is None: return ToolResult( ok=False, error="Coder tool has no model client configured", ) response = await self._model_client.chat( self._role, messages, max_output_tokens=self._max_output_tokens, ) return ToolResult( ok=True, output=response.content, metadata={ "role": self._role, "model": response.model, "latency_ms": response.latency_ms, }, ) except Exception as exc: logger.warning("Coder tool failed: %s", exc) return ToolResult(ok=False, error=f"Coder tool failed: {exc}")