974 lines
32 KiB
Python
974 lines
32 KiB
Python
"""
|
||
内置工具实现
|
||
"""
|
||
from typing import Dict, Any, Optional, List, Tuple
|
||
import httpx
|
||
import json
|
||
import os
|
||
import logging
|
||
import re
|
||
import math
|
||
from datetime import datetime, timedelta
|
||
import platform
|
||
import sys
|
||
import asyncio
|
||
import subprocess
|
||
from sqlalchemy import text
|
||
from sqlalchemy.exc import SQLAlchemyError
|
||
from app.core.config import settings
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
async def http_request_tool(
|
||
url: str,
|
||
method: str = "GET",
|
||
headers: Optional[Dict[str, str]] = None,
|
||
body: Any = None
|
||
) -> str:
|
||
"""
|
||
HTTP请求工具
|
||
|
||
Args:
|
||
url: 请求URL
|
||
method: HTTP方法 (GET, POST, PUT, DELETE)
|
||
headers: 请求头
|
||
body: 请求体(POST/PUT时使用)
|
||
|
||
Returns:
|
||
JSON格式的响应结果
|
||
"""
|
||
try:
|
||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||
method_upper = method.upper()
|
||
|
||
if method_upper == "GET":
|
||
response = await client.get(url, headers=headers)
|
||
elif method_upper == "POST":
|
||
response = await client.post(url, json=body, headers=headers)
|
||
elif method_upper == "PUT":
|
||
response = await client.put(url, json=body, headers=headers)
|
||
elif method_upper == "DELETE":
|
||
response = await client.delete(url, headers=headers)
|
||
else:
|
||
raise ValueError(f"不支持的HTTP方法: {method}")
|
||
|
||
# 尝试解析JSON响应
|
||
try:
|
||
response_body = response.json()
|
||
except:
|
||
response_body = response.text
|
||
|
||
result = {
|
||
"status_code": response.status_code,
|
||
"headers": dict(response.headers),
|
||
"body": response_body
|
||
}
|
||
|
||
return json.dumps(result, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"HTTP请求工具执行失败: {str(e)}")
|
||
return json.dumps({"error": str(e)}, ensure_ascii=False)
|
||
|
||
|
||
async def file_read_tool(file_path: str) -> str:
|
||
"""
|
||
文件读取工具
|
||
|
||
Args:
|
||
file_path: 文件路径
|
||
|
||
Returns:
|
||
文件内容或错误信息
|
||
"""
|
||
try:
|
||
# 安全检查:限制可读取的文件路径
|
||
# 只允许读取项目目录下的文件
|
||
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))
|
||
abs_path = os.path.abspath(file_path)
|
||
|
||
if not abs_path.startswith(project_root):
|
||
return json.dumps({
|
||
"error": f"不允许读取项目目录外的文件: {file_path}"
|
||
}, ensure_ascii=False)
|
||
|
||
with open(file_path, 'r', encoding='utf-8') as f:
|
||
content = f.read()
|
||
|
||
return json.dumps({
|
||
"file_path": file_path,
|
||
"content": content,
|
||
"size": len(content)
|
||
}, ensure_ascii=False)
|
||
except FileNotFoundError:
|
||
return json.dumps({
|
||
"error": f"文件不存在: {file_path}"
|
||
}, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"文件读取工具执行失败: {str(e)}")
|
||
return json.dumps({
|
||
"error": str(e)
|
||
}, ensure_ascii=False)
|
||
|
||
|
||
async def file_write_tool(file_path: str, content: str, mode: str = "w") -> str:
|
||
"""
|
||
文件写入工具
|
||
|
||
Args:
|
||
file_path: 文件路径
|
||
content: 要写入的内容
|
||
mode: 写入模式(w=覆盖,a=追加)
|
||
|
||
Returns:
|
||
写入结果
|
||
"""
|
||
try:
|
||
# 安全检查:限制可写入的文件路径
|
||
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))
|
||
abs_path = os.path.abspath(file_path)
|
||
|
||
if not abs_path.startswith(project_root):
|
||
return json.dumps({
|
||
"error": f"不允许写入项目目录外的文件: {file_path}"
|
||
}, ensure_ascii=False)
|
||
|
||
write_mode = "a" if mode == "a" else "w"
|
||
with open(file_path, write_mode, encoding='utf-8') as f:
|
||
f.write(content)
|
||
|
||
return json.dumps({
|
||
"success": True,
|
||
"file_path": file_path,
|
||
"mode": write_mode,
|
||
"content_length": len(content)
|
||
}, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"文件写入工具执行失败: {str(e)}")
|
||
return json.dumps({
|
||
"error": str(e)
|
||
}, ensure_ascii=False)
|
||
|
||
|
||
async def text_analyze_tool(text: str, operation: str = "count") -> str:
|
||
"""
|
||
文本分析工具
|
||
|
||
Args:
|
||
text: 要分析的文本
|
||
operation: 操作类型(count=统计, keywords=提取关键词, summary=摘要)
|
||
|
||
Returns:
|
||
分析结果
|
||
"""
|
||
try:
|
||
if operation == "count":
|
||
# 统计字数、字符数、行数等
|
||
char_count = len(text)
|
||
char_count_no_spaces = len(text.replace(" ", ""))
|
||
word_count = len(text.split())
|
||
line_count = len(text.splitlines())
|
||
paragraph_count = len([p for p in text.split("\n\n") if p.strip()])
|
||
|
||
return json.dumps({
|
||
"char_count": char_count,
|
||
"char_count_no_spaces": char_count_no_spaces,
|
||
"word_count": word_count,
|
||
"line_count": line_count,
|
||
"paragraph_count": paragraph_count
|
||
}, ensure_ascii=False)
|
||
|
||
elif operation == "keywords":
|
||
# 简单的关键词提取(基于词频)
|
||
words = re.findall(r'\b\w+\b', text.lower())
|
||
word_freq = {}
|
||
for word in words:
|
||
if len(word) > 2: # 忽略太短的词
|
||
word_freq[word] = word_freq.get(word, 0) + 1
|
||
|
||
# 取频率最高的10个词
|
||
top_keywords = sorted(word_freq.items(), key=lambda x: x[1], reverse=True)[:10]
|
||
|
||
return json.dumps({
|
||
"keywords": [{"word": k, "frequency": v} for k, v in top_keywords]
|
||
}, ensure_ascii=False)
|
||
|
||
elif operation == "summary":
|
||
# 简单的摘要(取前3句)
|
||
sentences = re.split(r'[.!?。!?]\s+', text)
|
||
summary = ". ".join(sentences[:3]) + "."
|
||
|
||
return json.dumps({
|
||
"summary": summary,
|
||
"original_length": len(text),
|
||
"summary_length": len(summary)
|
||
}, ensure_ascii=False)
|
||
|
||
else:
|
||
return json.dumps({
|
||
"error": f"不支持的操作类型: {operation}"
|
||
}, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"文本分析工具执行失败: {str(e)}")
|
||
return json.dumps({
|
||
"error": str(e)
|
||
}, ensure_ascii=False)
|
||
|
||
|
||
async def datetime_tool(operation: str = "now", format: str = "%Y-%m-%d %H:%M:%S",
|
||
timezone: Optional[str] = None) -> str:
|
||
"""
|
||
日期时间工具
|
||
|
||
Args:
|
||
operation: 操作类型(now=当前时间, format=格式化, parse=解析)
|
||
format: 时间格式
|
||
timezone: 时区(暂未实现)
|
||
|
||
Returns:
|
||
日期时间结果
|
||
"""
|
||
try:
|
||
if operation == "now":
|
||
now = datetime.now()
|
||
return json.dumps({
|
||
"datetime": now.strftime(format),
|
||
"timestamp": now.timestamp(),
|
||
"iso_format": now.isoformat()
|
||
}, ensure_ascii=False)
|
||
|
||
elif operation == "format":
|
||
now = datetime.now()
|
||
return json.dumps({
|
||
"formatted": now.strftime(format)
|
||
}, ensure_ascii=False)
|
||
|
||
else:
|
||
return json.dumps({
|
||
"error": f"不支持的操作类型: {operation}"
|
||
}, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"日期时间工具执行失败: {str(e)}")
|
||
return json.dumps({
|
||
"error": str(e)
|
||
}, ensure_ascii=False)
|
||
|
||
|
||
async def math_calculate_tool(expression: str) -> str:
|
||
"""
|
||
数学计算工具(安全限制:只允许基本数学运算)
|
||
|
||
Args:
|
||
expression: 数学表达式(如 "2+2", "sqrt(16)", "sin(0)")
|
||
|
||
Returns:
|
||
计算结果
|
||
"""
|
||
try:
|
||
# 安全检查:只允许数字、基本运算符和安全的数学函数
|
||
allowed_chars = set("0123456789+-*/.() ")
|
||
allowed_functions = ['sqrt', 'sin', 'cos', 'tan', 'log', 'exp', 'abs', 'pow']
|
||
|
||
# 检查表达式是否安全
|
||
if not all(c in allowed_chars or any(f in expression for f in allowed_functions) for c in expression):
|
||
return json.dumps({
|
||
"error": "表达式包含不安全的字符"
|
||
}, ensure_ascii=False)
|
||
|
||
# 构建安全的数学环境
|
||
safe_dict = {
|
||
"__builtins__": {},
|
||
"abs": abs,
|
||
"sqrt": math.sqrt,
|
||
"sin": math.sin,
|
||
"cos": math.cos,
|
||
"tan": math.tan,
|
||
"log": math.log,
|
||
"exp": math.exp,
|
||
"pow": pow,
|
||
"pi": math.pi,
|
||
"e": math.e
|
||
}
|
||
|
||
result = eval(expression, safe_dict)
|
||
|
||
return json.dumps({
|
||
"expression": expression,
|
||
"result": result,
|
||
"result_type": type(result).__name__
|
||
}, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"数学计算工具执行失败: {str(e)}")
|
||
return json.dumps({
|
||
"error": str(e)
|
||
}, ensure_ascii=False)
|
||
|
||
|
||
async def system_info_tool() -> str:
|
||
"""
|
||
系统信息工具
|
||
|
||
Returns:
|
||
系统信息
|
||
"""
|
||
try:
|
||
info = {
|
||
"platform": platform.system(),
|
||
"platform_version": platform.version(),
|
||
"architecture": platform.machine(),
|
||
"processor": platform.processor(),
|
||
"python_version": sys.version,
|
||
"python_version_info": {
|
||
"major": sys.version_info.major,
|
||
"minor": sys.version_info.minor,
|
||
"micro": sys.version_info.micro
|
||
}
|
||
}
|
||
|
||
return json.dumps(info, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"系统信息工具执行失败: {str(e)}")
|
||
return json.dumps({
|
||
"error": str(e)
|
||
}, ensure_ascii=False)
|
||
|
||
|
||
async def json_process_tool(json_string: str, operation: str = "parse") -> str:
|
||
"""
|
||
JSON处理工具
|
||
|
||
Args:
|
||
json_string: JSON字符串
|
||
operation: 操作类型(parse=解析, stringify=序列化, validate=验证)
|
||
|
||
Returns:
|
||
处理结果
|
||
"""
|
||
try:
|
||
if operation == "parse":
|
||
data = json.loads(json_string)
|
||
return json.dumps({
|
||
"parsed": data,
|
||
"type": type(data).__name__
|
||
}, ensure_ascii=False)
|
||
|
||
elif operation == "stringify":
|
||
# 如果输入已经是字符串,尝试解析后再序列化
|
||
try:
|
||
data = json.loads(json_string)
|
||
except:
|
||
data = json_string
|
||
|
||
return json.dumps({
|
||
"stringified": json.dumps(data, ensure_ascii=False, indent=2)
|
||
}, ensure_ascii=False)
|
||
|
||
elif operation == "validate":
|
||
try:
|
||
json.loads(json_string)
|
||
return json.dumps({
|
||
"valid": True
|
||
}, ensure_ascii=False)
|
||
except json.JSONDecodeError as e:
|
||
return json.dumps({
|
||
"valid": False,
|
||
"error": str(e)
|
||
}, ensure_ascii=False)
|
||
|
||
else:
|
||
return json.dumps({
|
||
"error": f"不支持的操作类型: {operation}"
|
||
}, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"JSON处理工具执行失败: {str(e)}")
|
||
return json.dumps({
|
||
"error": str(e)
|
||
}, ensure_ascii=False)
|
||
|
||
|
||
def _validate_sql_query(sql: str) -> Tuple[bool, Optional[str]]:
|
||
"""
|
||
验证SQL查询的安全性
|
||
|
||
Args:
|
||
sql: SQL查询语句
|
||
|
||
Returns:
|
||
(是否安全, 错误信息)
|
||
"""
|
||
# 去除注释和多余空白
|
||
sql_clean = re.sub(r'--.*?\n', '', sql)
|
||
sql_clean = re.sub(r'/\*.*?\*/', '', sql_clean, flags=re.DOTALL)
|
||
sql_clean = ' '.join(sql_clean.split())
|
||
|
||
# 转换为小写以便检查
|
||
sql_lower = sql_clean.lower().strip()
|
||
|
||
# 只允许SELECT查询
|
||
if not sql_lower.startswith('select'):
|
||
return False, "只允许SELECT查询,不允许INSERT、UPDATE、DELETE、DROP等操作"
|
||
|
||
# 检查危险关键字
|
||
dangerous_keywords = [
|
||
'insert', 'update', 'delete', 'drop', 'truncate', 'alter',
|
||
'create', 'grant', 'revoke', 'exec', 'execute', 'call',
|
||
'commit', 'rollback', 'savepoint'
|
||
]
|
||
|
||
for keyword in dangerous_keywords:
|
||
# 使用单词边界匹配,避免误判(如"select"中包含"select")
|
||
pattern = r'\b' + keyword + r'\b'
|
||
if re.search(pattern, sql_lower):
|
||
return False, f"检测到危险关键字: {keyword.upper()},查询被拒绝"
|
||
|
||
# 检查是否有多个SQL语句(防止SQL注入)
|
||
if ';' in sql_clean and sql_clean.count(';') > 1:
|
||
return False, "不允许执行多个SQL语句"
|
||
|
||
return True, None
|
||
|
||
|
||
async def _execute_default_db_query(sql: str, timeout: int = 30) -> List[Dict[str, Any]]:
|
||
"""
|
||
执行默认数据库查询
|
||
|
||
Args:
|
||
sql: SQL查询语句
|
||
timeout: 查询超时时间(秒)
|
||
|
||
Returns:
|
||
查询结果列表
|
||
"""
|
||
from app.core.database import engine
|
||
|
||
try:
|
||
# 使用asyncio实现超时控制
|
||
loop = asyncio.get_event_loop()
|
||
|
||
def _execute():
|
||
with engine.connect() as connection:
|
||
result = connection.execute(text(sql))
|
||
# 获取列名
|
||
columns = result.keys()
|
||
# 获取所有行
|
||
rows = result.fetchall()
|
||
# 转换为字典列表
|
||
return [dict(zip(columns, row)) for row in rows]
|
||
|
||
# 执行查询,带超时控制
|
||
result = await asyncio.wait_for(
|
||
asyncio.to_thread(_execute),
|
||
timeout=timeout
|
||
)
|
||
return result
|
||
except asyncio.TimeoutError:
|
||
raise Exception(f"查询超时(超过{timeout}秒)")
|
||
except SQLAlchemyError as e:
|
||
raise Exception(f"数据库查询失败: {str(e)}")
|
||
except Exception as e:
|
||
raise Exception(f"执行查询时发生错误: {str(e)}")
|
||
|
||
|
||
async def _execute_data_source_query(data_source_id: str, sql: str, timeout: int = 30) -> List[Dict[str, Any]]:
|
||
"""
|
||
通过数据源ID执行查询
|
||
|
||
Args:
|
||
data_source_id: 数据源ID
|
||
sql: SQL查询语句
|
||
timeout: 查询超时时间(秒)
|
||
|
||
Returns:
|
||
查询结果列表
|
||
"""
|
||
from app.core.database import SessionLocal
|
||
from app.models.data_source import DataSource
|
||
from app.services.data_source_connector import create_connector
|
||
|
||
db = SessionLocal()
|
||
try:
|
||
# 获取数据源配置
|
||
data_source = db.query(DataSource).filter(DataSource.id == data_source_id).first()
|
||
if not data_source:
|
||
raise Exception(f"数据源不存在: {data_source_id}")
|
||
|
||
if data_source.status != 'active':
|
||
raise Exception(f"数据源状态异常: {data_source.status}")
|
||
|
||
# 创建连接器
|
||
connector = create_connector(data_source.type, data_source.config)
|
||
|
||
# 执行查询(带超时控制)
|
||
query_params = {'sql': sql}
|
||
|
||
# 使用asyncio实现超时控制
|
||
def _execute():
|
||
return connector.query(query_params)
|
||
|
||
result = await asyncio.wait_for(
|
||
asyncio.to_thread(_execute),
|
||
timeout=timeout
|
||
)
|
||
|
||
# 确保结果是列表格式
|
||
if not isinstance(result, list):
|
||
result = [result] if result else []
|
||
|
||
return result
|
||
except asyncio.TimeoutError:
|
||
raise Exception(f"查询超时(超过{timeout}秒)")
|
||
except Exception as e:
|
||
raise Exception(f"数据源查询失败: {str(e)}")
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
async def database_query_tool(
|
||
query: str,
|
||
database: str = "default",
|
||
data_source_id: Optional[str] = None,
|
||
timeout: int = 30
|
||
) -> str:
|
||
"""
|
||
数据库查询工具
|
||
|
||
Args:
|
||
query: SQL查询语句(只允许SELECT)
|
||
database: 数据库名称(已废弃,保留用于兼容)
|
||
data_source_id: 数据源ID(可选,如果提供则使用指定数据源)
|
||
timeout: 查询超时时间(秒,默认30秒)
|
||
|
||
Returns:
|
||
JSON格式的查询结果
|
||
"""
|
||
try:
|
||
# 验证SQL安全性
|
||
is_safe, error_msg = _validate_sql_query(query)
|
||
if not is_safe:
|
||
return json.dumps({
|
||
"error": error_msg,
|
||
"query": query
|
||
}, ensure_ascii=False)
|
||
|
||
# 验证超时时间
|
||
if timeout <= 0 or timeout > 300:
|
||
return json.dumps({
|
||
"error": "超时时间必须在1-300秒之间",
|
||
"timeout": timeout
|
||
}, ensure_ascii=False)
|
||
|
||
# 执行查询
|
||
if data_source_id:
|
||
# 使用指定的数据源
|
||
result = await _execute_data_source_query(data_source_id, query, timeout)
|
||
else:
|
||
# 使用默认数据库
|
||
result = await _execute_default_db_query(query, timeout)
|
||
|
||
# 格式化结果
|
||
return json.dumps({
|
||
"success": True,
|
||
"row_count": len(result),
|
||
"data": result,
|
||
"query": query
|
||
}, ensure_ascii=False, default=str)
|
||
|
||
except Exception as e:
|
||
logger.error(f"数据库查询工具执行失败: {str(e)}")
|
||
return json.dumps({
|
||
"error": str(e),
|
||
"query": query
|
||
}, ensure_ascii=False)
|
||
|
||
|
||
async def adb_log_tool(
|
||
command: str = "logcat",
|
||
filter_tag: Optional[str] = None,
|
||
level: Optional[str] = None,
|
||
max_lines: int = 100,
|
||
timeout: int = 10
|
||
) -> str:
|
||
"""
|
||
ADB日志工具 - 获取Android设备日志
|
||
|
||
Args:
|
||
command: ADB命令类型(logcat=获取日志, devices=列出设备, shell=执行shell命令)
|
||
filter_tag: 日志标签过滤(如 "ActivityManager", "SystemServer")
|
||
level: 日志级别过滤(V/D/I/W/E/F/S,如 "E" 只显示错误)
|
||
max_lines: 最大返回行数(默认100行,避免输出过长)
|
||
timeout: 命令执行超时时间(秒,默认10秒)
|
||
|
||
Returns:
|
||
JSON格式的执行结果
|
||
"""
|
||
try:
|
||
# 验证超时时间
|
||
if timeout <= 0 or timeout > 60:
|
||
return json.dumps({
|
||
"error": "超时时间必须在1-60秒之间",
|
||
"timeout": timeout
|
||
}, ensure_ascii=False)
|
||
|
||
# 验证最大行数
|
||
if max_lines <= 0 or max_lines > 10000:
|
||
return json.dumps({
|
||
"error": "最大行数必须在1-10000之间",
|
||
"max_lines": max_lines
|
||
}, ensure_ascii=False)
|
||
|
||
# 构建adb命令
|
||
if command == "logcat":
|
||
# 获取日志
|
||
adb_cmd = ["adb", "logcat", "-d"] # -d 表示获取已缓存的日志
|
||
|
||
# 添加日志级别过滤
|
||
if level:
|
||
level = level.upper()
|
||
if level in ["V", "D", "I", "W", "E", "F", "S"]:
|
||
adb_cmd.extend([f"*:{level}"])
|
||
else:
|
||
return json.dumps({
|
||
"error": f"无效的日志级别: {level},支持: V/D/I/W/E/F/S"
|
||
}, ensure_ascii=False)
|
||
|
||
# 添加标签过滤
|
||
if filter_tag:
|
||
adb_cmd.append(filter_tag)
|
||
|
||
# 限制输出行数
|
||
adb_cmd.extend(["-t", str(max_lines)])
|
||
|
||
elif command == "devices":
|
||
# 列出连接的设备
|
||
adb_cmd = ["adb", "devices", "-l"]
|
||
|
||
elif command == "shell":
|
||
# 执行shell命令(受限,只允许安全命令)
|
||
if filter_tag:
|
||
# filter_tag在这里作为shell命令
|
||
# 只允许安全的命令
|
||
safe_commands = ["getprop", "dumpsys", "pm", "am", "settings"]
|
||
cmd_parts = filter_tag.split()
|
||
if not cmd_parts or cmd_parts[0] not in safe_commands:
|
||
return json.dumps({
|
||
"error": f"不允许执行命令: {filter_tag},只允许: {', '.join(safe_commands)}"
|
||
}, ensure_ascii=False)
|
||
adb_cmd = ["adb", "shell"] + cmd_parts
|
||
else:
|
||
return json.dumps({
|
||
"error": "shell命令需要提供filter_tag参数作为命令"
|
||
}, ensure_ascii=False)
|
||
else:
|
||
return json.dumps({
|
||
"error": f"不支持的ADB命令: {command},支持: logcat/devices/shell"
|
||
}, ensure_ascii=False)
|
||
|
||
# 执行adb命令
|
||
def _execute_adb():
|
||
try:
|
||
result = subprocess.run(
|
||
adb_cmd,
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=timeout,
|
||
encoding='utf-8',
|
||
errors='replace' # 处理编码错误
|
||
)
|
||
return result
|
||
except subprocess.TimeoutExpired:
|
||
raise Exception(f"ADB命令执行超时(超过{timeout}秒)")
|
||
except FileNotFoundError:
|
||
raise Exception("未找到adb命令,请确保已安装Android SDK Platform Tools并配置PATH")
|
||
except Exception as e:
|
||
raise Exception(f"执行ADB命令失败: {str(e)}")
|
||
|
||
# 异步执行命令
|
||
result = await asyncio.wait_for(
|
||
asyncio.to_thread(_execute_adb),
|
||
timeout=timeout + 2 # 额外2秒缓冲
|
||
)
|
||
|
||
# 处理结果
|
||
output_lines = result.stdout.split('\n') if result.stdout else []
|
||
error_lines = result.stderr.split('\n') if result.stderr else []
|
||
|
||
# 如果输出太长,截断
|
||
if len(output_lines) > max_lines:
|
||
output_lines = output_lines[:max_lines]
|
||
output_lines.append(f"... (已截断,共 {len(result.stdout.split(chr(10)))} 行)")
|
||
|
||
return json.dumps({
|
||
"success": result.returncode == 0,
|
||
"command": " ".join(adb_cmd),
|
||
"return_code": result.returncode,
|
||
"output": "\n".join(output_lines),
|
||
"output_lines": len(output_lines),
|
||
"error": "\n".join(error_lines) if error_lines and any(error_lines) else None,
|
||
"timestamp": datetime.now().isoformat()
|
||
}, ensure_ascii=False)
|
||
|
||
except asyncio.TimeoutError:
|
||
return json.dumps({
|
||
"error": f"ADB命令执行超时(超过{timeout}秒)",
|
||
"command": " ".join(adb_cmd) if 'adb_cmd' in locals() else command
|
||
}, ensure_ascii=False)
|
||
except Exception as e:
|
||
logger.error(f"ADB日志工具执行失败: {str(e)}")
|
||
return json.dumps({
|
||
"error": str(e),
|
||
"command": " ".join(adb_cmd) if 'adb_cmd' in locals() else command
|
||
}, ensure_ascii=False)
|
||
|
||
|
||
# 工具定义(OpenAI Function格式)
|
||
HTTP_REQUEST_SCHEMA = {
|
||
"type": "function",
|
||
"function": {
|
||
"name": "http_request",
|
||
"description": "发送HTTP请求,支持GET、POST、PUT、DELETE方法。可以用于调用API、获取网页内容等。",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"url": {
|
||
"type": "string",
|
||
"description": "请求的URL地址"
|
||
},
|
||
"method": {
|
||
"type": "string",
|
||
"enum": ["GET", "POST", "PUT", "DELETE"],
|
||
"description": "HTTP请求方法",
|
||
"default": "GET"
|
||
},
|
||
"headers": {
|
||
"type": "object",
|
||
"description": "HTTP请求头(可选)",
|
||
"additionalProperties": {
|
||
"type": "string"
|
||
}
|
||
},
|
||
"body": {
|
||
"type": "object",
|
||
"description": "请求体(POST/PUT时使用,可选)"
|
||
}
|
||
},
|
||
"required": ["url", "method"]
|
||
}
|
||
}
|
||
}
|
||
|
||
FILE_READ_SCHEMA = {
|
||
"type": "function",
|
||
"function": {
|
||
"name": "file_read",
|
||
"description": "读取文件内容。只能读取项目目录下的文件,确保文件路径正确。",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"file_path": {
|
||
"type": "string",
|
||
"description": "要读取的文件路径(相对于项目根目录或绝对路径)"
|
||
}
|
||
},
|
||
"required": ["file_path"]
|
||
}
|
||
}
|
||
}
|
||
|
||
FILE_WRITE_SCHEMA = {
|
||
"type": "function",
|
||
"function": {
|
||
"name": "file_write",
|
||
"description": "写入文件内容。只能写入项目目录下的文件,确保文件路径正确。",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"file_path": {
|
||
"type": "string",
|
||
"description": "要写入的文件路径(相对于项目根目录或绝对路径)"
|
||
},
|
||
"content": {
|
||
"type": "string",
|
||
"description": "要写入的内容"
|
||
},
|
||
"mode": {
|
||
"type": "string",
|
||
"enum": ["w", "a"],
|
||
"description": "写入模式:w=覆盖写入,a=追加写入",
|
||
"default": "w"
|
||
}
|
||
},
|
||
"required": ["file_path", "content"]
|
||
}
|
||
}
|
||
}
|
||
|
||
TEXT_ANALYZE_SCHEMA = {
|
||
"type": "function",
|
||
"function": {
|
||
"name": "text_analyze",
|
||
"description": "分析文本内容,支持统计字数、提取关键词、生成摘要等功能。",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"text": {
|
||
"type": "string",
|
||
"description": "要分析的文本内容"
|
||
},
|
||
"operation": {
|
||
"type": "string",
|
||
"enum": ["count", "keywords", "summary"],
|
||
"description": "操作类型:count=统计字数等信息,keywords=提取关键词,summary=生成摘要",
|
||
"default": "count"
|
||
}
|
||
},
|
||
"required": ["text"]
|
||
}
|
||
}
|
||
}
|
||
|
||
DATETIME_SCHEMA = {
|
||
"type": "function",
|
||
"function": {
|
||
"name": "datetime",
|
||
"description": "获取和处理日期时间信息,支持获取当前时间、格式化时间等。",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"operation": {
|
||
"type": "string",
|
||
"enum": ["now", "format"],
|
||
"description": "操作类型:now=获取当前时间,format=格式化时间",
|
||
"default": "now"
|
||
},
|
||
"format": {
|
||
"type": "string",
|
||
"description": "时间格式字符串(如:%Y-%m-%d %H:%M:%S)",
|
||
"default": "%Y-%m-%d %H:%M:%S"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
MATH_CALCULATE_SCHEMA = {
|
||
"type": "function",
|
||
"function": {
|
||
"name": "math_calculate",
|
||
"description": "执行数学计算,支持基本运算和常用数学函数(如sqrt, sin, cos, log等)。",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"expression": {
|
||
"type": "string",
|
||
"description": "数学表达式(如:2+2, sqrt(16), sin(0), pow(2,3))"
|
||
}
|
||
},
|
||
"required": ["expression"]
|
||
}
|
||
}
|
||
}
|
||
|
||
SYSTEM_INFO_SCHEMA = {
|
||
"type": "function",
|
||
"function": {
|
||
"name": "system_info",
|
||
"description": "获取系统信息,包括操作系统、Python版本等。",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {}
|
||
}
|
||
}
|
||
}
|
||
|
||
JSON_PROCESS_SCHEMA = {
|
||
"type": "function",
|
||
"function": {
|
||
"name": "json_process",
|
||
"description": "处理JSON数据,支持解析、序列化、验证等功能。",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"json_string": {
|
||
"type": "string",
|
||
"description": "JSON字符串"
|
||
},
|
||
"operation": {
|
||
"type": "string",
|
||
"enum": ["parse", "stringify", "validate"],
|
||
"description": "操作类型:parse=解析JSON,stringify=序列化为JSON,validate=验证JSON格式",
|
||
"default": "parse"
|
||
}
|
||
},
|
||
"required": ["json_string"]
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
ADB_LOG_SCHEMA = {
|
||
"type": "function",
|
||
"function": {
|
||
"name": "adb_log",
|
||
"description": "执行ADB命令获取Android设备日志。支持logcat(获取日志)、devices(列出设备)、shell(执行shell命令)。可以过滤日志标签和级别。",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"command": {
|
||
"type": "string",
|
||
"enum": ["logcat", "devices", "shell"],
|
||
"description": "ADB命令类型:logcat=获取日志,devices=列出连接的设备,shell=执行shell命令",
|
||
"default": "logcat"
|
||
},
|
||
"filter_tag": {
|
||
"type": "string",
|
||
"description": "日志标签过滤(如 'ActivityManager', 'SystemServer')或shell命令(当command=shell时)"
|
||
},
|
||
"level": {
|
||
"type": "string",
|
||
"enum": ["V", "D", "I", "W", "E", "F", "S"],
|
||
"description": "日志级别过滤:V=Verbose, D=Debug, I=Info, W=Warning, E=Error, F=Fatal, S=Silent"
|
||
},
|
||
"max_lines": {
|
||
"type": "integer",
|
||
"description": "最大返回行数(默认100行,避免输出过长)",
|
||
"default": 100
|
||
},
|
||
"timeout": {
|
||
"type": "integer",
|
||
"description": "命令执行超时时间(秒,默认10秒,最大60秒)",
|
||
"default": 10
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
DATABASE_QUERY_SCHEMA = {
|
||
"type": "function",
|
||
"function": {
|
||
"name": "database_query",
|
||
"description": "执行数据库查询(只允许SELECT查询,支持默认数据库和指定数据源)。可以查询工作流、Agent、执行记录等系统数据,或通过数据源ID查询外部数据库。",
|
||
"parameters": {
|
||
"type": "object",
|
||
"properties": {
|
||
"query": {
|
||
"type": "string",
|
||
"description": "SQL查询语句(只允许SELECT查询,不允许INSERT、UPDATE、DELETE等操作)"
|
||
},
|
||
"data_source_id": {
|
||
"type": "string",
|
||
"description": "数据源ID(可选,如果提供则使用指定的数据源,否则使用默认数据库)"
|
||
},
|
||
"timeout": {
|
||
"type": "integer",
|
||
"description": "查询超时时间(秒,默认30秒,最大300秒)",
|
||
"default": 30,
|
||
"minimum": 1,
|
||
"maximum": 300
|
||
}
|
||
},
|
||
"required": ["query"]
|
||
}
|
||
}
|
||
}
|