""" API 调用工具 - 安全的外部 API 调用 """ import httpx from typing import Dict, Any, Optional from dataclasses import dataclass from enum import Enum class APIPermission(Enum): """API 权限级别""" PUBLIC = "public" # 公开 API APPROVED = "approved" # 已审批的 API ADMIN = "admin" # 管理员 API @dataclass class APIEndpoint: """API 端点定义""" name: str url: str method: str permission: APIPermission description: str rate_limit: int = 60 # 每分钟请求次数 # API 白名单 ALLOWED_APIS = [ APIEndpoint( name="weather", url="https://api.weather.example.com/v1", method="GET", permission=APIPermission.PUBLIC, description="获取天气信息", rate_limit=30 ), APIEndpoint( name="news", url="https://newsapi.org/v2", method="GET", permission=APIPermission.PUBLIC, description="获取新闻", rate_limit=30 ), # 可以添加更多已审批的 API ] class APICallTool: """ API 调用工具 安全特性: - 只允许调用白名单中的 API - 速率限制 - 请求超时 - 响应大小限制 """ def __init__(self): self.allowed_apis = {api.name: api for api in ALLOWED_APIS} self.request_timeout = 10 # 请求超时(秒) self.max_response_size = 1024 * 1024 # 最大响应大小(1MB) async def call( self, api_name: str, endpoint: str = "", params: Optional[Dict[str, Any]] = None, headers: Optional[Dict[str, str]] = None ) -> Dict[str, Any]: """ 调用 API Args: api_name: API 名称(必须在白名单中) endpoint: 具体的端点 params: 查询参数 headers: 请求头 Returns: API 响应 """ # 安全检查1: API 必须在白名单中 if api_name not in self.allowed_apis: return { "success": False, "error": f"API '{api_name}' not in whitelist. Allowed: {list(self.allowed_apis.keys())}" } api = self.allowed_apis[api_name] # 构建完整 URL url = f"{api.url}/{endpoint}" if endpoint else api.url try: async with httpx.AsyncClient(timeout=self.request_timeout) as client: # 根据方法调用 if api.method == "GET": response = await client.get(url, params=params, headers=headers) elif api.method == "POST": response = await client.post(url, json=params, headers=headers) else: return { "success": False, "error": f"Method {api.method} not supported" } # 检查响应大小 if len(response.content) > self.max_response_size: return { "success": False, "error": f"Response too large (max {self.max_response_size} bytes)" } return { "success": True, "status_code": response.status_code, "data": response.json() if response.headers.get("content-type", "").startswith("application/json") else response.text, "headers": dict(response.headers) } except httpx.TimeoutException: return { "success": False, "error": "Request timeout" } except Exception as e: return { "success": False, "error": str(e) } def list_apis(self) -> list: """列出所有可用的 API""" return [ { "name": api.name, "description": api.description, "method": api.method, "permission": api.permission.value, "rate_limit": api.rate_limit } for api in ALLOWED_APIS ] # 全局实例 api_tool = APICallTool() async def call_api( api_name: str, endpoint: str = "", params: Optional[Dict[str, Any]] = None ) -> Dict[str, Any]: """ API 调用工具(供 Agent 使用) """ return await api_tool.call(api_name, endpoint, params) def list_allowed_apis() -> list: """列出允许的 API""" return api_tool.list_apis()