refactor: remove run_python tool, consolidate all execution to run_command
This commit is contained in:
parent
4bf0733907
commit
78544cb205
@ -108,7 +108,7 @@ python3 -m server.app --path <当前目录> --port 8091 --thinking-mode
|
|||||||
- `list_files`: 列出目录内容
|
- `list_files`: 列出目录内容
|
||||||
- `search_files`: 搜索文件
|
- `search_files`: 搜索文件
|
||||||
|
|
||||||
> 说明:目录创建/重命名/删除等通用文件管理建议统一使用 `run_command` 或 `run_python`。
|
> 说明:目录创建/重命名/删除等通用文件管理建议统一使用 `run_command`;需要 Python 时先探测并选择合适解释器。
|
||||||
|
|
||||||
#### 终端操作
|
#### 终端操作
|
||||||
|
|
||||||
@ -119,8 +119,7 @@ python3 -m server.app --path <当前目录> --port 8091 --thinking-mode
|
|||||||
- `sleep`: 等待命令执行完成
|
- `sleep`: 等待命令执行完成
|
||||||
|
|
||||||
**快速命令执行**:
|
**快速命令执行**:
|
||||||
- `run_command`: 执行 Shell 命令
|
- `run_command`: 执行 Shell 命令;需要 Python 时通过命令探测并调用合适解释器
|
||||||
- `run_python`: 执行 Python 代码
|
|
||||||
|
|
||||||
#### 网络检索
|
#### 网络检索
|
||||||
- `web_search`: 网络搜索
|
- `web_search`: 网络搜索
|
||||||
|
|||||||
@ -354,8 +354,8 @@ terminal_input(command="vim file.txt", output_wait=5) # vim 需要完整 TTY
|
|||||||
|
|
||||||
✅ **正确:**
|
✅ **正确:**
|
||||||
```python
|
```python
|
||||||
# 使用 run_python 执行 Python 代码
|
# 使用 run_command 探测并调用合适的 Python 解释器
|
||||||
run_python(code="print('hello')", timeout=5)
|
run_command(command="python3 -c 'print(\"hello\")'", timeout=5)
|
||||||
|
|
||||||
# 使用 edit_file 修改文件
|
# 使用 edit_file 修改文件
|
||||||
edit_file(file_path="file.txt", old_string="...", new_string="...")
|
edit_file(file_path="file.txt", old_string="...", new_string="...")
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
"label": "终端指令",
|
"label": "终端指令",
|
||||||
"default_enabled": true,
|
"default_enabled": true,
|
||||||
"silent_when_disabled": false,
|
"silent_when_disabled": false,
|
||||||
"tools": ["run_command", "run_python"]
|
"tools": ["run_command"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -38,4 +38,3 @@
|
|||||||
- `categories[].label`:分类展示名
|
- `categories[].label`:分类展示名
|
||||||
- `categories[].tools`:该分类下具体工具名称列表(用于前端渲染/筛选/白名单提示)
|
- `categories[].tools`:该分类下具体工具名称列表(用于前端渲染/筛选/白名单提示)
|
||||||
- `default_enabled/silent_when_disabled`:用于 UI/策略展示(并不等于“你一定能调用到该工具”,真实可用性仍取决于服务端策略与运行时环境)
|
- `default_enabled/silent_when_disabled`:用于 UI/策略展示(并不等于“你一定能调用到该工具”,真实可用性仍取决于服务端策略与运行时环境)
|
||||||
|
|
||||||
|
|||||||
@ -158,7 +158,6 @@ export function reduceTaskEvent(items: TimelineItem[], state: EventRenderState,
|
|||||||
|
|
||||||
function toolTitle(name: string, args: any, status: string): string {
|
function toolTitle(name: string, args: any, status: string): string {
|
||||||
if (name === 'run_command') return `运行 ${args.command || ''}`.trim();
|
if (name === 'run_command') return `运行 ${args.command || ''}`.trim();
|
||||||
if (name === 'run_python') return '运行 Python';
|
|
||||||
if (name === 'write_file') return `Write ${args.file_path || args.path || ''} ${countWrite(args.content || '')}`.trim();
|
if (name === 'write_file') return `Write ${args.file_path || args.path || ''} ${countWrite(args.content || '')}`.trim();
|
||||||
if (name === 'edit_file') return `Edited ${args.file_path || args.path || ''} ${countEdit(args)}`.trim();
|
if (name === 'edit_file') return `Edited ${args.file_path || args.path || ''} ${countEdit(args)}`.trim();
|
||||||
if (name === 'read_file' || name === 'read_skill') return `阅读 ${args.path || args.file_path || ''}`.trim();
|
if (name === 'read_file' || name === 'read_skill') return `阅读 ${args.path || args.file_path || ''}`.trim();
|
||||||
@ -193,7 +192,7 @@ function toolInitialBody(name: string, args: any): string {
|
|||||||
function toolResultBody(name: string, args: any, result: any): string {
|
function toolResultBody(name: string, args: any, result: any): string {
|
||||||
if (typeof result === 'string') return summarizeText(result);
|
if (typeof result === 'string') return summarizeText(result);
|
||||||
if (!result) return '';
|
if (!result) return '';
|
||||||
if (name === 'run_command' || name === 'terminal_input' || name === 'run_python') {
|
if (name === 'run_command' || name === 'terminal_input') {
|
||||||
const exitCode = result.exit_code ?? result.returncode;
|
const exitCode = result.exit_code ?? result.returncode;
|
||||||
const text = String(result?.output || result?.stdout || result?.stderr || '').trim();
|
const text = String(result?.output || result?.stdout || result?.stderr || '').trim();
|
||||||
if (exitCode !== undefined && exitCode !== 0) return text ? `exit code ${exitCode}` : `exit code ${exitCode}\n(no output)`;
|
if (exitCode !== undefined && exitCode !== 0) return text ? `exit code ${exitCode}` : `exit code ${exitCode}\n(no output)`;
|
||||||
|
|||||||
@ -131,7 +131,7 @@ class MainTerminal(MainTerminalCommandMixin, MainTerminalContextMixin, MainTermi
|
|||||||
broadcast_callback=None, # CLI模式不需要广播
|
broadcast_callback=None, # CLI模式不需要广播
|
||||||
container_session=container_session,
|
container_session=container_session,
|
||||||
)
|
)
|
||||||
# 让 run_command/run_python 复用终端容器,保持环境一致
|
# 让 run_command 复用终端容器,保持环境一致
|
||||||
self.terminal_ops.attach_terminal_manager(self.terminal_manager)
|
self.terminal_ops.attach_terminal_manager(self.terminal_manager)
|
||||||
self._apply_container_session(container_session)
|
self._apply_container_session(container_session)
|
||||||
|
|
||||||
|
|||||||
@ -368,8 +368,6 @@ class MainTerminalCommandMixin:
|
|||||||
print(f"{OUTPUT_FORMATS['terminal']} 执行终端命令")
|
print(f"{OUTPUT_FORMATS['terminal']} 执行终端命令")
|
||||||
elif tool_name == "web_search":
|
elif tool_name == "web_search":
|
||||||
print(f"{OUTPUT_FORMATS['search']} 网络搜索")
|
print(f"{OUTPUT_FORMATS['search']} 网络搜索")
|
||||||
elif tool_name == "run_python":
|
|
||||||
print(f"{OUTPUT_FORMATS['code']} 执行Python代码")
|
|
||||||
elif tool_name == "run_command":
|
elif tool_name == "run_command":
|
||||||
print(f"{OUTPUT_FORMATS['terminal']} 执行系统命令")
|
print(f"{OUTPUT_FORMATS['terminal']} 执行系统命令")
|
||||||
elif tool_name == "update_memory":
|
elif tool_name == "update_memory":
|
||||||
|
|||||||
@ -125,7 +125,7 @@ class MainTerminalContextMixin:
|
|||||||
detailed_rules = (
|
detailed_rules = (
|
||||||
"- 所有命令和工具均可直接使用,无需用户批准\n"
|
"- 所有命令和工具均可直接使用,无需用户批准\n"
|
||||||
"- run_command:支持任意命令,包括管道、重定向、子shell等\n"
|
"- run_command:支持任意命令,包括管道、重定向、子shell等\n"
|
||||||
"- 文件操作:read_file / write_file / edit_file 可直接使用;其他文件管理请通过 run_command 或 run_python 执行"
|
"- 文件操作:read_file / write_file / edit_file 可直接使用;其他文件管理请通过 run_command 执行;需要 Python 时先探测并选择合适解释器"
|
||||||
)
|
)
|
||||||
elif mode == "readonly":
|
elif mode == "readonly":
|
||||||
detailed_rules = (
|
detailed_rules = (
|
||||||
@ -138,7 +138,7 @@ class MainTerminalContextMixin:
|
|||||||
detailed_rules = (
|
detailed_rules = (
|
||||||
"- run_command:先在系统只读沙箱执行;仅当出现权限拒绝时才触发审批\n"
|
"- run_command:先在系统只读沙箱执行;仅当出现权限拒绝时才触发审批\n"
|
||||||
"- 审批通过后:仅当前这一次命令会重试为可写沙箱执行\n"
|
"- 审批通过后:仅当前这一次命令会重试为可写沙箱执行\n"
|
||||||
"- 需要用户批准:terminal_input、run_python、write_file、edit_file、save_webpage 及其他会修改工作区的操作\n"
|
"- 需要用户批准:terminal_input、write_file、edit_file、save_webpage 及其他会修改工作区的操作\n"
|
||||||
"- 被拒绝或超时:本次操作不会执行写入"
|
"- 被拒绝或超时:本次操作不会执行写入"
|
||||||
)
|
)
|
||||||
elif mode == "auto_approval":
|
elif mode == "auto_approval":
|
||||||
|
|||||||
@ -502,7 +502,7 @@ class MainTerminalToolsDefinitionMixin:
|
|||||||
"type": "function",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
"name": "read_file",
|
"name": "read_file",
|
||||||
"description": "读取/搜索/抽取 UTF-8 文本文件内容。通过 type 参数选择 read(阅读)、search(搜索)、extract(具体行段),支持限制返回字符数。若文件非 UTF-8 或过大,请改用 run_python。",
|
"description": "读取/搜索/抽取 UTF-8 文本文件内容。通过 type 参数选择 read(阅读)、search(搜索)、extract(具体行段),支持限制返回字符数。若文件非 UTF-8 或过大,请改用 run_command 调用合适的解析工具或 Python 解释器。",
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": self._inject_intent({
|
"properties": self._inject_intent({
|
||||||
@ -809,24 +809,6 @@ class MainTerminalToolsDefinitionMixin:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "function",
|
|
||||||
"function": {
|
|
||||||
"name": "run_python",
|
|
||||||
"description": "执行一次性 Python 脚本,可用于处理二进制或非 UTF-8 文件(如 Excel、Word、PDF、图片),或进行数据分析与验证。必须提供 timeout(最长60秒);一旦超时,脚本会被打断且无法继续执行(需要重新运行),并返回已捕获输出。",
|
|
||||||
"parameters": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": self._inject_intent({
|
|
||||||
"code": {"type": "string", "description": "Python代码"},
|
|
||||||
"timeout": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "超时时长(秒),必填,最大60"
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
"required": ["code", "timeout"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
|
|||||||
@ -195,7 +195,6 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
}
|
}
|
||||||
_APPROVAL_REQUIRED_TOOLS = {
|
_APPROVAL_REQUIRED_TOOLS = {
|
||||||
"run_command",
|
"run_command",
|
||||||
"run_python",
|
|
||||||
"terminal_input",
|
"terminal_input",
|
||||||
"write_file",
|
"write_file",
|
||||||
"edit_file",
|
"edit_file",
|
||||||
@ -1407,12 +1406,6 @@ class MainTerminalToolsExecutionMixin:
|
|||||||
"path": target_path
|
"path": target_path
|
||||||
}
|
}
|
||||||
|
|
||||||
elif tool_name == "run_python":
|
|
||||||
result = await self.terminal_ops.run_python_code(
|
|
||||||
arguments["code"],
|
|
||||||
timeout=arguments.get("timeout")
|
|
||||||
)
|
|
||||||
|
|
||||||
elif tool_name == "run_command":
|
elif tool_name == "run_command":
|
||||||
permission_mode = "unrestricted"
|
permission_mode = "unrestricted"
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -66,7 +66,7 @@ TOOL_CATEGORIES: Dict[str, ToolCategory] = {
|
|||||||
),
|
),
|
||||||
"terminal_command": ToolCategory(
|
"terminal_command": ToolCategory(
|
||||||
label="终端指令",
|
label="终端指令",
|
||||||
tools=["run_command", "run_python"],
|
tools=["run_command"],
|
||||||
),
|
),
|
||||||
"memory": ToolCategory(
|
"memory": ToolCategory(
|
||||||
label="记忆",
|
label="记忆",
|
||||||
|
|||||||
@ -82,7 +82,7 @@ class WebTerminal(MainTerminal):
|
|||||||
broadcast_callback=message_callback,
|
broadcast_callback=message_callback,
|
||||||
container_session=self.container_session
|
container_session=self.container_session
|
||||||
)
|
)
|
||||||
# 让 run_command/run_python 与实时终端共享同一容器环境
|
# 让 run_command 与实时终端共享同一容器环境
|
||||||
self.terminal_ops.attach_terminal_manager(self.terminal_manager)
|
self.terminal_ops.attach_terminal_manager(self.terminal_manager)
|
||||||
|
|
||||||
print(f"[WebTerminal] 初始化完成,项目路径: {project_path}")
|
print(f"[WebTerminal] 初始化完成,项目路径: {project_path}")
|
||||||
@ -477,12 +477,6 @@ class WebTerminal(MainTerminal):
|
|||||||
'status': 'saving_webpage',
|
'status': 'saving_webpage',
|
||||||
'detail': f'保存网页: {arguments.get("url", "")}'
|
'detail': f'保存网页: {arguments.get("url", "")}'
|
||||||
})
|
})
|
||||||
elif tool_name == "run_python":
|
|
||||||
self.broadcast('tool_status', {
|
|
||||||
'tool': tool_name,
|
|
||||||
'status': 'running_code',
|
|
||||||
'detail': '执行Python代码'
|
|
||||||
})
|
|
||||||
elif tool_name == "run_command":
|
elif tool_name == "run_command":
|
||||||
self.broadcast('tool_status', {
|
self.broadcast('tool_status', {
|
||||||
'tool': tool_name,
|
'tool': tool_name,
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
- 执行环境(Execution Mode)
|
- 执行环境(Execution Mode)
|
||||||
- 权限模式(Permission Mode)
|
- 权限模式(Permission Mode)
|
||||||
- 路径授权(Path Authorization)
|
- 路径授权(Path Authorization)
|
||||||
- `run_command` / `run_python` / `terminal` / `sub_agent` 的实际行为
|
- `run_command` / `terminal` / `sub_agent` 的实际行为
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -136,25 +136,20 @@
|
|||||||
- 在 approval 模式可触发“权限拒绝 -> 审批 -> 单次可写重试”
|
- 在 approval 模式可触发“权限拒绝 -> 审批 -> 单次可写重试”
|
||||||
- 在 auto_approval 模式可触发“权限拒绝 -> 自动审批 -> 单次可写重试”
|
- 在 auto_approval 模式可触发“权限拒绝 -> 自动审批 -> 单次可写重试”
|
||||||
|
|
||||||
### 7.2 run_python
|
### 7.2 terminal 系列
|
||||||
|
|
||||||
- 复用 `run_command` 执行路径与执行环境策略
|
|
||||||
- 与 `run_command` 一致受 sandbox/direct 与权限模式影响
|
|
||||||
|
|
||||||
### 7.3 terminal 系列
|
|
||||||
|
|
||||||
- 终端会话从启动时就在沙箱里(host+sandbox)
|
- 终端会话从启动时就在沙箱里(host+sandbox)
|
||||||
- 同一会话中的后续输入继承该会话沙箱上下文
|
- 同一会话中的后续输入继承该会话沙箱上下文
|
||||||
- 若路径授权更新,需要新开终端会话才能让该会话使用新策略
|
- 若路径授权更新,需要新开终端会话才能让该会话使用新策略
|
||||||
|
|
||||||
### 7.4 子智能体(sub_agent)
|
### 7.3 子智能体(sub_agent)
|
||||||
|
|
||||||
- 宿主机下已接入执行环境策略:
|
- 宿主机下已接入执行环境策略:
|
||||||
- `sandbox`:子智能体进程经宿主机沙箱启动
|
- `sandbox`:子智能体进程经宿主机沙箱启动
|
||||||
- `direct`:直接宿主机启动
|
- `direct`:直接宿主机启动
|
||||||
- Docker 模式维持容器执行
|
- Docker 模式维持容器执行
|
||||||
|
|
||||||
### 7.5 自动审批(approval agent)
|
### 7.4 自动审批(approval agent)
|
||||||
|
|
||||||
- 自动审批由独立审批智能体执行(与主智能体分离)
|
- 自动审批由独立审批智能体执行(与主智能体分离)
|
||||||
- 配置文件:`config/auto_approval.json`
|
- 配置文件:`config/auto_approval.json`
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import shlex
|
||||||
import string
|
import string
|
||||||
from typing import Dict, Any, Optional
|
from typing import Dict, Any, Optional
|
||||||
|
|
||||||
@ -62,8 +63,11 @@ class CustomToolExecutor:
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
return {"success": False, "error": f"模板渲染失败: {exc}", "tool_id": tool_id}
|
return {"success": False, "error": f"模板渲染失败: {exc}", "tool_id": tool_id}
|
||||||
|
|
||||||
# 执行 python 代码
|
# 通过统一的 run_command 链路执行 Python,复用同一套解释器探测、沙箱与审批语义。
|
||||||
result = await self.terminal_ops.run_python_code(rendered, timeout=timeout)
|
result = await self.terminal_ops.run_command(
|
||||||
|
f"python3 -u -c {shlex.quote(rendered)}",
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
result["custom_tool"] = True
|
result["custom_tool"] = True
|
||||||
result["tool_id"] = tool_id
|
result["tool_id"] = tool_id
|
||||||
result["code_rendered"] = rendered
|
result["code_rendered"] = rendered
|
||||||
|
|||||||
@ -445,7 +445,7 @@ class FileManager:
|
|||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
"error": "文件不是 UTF-8 文本,无法直接读取,请改用 run_python 解析。"
|
"error": "文件不是 UTF-8 文本,无法直接读取,请改用 run_command 调用合适的解析工具或 Python 解释器。"
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"success": False, "error": f"读取文件失败: {e}"}
|
return {"success": False, "error": f"读取文件失败: {e}"}
|
||||||
|
|||||||
@ -13,7 +13,6 @@ from typing import Dict, Optional, Tuple, TYPE_CHECKING
|
|||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
try:
|
try:
|
||||||
from config import (
|
from config import (
|
||||||
CODE_EXECUTION_TIMEOUT,
|
|
||||||
TERMINAL_COMMAND_TIMEOUT,
|
TERMINAL_COMMAND_TIMEOUT,
|
||||||
FORBIDDEN_COMMANDS,
|
FORBIDDEN_COMMANDS,
|
||||||
OUTPUT_FORMATS,
|
OUTPUT_FORMATS,
|
||||||
@ -25,7 +24,6 @@ except ImportError:
|
|||||||
if str(project_root) not in sys.path:
|
if str(project_root) not in sys.path:
|
||||||
sys.path.insert(0, str(project_root))
|
sys.path.insert(0, str(project_root))
|
||||||
from config import (
|
from config import (
|
||||||
CODE_EXECUTION_TIMEOUT,
|
|
||||||
TERMINAL_COMMAND_TIMEOUT,
|
TERMINAL_COMMAND_TIMEOUT,
|
||||||
FORBIDDEN_COMMANDS,
|
FORBIDDEN_COMMANDS,
|
||||||
OUTPUT_FORMATS,
|
OUTPUT_FORMATS,
|
||||||
@ -111,7 +109,7 @@ class TerminalOperator:
|
|||||||
def _resolve_active_container_session(self) -> Optional[SimpleNamespace]:
|
def _resolve_active_container_session(self) -> Optional[SimpleNamespace]:
|
||||||
"""
|
"""
|
||||||
若已存在活动终端且在容器内运行,返回一个临时的容器句柄,
|
若已存在活动终端且在容器内运行,返回一个临时的容器句柄,
|
||||||
以便 run_command/run_python 复用同一个容器环境。
|
以便 run_command 复用同一个容器环境。
|
||||||
"""
|
"""
|
||||||
manager = self._terminal_manager
|
manager = self._terminal_manager
|
||||||
if not manager:
|
if not manager:
|
||||||
@ -734,129 +732,6 @@ class TerminalOperator:
|
|||||||
"elapsed_ms": int((time.time() - start_ts) * 1000)
|
"elapsed_ms": int((time.time() - start_ts) * 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
async def run_python_code(
|
|
||||||
self,
|
|
||||||
code: str,
|
|
||||||
timeout: int = None
|
|
||||||
) -> Dict:
|
|
||||||
"""
|
|
||||||
执行Python代码
|
|
||||||
|
|
||||||
Args:
|
|
||||||
code: Python代码
|
|
||||||
timeout: 超时时间(秒)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
执行结果字典
|
|
||||||
"""
|
|
||||||
if timeout is None or timeout <= 0:
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"error": "timeout 参数必填且需大于0",
|
|
||||||
"status": "error",
|
|
||||||
"output": "timeout 参数缺失",
|
|
||||||
"return_code": -1
|
|
||||||
}
|
|
||||||
timeout = self._clamp_timeout(timeout, default=timeout, max_limit=CODE_EXECUTION_TIMEOUT)
|
|
||||||
|
|
||||||
# 强制重置工具容器,避免上一段代码仍在运行时输出混入
|
|
||||||
self._reset_toolbox()
|
|
||||||
|
|
||||||
# 创建临时Python文件
|
|
||||||
temp_file = self.project_path / ".temp_code.py"
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 写入代码
|
|
||||||
with open(temp_file, 'w', encoding='utf-8') as f:
|
|
||||||
f.write(code)
|
|
||||||
|
|
||||||
print(f"{OUTPUT_FORMATS['code']} 执行Python代码")
|
|
||||||
|
|
||||||
try:
|
|
||||||
relative_temp = temp_file.relative_to(self.project_path).as_posix()
|
|
||||||
except ValueError:
|
|
||||||
relative_temp = temp_file.as_posix()
|
|
||||||
|
|
||||||
# 使用通用 python3 占位,由 run_command 根据执行环境重写
|
|
||||||
result = await self.run_command(
|
|
||||||
f'python3 -u "{relative_temp}"',
|
|
||||||
timeout=timeout
|
|
||||||
)
|
|
||||||
|
|
||||||
# 添加代码到结果
|
|
||||||
result["code"] = code
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
finally:
|
|
||||||
# 清理临时文件
|
|
||||||
if temp_file.exists():
|
|
||||||
temp_file.unlink()
|
|
||||||
|
|
||||||
async def run_python_file(
|
|
||||||
self,
|
|
||||||
file_path: str,
|
|
||||||
args: str = "",
|
|
||||||
timeout: int = None
|
|
||||||
) -> Dict:
|
|
||||||
"""
|
|
||||||
执行Python文件
|
|
||||||
|
|
||||||
Args:
|
|
||||||
file_path: Python文件路径
|
|
||||||
args: 命令行参数
|
|
||||||
timeout: 超时时间(秒)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
执行结果字典
|
|
||||||
"""
|
|
||||||
# 构建完整路径
|
|
||||||
full_path = (self.project_path / file_path).resolve()
|
|
||||||
|
|
||||||
# 验证文件存在
|
|
||||||
if not full_path.exists():
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"error": "文件不存在",
|
|
||||||
"output": "",
|
|
||||||
"return_code": -1
|
|
||||||
}
|
|
||||||
|
|
||||||
# 验证是Python文件
|
|
||||||
if not full_path.suffix == '.py':
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"error": "不是Python文件",
|
|
||||||
"output": "",
|
|
||||||
"return_code": -1
|
|
||||||
}
|
|
||||||
|
|
||||||
# 验证文件在项目内
|
|
||||||
try:
|
|
||||||
full_path.relative_to(self.project_path)
|
|
||||||
except ValueError:
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"error": "文件必须在项目文件夹内",
|
|
||||||
"output": "",
|
|
||||||
"return_code": -1
|
|
||||||
}
|
|
||||||
|
|
||||||
print(f"{OUTPUT_FORMATS['code']} 执行Python文件: {file_path}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
relative_path = full_path.relative_to(self.project_path).as_posix()
|
|
||||||
except ValueError:
|
|
||||||
relative_path = full_path.as_posix()
|
|
||||||
|
|
||||||
# 使用通用 python3 占位,由 run_command 根据执行环境重写
|
|
||||||
command = f'python3 "{relative_path}"'
|
|
||||||
if args:
|
|
||||||
command += f" {args}"
|
|
||||||
|
|
||||||
# 执行命令
|
|
||||||
return await self.run_command(command, timeout=timeout)
|
|
||||||
|
|
||||||
async def install_package(self, package: str) -> Dict:
|
async def install_package(self, package: str) -> Dict:
|
||||||
"""
|
"""
|
||||||
安装Python包
|
安装Python包
|
||||||
|
|||||||
@ -55,7 +55,7 @@ def _build_sandbox_options(container_session: Optional["ContainerHandle"] = None
|
|||||||
|
|
||||||
|
|
||||||
class ToolboxContainer:
|
class ToolboxContainer:
|
||||||
"""为 run_command/run_python 提供的专用容器."""
|
"""为 run_command 提供的专用容器."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
| **信息检索** | 网络搜索(`web_search`)、网页内容提取(`extract_webpage`、`save_webpage`)、信息整合 |
|
| **信息检索** | 网络搜索(`web_search`)、网页内容提取(`extract_webpage`、`save_webpage`)、信息整合 |
|
||||||
| **MCP扩展** | 通过 MCP 服务扩展外部工具;可用 `list_mcp_servers` 查看已配置服务与工具别名 |
|
| **MCP扩展** | 通过 MCP 服务扩展外部工具;可用 `list_mcp_servers` 查看已配置服务与工具别名 |
|
||||||
| **文件管理** | 批量处理文件、目录操作、自动化任务 |
|
| **文件管理** | 批量处理文件、目录操作、自动化任务 |
|
||||||
| **终端操作** | 执行命令(`run_command`)、运行Python代码(`run_python`)、持久终端会话(`terminal_session` 系列) |
|
| **终端操作** | 执行命令(`run_command`,需要 Python 时先探测并选择合适解释器)、持久终端会话(`terminal_session` 系列) |
|
||||||
| **视觉理解** | 分析图片内容、识别文字/物体/表格(`vlm_analyze`/`view_image`) |
|
| **视觉理解** | 分析图片内容、识别文字/物体/表格(`vlm_analyze`/`view_image`) |
|
||||||
| **任务协作** | 创建子智能体(`create_sub_agent`)并行处理独立子任务 |
|
| **任务协作** | 创建子智能体(`create_sub_agent`)并行处理独立子任务 |
|
||||||
|
|
||||||
@ -87,7 +87,7 @@
|
|||||||
- `write_file`:写入文件(`append` 控制覆盖/追加)
|
- `write_file`:写入文件(`append` 控制覆盖/追加)
|
||||||
- `read_file`:读取文件(支持 `read`/`search`/`extract` 三种模式)
|
- `read_file`:读取文件(支持 `read`/`search`/`extract` 三种模式)
|
||||||
- `edit_file`:精确字符串替换(`replace_all` 必填,必须显式传入 `true/false`;`old_string` 建议至少 3 行以提升定位稳定性。需要批量替换的场景可以单行或不足一行)
|
- `edit_file`:精确字符串替换(`replace_all` 必填,必须显式传入 `true/false`;`old_string` 建议至少 3 行以提升定位稳定性。需要批量替换的场景可以单行或不足一行)
|
||||||
- 其余文件管理(创建/重命名/删除目录与文件)请优先使用 `run_command` / `run_python`
|
- 其余文件管理(创建/重命名/删除目录与文件)请优先使用 `run_command`
|
||||||
|
|
||||||
**注意**:超大文件用 `search` 定位 + `extract` 抽取,避免全文读取。
|
**注意**:超大文件用 `search` 定位 + `extract` 抽取,避免全文读取。
|
||||||
|
|
||||||
@ -105,8 +105,7 @@
|
|||||||
- 终端卡死需用 `terminal_session` 的 reset 操作恢复
|
- 终端卡死需用 `terminal_session` 的 reset 操作恢复
|
||||||
|
|
||||||
**快速执行**:
|
**快速执行**:
|
||||||
- `run_command`:一次性命令(最长30s,输出限10000字符)
|
- `run_command`:一次性命令(最长30s,输出限10000字符);需要 Python 时先用命令探测可用解释器(如 `python3 --version` / `python --version`)再执行
|
||||||
- `run_python`:执行 Python 脚本(最长60s)
|
|
||||||
|
|
||||||
**终端禁忌**:禁止运行交互式程序(vim、python REPL等);不确定命令是否结束时,必须用 `terminal_snapshot` 确认。
|
**终端禁忌**:禁止运行交互式程序(vim、python REPL等);不确定命令是否结束时,必须用 `terminal_snapshot` 确认。
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
| **信息检索** | 网络搜索(`web_search`)、网页内容提取(`extract_webpage`、`save_webpage`)、信息整合 |
|
| **信息检索** | 网络搜索(`web_search`)、网页内容提取(`extract_webpage`、`save_webpage`)、信息整合 |
|
||||||
| **MCP扩展** | 通过 MCP 服务扩展外部工具;可用 `list_mcp_servers` 查看已配置服务与工具别名 |
|
| **MCP扩展** | 通过 MCP 服务扩展外部工具;可用 `list_mcp_servers` 查看已配置服务与工具别名 |
|
||||||
| **文件管理** | 批量处理文件、目录操作、自动化任务 |
|
| **文件管理** | 批量处理文件、目录操作、自动化任务 |
|
||||||
| **终端操作** | 执行命令(`run_command`)、运行Python代码(`run_python`)、持久终端会话(`terminal_session` 系列) |
|
| **终端操作** | 执行命令(`run_command`,需要 Python 时先探测并选择合适解释器)、持久终端会话(`terminal_session` 系列) |
|
||||||
| **视觉理解** | 自带多模态能力,可直接分析图片内容、识别文字/物体/表格/场景 |
|
| **视觉理解** | 自带多模态能力,可直接分析图片内容、识别文字/物体/表格/场景 |
|
||||||
| **任务协作** | 创建子智能体(`create_sub_agent`)并行处理独立子任务 |
|
| **任务协作** | 创建子智能体(`create_sub_agent`)并行处理独立子任务 |
|
||||||
|
|
||||||
@ -87,7 +87,7 @@
|
|||||||
- `write_file`:写入文件(`append` 控制覆盖/追加)
|
- `write_file`:写入文件(`append` 控制覆盖/追加)
|
||||||
- `read_file`:读取文件(支持 `read`/`search`/`extract` 三种模式)
|
- `read_file`:读取文件(支持 `read`/`search`/`extract` 三种模式)
|
||||||
- `edit_file`:精确字符串替换(`replace_all` 必填,必须显式传入 `true/false`;`old_string` 建议至少 3 行以提升定位稳定性。需要批量替换的场景可以单行或不足一行)
|
- `edit_file`:精确字符串替换(`replace_all` 必填,必须显式传入 `true/false`;`old_string` 建议至少 3 行以提升定位稳定性。需要批量替换的场景可以单行或不足一行)
|
||||||
- 其余文件管理(创建/重命名/删除目录与文件)请优先使用 `run_command` / `run_python`
|
- 其余文件管理(创建/重命名/删除目录与文件)请优先使用 `run_command`
|
||||||
|
|
||||||
**注意**:超大文件用 `search` 定位 + `extract` 抽取,避免全文读取。
|
**注意**:超大文件用 `search` 定位 + `extract` 抽取,避免全文读取。
|
||||||
|
|
||||||
@ -104,7 +104,7 @@
|
|||||||
|
|
||||||
#### 细节增强与"切图放大"方法(推荐)
|
#### 细节增强与"切图放大"方法(推荐)
|
||||||
当图片文字很小、细节密集、或需要逐块检查时:
|
当图片文字很小、细节密集、或需要逐块检查时:
|
||||||
1. **用 `run_python` 切图**:把原图按区域裁切成若干张更小的局部图(例如:左上/右上/左下/右下;或按表格/按钮/车标/铭牌所在区域裁切)
|
1. **用 `run_command` 调用合适的 Python 解释器切图**:先探测可用解释器,再把原图按区域裁切成若干张更小的局部图(例如:左上/右上/左下/右下;或按表格/按钮/车标/铭牌所在区域裁切)
|
||||||
2. **必要时做多版本增强**:对同一区域输出多张增强版本(例如:对比度增强、锐化、灰度、二值化)用于读字/看边缘
|
2. **必要时做多版本增强**:对同一区域输出多张增强版本(例如:对比度增强、锐化、灰度、二值化)用于读字/看边缘
|
||||||
3. **再次查看局部图**:对每张局部图分别观察并给出结论,再把结论汇总回原图任务
|
3. **再次查看局部图**:对每张局部图分别观察并给出结论,再把结论汇总回原图任务
|
||||||
|
|
||||||
@ -123,8 +123,7 @@
|
|||||||
- 终端卡死需用 `terminal_session` 的 reset 操作恢复
|
- 终端卡死需用 `terminal_session` 的 reset 操作恢复
|
||||||
|
|
||||||
**快速执行**:
|
**快速执行**:
|
||||||
- `run_command`:一次性命令(最长30s,输出限10000字符)
|
- `run_command`:一次性命令(最长30s,输出限10000字符);需要 Python/切图处理时先用命令探测可用解释器(如 `python3 --version` / `python --version`)再执行
|
||||||
- `run_python`:执行 Python 脚本(最长60s,切图处理必备)
|
|
||||||
|
|
||||||
**终端禁忌**:禁止运行交互式程序(vim、python REPL等);不确定命令是否结束时,必须用 `terminal_snapshot` 确认。
|
**终端禁忌**:禁止运行交互式程序(vim、python REPL等);不确定命令是否结束时,必须用 `terminal_snapshot` 确认。
|
||||||
|
|
||||||
|
|||||||
@ -332,7 +332,7 @@ def detect_malformed_tool_call(text):
|
|||||||
'create_file', 'read_file', 'write_file', 'edit_file', 'delete_file',
|
'create_file', 'read_file', 'write_file', 'edit_file', 'delete_file',
|
||||||
'terminal_session', 'terminal_input', 'web_search',
|
'terminal_session', 'terminal_input', 'web_search',
|
||||||
'extract_webpage', 'save_webpage',
|
'extract_webpage', 'save_webpage',
|
||||||
'run_python', 'run_command', 'sleep',
|
'run_command', 'sleep',
|
||||||
]
|
]
|
||||||
for tool in tool_names:
|
for tool in tool_names:
|
||||||
if tool in text and '{' in text:
|
if tool in text and '{' in text:
|
||||||
|
|||||||
@ -120,16 +120,6 @@ def _build_tool_approval_preview(web_terminal, function_name: str, arguments: Di
|
|||||||
preview["summary"] = f"执行命令: {args.get('command') or ''}"
|
preview["summary"] = f"执行命令: {args.get('command') or ''}"
|
||||||
return preview
|
return preview
|
||||||
|
|
||||||
if function_name in {"run_python", "runpython"}:
|
|
||||||
code = str(args.get("code") or "")
|
|
||||||
timeout = args.get("timeout")
|
|
||||||
preview["code"] = code
|
|
||||||
preview["timeout"] = timeout
|
|
||||||
# 兼容前端已有 command 展示分支
|
|
||||||
preview["command"] = code
|
|
||||||
preview["summary"] = f"执行 Python 代码(timeout={timeout if timeout is not None else '未提供'}s)"
|
|
||||||
return preview
|
|
||||||
|
|
||||||
if function_name in {"create_file", "create_folder", "delete_file"}:
|
if function_name in {"create_file", "create_folder", "delete_file"}:
|
||||||
preview["path"] = args.get("path")
|
preview["path"] = args.get("path")
|
||||||
preview["summary"] = f"{function_name}: {args.get('path') or ''}"
|
preview["summary"] = f"{function_name}: {args.get('path') or ''}"
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>虚拟显示器 - 单次终端指令(run_python)演示</title>
|
<title>虚拟显示器 - 单次终端指令(run_command 调 Python)演示</title>
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--panel: #0b0d14;
|
--panel: #0b0d14;
|
||||||
@ -80,7 +80,7 @@
|
|||||||
<div class="monitor">
|
<div class="monitor">
|
||||||
<div class="monitor-shell">
|
<div class="monitor-shell">
|
||||||
<div class="monitor-top">
|
<div class="monitor-top">
|
||||||
<div>Agent Display Surface · run_python</div>
|
<div>Agent Display Surface · run_command Python</div>
|
||||||
<div class="status-pill" id="statusPill">待机</div>
|
<div class="status-pill" id="statusPill">待机</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="monitor-screen" id="monitorScreen">
|
<div class="monitor-screen" id="monitorScreen">
|
||||||
@ -318,7 +318,7 @@
|
|||||||
helpers.setStatus('正在准备');
|
helpers.setStatus('正在准备');
|
||||||
await helpers.sleep(600);
|
await helpers.sleep(600);
|
||||||
helpers.setStatus('正在规划');
|
helpers.setStatus('正在规划');
|
||||||
await helpers.showBubble('run_python:打开独立的 Python 窗口。', { duration: 2200 });
|
await helpers.showBubble('run_command:调用合适的 Python 解释器。', { duration: 2200 });
|
||||||
|
|
||||||
const pythonIcon = helpers.getAppIcon('pythonApp');
|
const pythonIcon = helpers.getAppIcon('pythonApp');
|
||||||
await helpers.moveMouseTo(pythonIcon);
|
await helpers.moveMouseTo(pythonIcon);
|
||||||
@ -352,7 +352,7 @@
|
|||||||
await helpers.sleep(1500);
|
await helpers.sleep(1500);
|
||||||
|
|
||||||
helpers.setStatus('正在输出');
|
helpers.setStatus('正在输出');
|
||||||
await helpers.showBubble('run_python 完成,输出已写入结果面板。', { duration: 2000 });
|
await helpers.showBubble('run_command 完成,输出已写入结果面板。', { duration: 2000 });
|
||||||
helpers.setStatus('已完成');
|
helpers.setStatus('已完成');
|
||||||
replayBtn.disabled = false;
|
replayBtn.disabled = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -133,16 +133,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else class="search-empty">未返回详细的搜索结果。</div>
|
<div v-else class="search-empty">未返回详细的搜索结果。</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="action.tool?.name === 'run_python' && action.tool?.result">
|
|
||||||
<div class="code-block">
|
|
||||||
<div class="code-label">代码:</div>
|
|
||||||
<pre><code class="language-python">{{ action.tool.result.code || action.tool.arguments?.code }}</code></pre>
|
|
||||||
</div>
|
|
||||||
<div v-if="action.tool.result.output" class="output-block">
|
|
||||||
<div class="output-label">输出:</div>
|
|
||||||
<pre>{{ action.tool.result.output }}</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<pre>{{
|
<pre>{{
|
||||||
JSON.stringify(action.tool?.result || action.tool?.arguments, null, 2)
|
JSON.stringify(action.tool?.result || action.tool?.arguments, null, 2)
|
||||||
|
|||||||
@ -159,8 +159,6 @@ function renderEnhancedToolResult(): string {
|
|||||||
// 终端指令类
|
// 终端指令类
|
||||||
else if (name === 'run_command') {
|
else if (name === 'run_command') {
|
||||||
return renderRunCommand(result, args);
|
return renderRunCommand(result, args);
|
||||||
} else if (name === 'run_python') {
|
|
||||||
return renderRunPython(result, args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 记忆类
|
// 记忆类
|
||||||
@ -733,25 +731,6 @@ function renderRunCommand(result: any, args: any): string {
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderRunPython(result: any, args: any): string {
|
|
||||||
const code = result.code || args.code || '';
|
|
||||||
const output = result.output || '';
|
|
||||||
|
|
||||||
let html = '<div class="code-block">';
|
|
||||||
html += '<div class="code-label">代码:</div>';
|
|
||||||
html += `<pre><code class="language-python">${escapeHtml(code)}</code></pre>`;
|
|
||||||
html += '</div>';
|
|
||||||
|
|
||||||
if (output) {
|
|
||||||
html += '<div class="output-block">';
|
|
||||||
html += '<div class="output-label">输出:</div>';
|
|
||||||
html += `<pre>${escapeHtml(output)}</pre>`;
|
|
||||||
html += '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== 记忆类 =====
|
// ===== 记忆类 =====
|
||||||
function renderUpdateMemory(result: any, args: any): string {
|
function renderUpdateMemory(result: any, args: any): string {
|
||||||
const operations = args.operations || [];
|
const operations = args.operations || [];
|
||||||
@ -1339,7 +1318,7 @@ function renderEasterEgg(result: any, args: any): string {
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 暗色模式:run_python 代码/输出统一白字,去除任何描边/阴影观感 */
|
/* 暗色模式:代码/输出统一白字,去除任何描边/阴影观感 */
|
||||||
:root[data-theme='dark'] .code-block pre,
|
:root[data-theme='dark'] .code-block pre,
|
||||||
:root[data-theme='dark'] .output-block pre {
|
:root[data-theme='dark'] .output-block pre {
|
||||||
color: var(--text-primary) !important;
|
color: var(--text-primary) !important;
|
||||||
|
|||||||
@ -95,8 +95,6 @@ export function renderEnhancedToolResult(
|
|||||||
// 终端指令类
|
// 终端指令类
|
||||||
else if (name === 'run_command') {
|
else if (name === 'run_command') {
|
||||||
return renderRunCommand(result, args);
|
return renderRunCommand(result, args);
|
||||||
} else if (name === 'run_python') {
|
|
||||||
return renderRunPython(result, args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 记忆类
|
// 记忆类
|
||||||
@ -673,25 +671,6 @@ function renderRunCommand(result: any, args: any): string {
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderRunPython(result: any, args: any): string {
|
|
||||||
const code = result.code || args.code || '';
|
|
||||||
const output = result.output || '';
|
|
||||||
|
|
||||||
let html = '<div class="code-block">';
|
|
||||||
html += '<div class="code-label">代码:</div>';
|
|
||||||
html += `<pre><code class="language-python">${escapeHtml(code)}</code></pre>`;
|
|
||||||
html += '</div>';
|
|
||||||
|
|
||||||
if (output) {
|
|
||||||
html += '<div class="output-block">';
|
|
||||||
html += '<div class="output-label">输出:</div>';
|
|
||||||
html += `<pre>${escapeHtml(output)}</pre>`;
|
|
||||||
html += '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记忆类渲染函数
|
// 记忆类渲染函数
|
||||||
function renderUpdateMemory(result: any, args: any): string {
|
function renderUpdateMemory(result: any, args: any): string {
|
||||||
const operation = args.operation || result.operation || '';
|
const operation = args.operation || result.operation || '';
|
||||||
|
|||||||
@ -3202,59 +3202,6 @@ export class MonitorDirector implements MonitorDriver {
|
|||||||
await sleep(500);
|
await sleep(500);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.sceneHandlers.runPython = async (payload, runtime) => {
|
|
||||||
const toolLabel = payload?.name || payload?.tool || 'run_python';
|
|
||||||
this.applySceneStatus(runtime, 'runPython', `调用 ${toolLabel}`);
|
|
||||||
const runId =
|
|
||||||
payload?.executionId ||
|
|
||||||
payload?.execution_id ||
|
|
||||||
payload?.id ||
|
|
||||||
`${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
||||||
// 丢弃过期的回放任务
|
|
||||||
if (this.latestPythonExecutionId && this.latestPythonExecutionId !== runId) {
|
|
||||||
const older =
|
|
||||||
typeof runId === 'number' && typeof this.latestPythonExecutionId === 'number'
|
|
||||||
? runId < this.latestPythonExecutionId
|
|
||||||
: false;
|
|
||||||
if (older) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.latestPythonExecutionId = runId;
|
|
||||||
const code = payload?.arguments?.code || 'print("Hello")';
|
|
||||||
const codeLines = this.normalizeLines(code);
|
|
||||||
const animate = codeLines.length <= 15;
|
|
||||||
const runToken = ++this.pythonRunToken;
|
|
||||||
const reuse = this.isWindowVisible(this.elements.pythonWindow);
|
|
||||||
if (reuse) {
|
|
||||||
this.showWindow(this.elements.pythonWindow);
|
|
||||||
this.scrollPythonToTop();
|
|
||||||
await this.focusPythonInput();
|
|
||||||
this.elements.pythonOutput.innerHTML = '';
|
|
||||||
} else {
|
|
||||||
await this.revealPythonWindow('Python');
|
|
||||||
}
|
|
||||||
await this.typePythonCode(code, { deletePrevious: true, animate });
|
|
||||||
const completion = await runtime.waitForResult(payload.executionId || payload.id);
|
|
||||||
if (!this.ensureSuccessOrErrorBubble(completion, payload, 'Python 执行失败')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (runToken !== this.pythonRunToken) {
|
|
||||||
// 有新的 Python 运行已启动,本次结果丢弃
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const output = completion?.result?.output || completion?.result?.stdout || '>>> 执行完成';
|
|
||||||
const lines = this.sanitizeTerminalOutput(
|
|
||||||
typeof output === 'string'
|
|
||||||
? output.split('\n')
|
|
||||||
: Array.isArray(output)
|
|
||||||
? output.map(String)
|
|
||||||
: [String(output || '')]
|
|
||||||
);
|
|
||||||
this.appendPythonOutput('输出', lines.join('\n') || '>>> 执行完成');
|
|
||||||
await sleep(500);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.sceneHandlers.reader = async (payload, runtime) => {
|
this.sceneHandlers.reader = async (payload, runtime) => {
|
||||||
const targetPath = payload?.arguments?.path || payload?.result?.path || '文档';
|
const targetPath = payload?.arguments?.path || payload?.result?.path || '文档';
|
||||||
const readMode = String(
|
const readMode = String(
|
||||||
|
|||||||
@ -12,7 +12,6 @@ export const SCENE_PROGRESS_LABELS: Record<string, string> = {
|
|||||||
unfocus: '正在处理',
|
unfocus: '正在处理',
|
||||||
// 运行类工具显示具体工具名,由运行时传入
|
// 运行类工具显示具体工具名,由运行时传入
|
||||||
runCommand: '运行命令',
|
runCommand: '运行命令',
|
||||||
runPython: '运行 Python',
|
|
||||||
terminalSession: '打开终端',
|
terminalSession: '打开终端',
|
||||||
terminalInput: '终端输入',
|
terminalInput: '终端输入',
|
||||||
terminalSnapshot: '获取终端输出',
|
terminalSnapshot: '获取终端输出',
|
||||||
|
|||||||
@ -187,7 +187,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
const isEditPreview = (item: any) => item?.tool_name === 'edit_file' && item?.preview?.edit_context;
|
const isEditPreview = (item: any) => item?.tool_name === 'edit_file' && item?.preview?.edit_context;
|
||||||
const isCommandPreview = (item: any) =>
|
const isCommandPreview = (item: any) =>
|
||||||
['run_command', 'terminal_input', 'run_python', 'runpython'].includes(item?.tool_name);
|
['run_command', 'terminal_input'].includes(item?.tool_name);
|
||||||
const isWritePreview = (item: any) => item?.tool_name === 'write_file';
|
const isWritePreview = (item: any) => item?.tool_name === 'write_file';
|
||||||
const isRenamePreview = (item: any) => item?.tool_name === 'rename_file';
|
const isRenamePreview = (item: any) => item?.tool_name === 'rename_file';
|
||||||
const isPathOnlyPreview = (item: any) =>
|
const isPathOnlyPreview = (item: any) =>
|
||||||
@ -210,9 +210,7 @@ const getToolLabel = (toolName: string) => {
|
|||||||
delete_file: '删除文件',
|
delete_file: '删除文件',
|
||||||
rename_file: '重命名文件',
|
rename_file: '重命名文件',
|
||||||
write_file: '写入文件',
|
write_file: '写入文件',
|
||||||
edit_file: '编辑文件',
|
edit_file: '编辑文件'
|
||||||
run_python: '执行 Python',
|
|
||||||
runpython: '执行 Python'
|
|
||||||
};
|
};
|
||||||
return map[name] || name || '待审批操作';
|
return map[name] || name || '待审批操作';
|
||||||
};
|
};
|
||||||
|
|||||||
@ -108,7 +108,6 @@ const TOOL_SCENE_MAP: Record<string, string> = {
|
|||||||
write_file: 'modifyFile',
|
write_file: 'modifyFile',
|
||||||
edit_file: 'modifyFile',
|
edit_file: 'modifyFile',
|
||||||
run_command: 'runCommand',
|
run_command: 'runCommand',
|
||||||
run_python: 'runPython',
|
|
||||||
terminal_session: 'terminalSession',
|
terminal_session: 'terminalSession',
|
||||||
terminal_input: 'terminalInput',
|
terminal_input: 'terminalInput',
|
||||||
terminal_snapshot: 'terminalSnapshot',
|
terminal_snapshot: 'terminalSnapshot',
|
||||||
|
|||||||
@ -16,7 +16,6 @@ const RUNNING_ANIMATIONS: Record<string, string> = {
|
|||||||
extract_webpage: 'search-animation',
|
extract_webpage: 'search-animation',
|
||||||
save_webpage: 'file-animation',
|
save_webpage: 'file-animation',
|
||||||
vlm_analyze: 'file-animation',
|
vlm_analyze: 'file-animation',
|
||||||
run_python: 'code-animation',
|
|
||||||
run_command: 'terminal-animation',
|
run_command: 'terminal-animation',
|
||||||
update_memory: 'memory-animation',
|
update_memory: 'memory-animation',
|
||||||
sleep: 'wait-animation',
|
sleep: 'wait-animation',
|
||||||
@ -40,7 +39,6 @@ const RUNNING_STATUS_TEXTS: Record<string, string> = {
|
|||||||
web_search: '正在搜索网络...',
|
web_search: '正在搜索网络...',
|
||||||
extract_webpage: '正在提取网页...',
|
extract_webpage: '正在提取网页...',
|
||||||
save_webpage: '正在保存网页...',
|
save_webpage: '正在保存网页...',
|
||||||
run_python: '调用 run_python',
|
|
||||||
run_command: '调用 run_command',
|
run_command: '调用 run_command',
|
||||||
update_memory: '正在更新记忆...',
|
update_memory: '正在更新记忆...',
|
||||||
terminal_session: '正在管理终端会话...',
|
terminal_session: '正在管理终端会话...',
|
||||||
@ -63,7 +61,6 @@ const COMPLETED_STATUS_TEXTS: Record<string, string> = {
|
|||||||
extract_webpage: '网页提取完成',
|
extract_webpage: '网页提取完成',
|
||||||
save_webpage: '网页保存完成(纯文本)',
|
save_webpage: '网页保存完成(纯文本)',
|
||||||
vlm_analyze: '图片解析完成',
|
vlm_analyze: '图片解析完成',
|
||||||
run_python: '代码执行完成',
|
|
||||||
run_command: '命令执行完成',
|
run_command: '命令执行完成',
|
||||||
update_memory: '记忆更新成功',
|
update_memory: '记忆更新成功',
|
||||||
terminal_session: '终端操作完成',
|
terminal_session: '终端操作完成',
|
||||||
|
|||||||
@ -67,7 +67,6 @@ export const TOOL_ICON_MAP = Object.freeze({
|
|||||||
read_skill: 'book',
|
read_skill: 'book',
|
||||||
rename_file: 'pencil',
|
rename_file: 'pencil',
|
||||||
run_command: 'terminal',
|
run_command: 'terminal',
|
||||||
run_python: 'python',
|
|
||||||
save_webpage: 'save',
|
save_webpage: 'save',
|
||||||
sleep: 'clock',
|
sleep: 'clock',
|
||||||
todo_create: 'stickyNote',
|
todo_create: 'stickyNote',
|
||||||
|
|||||||
@ -68,7 +68,6 @@ AUTO_SHALLOW_TOOL_WHITELIST = {
|
|||||||
"terminal_snapshot",
|
"terminal_snapshot",
|
||||||
"web_search",
|
"web_search",
|
||||||
"extract_webpage",
|
"extract_webpage",
|
||||||
"run_python",
|
|
||||||
"run_command",
|
"run_command",
|
||||||
"view_image",
|
"view_image",
|
||||||
"view_video",
|
"view_video",
|
||||||
|
|||||||
@ -816,14 +816,6 @@ def _format_run_command(result_data: Dict[str, Any]) -> str:
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def _format_run_python(result_data: Dict[str, Any]) -> str:
|
|
||||||
text = _plain_command_output(result_data)
|
|
||||||
if (result_data.get("status") or "").lower() == "timeout":
|
|
||||||
suggestion = "建议:将代码保存为脚本后,在持久终端中执行(terminal_session + terminal_input),或拆分/优化代码以缩短运行时间。"
|
|
||||||
text = f"{text}\n{suggestion}" if text else suggestion
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
def _format_todo_create(result_data: Dict[str, Any]) -> str:
|
def _format_todo_create(result_data: Dict[str, Any]) -> str:
|
||||||
if not result_data.get("success"):
|
if not result_data.get("success"):
|
||||||
return _format_failure("todo_create", result_data)
|
return _format_failure("todo_create", result_data)
|
||||||
@ -1191,7 +1183,6 @@ TOOL_FORMATTERS = {
|
|||||||
"terminal_input": _format_terminal_input,
|
"terminal_input": _format_terminal_input,
|
||||||
"sleep": _format_sleep,
|
"sleep": _format_sleep,
|
||||||
"run_command": _format_run_command,
|
"run_command": _format_run_command,
|
||||||
"run_python": _format_run_python,
|
|
||||||
"extract_webpage": _format_extract_webpage,
|
"extract_webpage": _format_extract_webpage,
|
||||||
"vlm_analyze": _format_vlm_analyze,
|
"vlm_analyze": _format_vlm_analyze,
|
||||||
"ocr_image": _format_ocr_image,
|
"ocr_image": _format_ocr_image,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user