Integrate memory policy and reflection runtime
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
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}")
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import Any
|
||||
|
||||
from duck_core.tools.base import Tool, ToolResult
|
||||
from duck_core.tools.coder import CoderTool
|
||||
from duck_core.tools.file_read import FileReadTool
|
||||
from duck_core.tools.file_write import FileWriteTool
|
||||
from duck_core.tools.list_dir import ListDirTool
|
||||
@@ -13,7 +14,7 @@ class ToolGateway:
|
||||
self.tools = {tool.name: tool for tool in tools}
|
||||
|
||||
@classmethod
|
||||
def default(cls, workspace: str) -> "ToolGateway":
|
||||
def default(cls, workspace: str, model_client: Any = None) -> "ToolGateway":
|
||||
return cls(
|
||||
[
|
||||
FileReadTool(workspace),
|
||||
@@ -21,9 +22,19 @@ class ToolGateway:
|
||||
ListDirTool(workspace),
|
||||
SearchFilesTool(workspace),
|
||||
ShellExecSafeTool(workspace),
|
||||
CoderTool(model_client=model_client),
|
||||
]
|
||||
)
|
||||
|
||||
def with_model_client(self, model_client: Any) -> "ToolGateway":
|
||||
"""Return a new gateway with model-dependent tools configured."""
|
||||
new_tools = list(self.tools.values())
|
||||
# Replace coder tool with one that has model_client
|
||||
new_tools = [
|
||||
t for t in new_tools if not isinstance(t, CoderTool)
|
||||
] + [CoderTool(model_client=model_client)]
|
||||
return self.__class__(new_tools)
|
||||
|
||||
async def run_action(
|
||||
self, action: dict[str, Any], approved: bool = False, password: str | None = None
|
||||
) -> ToolResult:
|
||||
|
||||
Reference in New Issue
Block a user