71 lines
2.2 KiB
Python
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}")
|