feat: 新增 agent/app/core 目录
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
271
agent/app/core/tools/impl/http.py
Normal file
271
agent/app/core/tools/impl/http.py
Normal file
@@ -0,0 +1,271 @@
|
||||
"""
|
||||
HTTP请求工具
|
||||
提供通用的HTTP API调用功能
|
||||
"""
|
||||
import httpx
|
||||
from typing import Dict, Any, Optional, List
|
||||
|
||||
|
||||
class HTTPClientConfig:
|
||||
"""HTTP客户端配置"""
|
||||
DEFAULT_TIMEOUT = 30 # 默认超时(秒)
|
||||
MAX_RESPONSE_SIZE = 5 * 1024 * 1024 # 最大响应大小(5MB)
|
||||
MAX_REDIRECTS = 5 # 最大重定向次数
|
||||
ALLOWED_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]
|
||||
|
||||
|
||||
class HTTPClient:
|
||||
"""
|
||||
HTTP客户端工具
|
||||
|
||||
安全特性:
|
||||
- 只允许特定HTTP方法
|
||||
- 响应大小限制
|
||||
- 超时控制
|
||||
- 请求/响应日志
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.default_timeout = HTTPClientConfig.DEFAULT_TIMEOUT
|
||||
|
||||
async def request(
|
||||
self,
|
||||
url: str,
|
||||
method: str = "GET",
|
||||
params: Optional[Dict[str, Any]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
json_data: Optional[Dict[str, Any]] = None,
|
||||
data: Optional[Any] = None,
|
||||
timeout: Optional[int] = None,
|
||||
allow_redirects: bool = True
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
发送HTTP请求
|
||||
|
||||
Args:
|
||||
url: 目标URL
|
||||
method: HTTP方法
|
||||
params: 查询参数
|
||||
headers: 请求头
|
||||
json_data: JSON请求体
|
||||
data: 原始请求体
|
||||
timeout: 超时时间
|
||||
allow_redirects: 是否允许重定向
|
||||
|
||||
Returns:
|
||||
响应结果
|
||||
"""
|
||||
# 安全检查:方法
|
||||
method = method.upper()
|
||||
if method not in HTTPClientConfig.ALLOWED_METHODS:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Method '{method}' not allowed. Allowed: {HTTPClientConfig.ALLOWED_METHODS}"
|
||||
}
|
||||
|
||||
# 安全检查:协议
|
||||
if not url.startswith(("http://", "https://")):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Only HTTP and HTTPS protocols are allowed"
|
||||
}
|
||||
|
||||
timeout = timeout or self.default_timeout
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(
|
||||
timeout=timeout,
|
||||
max_redirects=HTTPClientConfig.MAX_REDIRECTS if allow_redirects else 0,
|
||||
follow_redirects=allow_redirects,
|
||||
) as client:
|
||||
response = await client.request(
|
||||
method=method,
|
||||
url=url,
|
||||
params=params,
|
||||
headers=headers,
|
||||
json=json_data,
|
||||
content=data,
|
||||
)
|
||||
|
||||
# 检查响应大小
|
||||
content_length = len(response.content)
|
||||
if content_length > HTTPClientConfig.MAX_RESPONSE_SIZE:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Response too large: {content_length} bytes"
|
||||
}
|
||||
|
||||
# 解析响应
|
||||
content_type = response.headers.get("content-type", "")
|
||||
|
||||
if "application/json" in content_type:
|
||||
try:
|
||||
return {
|
||||
"success": True,
|
||||
"status_code": response.status_code,
|
||||
"url": str(response.url),
|
||||
"headers": dict(response.headers),
|
||||
"json": response.json()
|
||||
}
|
||||
except:
|
||||
pass
|
||||
|
||||
# 文本响应
|
||||
return {
|
||||
"success": True,
|
||||
"status_code": response.status_code,
|
||||
"url": str(response.url),
|
||||
"headers": dict(response.headers),
|
||||
"text": response.text[:HTTPClientConfig.MAX_RESPONSE_SIZE]
|
||||
}
|
||||
|
||||
except httpx.TimeoutException:
|
||||
return {
|
||||
"success": False,
|
||||
"error": f"Request timeout ({timeout}s)"
|
||||
}
|
||||
except httpx.InvalidURL:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Invalid URL"
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
async def get(
|
||||
self,
|
||||
url: str,
|
||||
params: Optional[Dict[str, Any]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
timeout: Optional[int] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""发送GET请求"""
|
||||
return await self.request(url, "GET", params, headers, timeout=timeout)
|
||||
|
||||
async def post(
|
||||
self,
|
||||
url: str,
|
||||
json_data: Optional[Dict[str, Any]] = None,
|
||||
data: Optional[Any] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
timeout: Optional[int] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""发送POST请求"""
|
||||
return await self.request(url, "POST", None, headers, json_data, data, timeout)
|
||||
|
||||
async def put(
|
||||
self,
|
||||
url: str,
|
||||
json_data: Optional[Dict[str, Any]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
timeout: Optional[int] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""发送PUT请求"""
|
||||
return await self.request(url, "PUT", None, headers, json_data, None, timeout)
|
||||
|
||||
async def delete(
|
||||
self,
|
||||
url: str,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
timeout: Optional[int] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""发送DELETE请求"""
|
||||
return await self.request(url, "DELETE", None, headers, timeout=timeout)
|
||||
|
||||
|
||||
# 全局HTTP客户端
|
||||
http_client = HTTPClient()
|
||||
|
||||
|
||||
# 便捷函数
|
||||
async def http_request(
|
||||
url: str,
|
||||
method: str = "GET",
|
||||
params: Optional[Dict[str, Any]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
json_data: Optional[Dict[str, Any]] = None,
|
||||
timeout: Optional[int] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""发送HTTP请求"""
|
||||
return await http_client.request(url, method, params, headers, json_data, None, timeout)
|
||||
|
||||
|
||||
async def http_get(
|
||||
url: str,
|
||||
params: Optional[Dict[str, Any]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
timeout: Optional[int] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""发送GET请求"""
|
||||
return await http_client.get(url, params, headers, timeout)
|
||||
|
||||
|
||||
async def http_post(
|
||||
url: str,
|
||||
json_data: Optional[Dict[str, Any]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
timeout: Optional[int] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""发送POST请求"""
|
||||
return await http_client.post(url, json_data, None, headers, timeout)
|
||||
|
||||
|
||||
async def http_put(
|
||||
url: str,
|
||||
json_data: Optional[Dict[str, Any]] = None,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
timeout: Optional[int] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""发送PUT请求"""
|
||||
return await http_client.put(url, json_data, headers, timeout)
|
||||
|
||||
|
||||
async def http_delete(
|
||||
url: str,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
timeout: Optional[int] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""发送DELETE请求"""
|
||||
return await http_client.delete(url, headers, timeout)
|
||||
|
||||
|
||||
# 工具定义
|
||||
HTTP_REQUEST_TOOL = {
|
||||
"name": "http_request",
|
||||
"description": "Make HTTP requests to APIs. Supports GET, POST, PUT, DELETE methods with JSON data.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "The URL to request"
|
||||
},
|
||||
"method": {
|
||||
"type": "string",
|
||||
"description": "HTTP method (GET, POST, PUT, DELETE, PATCH)",
|
||||
"default": "GET"
|
||||
},
|
||||
"params": {
|
||||
"type": "object",
|
||||
"description": "Query parameters for GET requests"
|
||||
},
|
||||
"headers": {
|
||||
"type": "object",
|
||||
"description": "Request headers"
|
||||
},
|
||||
"json_data": {
|
||||
"type": "object",
|
||||
"description": "JSON body for POST/PUT requests"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "integer",
|
||||
"description": "Request timeout in seconds",
|
||||
"default": 30
|
||||
}
|
||||
},
|
||||
"required": ["url"]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user