ducklm/duck_core/tools/coder.py

71 lines
2.2 KiB
Python

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}")