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.
Files changed (211) hide show
  1. Undefined/__init__.py +3 -0
  2. Undefined/__main__.py +6 -0
  3. Undefined/ai.py +1215 -0
  4. Undefined/config.py +371 -0
  5. Undefined/end_summary_storage.py +48 -0
  6. Undefined/faq.py +244 -0
  7. Undefined/handlers.py +1247 -0
  8. Undefined/injection_response_agent.py +131 -0
  9. Undefined/main.py +126 -0
  10. Undefined/memory.py +120 -0
  11. Undefined/onebot.py +512 -0
  12. Undefined/rate_limit.py +130 -0
  13. Undefined/render.py +123 -0
  14. Undefined/scheduled_task_storage.py +88 -0
  15. Undefined/services/__init__.py +1 -0
  16. Undefined/services/queue_manager.py +206 -0
  17. Undefined/skills/README.md +53 -0
  18. Undefined/skills/__init__.py +10 -0
  19. Undefined/skills/agents/README.md +144 -0
  20. Undefined/skills/agents/__init__.py +116 -0
  21. Undefined/skills/agents/entertainment_agent/config.json +17 -0
  22. Undefined/skills/agents/entertainment_agent/handler.py +220 -0
  23. Undefined/skills/agents/entertainment_agent/intro.md +25 -0
  24. Undefined/skills/agents/entertainment_agent/prompt.md +20 -0
  25. Undefined/skills/agents/entertainment_agent/tools/__init__.py +1 -0
  26. Undefined/skills/agents/entertainment_agent/tools/ai_draw_one/config.json +34 -0
  27. Undefined/skills/agents/entertainment_agent/tools/ai_draw_one/handler.py +62 -0
  28. Undefined/skills/agents/entertainment_agent/tools/ai_study_helper/config.json +22 -0
  29. Undefined/skills/agents/entertainment_agent/tools/ai_study_helper/handler.py +35 -0
  30. Undefined/skills/agents/entertainment_agent/tools/get_current_time/config.json +12 -0
  31. Undefined/skills/agents/entertainment_agent/tools/get_current_time/handler.py +5 -0
  32. Undefined/skills/agents/entertainment_agent/tools/horoscope/config.json +24 -0
  33. Undefined/skills/agents/entertainment_agent/tools/horoscope/handler.py +141 -0
  34. Undefined/skills/agents/entertainment_agent/tools/minecraft_skin/config.json +43 -0
  35. Undefined/skills/agents/entertainment_agent/tools/minecraft_skin/handler.py +55 -0
  36. Undefined/skills/agents/entertainment_agent/tools/novel_search/config.json +25 -0
  37. Undefined/skills/agents/entertainment_agent/tools/novel_search/handler.py +31 -0
  38. Undefined/skills/agents/entertainment_agent/tools/renjian/config.json +12 -0
  39. Undefined/skills/agents/entertainment_agent/tools/renjian/handler.py +30 -0
  40. Undefined/skills/agents/entertainment_agent/tools/wenchang_dijun/config.json +12 -0
  41. Undefined/skills/agents/entertainment_agent/tools/wenchang_dijun/handler.py +44 -0
  42. Undefined/skills/agents/file_analysis_agent/__init__.py +1 -0
  43. Undefined/skills/agents/file_analysis_agent/config.json +21 -0
  44. Undefined/skills/agents/file_analysis_agent/handler.py +248 -0
  45. Undefined/skills/agents/file_analysis_agent/intro.md +22 -0
  46. Undefined/skills/agents/file_analysis_agent/prompt.md +36 -0
  47. Undefined/skills/agents/file_analysis_agent/tools/__init__.py +1 -0
  48. Undefined/skills/agents/file_analysis_agent/tools/analyze_code/config.json +17 -0
  49. Undefined/skills/agents/file_analysis_agent/tools/analyze_code/handler.py +427 -0
  50. Undefined/skills/agents/file_analysis_agent/tools/analyze_multimodal/config.json +25 -0
  51. Undefined/skills/agents/file_analysis_agent/tools/analyze_multimodal/handler.py +178 -0
  52. Undefined/skills/agents/file_analysis_agent/tools/cleanup_temp/config.json +16 -0
  53. Undefined/skills/agents/file_analysis_agent/tools/cleanup_temp/handler.py +35 -0
  54. Undefined/skills/agents/file_analysis_agent/tools/detect_file_type/config.json +17 -0
  55. Undefined/skills/agents/file_analysis_agent/tools/detect_file_type/handler.py +221 -0
  56. Undefined/skills/agents/file_analysis_agent/tools/download_file/config.json +21 -0
  57. Undefined/skills/agents/file_analysis_agent/tools/download_file/handler.py +124 -0
  58. Undefined/skills/agents/file_analysis_agent/tools/extract_archive/config.json +25 -0
  59. Undefined/skills/agents/file_analysis_agent/tools/extract_archive/handler.py +190 -0
  60. Undefined/skills/agents/file_analysis_agent/tools/extract_docx/config.json +17 -0
  61. Undefined/skills/agents/file_analysis_agent/tools/extract_docx/handler.py +78 -0
  62. Undefined/skills/agents/file_analysis_agent/tools/extract_pdf/config.json +21 -0
  63. Undefined/skills/agents/file_analysis_agent/tools/extract_pdf/handler.py +67 -0
  64. Undefined/skills/agents/file_analysis_agent/tools/extract_pptx/config.json +17 -0
  65. Undefined/skills/agents/file_analysis_agent/tools/extract_pptx/handler.py +73 -0
  66. Undefined/skills/agents/file_analysis_agent/tools/extract_xlsx/config.json +17 -0
  67. Undefined/skills/agents/file_analysis_agent/tools/extract_xlsx/handler.py +101 -0
  68. Undefined/skills/agents/file_analysis_agent/tools/get_current_time/config.json +12 -0
  69. Undefined/skills/agents/file_analysis_agent/tools/get_current_time/handler.py +5 -0
  70. Undefined/skills/agents/file_analysis_agent/tools/read_text_file/config.json +21 -0
  71. Undefined/skills/agents/file_analysis_agent/tools/read_text_file/handler.py +90 -0
  72. Undefined/skills/agents/info_agent/config.json +17 -0
  73. Undefined/skills/agents/info_agent/handler.py +220 -0
  74. Undefined/skills/agents/info_agent/intro.md +22 -0
  75. Undefined/skills/agents/info_agent/prompt.md +27 -0
  76. Undefined/skills/agents/info_agent/tools/__init__.py +1 -0
  77. Undefined/skills/agents/info_agent/tools/baiduhot/config.json +18 -0
  78. Undefined/skills/agents/info_agent/tools/baiduhot/handler.py +49 -0
  79. Undefined/skills/agents/info_agent/tools/base64/config.json +22 -0
  80. Undefined/skills/agents/info_agent/tools/base64/handler.py +44 -0
  81. Undefined/skills/agents/info_agent/tools/douyinhot/config.json +18 -0
  82. Undefined/skills/agents/info_agent/tools/douyinhot/handler.py +53 -0
  83. Undefined/skills/agents/info_agent/tools/get_current_time/config.json +12 -0
  84. Undefined/skills/agents/info_agent/tools/get_current_time/handler.py +5 -0
  85. Undefined/skills/agents/info_agent/tools/gold_price/config.json +12 -0
  86. Undefined/skills/agents/info_agent/tools/gold_price/handler.py +58 -0
  87. Undefined/skills/agents/info_agent/tools/hash/config.json +22 -0
  88. Undefined/skills/agents/info_agent/tools/hash/handler.py +43 -0
  89. Undefined/skills/agents/info_agent/tools/history/config.json +12 -0
  90. Undefined/skills/agents/info_agent/tools/history/handler.py +37 -0
  91. Undefined/skills/agents/info_agent/tools/net_check/config.json +17 -0
  92. Undefined/skills/agents/info_agent/tools/net_check/handler.py +117 -0
  93. Undefined/skills/agents/info_agent/tools/news_tencent/config.json +17 -0
  94. Undefined/skills/agents/info_agent/tools/news_tencent/handler.py +38 -0
  95. Undefined/skills/agents/info_agent/tools/qq_level_query/config.json +29 -0
  96. Undefined/skills/agents/info_agent/tools/qq_level_query/handler.py +48 -0
  97. Undefined/skills/agents/info_agent/tools/speed/config.json +17 -0
  98. Undefined/skills/agents/info_agent/tools/speed/handler.py +37 -0
  99. Undefined/skills/agents/info_agent/tools/tcping/config.json +21 -0
  100. Undefined/skills/agents/info_agent/tools/tcping/handler.py +53 -0
  101. Undefined/skills/agents/info_agent/tools/weather_query/config.json +22 -0
  102. Undefined/skills/agents/info_agent/tools/weather_query/handler.py +207 -0
  103. Undefined/skills/agents/info_agent/tools/weibohot/config.json +18 -0
  104. Undefined/skills/agents/info_agent/tools/weibohot/handler.py +49 -0
  105. Undefined/skills/agents/info_agent/tools/whois/config.json +17 -0
  106. Undefined/skills/agents/info_agent/tools/whois/handler.py +63 -0
  107. Undefined/skills/agents/naga_code_analysis_agent/config.json +17 -0
  108. Undefined/skills/agents/naga_code_analysis_agent/handler.py +222 -0
  109. Undefined/skills/agents/naga_code_analysis_agent/intro.md +17 -0
  110. Undefined/skills/agents/naga_code_analysis_agent/prompt.md +19 -0
  111. Undefined/skills/agents/naga_code_analysis_agent/tools/__init__.py +1 -0
  112. Undefined/skills/agents/naga_code_analysis_agent/tools/get_current_time/config.json +12 -0
  113. Undefined/skills/agents/naga_code_analysis_agent/tools/get_current_time/handler.py +5 -0
  114. Undefined/skills/agents/naga_code_analysis_agent/tools/glob/config.json +17 -0
  115. Undefined/skills/agents/naga_code_analysis_agent/tools/glob/handler.py +37 -0
  116. Undefined/skills/agents/naga_code_analysis_agent/tools/list_directory/config.json +17 -0
  117. Undefined/skills/agents/naga_code_analysis_agent/tools/list_directory/handler.py +31 -0
  118. Undefined/skills/agents/naga_code_analysis_agent/tools/read_file/config.json +17 -0
  119. Undefined/skills/agents/naga_code_analysis_agent/tools/read_file/handler.py +66 -0
  120. Undefined/skills/agents/naga_code_analysis_agent/tools/read_naga_intro/config.json +12 -0
  121. Undefined/skills/agents/naga_code_analysis_agent/tools/read_naga_intro/handler.py +327 -0
  122. Undefined/skills/agents/naga_code_analysis_agent/tools/search_file_content/config.json +25 -0
  123. Undefined/skills/agents/naga_code_analysis_agent/tools/search_file_content/handler.py +46 -0
  124. Undefined/skills/agents/scheduler_agent/__init__.py +1 -0
  125. Undefined/skills/agents/scheduler_agent/config.json +17 -0
  126. Undefined/skills/agents/scheduler_agent/handler.py +218 -0
  127. Undefined/skills/agents/scheduler_agent/intro.md +17 -0
  128. Undefined/skills/agents/scheduler_agent/prompt.md +67 -0
  129. Undefined/skills/agents/scheduler_agent/tools/__init__.py +1 -0
  130. Undefined/skills/agents/scheduler_agent/tools/create_schedule_task/config.json +37 -0
  131. Undefined/skills/agents/scheduler_agent/tools/create_schedule_task/handler.py +68 -0
  132. Undefined/skills/agents/scheduler_agent/tools/delete_schedule_task/config.json +19 -0
  133. Undefined/skills/agents/scheduler_agent/tools/delete_schedule_task/handler.py +26 -0
  134. Undefined/skills/agents/scheduler_agent/tools/get_current_time/config.json +12 -0
  135. Undefined/skills/agents/scheduler_agent/tools/get_current_time/handler.py +5 -0
  136. Undefined/skills/agents/scheduler_agent/tools/list_schedule_tasks/config.json +11 -0
  137. Undefined/skills/agents/scheduler_agent/tools/list_schedule_tasks/handler.py +47 -0
  138. Undefined/skills/agents/scheduler_agent/tools/update_schedule_task/config.json +39 -0
  139. Undefined/skills/agents/scheduler_agent/tools/update_schedule_task/handler.py +46 -0
  140. Undefined/skills/agents/social_agent/config.json +17 -0
  141. Undefined/skills/agents/social_agent/handler.py +220 -0
  142. Undefined/skills/agents/social_agent/intro.md +17 -0
  143. Undefined/skills/agents/social_agent/prompt.md +19 -0
  144. Undefined/skills/agents/social_agent/tools/__init__.py +1 -0
  145. Undefined/skills/agents/social_agent/tools/bilibili_search/config.json +21 -0
  146. Undefined/skills/agents/social_agent/tools/bilibili_search/handler.py +68 -0
  147. Undefined/skills/agents/social_agent/tools/bilibili_user_info/config.json +17 -0
  148. Undefined/skills/agents/social_agent/tools/bilibili_user_info/handler.py +68 -0
  149. Undefined/skills/agents/social_agent/tools/get_current_time/config.json +12 -0
  150. Undefined/skills/agents/social_agent/tools/get_current_time/handler.py +5 -0
  151. Undefined/skills/agents/social_agent/tools/music_global_search/config.json +21 -0
  152. Undefined/skills/agents/social_agent/tools/music_global_search/handler.py +47 -0
  153. Undefined/skills/agents/social_agent/tools/music_info_get/config.json +22 -0
  154. Undefined/skills/agents/social_agent/tools/music_info_get/handler.py +35 -0
  155. Undefined/skills/agents/social_agent/tools/music_lyrics/config.json +22 -0
  156. Undefined/skills/agents/social_agent/tools/music_lyrics/handler.py +26 -0
  157. Undefined/skills/agents/social_agent/tools/video_random_recommend/config.json +21 -0
  158. Undefined/skills/agents/social_agent/tools/video_random_recommend/handler.py +21 -0
  159. Undefined/skills/agents/web_agent/config.json +17 -0
  160. Undefined/skills/agents/web_agent/handler.py +221 -0
  161. Undefined/skills/agents/web_agent/intro.md +14 -0
  162. Undefined/skills/agents/web_agent/prompt.md +16 -0
  163. Undefined/skills/agents/web_agent/tools/__init__.py +1 -0
  164. Undefined/skills/agents/web_agent/tools/crawl_webpage/config.json +21 -0
  165. Undefined/skills/agents/web_agent/tools/crawl_webpage/handler.py +102 -0
  166. Undefined/skills/agents/web_agent/tools/get_current_time/config.json +12 -0
  167. Undefined/skills/agents/web_agent/tools/get_current_time/handler.py +5 -0
  168. Undefined/skills/agents/web_agent/tools/web_search/config.json +21 -0
  169. Undefined/skills/agents/web_agent/tools/web_search/handler.py +29 -0
  170. Undefined/skills/tools/README.md +85 -0
  171. Undefined/skills/tools/__init__.py +120 -0
  172. Undefined/skills/tools/debug/config.json +17 -0
  173. Undefined/skills/tools/debug/handler.py +35 -0
  174. Undefined/skills/tools/end/config.json +17 -0
  175. Undefined/skills/tools/end/handler.py +24 -0
  176. Undefined/skills/tools/get_current_time/config.json +12 -0
  177. Undefined/skills/tools/get_current_time/handler.py +5 -0
  178. Undefined/skills/tools/get_forward_msg/config.json +17 -0
  179. Undefined/skills/tools/get_forward_msg/handler.py +131 -0
  180. Undefined/skills/tools/get_group_member_info/config.json +38 -0
  181. Undefined/skills/tools/get_group_member_info/handler.py +142 -0
  182. Undefined/skills/tools/get_messages_by_time/config.json +30 -0
  183. Undefined/skills/tools/get_messages_by_time/handler.py +128 -0
  184. Undefined/skills/tools/get_picture/config.json +45 -0
  185. Undefined/skills/tools/get_picture/handler.py +191 -0
  186. Undefined/skills/tools/get_recent_messages/config.json +30 -0
  187. Undefined/skills/tools/get_recent_messages/handler.py +88 -0
  188. Undefined/skills/tools/qq_like/config.json +22 -0
  189. Undefined/skills/tools/qq_like/handler.py +58 -0
  190. Undefined/skills/tools/render_html/config.json +26 -0
  191. Undefined/skills/tools/render_html/handler.py +39 -0
  192. Undefined/skills/tools/render_latex/config.json +26 -0
  193. Undefined/skills/tools/render_latex/handler.py +78 -0
  194. Undefined/skills/tools/render_markdown/config.json +26 -0
  195. Undefined/skills/tools/render_markdown/handler.py +63 -0
  196. Undefined/skills/tools/save_memory/config.json +17 -0
  197. Undefined/skills/tools/save_memory/handler.py +17 -0
  198. Undefined/skills/tools/send_message/config.json +21 -0
  199. Undefined/skills/tools/send_message/handler.py +60 -0
  200. Undefined/skills/tools/send_private_message/config.json +21 -0
  201. Undefined/skills/tools/send_private_message/handler.py +35 -0
  202. Undefined/utils/__init__.py +0 -0
  203. Undefined/utils/common.py +186 -0
  204. Undefined/utils/history.py +284 -0
  205. Undefined/utils/scheduler.py +286 -0
  206. Undefined/utils/sender.py +140 -0
  207. undefined_bot-2.1.0.dist-info/METADATA +259 -0
  208. undefined_bot-2.1.0.dist-info/RECORD +211 -0
  209. undefined_bot-2.1.0.dist-info/WHEEL +4 -0
  210. undefined_bot-2.1.0.dist-info/entry_points.txt +2 -0
  211. undefined_bot-2.1.0.dist-info/licenses/LICENSE +7 -0
