fix: 修复 'ToolResult' object has no attribute 'get'

根因: cli_tools.process_tool_call 在启用 UnifiedToolManager 时直接返回
ToolResult 对象,而 7 个调用点全部按 dict 用 result.get('result'),触发
AttributeError;FC 路径 _execute_tool_by_name 也可能把 ToolResult 透传出去,
后续 result[:5000] 会再炸 TypeError。

- tool_result.py: 新增 normalize_tool_text / normalize_tool_dict,
  统一收敛 ToolResult / dict / str / None 三种历史返回形态。
- cli_tools.process_tool_call: 统一返回 dict(附带 success/error_code/
  tool_result),回退 handler 路径同样归一。
- cli_core._execute_tool_by_name: handler / memory / scheduler 返回值
  归一成 str,保证 FC 路径可安全切片。
- openai/ollama/manual 引擎、websocket_server、cli_clawli: 取工具结果改用
  normalize_tool_text(带本地兜底实现,避免循环导入)。
- cli_clawli: 顺带修复只回传最后一个工具结果的 bug,改为汇总全部。
- 测试: 23 项全过;纯格式/讯飞 500 降级回归冒烟通过。
This commit is contained in:
shuiyu67 committed 2026-08-05 10:54:39 +08:00
1 parent dfc42d8583
commit a63b9d2f97
8 files changed
+129 -20

No files matched your search

+54
View File
@@ -117,3 +117,57 @@ class ToolResult:
def to_string(self) -> str:
"""转为字符串(兼容旧插件 handle() 返回值)"""
return str(self)
# ── 归一化辅助(跨层兼容:ToolResult / dict / str / None)──
def normalize_tool_text(result: Any, default: str = "无结果") -> str:
"""把任意工具返回值统一成字符串。
工具链里三种返回形态都可能出现:
- ToolResult(新插件 / UnifiedToolManager.execute)
- {"result": ...}(旧插件 / to_dict)
- 纯字符串(最老的 handler)
统一收敛成字符串,避免上层对 ToolResult 调用 .get() 报
"'ToolResult' object has no attribute 'get'"。
"""
if result is None:
return default
if isinstance(result, ToolResult):
text = str(result)
elif isinstance(result, dict):
text = result.get("result", result.get("content", ""))
if isinstance(text, (dict, list)):
import json as _json
try:
text = _json.dumps(text, ensure_ascii=False)
except Exception:
text = str(text)
elif not isinstance(text, str):
text = str(text) if text is not None else ""
else:
text = str(result)
text = text.strip() if isinstance(text, str) else str(text)
return text if text else default
def normalize_tool_dict(result: Any, default: str = "无结果") -> dict:
"""把任意工具返回值统一成 {"result": str, ...} 结构。
额外附带 success / error_code / tool_result,方便需要细节的调用方,
同时保证 .get('result') 一定可用。
"""
text = normalize_tool_text(result, default)
if isinstance(result, ToolResult):
return {
"result": text,
"success": result.success,
"error_code": result.error_code,
"tool_result": result,
}
if isinstance(result, dict):
out = dict(result)
out["result"] = text
out.setdefault("success", True)
return out
return {"result": text, "success": True}