Undefined-bot 2.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Undefined/__init__.py +3 -0
- Undefined/__main__.py +6 -0
- Undefined/ai.py +1215 -0
- Undefined/config.py +371 -0
- Undefined/end_summary_storage.py +48 -0
- Undefined/faq.py +244 -0
- Undefined/handlers.py +1247 -0
- Undefined/injection_response_agent.py +131 -0
- Undefined/main.py +126 -0
- Undefined/memory.py +120 -0
- Undefined/onebot.py +512 -0
- Undefined/rate_limit.py +130 -0
- Undefined/render.py +123 -0
- Undefined/scheduled_task_storage.py +88 -0
- Undefined/services/__init__.py +1 -0
- Undefined/services/queue_manager.py +206 -0
- Undefined/skills/README.md +53 -0
- Undefined/skills/__init__.py +10 -0
- Undefined/skills/agents/README.md +144 -0
- Undefined/skills/agents/__init__.py +116 -0
- Undefined/skills/agents/entertainment_agent/config.json +17 -0
- Undefined/skills/agents/entertainment_agent/handler.py +220 -0
- Undefined/skills/agents/entertainment_agent/intro.md +25 -0
- Undefined/skills/agents/entertainment_agent/prompt.md +20 -0
- Undefined/skills/agents/entertainment_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/entertainment_agent/tools/ai_draw_one/config.json +34 -0
- Undefined/skills/agents/entertainment_agent/tools/ai_draw_one/handler.py +62 -0
- Undefined/skills/agents/entertainment_agent/tools/ai_study_helper/config.json +22 -0
- Undefined/skills/agents/entertainment_agent/tools/ai_study_helper/handler.py +35 -0
- Undefined/skills/agents/entertainment_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/entertainment_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/entertainment_agent/tools/horoscope/config.json +24 -0
- Undefined/skills/agents/entertainment_agent/tools/horoscope/handler.py +141 -0
- Undefined/skills/agents/entertainment_agent/tools/minecraft_skin/config.json +43 -0
- Undefined/skills/agents/entertainment_agent/tools/minecraft_skin/handler.py +55 -0
- Undefined/skills/agents/entertainment_agent/tools/novel_search/config.json +25 -0
- Undefined/skills/agents/entertainment_agent/tools/novel_search/handler.py +31 -0
- Undefined/skills/agents/entertainment_agent/tools/renjian/config.json +12 -0
- Undefined/skills/agents/entertainment_agent/tools/renjian/handler.py +30 -0
- Undefined/skills/agents/entertainment_agent/tools/wenchang_dijun/config.json +12 -0
- Undefined/skills/agents/entertainment_agent/tools/wenchang_dijun/handler.py +44 -0
- Undefined/skills/agents/file_analysis_agent/__init__.py +1 -0
- Undefined/skills/agents/file_analysis_agent/config.json +21 -0
- Undefined/skills/agents/file_analysis_agent/handler.py +248 -0
- Undefined/skills/agents/file_analysis_agent/intro.md +22 -0
- Undefined/skills/agents/file_analysis_agent/prompt.md +36 -0
- Undefined/skills/agents/file_analysis_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/file_analysis_agent/tools/analyze_code/config.json +17 -0
- Undefined/skills/agents/file_analysis_agent/tools/analyze_code/handler.py +427 -0
- Undefined/skills/agents/file_analysis_agent/tools/analyze_multimodal/config.json +25 -0
- Undefined/skills/agents/file_analysis_agent/tools/analyze_multimodal/handler.py +178 -0
- Undefined/skills/agents/file_analysis_agent/tools/cleanup_temp/config.json +16 -0
- Undefined/skills/agents/file_analysis_agent/tools/cleanup_temp/handler.py +35 -0
- Undefined/skills/agents/file_analysis_agent/tools/detect_file_type/config.json +17 -0
- Undefined/skills/agents/file_analysis_agent/tools/detect_file_type/handler.py +221 -0
- Undefined/skills/agents/file_analysis_agent/tools/download_file/config.json +21 -0
- Undefined/skills/agents/file_analysis_agent/tools/download_file/handler.py +124 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_archive/config.json +25 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_archive/handler.py +190 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_docx/config.json +17 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_docx/handler.py +78 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_pdf/config.json +21 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_pdf/handler.py +67 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_pptx/config.json +17 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_pptx/handler.py +73 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_xlsx/config.json +17 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_xlsx/handler.py +101 -0
- Undefined/skills/agents/file_analysis_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/file_analysis_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/file_analysis_agent/tools/read_text_file/config.json +21 -0
- Undefined/skills/agents/file_analysis_agent/tools/read_text_file/handler.py +90 -0
- Undefined/skills/agents/info_agent/config.json +17 -0
- Undefined/skills/agents/info_agent/handler.py +220 -0
- Undefined/skills/agents/info_agent/intro.md +22 -0
- Undefined/skills/agents/info_agent/prompt.md +27 -0
- Undefined/skills/agents/info_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/info_agent/tools/baiduhot/config.json +18 -0
- Undefined/skills/agents/info_agent/tools/baiduhot/handler.py +49 -0
- Undefined/skills/agents/info_agent/tools/base64/config.json +22 -0
- Undefined/skills/agents/info_agent/tools/base64/handler.py +44 -0
- Undefined/skills/agents/info_agent/tools/douyinhot/config.json +18 -0
- Undefined/skills/agents/info_agent/tools/douyinhot/handler.py +53 -0
- Undefined/skills/agents/info_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/info_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/info_agent/tools/gold_price/config.json +12 -0
- Undefined/skills/agents/info_agent/tools/gold_price/handler.py +58 -0
- Undefined/skills/agents/info_agent/tools/hash/config.json +22 -0
- Undefined/skills/agents/info_agent/tools/hash/handler.py +43 -0
- Undefined/skills/agents/info_agent/tools/history/config.json +12 -0
- Undefined/skills/agents/info_agent/tools/history/handler.py +37 -0
- Undefined/skills/agents/info_agent/tools/net_check/config.json +17 -0
- Undefined/skills/agents/info_agent/tools/net_check/handler.py +117 -0
- Undefined/skills/agents/info_agent/tools/news_tencent/config.json +17 -0
- Undefined/skills/agents/info_agent/tools/news_tencent/handler.py +38 -0
- Undefined/skills/agents/info_agent/tools/qq_level_query/config.json +29 -0
- Undefined/skills/agents/info_agent/tools/qq_level_query/handler.py +48 -0
- Undefined/skills/agents/info_agent/tools/speed/config.json +17 -0
- Undefined/skills/agents/info_agent/tools/speed/handler.py +37 -0
- Undefined/skills/agents/info_agent/tools/tcping/config.json +21 -0
- Undefined/skills/agents/info_agent/tools/tcping/handler.py +53 -0
- Undefined/skills/agents/info_agent/tools/weather_query/config.json +22 -0
- Undefined/skills/agents/info_agent/tools/weather_query/handler.py +207 -0
- Undefined/skills/agents/info_agent/tools/weibohot/config.json +18 -0
- Undefined/skills/agents/info_agent/tools/weibohot/handler.py +49 -0
- Undefined/skills/agents/info_agent/tools/whois/config.json +17 -0
- Undefined/skills/agents/info_agent/tools/whois/handler.py +63 -0
- Undefined/skills/agents/naga_code_analysis_agent/config.json +17 -0
- Undefined/skills/agents/naga_code_analysis_agent/handler.py +222 -0
- Undefined/skills/agents/naga_code_analysis_agent/intro.md +17 -0
- Undefined/skills/agents/naga_code_analysis_agent/prompt.md +19 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/glob/config.json +17 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/glob/handler.py +37 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/list_directory/config.json +17 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/list_directory/handler.py +31 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/read_file/config.json +17 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/read_file/handler.py +66 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/read_naga_intro/config.json +12 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/read_naga_intro/handler.py +327 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/search_file_content/config.json +25 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/search_file_content/handler.py +46 -0
- Undefined/skills/agents/scheduler_agent/__init__.py +1 -0
- Undefined/skills/agents/scheduler_agent/config.json +17 -0
- Undefined/skills/agents/scheduler_agent/handler.py +218 -0
- Undefined/skills/agents/scheduler_agent/intro.md +17 -0
- Undefined/skills/agents/scheduler_agent/prompt.md +67 -0
- Undefined/skills/agents/scheduler_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/scheduler_agent/tools/create_schedule_task/config.json +37 -0
- Undefined/skills/agents/scheduler_agent/tools/create_schedule_task/handler.py +68 -0
- Undefined/skills/agents/scheduler_agent/tools/delete_schedule_task/config.json +19 -0
- Undefined/skills/agents/scheduler_agent/tools/delete_schedule_task/handler.py +26 -0
- Undefined/skills/agents/scheduler_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/scheduler_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/scheduler_agent/tools/list_schedule_tasks/config.json +11 -0
- Undefined/skills/agents/scheduler_agent/tools/list_schedule_tasks/handler.py +47 -0
- Undefined/skills/agents/scheduler_agent/tools/update_schedule_task/config.json +39 -0
- Undefined/skills/agents/scheduler_agent/tools/update_schedule_task/handler.py +46 -0
- Undefined/skills/agents/social_agent/config.json +17 -0
- Undefined/skills/agents/social_agent/handler.py +220 -0
- Undefined/skills/agents/social_agent/intro.md +17 -0
- Undefined/skills/agents/social_agent/prompt.md +19 -0
- Undefined/skills/agents/social_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/social_agent/tools/bilibili_search/config.json +21 -0
- Undefined/skills/agents/social_agent/tools/bilibili_search/handler.py +68 -0
- Undefined/skills/agents/social_agent/tools/bilibili_user_info/config.json +17 -0
- Undefined/skills/agents/social_agent/tools/bilibili_user_info/handler.py +68 -0
- Undefined/skills/agents/social_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/social_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/social_agent/tools/music_global_search/config.json +21 -0
- Undefined/skills/agents/social_agent/tools/music_global_search/handler.py +47 -0
- Undefined/skills/agents/social_agent/tools/music_info_get/config.json +22 -0
- Undefined/skills/agents/social_agent/tools/music_info_get/handler.py +35 -0
- Undefined/skills/agents/social_agent/tools/music_lyrics/config.json +22 -0
- Undefined/skills/agents/social_agent/tools/music_lyrics/handler.py +26 -0
- Undefined/skills/agents/social_agent/tools/video_random_recommend/config.json +21 -0
- Undefined/skills/agents/social_agent/tools/video_random_recommend/handler.py +21 -0
- Undefined/skills/agents/web_agent/config.json +17 -0
- Undefined/skills/agents/web_agent/handler.py +221 -0
- Undefined/skills/agents/web_agent/intro.md +14 -0
- Undefined/skills/agents/web_agent/prompt.md +16 -0
- Undefined/skills/agents/web_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/web_agent/tools/crawl_webpage/config.json +21 -0
- Undefined/skills/agents/web_agent/tools/crawl_webpage/handler.py +102 -0
- Undefined/skills/agents/web_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/web_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/web_agent/tools/web_search/config.json +21 -0
- Undefined/skills/agents/web_agent/tools/web_search/handler.py +29 -0
- Undefined/skills/tools/README.md +85 -0
- Undefined/skills/tools/__init__.py +120 -0
- Undefined/skills/tools/debug/config.json +17 -0
- Undefined/skills/tools/debug/handler.py +35 -0
- Undefined/skills/tools/end/config.json +17 -0
- Undefined/skills/tools/end/handler.py +24 -0
- Undefined/skills/tools/get_current_time/config.json +12 -0
- Undefined/skills/tools/get_current_time/handler.py +5 -0
- Undefined/skills/tools/get_forward_msg/config.json +17 -0
- Undefined/skills/tools/get_forward_msg/handler.py +131 -0
- Undefined/skills/tools/get_group_member_info/config.json +38 -0
- Undefined/skills/tools/get_group_member_info/handler.py +142 -0
- Undefined/skills/tools/get_messages_by_time/config.json +30 -0
- Undefined/skills/tools/get_messages_by_time/handler.py +128 -0
- Undefined/skills/tools/get_picture/config.json +45 -0
- Undefined/skills/tools/get_picture/handler.py +191 -0
- Undefined/skills/tools/get_recent_messages/config.json +30 -0
- Undefined/skills/tools/get_recent_messages/handler.py +88 -0
- Undefined/skills/tools/qq_like/config.json +22 -0
- Undefined/skills/tools/qq_like/handler.py +58 -0
- Undefined/skills/tools/render_html/config.json +26 -0
- Undefined/skills/tools/render_html/handler.py +39 -0
- Undefined/skills/tools/render_latex/config.json +26 -0
- Undefined/skills/tools/render_latex/handler.py +78 -0
- Undefined/skills/tools/render_markdown/config.json +26 -0
- Undefined/skills/tools/render_markdown/handler.py +63 -0
- Undefined/skills/tools/save_memory/config.json +17 -0
- Undefined/skills/tools/save_memory/handler.py +17 -0
- Undefined/skills/tools/send_message/config.json +21 -0
- Undefined/skills/tools/send_message/handler.py +60 -0
- Undefined/skills/tools/send_private_message/config.json +21 -0
- Undefined/skills/tools/send_private_message/handler.py +35 -0
- Undefined/utils/__init__.py +0 -0
- Undefined/utils/common.py +186 -0
- Undefined/utils/history.py +284 -0
- Undefined/utils/scheduler.py +286 -0
- Undefined/utils/sender.py +140 -0
- undefined_bot-2.1.0.dist-info/METADATA +259 -0
- undefined_bot-2.1.0.dist-info/RECORD +211 -0
- undefined_bot-2.1.0.dist-info/WHEEL +4 -0
- undefined_bot-2.1.0.dist-info/entry_points.txt +2 -0
- undefined_bot-2.1.0.dist-info/licenses/LICENSE +7 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Dict
|
|
3
|
+
import base64
|
|
4
|
+
import logging
|
|
5
|
+
import aiofiles
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# 媒体类型的 MIME 映射表
|
|
11
|
+
MIME_TYPES_MAP = {
|
|
12
|
+
"image": {
|
|
13
|
+
".jpg": "image/jpeg",
|
|
14
|
+
".jpeg": "image/jpeg",
|
|
15
|
+
".png": "image/png",
|
|
16
|
+
".gif": "image/gif",
|
|
17
|
+
".bmp": "image/bmp",
|
|
18
|
+
".webp": "image/webp",
|
|
19
|
+
".svg": "image/svg+xml",
|
|
20
|
+
".ico": "image/x-icon",
|
|
21
|
+
},
|
|
22
|
+
"audio": {
|
|
23
|
+
".mp3": "audio/mpeg",
|
|
24
|
+
".wav": "audio/wav",
|
|
25
|
+
".flac": "audio/flac",
|
|
26
|
+
".aac": "audio/aac",
|
|
27
|
+
".m4a": "audio/mp4",
|
|
28
|
+
".ogg": "audio/ogg",
|
|
29
|
+
".wma": "audio/x-ms-wma",
|
|
30
|
+
},
|
|
31
|
+
"video": {
|
|
32
|
+
".mp4": "video/mp4",
|
|
33
|
+
".avi": "video/x-msvideo",
|
|
34
|
+
".mov": "video/quicktime",
|
|
35
|
+
".mkv": "video/x-matroska",
|
|
36
|
+
".webm": "video/webm",
|
|
37
|
+
".flv": "video/x-flv",
|
|
38
|
+
".wmv": "video/x-ms-wmv",
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# 默认 MIME 类型
|
|
43
|
+
DEFAULT_MIMES = {
|
|
44
|
+
"image": "image/jpeg",
|
|
45
|
+
"audio": "audio/mpeg",
|
|
46
|
+
"video": "video/mp4",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# 扩展名分组
|
|
50
|
+
EXTENSION_GROUPS = {
|
|
51
|
+
"image": [
|
|
52
|
+
".jpg",
|
|
53
|
+
".jpeg",
|
|
54
|
+
".png",
|
|
55
|
+
".gif",
|
|
56
|
+
".bmp",
|
|
57
|
+
".webp",
|
|
58
|
+
".svg",
|
|
59
|
+
".ico",
|
|
60
|
+
],
|
|
61
|
+
"audio": [".mp3", ".wav", ".flac", ".aac", ".m4a", ".ogg", ".wma"],
|
|
62
|
+
"video": [".mp4", ".avi", ".mov", ".mkv", ".webm", ".flv", ".wmv"],
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
|
|
67
|
+
file_path: str = args.get("file_path", "")
|
|
68
|
+
media_type: str = args.get("media_type", "auto")
|
|
69
|
+
prompt_extra: str = args.get("prompt", "")
|
|
70
|
+
|
|
71
|
+
path = Path(file_path)
|
|
72
|
+
|
|
73
|
+
if not path.exists():
|
|
74
|
+
return f"错误:文件不存在 {file_path}"
|
|
75
|
+
|
|
76
|
+
if not path.is_file():
|
|
77
|
+
return f"错误:{file_path} 不是文件"
|
|
78
|
+
|
|
79
|
+
ai_client = context.get("ai_client")
|
|
80
|
+
if not ai_client:
|
|
81
|
+
return "错误:AI client 未在上下文中提供"
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
detected_type = _detect_media_type(path, media_type)
|
|
85
|
+
|
|
86
|
+
with open(path, "rb") as f:
|
|
87
|
+
media_data = base64.b64encode(f.read()).decode()
|
|
88
|
+
|
|
89
|
+
mime_type = _get_mime_type(detected_type, path)
|
|
90
|
+
media_content = f"data:{mime_type};base64,{media_data}"
|
|
91
|
+
|
|
92
|
+
async with aiofiles.open(
|
|
93
|
+
"res/prompts/analyze_multimodal.txt", "r", encoding="utf-8"
|
|
94
|
+
) as f:
|
|
95
|
+
prompt = await f.read()
|
|
96
|
+
|
|
97
|
+
if prompt_extra:
|
|
98
|
+
prompt += f"\n\n【补充指令】\n{prompt_extra}"
|
|
99
|
+
|
|
100
|
+
content_items: list[dict[str, Any]] = [{"type": "text", "text": prompt}]
|
|
101
|
+
|
|
102
|
+
if detected_type == "image":
|
|
103
|
+
content_items.append(
|
|
104
|
+
{"type": "image_url", "image_url": {"url": media_content}}
|
|
105
|
+
)
|
|
106
|
+
elif detected_type == "audio":
|
|
107
|
+
content_items.append(
|
|
108
|
+
{"type": "audio_url", "audio_url": {"url": media_content}}
|
|
109
|
+
)
|
|
110
|
+
elif detected_type == "video":
|
|
111
|
+
content_items.append(
|
|
112
|
+
{"type": "video_url", "video_url": {"url": media_content}}
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
response = await ai_client._http_client.post(
|
|
116
|
+
ai_client.vision_config.api_url,
|
|
117
|
+
headers={
|
|
118
|
+
"Authorization": f"Bearer {ai_client.vision_config.api_key}",
|
|
119
|
+
"Content-Type": "application/json",
|
|
120
|
+
},
|
|
121
|
+
json=ai_client._build_request_body(
|
|
122
|
+
model_config=ai_client.vision_config,
|
|
123
|
+
messages=[{"role": "user", "content": content_items}],
|
|
124
|
+
max_tokens=8192,
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
response.raise_for_status()
|
|
128
|
+
result = response.json()
|
|
129
|
+
|
|
130
|
+
content = ai_client._extract_choices_content(result)
|
|
131
|
+
return str(content) if content else "分析失败"
|
|
132
|
+
|
|
133
|
+
except Exception as e:
|
|
134
|
+
logger.exception(f"多模态分析失败: {e}")
|
|
135
|
+
return f"多模态分析失败: {e}"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _detect_media_type(path: Path, media_type: str) -> str:
|
|
139
|
+
"""根据文件扩展名检测媒体类型
|
|
140
|
+
|
|
141
|
+
参数:
|
|
142
|
+
path: 文件路径
|
|
143
|
+
media_type: 用户指定的媒体类型(如果是 "auto" 则自动检测)
|
|
144
|
+
|
|
145
|
+
返回:
|
|
146
|
+
检测到的媒体类型 ("image", "audio", "video" 或默认 "image")
|
|
147
|
+
"""
|
|
148
|
+
if media_type != "auto":
|
|
149
|
+
return media_type
|
|
150
|
+
|
|
151
|
+
suffix = path.suffix.lower()
|
|
152
|
+
|
|
153
|
+
if suffix in EXTENSION_GROUPS["image"]:
|
|
154
|
+
return "image"
|
|
155
|
+
elif suffix in EXTENSION_GROUPS["audio"]:
|
|
156
|
+
return "audio"
|
|
157
|
+
elif suffix in EXTENSION_GROUPS["video"]:
|
|
158
|
+
return "video"
|
|
159
|
+
else:
|
|
160
|
+
return "image"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _get_mime_type(media_type: str, path: Path) -> str:
|
|
164
|
+
"""获取文件的 MIME 类型
|
|
165
|
+
|
|
166
|
+
参数:
|
|
167
|
+
media_type: 媒体类型
|
|
168
|
+
path: 文件路径
|
|
169
|
+
|
|
170
|
+
返回:
|
|
171
|
+
MIME 类型字符串
|
|
172
|
+
"""
|
|
173
|
+
suffix = path.suffix.lower()
|
|
174
|
+
|
|
175
|
+
if media_type in MIME_TYPES_MAP and suffix in MIME_TYPES_MAP[media_type]:
|
|
176
|
+
return MIME_TYPES_MAP[media_type][suffix]
|
|
177
|
+
|
|
178
|
+
return DEFAULT_MIMES.get(media_type, "application/octet-stream")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "cleanup_temp",
|
|
5
|
+
"description": "清理指定的临时目录。用于在文件分析完成后删除临时下载的文件。",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"task_uuid": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "任务 UUID(临时目录名),留空则清理所有 temp 目录"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Dict
|
|
3
|
+
import logging
|
|
4
|
+
import shutil
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
|
|
10
|
+
task_uuid: str = args.get("task_uuid", "")
|
|
11
|
+
|
|
12
|
+
temp_dir = Path.cwd() / "temp"
|
|
13
|
+
|
|
14
|
+
if not temp_dir.exists():
|
|
15
|
+
return "临时目录不存在"
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
if task_uuid:
|
|
19
|
+
target_dir = temp_dir / task_uuid
|
|
20
|
+
if target_dir.exists():
|
|
21
|
+
shutil.rmtree(target_dir)
|
|
22
|
+
return f"已清理临时目录: {target_dir}"
|
|
23
|
+
else:
|
|
24
|
+
return f"临时目录不存在: {target_dir}"
|
|
25
|
+
else:
|
|
26
|
+
count = 0
|
|
27
|
+
for item in temp_dir.iterdir():
|
|
28
|
+
if item.is_dir():
|
|
29
|
+
shutil.rmtree(item)
|
|
30
|
+
count += 1
|
|
31
|
+
return f"已清理 {count} 个临时目录"
|
|
32
|
+
|
|
33
|
+
except Exception as e:
|
|
34
|
+
logger.exception(f"清理临时目录失败: {e}")
|
|
35
|
+
return f"清理临时目录失败: {e}"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "detect_file_type",
|
|
5
|
+
"description": "检测文件类型。Linux 系统使用 file 命令,非 Linux 系统使用魔数检测。",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"file_path": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "本地文件路径"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"required": ["file_path"]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import platform
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Dict
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
MAGIC_NUMBERS: dict[bytes, str] = {
|
|
10
|
+
# 常用压缩格式
|
|
11
|
+
b"\x50\x4b\x03\x04": "ZIP Archive",
|
|
12
|
+
b"\x50\x4b\x05\x06": "ZIP Archive (empty)",
|
|
13
|
+
b"\x50\x4b\x07\x08": "ZIP Archive (spanned)",
|
|
14
|
+
# 文档格式
|
|
15
|
+
b"\x25\x50\x44\x46": "PDF Document",
|
|
16
|
+
b"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1": "Microsoft Office Document (OLE)",
|
|
17
|
+
b"\x50\x4b\x03\x04\x14\x00\x06\x00": "OpenDocument Format",
|
|
18
|
+
# 图片格式
|
|
19
|
+
b"\xff\xd8\xff": "JPEG Image",
|
|
20
|
+
b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a": "PNG Image",
|
|
21
|
+
b"GIF8": "GIF Image",
|
|
22
|
+
b"\x49\x49\x2a\x00": "TIFF Image (little-endian)",
|
|
23
|
+
b"\x4d\x4d\x00\x2a": "TIFF Image (big-endian)",
|
|
24
|
+
b"\x42\x4d": "BMP Image",
|
|
25
|
+
# 音频格式
|
|
26
|
+
b"\x49\x44\x33": "MP3 Audio (ID3)",
|
|
27
|
+
b"\xff\xfb": "MP3 Audio (MPEG)",
|
|
28
|
+
b"\xff\xfa": "MP3 Audio (MPEG)",
|
|
29
|
+
b"\xff\xf3": "MP3 Audio (MPEG)",
|
|
30
|
+
b"\xff\xf2": "MP3 Audio (MPEG)",
|
|
31
|
+
# 视频格式
|
|
32
|
+
b"\x1a\x45\xdf\xa3": "WebM/Matroska Video",
|
|
33
|
+
b"\x00\x00\x00\x18ftypmp42": "MP4 Video (isom)",
|
|
34
|
+
b"\x00\x00\x00\x1cftypisom": "MP4 Video (isom)",
|
|
35
|
+
b"\x00\x00\x00\x20ftypmp41": "MP4 Video (isom)",
|
|
36
|
+
b"\x52\x49\x46\x46": "AVI/RIFF Video",
|
|
37
|
+
# 其他压缩格式
|
|
38
|
+
b"\x1f\x8b": "GZIP Archive",
|
|
39
|
+
b"\xfd7zXZ": "XZ Archive",
|
|
40
|
+
b"BZh": "BZIP2 Archive",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
EXTENSION_MAP: dict[str, str] = {
|
|
44
|
+
".pdf": "PDF Document",
|
|
45
|
+
".docx": "Microsoft Word Document",
|
|
46
|
+
".doc": "Microsoft Word Document (legacy)",
|
|
47
|
+
".pptx": "Microsoft PowerPoint Document",
|
|
48
|
+
".ppt": "Microsoft PowerPoint Document (legacy)",
|
|
49
|
+
".xlsx": "Microsoft Excel Document",
|
|
50
|
+
".xls": "Microsoft Excel Document (legacy)",
|
|
51
|
+
".odt": "OpenDocument Text",
|
|
52
|
+
".ods": "OpenDocument Spreadsheet",
|
|
53
|
+
".odp": "OpenDocument Presentation",
|
|
54
|
+
".txt": "Plain Text",
|
|
55
|
+
".md": "Markdown Document",
|
|
56
|
+
".rst": "reStructuredText Document",
|
|
57
|
+
".log": "Log File",
|
|
58
|
+
".json": "JSON Data",
|
|
59
|
+
".yaml": "YAML Data",
|
|
60
|
+
".yml": "YAML Data",
|
|
61
|
+
".xml": "XML Data",
|
|
62
|
+
".toml": "TOML Data",
|
|
63
|
+
".ini": "INI Configuration",
|
|
64
|
+
".py": "Python Source Code",
|
|
65
|
+
".js": "JavaScript Source Code",
|
|
66
|
+
".ts": "TypeScript Source Code",
|
|
67
|
+
".c": "C Source Code",
|
|
68
|
+
".cpp": "C++ Source Code",
|
|
69
|
+
".cc": "C++ Source Code",
|
|
70
|
+
".h": "C/C++ Header",
|
|
71
|
+
".hpp": "C++ Header",
|
|
72
|
+
".java": "Java Source Code",
|
|
73
|
+
".go": "Go Source Code",
|
|
74
|
+
".rs": "Rust Source Code",
|
|
75
|
+
".php": "PHP Source Code",
|
|
76
|
+
".rb": "Ruby Source Code",
|
|
77
|
+
".swift": "Swift Source Code",
|
|
78
|
+
".kt": "Kotlin Source Code",
|
|
79
|
+
".sql": "SQL Script",
|
|
80
|
+
".r": "R Source Code",
|
|
81
|
+
".lua": "Lua Script",
|
|
82
|
+
".sh": "Shell Script",
|
|
83
|
+
".bash": "Bash Script",
|
|
84
|
+
".html": "HTML Document",
|
|
85
|
+
".htm": "HTML Document",
|
|
86
|
+
".css": "CSS Stylesheet",
|
|
87
|
+
".scss": "SCSS Stylesheet",
|
|
88
|
+
".less": "LESS Stylesheet",
|
|
89
|
+
".vue": "Vue Component",
|
|
90
|
+
".jsx": "React Component",
|
|
91
|
+
".tsx": "React TypeScript Component",
|
|
92
|
+
".jpg": "JPEG Image",
|
|
93
|
+
".jpeg": "JPEG Image",
|
|
94
|
+
".png": "PNG Image",
|
|
95
|
+
".gif": "GIF Image",
|
|
96
|
+
".bmp": "BMP Image",
|
|
97
|
+
".webp": "WebP Image",
|
|
98
|
+
".svg": "SVG Image",
|
|
99
|
+
".ico": "ICO Icon",
|
|
100
|
+
".psd": "Photoshop Document",
|
|
101
|
+
".mp3": "MP3 Audio",
|
|
102
|
+
".wav": "WAV Audio",
|
|
103
|
+
".flac": "FLAC Audio",
|
|
104
|
+
".aac": "AAC Audio",
|
|
105
|
+
".m4a": "MPEG-4 Audio",
|
|
106
|
+
".ogg": "OGG Audio",
|
|
107
|
+
".wma": "Windows Media Audio",
|
|
108
|
+
".mp4": "MP4 Video",
|
|
109
|
+
".avi": "AVI Video",
|
|
110
|
+
".mov": "QuickTime Video",
|
|
111
|
+
".mkv": "Matroska Video",
|
|
112
|
+
".webm": "WebM Video",
|
|
113
|
+
".flv": "Flash Video",
|
|
114
|
+
".wmv": "Windows Media Video",
|
|
115
|
+
".zip": "ZIP Archive",
|
|
116
|
+
".tar": "TAR Archive",
|
|
117
|
+
".gz": "GZIP Archive",
|
|
118
|
+
".tgz": "GZIP-compressed TAR",
|
|
119
|
+
".bz2": "BZIP2 Archive",
|
|
120
|
+
".xz": "XZ Archive",
|
|
121
|
+
".7z": "7-Zip Archive",
|
|
122
|
+
".rar": "RAR Archive",
|
|
123
|
+
".exe": "Windows Executable",
|
|
124
|
+
".dll": "Windows Dynamic Library",
|
|
125
|
+
".so": "Linux Shared Library",
|
|
126
|
+
".dylib": "macOS Dynamic Library",
|
|
127
|
+
".class": "Java Bytecode",
|
|
128
|
+
".jar": "Java Archive",
|
|
129
|
+
".war": "Java Web Archive",
|
|
130
|
+
".apk": "Android Package",
|
|
131
|
+
".ipa": "iOS App Package",
|
|
132
|
+
".deb": "Debian Package",
|
|
133
|
+
".rpm": "RPM Package",
|
|
134
|
+
".dmg": "macOS Disk Image",
|
|
135
|
+
".iso": "ISO Disk Image",
|
|
136
|
+
".csv": "CSV Data",
|
|
137
|
+
".tsv": "TSV Data",
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
|
|
142
|
+
"""执行文件类型检测
|
|
143
|
+
|
|
144
|
+
检测流程:
|
|
145
|
+
1. Linux 系统优先使用 `file` 命令
|
|
146
|
+
2. 其他系统或 file 命令失败时,使用文件头魔数 (Magic Number) 检测
|
|
147
|
+
3. 最后尝试使用文件扩展名检测
|
|
148
|
+
4. 如果都失败,返回 Unknown
|
|
149
|
+
|
|
150
|
+
参数:
|
|
151
|
+
args: 工具参数
|
|
152
|
+
context: 执行上下文
|
|
153
|
+
|
|
154
|
+
返回:
|
|
155
|
+
检测结果描述
|
|
156
|
+
"""
|
|
157
|
+
file_path: str = args.get("file_path", "")
|
|
158
|
+
path = Path(file_path)
|
|
159
|
+
|
|
160
|
+
if not path.exists():
|
|
161
|
+
return f"错误:文件不存在 {file_path}"
|
|
162
|
+
|
|
163
|
+
if not path.is_file():
|
|
164
|
+
return f"错误:{file_path} 不是文件"
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
file_size = path.stat().st_size
|
|
168
|
+
system = platform.system()
|
|
169
|
+
|
|
170
|
+
if system == "Linux":
|
|
171
|
+
result = _detect_by_file_command(path, file_size)
|
|
172
|
+
if result:
|
|
173
|
+
return result
|
|
174
|
+
else:
|
|
175
|
+
result = _detect_by_magic_number(path, file_size)
|
|
176
|
+
if result:
|
|
177
|
+
return result
|
|
178
|
+
|
|
179
|
+
result = _detect_by_extension(path)
|
|
180
|
+
if result:
|
|
181
|
+
return result
|
|
182
|
+
|
|
183
|
+
return f"Unknown file type (大小: {file_size} 字节)"
|
|
184
|
+
|
|
185
|
+
except Exception as e:
|
|
186
|
+
logger.exception(f"检测文件类型失败: {e}")
|
|
187
|
+
return f"检测文件类型失败: {e}"
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _detect_by_file_command(path: Path, file_size: int) -> str | None:
|
|
191
|
+
try:
|
|
192
|
+
result = subprocess.run(
|
|
193
|
+
["file", "-b", "-L", str(path)], capture_output=True, text=True, timeout=5
|
|
194
|
+
)
|
|
195
|
+
output = result.stdout.strip()
|
|
196
|
+
if output:
|
|
197
|
+
return f"{output} (大小: {file_size} 字节)"
|
|
198
|
+
except Exception as e:
|
|
199
|
+
logger.warning(f"file 命令执行失败: {e}")
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _detect_by_magic_number(path: Path, file_size: int) -> str | None:
|
|
204
|
+
try:
|
|
205
|
+
with open(path, "rb") as f:
|
|
206
|
+
header = f.read(32)
|
|
207
|
+
|
|
208
|
+
for magic, file_type in MAGIC_NUMBERS.items():
|
|
209
|
+
if header.startswith(magic):
|
|
210
|
+
return f"{file_type} (大小: {file_size} 字节)"
|
|
211
|
+
except Exception as e:
|
|
212
|
+
logger.warning(f"魔数检测失败: {e}")
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _detect_by_extension(path: Path) -> str | None:
|
|
217
|
+
ext = path.suffix.lower()
|
|
218
|
+
file_type = EXTENSION_MAP.get(ext)
|
|
219
|
+
if file_type:
|
|
220
|
+
return file_type
|
|
221
|
+
return None
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "download_file",
|
|
5
|
+
"description": "下载文件到临时目录。支持 URL 或 QQ 的 file_id。对于 URL 会先尝试获取文件大小,获取失败则拒绝下载。",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"file_source": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "文件源:URL 或 QQ 的 file_id"
|
|
12
|
+
},
|
|
13
|
+
"max_size_mb": {
|
|
14
|
+
"type": "number",
|
|
15
|
+
"description": "最大允许的文件大小(MB),默认 100MB"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"required": ["file_source"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Dict
|
|
4
|
+
import logging
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
SIZE_LIMITS = {
|
|
10
|
+
"text": 10 * 1024 * 1024,
|
|
11
|
+
"code": 5 * 1024 * 1024,
|
|
12
|
+
"pdf": 50 * 1024 * 1024,
|
|
13
|
+
"docx": 20 * 1024 * 1024,
|
|
14
|
+
"pptx": 20 * 1024 * 1024,
|
|
15
|
+
"xlsx": 10 * 1024 * 1024,
|
|
16
|
+
"image": 10 * 1024 * 1024,
|
|
17
|
+
"audio": 50 * 1024 * 1024,
|
|
18
|
+
"video": 100 * 1024 * 1024,
|
|
19
|
+
"archive": 100 * 1024 * 1024,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
DEFAULT_SIZE_LIMIT = 100 * 1024 * 1024
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
|
|
26
|
+
file_source: str = args.get("file_source", "")
|
|
27
|
+
max_size_mb: float = args.get("max_size_mb", 100)
|
|
28
|
+
|
|
29
|
+
if not file_source:
|
|
30
|
+
return "错误:文件源不能为空"
|
|
31
|
+
|
|
32
|
+
task_uuid: str = uuid.uuid4().hex[:16]
|
|
33
|
+
temp_dir: Path = Path.cwd() / "temp" / task_uuid
|
|
34
|
+
temp_dir.mkdir(parents=True, exist_ok=True)
|
|
35
|
+
|
|
36
|
+
is_url: bool = file_source.startswith("http://") or file_source.startswith(
|
|
37
|
+
"https://"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if is_url:
|
|
41
|
+
return await _download_from_url(file_source, temp_dir, max_size_mb, task_uuid)
|
|
42
|
+
else:
|
|
43
|
+
return await _download_from_file_id(file_source, temp_dir, context, task_uuid)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
async def _download_from_url(
|
|
47
|
+
url: str, temp_dir: Path, max_size_mb: float, task_uuid: str
|
|
48
|
+
) -> str:
|
|
49
|
+
max_size_bytes: int = int(max_size_mb * 1024 * 1024)
|
|
50
|
+
|
|
51
|
+
async with httpx.AsyncClient(timeout=60.0) as client:
|
|
52
|
+
try:
|
|
53
|
+
logger.info(f"正在获取文件大小: {url}")
|
|
54
|
+
head_response = await client.head(url, timeout=30.0)
|
|
55
|
+
content_length = head_response.headers.get("content-length")
|
|
56
|
+
|
|
57
|
+
if content_length is None:
|
|
58
|
+
return "错误:无法获取文件大小,拒绝下载"
|
|
59
|
+
|
|
60
|
+
file_size = int(content_length)
|
|
61
|
+
if file_size > max_size_bytes:
|
|
62
|
+
return f"错误:文件大小 ({file_size / 1024 / 1024:.2f}MB) 超过限制 ({max_size_mb}MB)"
|
|
63
|
+
|
|
64
|
+
logger.info(f"文件大小: {file_size / 1024 / 1024:.2f}MB,允许下载")
|
|
65
|
+
|
|
66
|
+
logger.info("正在下载文件...")
|
|
67
|
+
response = await client.get(url, timeout=120.0)
|
|
68
|
+
response.raise_for_status()
|
|
69
|
+
|
|
70
|
+
filename = _extract_filename_from_url(url)
|
|
71
|
+
if not filename or "." not in filename:
|
|
72
|
+
filename = f"downloaded_{task_uuid}"
|
|
73
|
+
|
|
74
|
+
file_path = temp_dir / filename
|
|
75
|
+
file_path.write_bytes(response.content)
|
|
76
|
+
|
|
77
|
+
logger.info(f"文件已保存到: {file_path}")
|
|
78
|
+
return str(file_path)
|
|
79
|
+
|
|
80
|
+
except httpx.TimeoutException:
|
|
81
|
+
return "错误:下载超时"
|
|
82
|
+
except httpx.HTTPStatusError as e:
|
|
83
|
+
return f"错误:HTTP 错误 {e.response.status_code}"
|
|
84
|
+
except Exception as e:
|
|
85
|
+
logger.exception(f"下载失败: {e}")
|
|
86
|
+
return f"错误:下载失败 - {e}"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
async def _download_from_file_id(
|
|
90
|
+
file_id: str, temp_dir: Path, context: Dict[str, Any], task_uuid: str
|
|
91
|
+
) -> str:
|
|
92
|
+
get_image_url_callback = context.get("get_image_url_callback")
|
|
93
|
+
if not get_image_url_callback:
|
|
94
|
+
return "错误:file_id 模式需要 get_image_url_callback"
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
logger.info(f"正在解析 file_id: {file_id}")
|
|
98
|
+
url = await get_image_url_callback(file_id)
|
|
99
|
+
if not url:
|
|
100
|
+
return f"错误:无法将 file_id {file_id} 解析为 URL"
|
|
101
|
+
|
|
102
|
+
logger.info(f"获取到 URL: {url}")
|
|
103
|
+
|
|
104
|
+
async with httpx.AsyncClient(timeout=120.0) as client:
|
|
105
|
+
response = await client.get(url, timeout=120.0)
|
|
106
|
+
response.raise_for_status()
|
|
107
|
+
|
|
108
|
+
filename = f"file_{file_id}"
|
|
109
|
+
file_path = temp_dir / filename
|
|
110
|
+
file_path.write_bytes(response.content)
|
|
111
|
+
|
|
112
|
+
logger.info(f"文件已保存到: {file_path}")
|
|
113
|
+
return str(file_path)
|
|
114
|
+
|
|
115
|
+
except Exception as e:
|
|
116
|
+
logger.exception(f"下载失败(file_id 模式): {e}")
|
|
117
|
+
return f"错误:下载失败 - {e}"
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _extract_filename_from_url(url: str) -> str:
|
|
121
|
+
if "?" in url:
|
|
122
|
+
url = url.split("?")[0]
|
|
123
|
+
filename = url.split("/")[-1]
|
|
124
|
+
return filename
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "extract_archive",
|
|
5
|
+
"description": "提取压缩包文件内容。支持 zip, tar, gz, bz2, xz, 7z, rar 等格式。支持列出文件列表或解压到临时目录。",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"file_path": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "本地压缩包路径"
|
|
12
|
+
},
|
|
13
|
+
"action": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "操作类型:'list'(列出文件列表)或 'extract'(解压到临时目录),默认为 'list'"
|
|
16
|
+
},
|
|
17
|
+
"extract_path": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "解压目标路径(可选),默认为临时目录"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"required": ["file_path"]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|