Add send_file tool to send workspace files to Telegram chat

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-18 13:59:40 +02:00
parent 310ee4a266
commit 95cac39fcf
2 changed files with 46 additions and 2 deletions

View File

@@ -332,9 +332,33 @@ class AIService:
def _get_allowed_roots(self) -> tuple[Path, ...]: def _get_allowed_roots(self) -> tuple[Path, ...]:
return (self.config.workspace_dir, self.config.prompt.root) return (self.config.workspace_dir, self.config.prompt.root)
async def _execute_send_file(
self,
arguments: dict,
ctx: "CommandContext",
allowed_roots: tuple[Path, ...],
) -> str:
from app.ai.tools import _resolve_sandboxed_path
raw_path = str(arguments.get("path", "")).strip()
caption = arguments.get("caption") or None
if not raw_path:
raise ValueError("send_file: 'path' is required")
path = _resolve_sandboxed_path(raw_path, allowed_roots)
if not path.exists():
raise ValueError(f"send_file: file not found: {raw_path!r}")
if not path.is_file():
raise ValueError(f"send_file: not a file: {raw_path!r}")
await ctx.client.send_document(
ctx.message.chat.id,
document=str(path),
caption=caption,
)
return f"Sent {raw_path} to chat"
async def _execute_tool_call( async def _execute_tool_call(
self, self,
tool_call: Any, tool_call: Any,
ctx: "CommandContext",
mcp_client: MinimaxImageUnderstandClient | None, mcp_client: MinimaxImageUnderstandClient | None,
mcp_names: set[str], mcp_names: set[str],
allowed_roots: tuple[Path, ...], allowed_roots: tuple[Path, ...],
@@ -349,7 +373,9 @@ class AIService:
return f"Tool {name!r}: invalid JSON arguments: {exc}" return f"Tool {name!r}: invalid JSON arguments: {exc}"
try: try:
if name in FILE_TOOL_DISPATCH: if name == "send_file":
result = await self._execute_send_file(arguments, ctx, allowed_roots)
elif name in FILE_TOOL_DISPATCH:
result = await FILE_TOOL_DISPATCH[name](arguments, allowed_roots) result = await FILE_TOOL_DISPATCH[name](arguments, allowed_roots)
elif name in mcp_names and mcp_client is not None: elif name in mcp_names and mcp_client is not None:
result = await mcp_client.call_tool(name, arguments) result = await mcp_client.call_tool(name, arguments)
@@ -463,7 +489,7 @@ class AIService:
tc_name = getattr(getattr(tc, "function", None), "name", "?") tc_name = getattr(getattr(tc, "function", None), "name", "?")
await self._maybe_call_progress(progress, f"🛠 Calling tool: {tc_name}") await self._maybe_call_progress(progress, f"🛠 Calling tool: {tc_name}")
result_text = await self._execute_tool_call( result_text = await self._execute_tool_call(
tc, mcp_client, mcp_names, allowed_roots, progress tc, ctx, mcp_client, mcp_names, allowed_roots, progress
) )
messages.append({ messages.append({
"role": "tool", "role": "tool",

View File

@@ -196,6 +196,24 @@ FILE_TOOL_DISPATCH: dict[str, ToolExecutor] = {
} }
FILE_TOOL_SCHEMAS: list[dict[str, Any]] = [ FILE_TOOL_SCHEMAS: list[dict[str, Any]] = [
{
"type": "function",
"function": {
"name": "send_file",
"description": (
"Send a file from workspace/ or prompts/ to the current Telegram chat as a document. "
"Use this when the user asks to receive, download, or share a file."
),
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Relative file path"},
"caption": {"type": "string", "description": "Optional caption for the file"},
},
"required": ["path"],
},
},
},
{ {
"type": "function", "type": "function",
"function": { "function": {