Undefined/render.py ADDED
@@ -0,0 +1,123 @@
1
+ import markdown
2
+ import asyncio
3
+ from playwright.async_api import async_playwright
4
+
5
+ from typing import Any
6
+
7
+
8
+ async def render_markdown_to_html(md_text: str) -> str:
9
+ # 定义同步的解析逻辑
10
+ def _parse() -> str:
11
+ extensions = [
12
+ "toc",
13
+ "tables",
14
+ "fenced_code",
15
+ "codehilite",
16
+ "md_in_html",
17
+ "attr_list",
18
+ "pymdownx.superfences",
19
+ "pymdownx.arithmatex",
20
+ "pymdownx.tasklist",
21
+ "pymdownx.tilde",
22
+ "pymdownx.emoji",
23
+ ]
24
+
25
+ extension_configs: dict[str, dict[str, Any]] = {
26
+ "pymdownx.superfences": {
27
+ "custom_fences": [
28
+ {
29
+ "name": "mermaid",
30
+ "class": "mermaid",
31
+ "format": lambda source,
32
+ language,
33
+ css_class,
34
+ options,
35
+ md,
36
+ **kwargs: f'<pre class="{css_class}">{source}</pre>',
37
+ }
38
+ ]
39
+ },
40
+ "pymdownx.arithmatex": {
41
+ "generic": True,
42
+ },
43
+ }
44
+
45
+ return str(
46
+ markdown.markdown(
47
+ md_text, extensions=extensions, extension_configs=extension_configs
48
+ )
49
+ )
50
+
51
+ # 使用 to_thread 在独立的线程中运行同步的 markdown 解析,避免阻塞主循环
52
+ html_content = await asyncio.to_thread(_parse)
53
+
54
+ # 拼接 HTML 模板(这部分是纯字符串操作,速度极快,无需放进线程)
55
+ full_html = f"""
56
+ <!DOCTYPE html>
57
+ <html>
58
+ <head>
59
+ <meta charset="utf-8">
60
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.2.0/github-markdown.min.css">
61
+ <style>
62
+ body {{ background-color: white; padding: 45px; }}
63
+ .markdown-body {{ box-sizing: border-box; min-width: 200px; max-width: 980px; margin: 0 auto; }}
64
+ .mermaid {{ background: transparent !important; border: none !important; }}
65
+ </style>
66
+ <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
67
+ <script>
68
+ window.MathJax = {{
69
+ tex: {{ inlineMath: [['$', '$'], ['\\\\(', '\\\\)']] }}
70
+ }};
71
+ </script>
72
+ <script src="https://cdn.jsdelivr.net/npm/mermaid@11.1.0/dist/mermaid.min.js"></script>
73
+ </head>
74
+ <body>
75
+ <article class="markdown-body">
76
+ {html_content}
77
+ </article>
78
+ <script>
79
+ mermaid.initialize({{
80
+ startOnLoad: true,
81
+ theme: 'default',
82
+ securityLevel: 'loose',
83
+ fontFamily: 'arial'
84
+ }});
85
+ </script>
86
+ </body>
87
+ </html>
88
+ """
89
+ return full_html
90
+
91
+
92
+ async def render_html_to_image(html_content: str, output_path: str) -> None:
93
+ """
94
+ 将 HTML 字符串转换为 PNG 图片
95
+
96
+ 参数:
97
+ html_content: 完整的 HTML 字符串
98
+ output_path: 输出图片路径 (例如 'result.png')
99
+ """
100
+ async with async_playwright() as p:
101
+ # 启动无头浏览器
102
+ browser = await p.chromium.launch(headless=True)
103
+ # 设置上下文,可以指定缩放比例(device_scale_factor),2代表2倍清晰度(Retina)
104
+ context = await browser.new_context(device_scale_factor=2)
105
+ page = await context.new_page()
106
+
107
+ # 设置页面内容
108
+ await page.set_content(html_content)
109
+
110
+ # --- 关键:等待渲染完成 ---
111
+ # 1. 等待网络空闲(确保 CDN 上的 MathJax/Mermaid 脚本加载完)
112
+ await page.wait_for_load_state("networkidle")
113
+
114
+ # 2. 如果有 Mermaid,给它一点时间执行 JS 绘图
115
+ # 如果页面里没有 mermaid 脚本,这行会很快跳过
116
+ await asyncio.sleep(1) # 等待 1 秒钟让 Mermaid 渲染完成
117
+
118
+ # 3. 自动调整视口大小以匹配内容
119
+ # 如果你想截取整个页面,使用 full_page=True
120
+ # 如果只想截取特定容器,可以定位 element = page.locator(".container")
121
+ await page.screenshot(path=output_path, full_page=True)
122
+
123
+ await browser.close()
@@ -0,0 +1,88 @@
1
+ """定时任务持久化存储模块"""
2
+
3
+ import json
4
+ import logging
5
+ from dataclasses import dataclass, asdict
6
+ from pathlib import Path
7
+ from typing import Any, Dict, Optional
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ # 任务数据存储路径
12
+ TASKS_FILE_PATH = Path("data/scheduled_tasks.json")
13
+
14
+
15
+ @dataclass
16
+ class ScheduledTask:
17
+ """定时任务数据模型"""
18
+
19
+ task_id: str
20
+ tool_name: str
21
+ tool_args: Dict[str, Any]
22
+ cron: str
23
+ target_id: Optional[int]
24
+ target_type: str
25
+ task_name: str
26
+ max_executions: Optional[int]
27
+ current_executions: int = 0
28
+ created_at: str = ""
29
+
30
+ def to_dict(self) -> Dict[str, Any]:
31
+ """转换为字典"""
32
+ return asdict(self)
33
+
34
+ @classmethod
35
+ def from_dict(cls, data: Dict[str, Any]) -> "ScheduledTask":
36
+ """从字典创建实例"""
37
+ return cls(**data)
38
+
39
+
40
+ class ScheduledTaskStorage:
41
+ """定时任务存储管理器"""
42
+
43
+ def __init__(self) -> None:
44
+ """初始化存储"""
45
+ self._load()
46
+
47
+ def _load(self) -> Dict[str, ScheduledTask]:
48
+ """从文件加载所有任务"""
49
+ if not TASKS_FILE_PATH.exists():
50
+ return {}
51
+
52
+ try:
53
+ with open(TASKS_FILE_PATH, "r", encoding="utf-8") as f:
54
+ data = json.load(f)
55
+ return {
56
+ task_id: ScheduledTask.from_dict(task_data)
57
+ for task_id, task_data in data.items()
58
+ }
59
+ except Exception as e:
60
+ logger.error(f"加载定时任务数据失败: {e}")
61
+ return {}
62
+
63
+ def save_all(self, tasks: Dict[str, Any]) -> None:
64
+ """保存所有任务到文件"""
65
+ try:
66
+ TASKS_FILE_PATH.parent.mkdir(parents=True, exist_ok=True)
67
+
68
+ # 确保保存的是基础类型字典
69
+ data_to_save = {}
70
+ for task_id, task_info in tasks.items():
71
+ if isinstance(task_info, ScheduledTask):
72
+ data_to_save[task_id] = task_info.to_dict()
73
+ elif isinstance(task_info, dict):
74
+ # 兼容 TaskScheduler 内部的 dict 格式
75
+ data_to_save[task_id] = task_info
76
+ else:
77
+ logger.warning(f"未知任务数据格式: {task_id}")
78
+
79
+ with open(TASKS_FILE_PATH, "w", encoding="utf-8") as f:
80
+ json.dump(data_to_save, f, ensure_ascii=False, indent=2)
81
+ logger.debug(f"已保存 {len(data_to_save)} 个定时任务")
82
+ except Exception as e:
83
+ logger.error(f"保存定时任务数据失败: {e}")
84
+
85
+ def load_tasks(self) -> Dict[str, Any]:
86
+ """读取所有任务(返回原始字典格式以适配现有代码)"""
87
+ tasks = self._load()
88
+ return {task_id: task.to_dict() for task_id, task in tasks.items()}
@@ -0,0 +1 @@
1
+ """服务层初始化"""
@@ -0,0 +1,206 @@
1
+ """AI 请求队列管理服务"""
2
+
3
+ import asyncio
4
+ import logging
5
+ import time
6
+ from typing import Any, Callable, Coroutine
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ class QueueManager:
12
+ """负责 AI 请求的队列管理和调度"""
13
+
14
+ def __init__(self, ai_request_interval: float = 1.0) -> None:
15
+ self.ai_request_interval = ai_request_interval
16
+
17
+ # AI 请求队列(四个队列)
18
+ self._superadmin_queue: asyncio.Queue[dict[str, Any]] = (
19
+ asyncio.Queue()
20
+ ) # 超级管理员私聊队列(最高优先级)
21
+ self._private_queue: asyncio.Queue[dict[str, Any]] = (
22
+ asyncio.Queue()
23
+ ) # 普通私聊队列(高优先级)
24
+ self._group_mention_queue: asyncio.Queue[dict[str, Any]] = (
25
+ asyncio.Queue()
26
+ ) # 群聊被@队列(中等优先级)
27
+ self._group_normal_queue: asyncio.Queue[dict[str, Any]] = (
28
+ asyncio.Queue()
29
+ ) # 群聊普通队列(最低优先级)
30
+
31
+ self._processor_task: asyncio.Task[None] | None = None
32
+ self._request_handler: (
33
+ Callable[[dict[str, Any]], Coroutine[Any, Any, None]] | None
34
+ ) = None
35
+
36
+ def start(
37
+ self, request_handler: Callable[[dict[str, Any]], Coroutine[Any, Any, None]]
38
+ ) -> None:
39
+ """启动队列处理任务
40
+
41
+ 参数:
42
+ request_handler: 处理单个请求的异步回调函数
43
+ """
44
+ self._request_handler = request_handler
45
+ if self._processor_task is None or self._processor_task.done():
46
+ self._processor_task = asyncio.create_task(self._process_queue_loop())
47
+ logger.info("[队列服务] 队列处理主循环已启动")
48
+
49
+ async def stop(self) -> None:
50
+ """停止队列处理任务"""
51
+ if self._processor_task and not self._processor_task.done():
52
+ logger.info("[队列服务] 正在停止队列处理任务...")
53
+ self._processor_task.cancel()
54
+ try:
55
+ await self._processor_task
56
+ except asyncio.CancelledError:
57
+ pass
58
+ self._processor_task = None
59
+ logger.info("[队列服务] 队列处理任务已停止")
60
+
61
+ async def add_superadmin_request(self, request: dict[str, Any]) -> None:
62
+ """添加超级管理员请求"""
63
+ await self._superadmin_queue.put(request)
64
+ logger.info(
65
+ f"[队列入队] 超级管理员私聊: 队列长度={self._superadmin_queue.qsize()}"
66
+ )
67
+
68
+ async def add_private_request(self, request: dict[str, Any]) -> None:
69
+ """添加普通私聊请求"""
70
+ await self._private_queue.put(request)
71
+ logger.info(f"[队列入队] 普通私聊: 队列长度={self._private_queue.qsize()}")
72
+
73
+ async def add_group_mention_request(self, request: dict[str, Any]) -> None:
74
+ """添加群聊被@请求"""
75
+ await self._group_mention_queue.put(request)
76
+ logger.info(f"[队列入队] 群聊被@: 队列长度={self._group_mention_queue.qsize()}")
77
+
78
+ async def add_group_normal_request(self, request: dict[str, Any]) -> None:
79
+ """添加群聊普通请求 (会自动裁剪)"""
80
+ self._trim_normal_queue()
81
+ await self._group_normal_queue.put(request)
82
+ logger.info(f"[队列入队] 群聊普通: 队列长度={self._group_normal_queue.qsize()}")
83
+
84
+ def _trim_normal_queue(self) -> None:
85
+ """如果群聊普通队列超过10个,仅保留最新的2个"""
86
+ queue_size = self._group_normal_queue.qsize()
87
+ if queue_size > 10:
88
+ logger.info(
89
+ f"[队列修剪] 群聊普通队列长度 {queue_size} 超过阈值(10),正在修剪..."
90
+ )
91
+ # 取出所有元素
92
+ all_requests: list[dict[str, Any]] = []
93
+ while not self._group_normal_queue.empty():
94
+ all_requests.append(self._group_normal_queue.get_nowait())
95
+ # 只保留最新的2个
96
+ latest_requests = all_requests[-2:]
97
+ # 放回队列
98
+ for req in latest_requests:
99
+ self._group_normal_queue.put_nowait(req)
100
+ logger.info(f"[队列修剪] 修剪完成,保留最新 {len(latest_requests)} 个请求")
101
+
102
+ async def _process_queue_loop(self) -> None:
103
+ """队列处理主循环"""
104
+ queues = [
105
+ self._superadmin_queue,
106
+ self._private_queue,
107
+ self._group_mention_queue,
108
+ self._group_normal_queue,
109
+ ]
110
+ queue_names = ["超级管理员私聊", "私聊", "群聊被@", "群聊普通"]
111
+
112
+ current_queue_idx = 0
113
+ current_queue_processed = 0
114
+ last_transfer_to_normal = False
115
+ transfer_count = 0
116
+
117
+ try:
118
+ while True:
119
+ try:
120
+ current_queue = queues[current_queue_idx]
121
+
122
+ if current_queue.empty():
123
+ all_empty = all(q.empty() for q in queues)
124
+ if all_empty:
125
+ await asyncio.sleep(0.2)
126
+ continue
127
+
128
+ current_queue_idx = (current_queue_idx + 1) % 4
129
+ current_queue_processed = 0
130
+ transfer_count += 1
131
+ continue
132
+
133
+ request = await current_queue.get()
134
+ request_type = request.get("type", "unknown")
135
+
136
+ logger.info(
137
+ f"[队列处理] 正在处理 {queue_names[current_queue_idx]} 请求: {request_type} "
138
+ f"(剩余={current_queue.qsize()})"
139
+ )
140
+
141
+ try:
142
+ start_time = time.perf_counter()
143
+ if self._request_handler:
144
+ await self._request_handler(request)
145
+ duration = time.perf_counter() - start_time
146
+ logger.info(
147
+ f"[队列处理] {queue_names[current_queue_idx]} 请求处理完成, 耗时={duration:.2f}s"
148
+ )
149
+ except Exception as e:
150
+ logger.exception(f"[队列处理] 处理请求失败: {e}")
151
+ finally:
152
+ current_queue.task_done()
153
+
154
+ current_queue_processed += 1
155
+
156
+ # 调度逻辑:每个高优先级队列处理2个后切换
157
+ if current_queue_processed >= 2:
158
+ next_queue_idx = (current_queue_idx + 1) % 4
159
+ logger.info(
160
+ f"QueueManager: {queue_names[current_queue_idx]}队列已处理2条,"
161
+ f"转移到{queue_names[next_queue_idx]}队列"
162
+ )
163
+
164
+ if next_queue_idx == 3:
165
+ last_transfer_to_normal = True
166
+ else:
167
+ last_transfer_to_normal = False
168
+
169
+ current_queue_idx = next_queue_idx
170
+ current_queue_processed = 0
171
+ transfer_count += 1
172
+
173
+ # 防饿死逻辑:强制处理普通队列
174
+ if (
175
+ transfer_count > 0
176
+ and transfer_count % 2 == 0
177
+ and not last_transfer_to_normal
178
+ ):
179
+ if not self._group_normal_queue.empty():
180
+ normal_request = await self._group_normal_queue.get()
181
+ normal_type = normal_request.get("type", "unknown")
182
+ logger.info(
183
+ f"QueueManager: 强制处理群聊普通请求: {normal_type}"
184
+ )
185
+ try:
186
+ if self._request_handler:
187
+ await self._request_handler(normal_request)
188
+ except Exception as e:
189
+ logger.exception(
190
+ f"QueueManager: 处理群聊普通请求失败: {e}"
191
+ )
192
+ finally:
193
+ self._group_normal_queue.task_done()
194
+ transfer_count = 0
195
+
196
+ await asyncio.sleep(self.ai_request_interval)
197
+
198
+ except asyncio.CancelledError:
199
+ raise
200
+ except Exception as e:
201
+ logger.exception(f"QueueManager: 队列循环异常: {e}")
202
+ await asyncio.sleep(1.0)
203
+ except asyncio.CancelledError:
204
+ logger.info("QueueManager: 任务被取消")
205
+ finally:
206
+ logger.info("QueueManager: 任务已退出")
@@ -0,0 +1,53 @@
1
+ # 技能目录 (Skills Directory)
2
+
3
+ 技能目录,包含基础工具(tools)和工具集合(agents)。
4
+
5
+ ## 目录结构
6
+
7
+ ```
8
+ skills/
9
+ ├── tools/ # 基础小工具,直接暴露给 AI 调用
10
+ │ ├── __init__.py
11
+ │ ├── send_message/
12
+ │ ├── send_private_message/
13
+ │ └── ...
14
+
15
+ └── agents/ # 工具集合,AI Agent 封装
16
+ ├── __init__.py
17
+ ├── code_analysis_agent/
18
+ ├── web_agent/
19
+ ├── social_agent/
20
+ ├── entertainment_agent/
21
+ └── info_agent/
22
+ ```
23
+
24
+ ## Tools vs Agents
25
+
26
+ ### Tools(工具)
27
+ - 单一功能,直接暴露给主 AI
28
+ - 使用 OpenAI function calling 格式
29
+ - 示例:`send_message`, `get_current_time`, `analyze_multimodal`
30
+
31
+ ### Agents(代理)
32
+ - 工具集合,内部可调用多个子工具
33
+ - 使用 OpenAI function calling 格式暴露
34
+ - 参数统一为 `prompt`,由 Agent 内部解析
35
+ - 支持自动发现子工具并注册
36
+ - 适用于复杂场景和领域特定任务
37
+
38
+ ## 添加新工具
39
+
40
+ ### 添加 Tools
41
+ 1. 在 `skills/tools/` 下创建新目录
42
+ 2. 添加 `config.json`(工具定义)
43
+ 3. 添加 `handler.py`(执行逻辑)
44
+ 4. 自动被 `ToolRegistry` 发现和注册
45
+
46
+ ### 添加 Agents
47
+ 1. 在 `skills/agents/` 下创建新目录
48
+ 2. 添加 `intro.md`(给主 AI 看的能力说明)
49
+ 3. 添加 `prompt.md`(Agent 系统提示词)
50
+ 4. 添加 `config.json`(Agent 定义)
51
+ 5. 添加 `handler.py`(Agent 执行逻辑)
52
+ 6. 在 `tools/` 子目录中添加子工具
53
+ 7. 自动被 `AgentRegistry` 发现和注册
@@ -0,0 +1,10 @@
1
+ """
2
+ Skills Package
3
+
4
+ 包含基础工具(tools)和 AI 代理(agents)的技能目录。
5
+ """
6
+
7
+ from .tools import ToolRegistry
8
+ from .agents import AgentRegistry
9
+
10
+ __all__ = ["ToolRegistry", "AgentRegistry"]
@@ -0,0 +1,144 @@
1
+ # Agent 目录 (Agents Directory)
2
+
3
+ AI Agent 目录,每个 Agent 是一个工具集合。
4
+
5
+ ## Agent 结构
6
+
7
+ 每个 Agent 是一个目录,包含:
8
+
9
+ ```
10
+ agent_name/
11
+ ├── intro.md # 给主 AI 看的能力说明
12
+ ├── prompt.md # Agent 系统提示词(从文件加载)
13
+ ├── config.json # Agent 定义(OpenAI function calling 格式)
14
+ ├── handler.py # Agent 执行逻辑
15
+ └── tools/ # Agent 专属子工具目录
16
+ ├── tool1/
17
+ ├── tool2/
18
+ └── __init__.py
19
+ ```
20
+
21
+ ## 模型配置
22
+
23
+ Agent 使用独立的模型配置,通过环境变量设置:
24
+
25
+ ```env
26
+ # Agent 模型配置 (用于执行 agents)
27
+ AGENT_MODEL_API_URL= # API 地址
28
+ AGENT_MODEL_API_KEY= # API 密钥
29
+ AGENT_MODEL_NAME= # 模型名称
30
+ AGENT_MODEL_MAX_TOKENS=4096 # 最大 token 数
31
+ AGENT_MODEL_THINKING_ENABLED=false # 是否启用 thinking(思维链)
32
+ AGENT_MODEL_THINKING_BUDGET_TOKENS=0 # thinking budget tokens
33
+ ```
34
+
35
+ ### 配置说明
36
+
37
+ | 环境变量 | 说明 | 默认值 |
38
+ |---------|------|-------|
39
+ | `AGENT_MODEL_API_URL` | Agent 模型 API 地址 | 无(必填) |
40
+ | `AGENT_MODEL_API_KEY` | Agent 模型 API 密钥 | 无(必填) |
41
+ | `AGENT_MODEL_NAME` | Agent 模型名称 | 无(必填) |
42
+ | `AGENT_MODEL_MAX_TOKENS` | 单次响应最大 token 数 | 4096 |
43
+ | `AGENT_MODEL_THINKING_ENABLED` | 是否启用思维链 | false |
44
+ | `AGENT_MODEL_THINKING_BUDGET_TOKENS` | 思维链预算 token 数量 | 0 |
45
+
46
+ ## 核心文件说明
47
+
48
+ ### intro.md
49
+ 给主 AI 参考的 Agent 能力说明,包括:
50
+ - Agent 的功能概述
51
+ - 支持的能力列表
52
+ - 使用方式和参数说明
53
+ - 返回格式
54
+
55
+ **这是主 AI 看到的核心描述**,系统会自动将 `intro.md` 的内容作为 Agent 的 description 传递给 AI。
56
+
57
+ 示例:
58
+ ```markdown
59
+ # XXX 助手
60
+
61
+ ## 能力
62
+ - 功能1
63
+ - 功能2
64
+
65
+ ## 使用方式
66
+ - 提供 xxx 参数
67
+ ```
68
+
69
+ ### prompt.md
70
+ Agent 内部的系统提示词,**从文件加载**,指导 Agent 如何选择和使用工具。
71
+
72
+ 文件位置:`skills/agents/{agent_name}/prompt.md`
73
+
74
+ 示例:
75
+ ```markdown
76
+ 你是一个 XXX 助手...
77
+
78
+ ## 你的任务
79
+ 1. 理解用户需求
80
+ 2. 选择合适的工具
81
+ 3. 返回结果
82
+ ```
83
+
84
+ ### config.json
85
+ Agent 的 OpenAI function calling 定义。
86
+
87
+ **注意**:description 字段可选,不建议手动填写。系统会自动从 `intro.md` 读取内容作为 description 传递给 AI。
88
+
89
+ 现有配置中的 description 仅用于向后兼容,未来将逐步移除。
90
+
91
+ ```json
92
+ {
93
+ "type": "function",
94
+ "function": {
95
+ "name": "agent_name",
96
+ "description": "Agent 描述(无需填写,将自动从 intro.md 覆盖)",
97
+ "parameters": {
98
+ "type": "object",
99
+ "properties": {
100
+ "prompt": {
101
+ "type": "string",
102
+ "description": "用户需求描述"
103
+ }
104
+ },
105
+ "required": ["prompt"]
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ ### handler.py
112
+ Agent 的执行逻辑,负责:
113
+ 1. 从 `prompt.md` 加载系统提示词
114
+ 2. 使用 `AGENT_MODEL_*` 配置调用模型
115
+ 3. 通过 `AgentToolRegistry` 调用子工具
116
+ 4. 返回结果给主 AI
117
+
118
+ ## 添加新 Agent
119
+
120
+ ### 1. 创建 Agent 目录
121
+ ```bash
122
+ mkdir -p skills/agents/my_agent/tools
123
+ ```
124
+
125
+ ### 2. 创建必要文件
126
+ - `intro.md` - Agent 能力说明
127
+ - `prompt.md` - Agent 系统提示词
128
+ - `config.json` - Agent 定义
129
+ - `handler.py` - Agent 执行逻辑
130
+
131
+ ### 3. 添加子工具
132
+ 将工具目录移动到 `tools/` 下:
133
+ ```bash
134
+ mv skills/tools/my_tool skills/agents/my_agent/tools/
135
+ ```
136
+ 或添加工具。
137
+
138
+ ### 4. 自动发现
139
+ 重启后 `AgentRegistry` 会自动发现并加载新 Agent。
140
+
141
+ ## 自动发现
142
+
143
+ `AgentRegistry` 会自动发现 `skills/agents/` 下的所有 Agent 并加载。
144
+ 每个 Agent 内部的子工具由 `AgentToolRegistry` 自动发现。