tool_logging.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. """工具调用日志的通用封装。"""
  2. from __future__ import annotations
  3. import json
  4. from typing import Any, Dict
  5. from .log_capture import log, log_fold
  6. def _pretty_json_if_possible(text: str) -> str:
  7. """如果文本是合法 JSON,则返回带缩进的可读格式;否则原样返回。"""
  8. raw = (text or "").strip()
  9. if not raw:
  10. return text
  11. if not (raw.startswith("{") or raw.startswith("[")):
  12. return text
  13. try:
  14. parsed = json.loads(raw)
  15. except Exception:
  16. return text
  17. return json.dumps(parsed, ensure_ascii=False, indent=2)
  18. def format_tool_result_for_log(result: Any) -> str:
  19. """将 ToolResult 或普通字符串格式化为可写入日志的文本(避免过长 metadata 刷屏)。"""
  20. if result is None:
  21. return ""
  22. if isinstance(result, str):
  23. s = result
  24. return s if len(s) <= 8000 else s[:8000] + "\n...(truncated)"
  25. title = getattr(result, "title", "") or ""
  26. output = getattr(result, "output", None) or ""
  27. err = getattr(result, "error", None)
  28. truncated = output if len(output) <= 6000 else output[:6000] + "\n...(truncated)"
  29. payload: Dict[str, Any] = {"title": title, "output": truncated}
  30. if err:
  31. payload["error"] = err
  32. md = getattr(result, "metadata", None)
  33. if isinstance(md, dict) and md:
  34. payload["metadata_keys"] = list(md.keys())
  35. # 批量画像等:log 里仅列 keys 会导致排障困难,附加 results 摘要(仍可能较长故截断)
  36. if "results" in md and isinstance(md["results"], list):
  37. try:
  38. rs = json.dumps(md["results"], ensure_ascii=False)
  39. except (TypeError, ValueError):
  40. rs = str(md["results"])
  41. max_rs = 24_000
  42. payload["metadata_results"] = (
  43. rs if len(rs) <= max_rs else rs[:max_rs] + "\n...(truncated)"
  44. )
  45. if isinstance(md.get("snapshot_path"), str):
  46. payload["snapshot_path"] = md["snapshot_path"]
  47. return json.dumps(payload, ensure_ascii=False)
  48. def log_tool_call(tool_name: str, params: Dict[str, Any], result: str) -> None:
  49. """以折叠块结构化输出工具调用参数与返回内容。"""
  50. with log_fold(f"🔧 {tool_name}"):
  51. with log_fold("📥 调用参数"):
  52. log(json.dumps(params, ensure_ascii=False, indent=2))
  53. with log_fold("📤 返回内容"):
  54. log(_pretty_json_if_possible(result))