Compare commits
2 Commits
6ffb769b95
...
95cac39fcf
| Author | SHA1 | Date | |
|---|---|---|---|
| 95cac39fcf | |||
| 310ee4a266 |
@@ -17,6 +17,8 @@ Copy `config.example.json` to `config.json` and fill in credentials, or use envi
|
|||||||
|
|
||||||
Key env vars: `TG_API_ID`, `TG_API_HASH`, `SESSION_DIR`, `ACCOUNTS_JSON` (JSON list) or `ACCOUNT_SESSIONS` (comma-separated session names), `AI_CONFIG` (full AI block as JSON), `BOT_CONFIG` (path to config file), `BOT_DOTENV_PATH` (path to .env).
|
Key env vars: `TG_API_ID`, `TG_API_HASH`, `SESSION_DIR`, `ACCOUNTS_JSON` (JSON list) or `ACCOUNT_SESSIONS` (comma-separated session names), `AI_CONFIG` (full AI block as JSON), `BOT_CONFIG` (path to config file), `BOT_DOTENV_PATH` (path to .env).
|
||||||
|
|
||||||
|
`ai.image_understand.command` defaults to `["uvx", "minimax-coding-plan-mcp", "-y"]`. If `uvx` is not on PATH but `uv` is available via Python, use `["python3", "-m", "uv", "tool", "run", "minimax-coding-plan-mcp", "-y"]` instead.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
### Boot path
|
### Boot path
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
Reference in New Issue
Block a user