"""自定义工具执行器。 提供执行自定义 HTTP 工具的功能,支持多种认证方式(API Key、Bearer Token)和 HTTP 方法。 """ import httpx import json class CustomToolExecutor: """自定义工具执行器类。 根据工具定义(端点 URL、HTTP 方法、认证配置等)执行 HTTP 请求。 Attributes: endpoint_url: API 端点的基础 URL。 method: HTTP 请求方法。 path: API 路径。 headers: 请求头字典。 auth_type: 认证类型(none/api_key/bearer)。 auth_config: 认证配置信息。 timeout: 请求超时时间(秒)。 """ def __init__(self, tool_def: dict): """初始化工具执行器。 Args: tool_def: 工具定义字典,包含 endpoint_url、method、path、headers_json、auth_type、auth_config、timeout 等字段。 """ self.endpoint_url = tool_def.get("endpoint_url", "") # API 基础 URL self.method = tool_def.get("method", "GET") # HTTP 请求方法,默认为 GET self.path = tool_def.get("path", "") # API 路径 self.headers = dict(tool_def.get("headers_json", {})) # 请求头字典 self.auth_type = tool_def.get("auth_type", "none") # 认证类型,默认为无认证 self.auth_config = dict(tool_def.get("auth_config", {})) # 认证配置信息 self.timeout = int(tool_def.get("timeout", 30)) # 请求超时时间(秒),默认 30 秒 async def execute(self, params: dict) -> str: """执行自定义工具请求。 根据工具定义构造完整的 URL,应用认证信息,发送 HTTP 请求并返回响应结果。 Args: params: 请求参数,GET 请求作为查询参数,其他方法作为 JSON 请求体。 Returns: str: 响应内容的字符串表示,最大长度 4000 字符。优先返回 JSON 格式,否则返回纯文本。 """ # 构造完整 URL(确保基础 URL 和路径之间只有一个斜杠) url = f"{self.endpoint_url.rstrip('/')}/{self.path.lstrip('/')}" headers = dict(self.headers) # 复制请求头 req_params = dict(params) # 复制请求参数 # 根据认证类型添加认证信息 if self.auth_type == "api_key": key = self.auth_config.get("key", "") # API Key loc = self.auth_config.get("location", "header") # 认证位置(header/query) name = self.auth_config.get("name", "X-API-Key") # 认证参数名 if loc == "header": headers[name] = key # 添加到请求头 else: req_params[name] = key # 添加到查询参数 elif self.auth_type == "bearer": headers["Authorization"] = f"Bearer {self.auth_config.get('token', '')}" # Bearer Token 认证 timeout = httpx.Timeout(self.timeout) # 创建超时配置 async with httpx.AsyncClient(timeout=timeout) as client: if self.method == "GET": resp = await client.get(url, params=req_params, headers=headers) else: resp = await client.request( self.method, url, json=req_params, headers=headers ) # 尝试解析 JSON 响应,否则返回纯文本 try: data = resp.json() return json.dumps(data, ensure_ascii=False, indent=2)[:4000] # 格式化 JSON,限制最大长度 except Exception: return resp.text[:4000] # 返回纯文本响应,限制最大长度