"""Tool 基类: 子类只需声明 name/description/parameters 和 execute。""" from __future__ import annotations from abc import ABC, abstractmethod from pathlib import Path from typing import Optional class Tool(ABC): name: str = "" description: str = "" parameters: dict = {} def __init__(self, base_dir: Optional[Path] = None, user_root: Optional[Path] = None) -> None: self.base_dir: Path = Path(base_dir) if base_dir else Path.cwd() # tool 输出渲染路径用:user_root 内的 path 渲成相对 POSIX 串,user_root 外 # (用户 --working-dir 指向外部目录)保持绝对。None → 全部按绝对渲染。 # 目的:不让 tool result 文本里出现 user_id / 部署绝对路径,SPA 截图分享更安全; # 顺便让 web SPA 的 artifact chip 抽取(限定 / 前缀)更稳。 self.user_root: Optional[Path] = Path(user_root) if user_root else None @abstractmethod def execute(self, **kwargs) -> str: ... @property def schema(self) -> dict: return { "type": "function", "function": { "name": self.name, "description": self.description, "parameters": self.parameters, }, } def _resolve(self, path: str) -> Path: p = Path(path) return p if p.is_absolute() else (self.base_dir / p) def _display(self, p: Path) -> str: """对外渲染路径:在 user_root 内 → POSIX 相对串;否则原绝对。""" if self.user_root is not None: try: return p.resolve().relative_to(self.user_root.resolve()).as_posix() except (ValueError, OSError): pass return str(p)