AstrBot 3.5.6__py3-none-any.whl → 4.7.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 (288) hide show
  1. astrbot/api/__init__.py +16 -4
  2. astrbot/api/all.py +2 -1
  3. astrbot/api/event/__init__.py +5 -6
  4. astrbot/api/event/filter/__init__.py +37 -34
  5. astrbot/api/platform/__init__.py +7 -8
  6. astrbot/api/provider/__init__.py +8 -7
  7. astrbot/api/star/__init__.py +3 -4
  8. astrbot/api/util/__init__.py +2 -2
  9. astrbot/cli/__init__.py +1 -0
  10. astrbot/cli/__main__.py +18 -197
  11. astrbot/cli/commands/__init__.py +6 -0
  12. astrbot/cli/commands/cmd_conf.py +209 -0
  13. astrbot/cli/commands/cmd_init.py +56 -0
  14. astrbot/cli/commands/cmd_plug.py +245 -0
  15. astrbot/cli/commands/cmd_run.py +62 -0
  16. astrbot/cli/utils/__init__.py +18 -0
  17. astrbot/cli/utils/basic.py +76 -0
  18. astrbot/cli/utils/plugin.py +246 -0
  19. astrbot/cli/utils/version_comparator.py +90 -0
  20. astrbot/core/__init__.py +17 -19
  21. astrbot/core/agent/agent.py +14 -0
  22. astrbot/core/agent/handoff.py +38 -0
  23. astrbot/core/agent/hooks.py +30 -0
  24. astrbot/core/agent/mcp_client.py +385 -0
  25. astrbot/core/agent/message.py +175 -0
  26. astrbot/core/agent/response.py +14 -0
  27. astrbot/core/agent/run_context.py +22 -0
  28. astrbot/core/agent/runners/__init__.py +3 -0
  29. astrbot/core/agent/runners/base.py +65 -0
  30. astrbot/core/agent/runners/coze/coze_agent_runner.py +367 -0
  31. astrbot/core/agent/runners/coze/coze_api_client.py +324 -0
  32. astrbot/core/agent/runners/dashscope/dashscope_agent_runner.py +403 -0
  33. astrbot/core/agent/runners/dify/dify_agent_runner.py +336 -0
  34. astrbot/core/agent/runners/dify/dify_api_client.py +195 -0
  35. astrbot/core/agent/runners/tool_loop_agent_runner.py +400 -0
  36. astrbot/core/agent/tool.py +285 -0
  37. astrbot/core/agent/tool_executor.py +17 -0
  38. astrbot/core/astr_agent_context.py +19 -0
  39. astrbot/core/astr_agent_hooks.py +36 -0
  40. astrbot/core/astr_agent_run_util.py +80 -0
  41. astrbot/core/astr_agent_tool_exec.py +246 -0
  42. astrbot/core/astrbot_config_mgr.py +275 -0
  43. astrbot/core/config/__init__.py +2 -2
  44. astrbot/core/config/astrbot_config.py +60 -20
  45. astrbot/core/config/default.py +1972 -453
  46. astrbot/core/config/i18n_utils.py +110 -0
  47. astrbot/core/conversation_mgr.py +285 -75
  48. astrbot/core/core_lifecycle.py +167 -62
  49. astrbot/core/db/__init__.py +305 -102
  50. astrbot/core/db/migration/helper.py +69 -0
  51. astrbot/core/db/migration/migra_3_to_4.py +357 -0
  52. astrbot/core/db/migration/migra_45_to_46.py +44 -0
  53. astrbot/core/db/migration/migra_webchat_session.py +131 -0
  54. astrbot/core/db/migration/shared_preferences_v3.py +48 -0
  55. astrbot/core/db/migration/sqlite_v3.py +497 -0
  56. astrbot/core/db/po.py +259 -55
  57. astrbot/core/db/sqlite.py +773 -528
  58. astrbot/core/db/vec_db/base.py +73 -0
  59. astrbot/core/db/vec_db/faiss_impl/__init__.py +3 -0
  60. astrbot/core/db/vec_db/faiss_impl/document_storage.py +392 -0
  61. astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +93 -0
  62. astrbot/core/db/vec_db/faiss_impl/sqlite_init.sql +17 -0
  63. astrbot/core/db/vec_db/faiss_impl/vec_db.py +204 -0
  64. astrbot/core/event_bus.py +26 -22
  65. astrbot/core/exceptions.py +9 -0
  66. astrbot/core/file_token_service.py +98 -0
  67. astrbot/core/initial_loader.py +19 -10
  68. astrbot/core/knowledge_base/chunking/__init__.py +9 -0
  69. astrbot/core/knowledge_base/chunking/base.py +25 -0
  70. astrbot/core/knowledge_base/chunking/fixed_size.py +59 -0
  71. astrbot/core/knowledge_base/chunking/recursive.py +161 -0
  72. astrbot/core/knowledge_base/kb_db_sqlite.py +301 -0
  73. astrbot/core/knowledge_base/kb_helper.py +642 -0
  74. astrbot/core/knowledge_base/kb_mgr.py +330 -0
  75. astrbot/core/knowledge_base/models.py +120 -0
  76. astrbot/core/knowledge_base/parsers/__init__.py +13 -0
  77. astrbot/core/knowledge_base/parsers/base.py +51 -0
  78. astrbot/core/knowledge_base/parsers/markitdown_parser.py +26 -0
  79. astrbot/core/knowledge_base/parsers/pdf_parser.py +101 -0
  80. astrbot/core/knowledge_base/parsers/text_parser.py +42 -0
  81. astrbot/core/knowledge_base/parsers/url_parser.py +103 -0
  82. astrbot/core/knowledge_base/parsers/util.py +13 -0
  83. astrbot/core/knowledge_base/prompts.py +65 -0
  84. astrbot/core/knowledge_base/retrieval/__init__.py +14 -0
  85. astrbot/core/knowledge_base/retrieval/hit_stopwords.txt +767 -0
  86. astrbot/core/knowledge_base/retrieval/manager.py +276 -0
  87. astrbot/core/knowledge_base/retrieval/rank_fusion.py +142 -0
  88. astrbot/core/knowledge_base/retrieval/sparse_retriever.py +136 -0
  89. astrbot/core/log.py +21 -15
  90. astrbot/core/message/components.py +413 -287
  91. astrbot/core/message/message_event_result.py +35 -24
  92. astrbot/core/persona_mgr.py +192 -0
  93. astrbot/core/pipeline/__init__.py +14 -14
  94. astrbot/core/pipeline/content_safety_check/stage.py +13 -9
  95. astrbot/core/pipeline/content_safety_check/strategies/__init__.py +1 -2
  96. astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +13 -14
  97. astrbot/core/pipeline/content_safety_check/strategies/keywords.py +2 -1
  98. astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
  99. astrbot/core/pipeline/context.py +7 -1
  100. astrbot/core/pipeline/context_utils.py +107 -0
  101. astrbot/core/pipeline/preprocess_stage/stage.py +63 -36
  102. astrbot/core/pipeline/process_stage/method/agent_request.py +48 -0
  103. astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py +464 -0
  104. astrbot/core/pipeline/process_stage/method/agent_sub_stages/third_party.py +202 -0
  105. astrbot/core/pipeline/process_stage/method/star_request.py +26 -32
  106. astrbot/core/pipeline/process_stage/stage.py +21 -15
  107. astrbot/core/pipeline/process_stage/utils.py +125 -0
  108. astrbot/core/pipeline/rate_limit_check/stage.py +34 -36
  109. astrbot/core/pipeline/respond/stage.py +142 -101
  110. astrbot/core/pipeline/result_decorate/stage.py +124 -57
  111. astrbot/core/pipeline/scheduler.py +21 -16
  112. astrbot/core/pipeline/session_status_check/stage.py +37 -0
  113. astrbot/core/pipeline/stage.py +11 -76
  114. astrbot/core/pipeline/waking_check/stage.py +69 -33
  115. astrbot/core/pipeline/whitelist_check/stage.py +10 -7
  116. astrbot/core/platform/__init__.py +6 -6
  117. astrbot/core/platform/astr_message_event.py +107 -129
  118. astrbot/core/platform/astrbot_message.py +32 -12
  119. astrbot/core/platform/manager.py +62 -18
  120. astrbot/core/platform/message_session.py +30 -0
  121. astrbot/core/platform/platform.py +16 -24
  122. astrbot/core/platform/platform_metadata.py +9 -4
  123. astrbot/core/platform/register.py +12 -7
  124. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +136 -60
  125. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +126 -46
  126. astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +63 -31
  127. astrbot/core/platform/sources/dingtalk/dingtalk_event.py +30 -26
  128. astrbot/core/platform/sources/discord/client.py +129 -0
  129. astrbot/core/platform/sources/discord/components.py +139 -0
  130. astrbot/core/platform/sources/discord/discord_platform_adapter.py +473 -0
  131. astrbot/core/platform/sources/discord/discord_platform_event.py +313 -0
  132. astrbot/core/platform/sources/lark/lark_adapter.py +27 -18
  133. astrbot/core/platform/sources/lark/lark_event.py +39 -13
  134. astrbot/core/platform/sources/misskey/misskey_adapter.py +770 -0
  135. astrbot/core/platform/sources/misskey/misskey_api.py +964 -0
  136. astrbot/core/platform/sources/misskey/misskey_event.py +163 -0
  137. astrbot/core/platform/sources/misskey/misskey_utils.py +550 -0
  138. astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +149 -33
  139. astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +41 -26
  140. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +36 -17
  141. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_event.py +3 -1
  142. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +14 -8
  143. astrbot/core/platform/sources/satori/satori_adapter.py +792 -0
  144. astrbot/core/platform/sources/satori/satori_event.py +432 -0
  145. astrbot/core/platform/sources/slack/client.py +164 -0
  146. astrbot/core/platform/sources/slack/slack_adapter.py +416 -0
  147. astrbot/core/platform/sources/slack/slack_event.py +253 -0
  148. astrbot/core/platform/sources/telegram/tg_adapter.py +100 -43
  149. astrbot/core/platform/sources/telegram/tg_event.py +136 -36
  150. astrbot/core/platform/sources/webchat/webchat_adapter.py +72 -22
  151. astrbot/core/platform/sources/webchat/webchat_event.py +46 -22
  152. astrbot/core/platform/sources/webchat/webchat_queue_mgr.py +35 -0
  153. astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +926 -0
  154. astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +178 -0
  155. astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +159 -0
  156. astrbot/core/platform/sources/wecom/wecom_adapter.py +169 -27
  157. astrbot/core/platform/sources/wecom/wecom_event.py +162 -77
  158. astrbot/core/platform/sources/wecom/wecom_kf.py +279 -0
  159. astrbot/core/platform/sources/wecom/wecom_kf_message.py +196 -0
  160. astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +297 -0
  161. astrbot/core/platform/sources/wecom_ai_bot/__init__.py +15 -0
  162. astrbot/core/platform/sources/wecom_ai_bot/ierror.py +19 -0
  163. astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +472 -0
  164. astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +417 -0
  165. astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +152 -0
  166. astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +153 -0
  167. astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +168 -0
  168. astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +209 -0
  169. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +306 -0
  170. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +186 -0
  171. astrbot/core/platform_message_history_mgr.py +49 -0
  172. astrbot/core/provider/__init__.py +2 -3
  173. astrbot/core/provider/entites.py +8 -8
  174. astrbot/core/provider/entities.py +154 -98
  175. astrbot/core/provider/func_tool_manager.py +446 -458
  176. astrbot/core/provider/manager.py +345 -207
  177. astrbot/core/provider/provider.py +188 -73
  178. astrbot/core/provider/register.py +9 -7
  179. astrbot/core/provider/sources/anthropic_source.py +295 -115
  180. astrbot/core/provider/sources/azure_tts_source.py +224 -0
  181. astrbot/core/provider/sources/bailian_rerank_source.py +236 -0
  182. astrbot/core/provider/sources/dashscope_tts.py +138 -14
  183. astrbot/core/provider/sources/edge_tts_source.py +24 -19
  184. astrbot/core/provider/sources/fishaudio_tts_api_source.py +58 -13
  185. astrbot/core/provider/sources/gemini_embedding_source.py +61 -0
  186. astrbot/core/provider/sources/gemini_source.py +310 -132
  187. astrbot/core/provider/sources/gemini_tts_source.py +81 -0
  188. astrbot/core/provider/sources/groq_source.py +15 -0
  189. astrbot/core/provider/sources/gsv_selfhosted_source.py +151 -0
  190. astrbot/core/provider/sources/gsvi_tts_source.py +14 -7
  191. astrbot/core/provider/sources/minimax_tts_api_source.py +159 -0
  192. astrbot/core/provider/sources/openai_embedding_source.py +40 -0
  193. astrbot/core/provider/sources/openai_source.py +241 -145
  194. astrbot/core/provider/sources/openai_tts_api_source.py +18 -7
  195. astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
  196. astrbot/core/provider/sources/vllm_rerank_source.py +71 -0
  197. astrbot/core/provider/sources/volcengine_tts.py +115 -0
  198. astrbot/core/provider/sources/whisper_api_source.py +18 -13
  199. astrbot/core/provider/sources/whisper_selfhosted_source.py +19 -12
  200. astrbot/core/provider/sources/xinference_rerank_source.py +116 -0
  201. astrbot/core/provider/sources/xinference_stt_provider.py +197 -0
  202. astrbot/core/provider/sources/zhipu_source.py +6 -73
  203. astrbot/core/star/__init__.py +43 -11
  204. astrbot/core/star/config.py +17 -18
  205. astrbot/core/star/context.py +362 -138
  206. astrbot/core/star/filter/__init__.py +4 -3
  207. astrbot/core/star/filter/command.py +111 -35
  208. astrbot/core/star/filter/command_group.py +46 -34
  209. astrbot/core/star/filter/custom_filter.py +6 -5
  210. astrbot/core/star/filter/event_message_type.py +4 -2
  211. astrbot/core/star/filter/permission.py +4 -2
  212. astrbot/core/star/filter/platform_adapter_type.py +45 -12
  213. astrbot/core/star/filter/regex.py +4 -2
  214. astrbot/core/star/register/__init__.py +19 -15
  215. astrbot/core/star/register/star.py +41 -13
  216. astrbot/core/star/register/star_handler.py +236 -86
  217. astrbot/core/star/session_llm_manager.py +280 -0
  218. astrbot/core/star/session_plugin_manager.py +170 -0
  219. astrbot/core/star/star.py +36 -43
  220. astrbot/core/star/star_handler.py +47 -85
  221. astrbot/core/star/star_manager.py +442 -260
  222. astrbot/core/star/star_tools.py +167 -45
  223. astrbot/core/star/updator.py +17 -20
  224. astrbot/core/umop_config_router.py +106 -0
  225. astrbot/core/updator.py +38 -13
  226. astrbot/core/utils/astrbot_path.py +39 -0
  227. astrbot/core/utils/command_parser.py +1 -1
  228. astrbot/core/utils/io.py +119 -60
  229. astrbot/core/utils/log_pipe.py +1 -1
  230. astrbot/core/utils/metrics.py +11 -10
  231. astrbot/core/utils/migra_helper.py +73 -0
  232. astrbot/core/utils/path_util.py +63 -62
  233. astrbot/core/utils/pip_installer.py +37 -15
  234. astrbot/core/utils/session_lock.py +29 -0
  235. astrbot/core/utils/session_waiter.py +19 -20
  236. astrbot/core/utils/shared_preferences.py +174 -34
  237. astrbot/core/utils/t2i/__init__.py +4 -1
  238. astrbot/core/utils/t2i/local_strategy.py +386 -238
  239. astrbot/core/utils/t2i/network_strategy.py +109 -49
  240. astrbot/core/utils/t2i/renderer.py +29 -14
  241. astrbot/core/utils/t2i/template/astrbot_powershell.html +184 -0
  242. astrbot/core/utils/t2i/template_manager.py +111 -0
  243. astrbot/core/utils/tencent_record_helper.py +115 -1
  244. astrbot/core/utils/version_comparator.py +10 -13
  245. astrbot/core/zip_updator.py +112 -65
  246. astrbot/dashboard/routes/__init__.py +20 -13
  247. astrbot/dashboard/routes/auth.py +20 -9
  248. astrbot/dashboard/routes/chat.py +297 -141
  249. astrbot/dashboard/routes/config.py +652 -55
  250. astrbot/dashboard/routes/conversation.py +107 -37
  251. astrbot/dashboard/routes/file.py +26 -0
  252. astrbot/dashboard/routes/knowledge_base.py +1244 -0
  253. astrbot/dashboard/routes/log.py +27 -2
  254. astrbot/dashboard/routes/persona.py +202 -0
  255. astrbot/dashboard/routes/plugin.py +197 -139
  256. astrbot/dashboard/routes/route.py +27 -7
  257. astrbot/dashboard/routes/session_management.py +354 -0
  258. astrbot/dashboard/routes/stat.py +85 -18
  259. astrbot/dashboard/routes/static_file.py +5 -2
  260. astrbot/dashboard/routes/t2i.py +233 -0
  261. astrbot/dashboard/routes/tools.py +184 -120
  262. astrbot/dashboard/routes/update.py +59 -36
  263. astrbot/dashboard/server.py +96 -36
  264. astrbot/dashboard/utils.py +165 -0
  265. astrbot-4.7.0.dist-info/METADATA +294 -0
  266. astrbot-4.7.0.dist-info/RECORD +274 -0
  267. {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/WHEEL +1 -1
  268. astrbot/core/db/plugin/sqlite_impl.py +0 -112
  269. astrbot/core/db/sqlite_init.sql +0 -50
  270. astrbot/core/pipeline/platform_compatibility/stage.py +0 -56
  271. astrbot/core/pipeline/process_stage/method/llm_request.py +0 -606
  272. astrbot/core/platform/sources/gewechat/client.py +0 -806
  273. astrbot/core/platform/sources/gewechat/downloader.py +0 -55
  274. astrbot/core/platform/sources/gewechat/gewechat_event.py +0 -255
  275. astrbot/core/platform/sources/gewechat/gewechat_platform_adapter.py +0 -103
  276. astrbot/core/platform/sources/gewechat/xml_data_parser.py +0 -110
  277. astrbot/core/provider/sources/dashscope_source.py +0 -203
  278. astrbot/core/provider/sources/dify_source.py +0 -281
  279. astrbot/core/provider/sources/llmtuner_source.py +0 -132
  280. astrbot/core/rag/embedding/openai_source.py +0 -20
  281. astrbot/core/rag/knowledge_db_mgr.py +0 -94
  282. astrbot/core/rag/store/__init__.py +0 -9
  283. astrbot/core/rag/store/chroma_db.py +0 -42
  284. astrbot/core/utils/dify_api_client.py +0 -152
  285. astrbot-3.5.6.dist-info/METADATA +0 -249
  286. astrbot-3.5.6.dist-info/RECORD +0 -158
  287. {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/entry_points.txt +0 -0
  288. {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,16 +1,17 @@
1
- """
2
- 本地 Agent 模式的 AstrBot 插件调用 Stage
3
- """
1
+ """本地 Agent 模式的 AstrBot 插件调用 Stage"""
2
+
3
+ import traceback
4
+ from collections.abc import AsyncGenerator
5
+ from typing import Any
4
6
 
5
- from ...context import PipelineContext
6
- from ..stage import Stage
7
- from typing import Dict, Any, List, AsyncGenerator, Union
8
- from astrbot.core.platform.astr_message_event import AstrMessageEvent
9
- from astrbot.core.message.message_event_result import MessageEventResult
10
7
  from astrbot.core import logger
11
- from astrbot.core.star.star_handler import StarHandlerMetadata
8
+ from astrbot.core.message.message_event_result import MessageEventResult
9
+ from astrbot.core.platform.astr_message_event import AstrMessageEvent
12
10
  from astrbot.core.star.star import star_map
13
- import traceback
11
+ from astrbot.core.star.star_handler import StarHandlerMetadata
12
+
13
+ from ...context import PipelineContext, call_handler
14
+ from ..stage import Stage
14
15
 
15
16
 
16
17
  class StarRequestSubStage(Stage):
@@ -21,36 +22,29 @@ class StarRequestSubStage(Stage):
21
22
  self.ctx = ctx
22
23
 
23
24
  async def process(
24
- self, event: AstrMessageEvent
25
- ) -> Union[None, AsyncGenerator[None, None]]:
26
- activated_handlers: List[StarHandlerMetadata] = event.get_extra(
27
- "activated_handlers"
25
+ self,
26
+ event: AstrMessageEvent,
27
+ ) -> AsyncGenerator[None, None]:
28
+ activated_handlers: list[StarHandlerMetadata] = event.get_extra(
29
+ "activated_handlers",
28
30
  )
29
- handlers_parsed_params: Dict[str, Dict[str, Any]] = event.get_extra(
30
- "handlers_parsed_params"
31
+ handlers_parsed_params: dict[str, dict[str, Any]] = event.get_extra(
32
+ "handlers_parsed_params",
31
33
  )
32
34
  if not handlers_parsed_params:
33
35
  handlers_parsed_params = {}
34
36
 
35
37
  for handler in activated_handlers:
36
- # 检查处理器是否在当前平台兼容
37
- if (
38
- hasattr(handler, "platform_compatible")
39
- and handler.platform_compatible is False
40
- ):
41
- logger.debug(
42
- f"处理器 {handler.handler_name} 在当前平台不兼容,跳过执行"
38
+ params = handlers_parsed_params.get(handler.handler_full_name, {})
39
+ md = star_map.get(handler.handler_module_path)
40
+ if not md:
41
+ logger.warning(
42
+ f"Cannot find plugin for given handler module path: {handler.handler_module_path}",
43
43
  )
44
44
  continue
45
-
46
- params = handlers_parsed_params.get(handler.handler_full_name, {})
45
+ logger.debug(f"plugin -> {md.name} - {handler.handler_name}")
47
46
  try:
48
- if handler.handler_module_path not in star_map:
49
- continue
50
- logger.debug(
51
- f"plugin -> {star_map.get(handler.handler_module_path).name} - {handler.handler_name}"
52
- )
53
- wrapper = self._call_handler(self.ctx, event, handler.handler, **params)
47
+ wrapper = call_handler(event, handler.handler, **params)
54
48
  async for ret in wrapper:
55
49
  yield ret
56
50
  event.clear_result() # 清除上一个 handler 的结果
@@ -59,7 +53,7 @@ class StarRequestSubStage(Stage):
59
53
  logger.error(f"Star {handler.handler_full_name} handle error: {e}")
60
54
 
61
55
  if event.is_at_or_wake_command:
62
- ret = f":(\n\n在调用插件 {star_map.get(handler.handler_module_path).name} 的处理函数 {handler.handler_name} 时出现异常:{e}"
56
+ ret = f":(\n\n在调用插件 {md.name} 的处理函数 {handler.handler_name} 时出现异常:{e}"
63
57
  event.set_result(MessageEventResult().message(ret))
64
58
  yield
65
59
  event.clear_result()
@@ -1,12 +1,14 @@
1
- from typing import List, Union, AsyncGenerator
2
- from ..stage import Stage, register_stage
3
- from ..context import PipelineContext
4
- from .method.llm_request import LLMRequestSubStage
5
- from .method.star_request import StarRequestSubStage
1
+ from collections.abc import AsyncGenerator
2
+
3
+ from astrbot.core import logger
6
4
  from astrbot.core.platform.astr_message_event import AstrMessageEvent
7
- from astrbot.core.star.star_handler import StarHandlerMetadata
8
5
  from astrbot.core.provider.entities import ProviderRequest
9
- from astrbot.core import logger
6
+ from astrbot.core.star.star_handler import StarHandlerMetadata
7
+
8
+ from ..context import PipelineContext
9
+ from ..stage import Stage, register_stage
10
+ from .method.agent_request import AgentRequestSubStage
11
+ from .method.star_request import StarRequestSubStage
10
12
 
11
13
 
12
14
  @register_stage
@@ -15,18 +17,22 @@ class ProcessStage(Stage):
15
17
  self.ctx = ctx
16
18
  self.config = ctx.astrbot_config
17
19
  self.plugin_manager = ctx.plugin_manager
18
- self.llm_request_sub_stage = LLMRequestSubStage()
19
- await self.llm_request_sub_stage.initialize(ctx)
20
20
 
21
+ # initialize agent sub stage
22
+ self.agent_sub_stage = AgentRequestSubStage()
23
+ await self.agent_sub_stage.initialize(ctx)
24
+
25
+ # initialize star request sub stage
21
26
  self.star_request_sub_stage = StarRequestSubStage()
22
27
  await self.star_request_sub_stage.initialize(ctx)
23
28
 
24
29
  async def process(
25
- self, event: AstrMessageEvent
26
- ) -> Union[None, AsyncGenerator[None, None]]:
30
+ self,
31
+ event: AstrMessageEvent,
32
+ ) -> None | AsyncGenerator[None, None]:
27
33
  """处理事件"""
28
- activated_handlers: List[StarHandlerMetadata] = event.get_extra(
29
- "activated_handlers"
34
+ activated_handlers: list[StarHandlerMetadata] = event.get_extra(
35
+ "activated_handlers",
30
36
  )
31
37
  # 有插件 Handler 被激活
32
38
  if activated_handlers:
@@ -36,7 +42,7 @@ class ProcessStage(Stage):
36
42
  # Handler 的 LLM 请求
37
43
  event.set_extra("provider_request", resp)
38
44
  _t = False
39
- async for _ in self.llm_request_sub_stage.process(event):
45
+ async for _ in self.agent_sub_stage.process(event):
40
46
  _t = True
41
47
  yield
42
48
  if not _t:
@@ -64,5 +70,5 @@ class ProcessStage(Stage):
64
70
  logger.info("未找到可用的 LLM 提供商,请先前往配置服务提供商。")
65
71
  return
66
72
 
67
- async for _ in self.llm_request_sub_stage.process(event):
73
+ async for _ in self.agent_sub_stage.process(event):
68
74
  yield
@@ -0,0 +1,125 @@
1
+ from pydantic import Field
2
+ from pydantic.dataclasses import dataclass
3
+
4
+ from astrbot.api import logger, sp
5
+ from astrbot.core.agent.run_context import ContextWrapper
6
+ from astrbot.core.agent.tool import FunctionTool, ToolExecResult
7
+ from astrbot.core.astr_agent_context import AstrAgentContext
8
+ from astrbot.core.star.context import Context
9
+
10
+
11
+ @dataclass
12
+ class KnowledgeBaseQueryTool(FunctionTool[AstrAgentContext]):
13
+ name: str = "astr_kb_search"
14
+ description: str = (
15
+ "Query the knowledge base for facts or relevant context. "
16
+ "Use this tool when the user's question requires factual information, "
17
+ "definitions, background knowledge, or previously indexed content. "
18
+ "Only send short keywords or a concise question as the query."
19
+ )
20
+ parameters: dict = Field(
21
+ default_factory=lambda: {
22
+ "type": "object",
23
+ "properties": {
24
+ "query": {
25
+ "type": "string",
26
+ "description": "A concise keyword query for the knowledge base.",
27
+ },
28
+ },
29
+ "required": ["query"],
30
+ }
31
+ )
32
+
33
+ async def call(
34
+ self, context: ContextWrapper[AstrAgentContext], **kwargs
35
+ ) -> ToolExecResult:
36
+ query = kwargs.get("query", "")
37
+ if not query:
38
+ return "error: Query parameter is empty."
39
+ result = await retrieve_knowledge_base(
40
+ query=kwargs.get("query", ""),
41
+ umo=context.context.event.unified_msg_origin,
42
+ context=context.context.context,
43
+ )
44
+ if not result:
45
+ return "No relevant knowledge found."
46
+ return result
47
+
48
+
49
+ async def retrieve_knowledge_base(
50
+ query: str,
51
+ umo: str,
52
+ context: Context,
53
+ ) -> str | None:
54
+ """Inject knowledge base context into the provider request
55
+
56
+ Args:
57
+ umo: Unique message object (session ID)
58
+ p_ctx: Pipeline context
59
+ """
60
+ kb_mgr = context.kb_manager
61
+ config = context.get_config(umo=umo)
62
+
63
+ # 1. 优先读取会话级配置
64
+ session_config = await sp.session_get(umo, "kb_config", default={})
65
+
66
+ if session_config and "kb_ids" in session_config:
67
+ # 会话级配置
68
+ kb_ids = session_config.get("kb_ids", [])
69
+
70
+ # 如果配置为空列表,明确表示不使用知识库
71
+ if not kb_ids:
72
+ logger.info(f"[知识库] 会话 {umo} 已被配置为不使用知识库")
73
+ return
74
+
75
+ top_k = session_config.get("top_k", 5)
76
+
77
+ # 将 kb_ids 转换为 kb_names
78
+ kb_names = []
79
+ invalid_kb_ids = []
80
+ for kb_id in kb_ids:
81
+ kb_helper = await kb_mgr.get_kb(kb_id)
82
+ if kb_helper:
83
+ kb_names.append(kb_helper.kb.kb_name)
84
+ else:
85
+ logger.warning(f"[知识库] 知识库不存在或未加载: {kb_id}")
86
+ invalid_kb_ids.append(kb_id)
87
+
88
+ if invalid_kb_ids:
89
+ logger.warning(
90
+ f"[知识库] 会话 {umo} 配置的以下知识库无效: {invalid_kb_ids}",
91
+ )
92
+
93
+ if not kb_names:
94
+ return
95
+
96
+ logger.debug(f"[知识库] 使用会话级配置,知识库数量: {len(kb_names)}")
97
+ else:
98
+ kb_names = config.get("kb_names", [])
99
+ top_k = config.get("kb_final_top_k", 5)
100
+ logger.debug(f"[知识库] 使用全局配置,知识库数量: {len(kb_names)}")
101
+
102
+ top_k_fusion = config.get("kb_fusion_top_k", 20)
103
+
104
+ if not kb_names:
105
+ return
106
+
107
+ logger.debug(f"[知识库] 开始检索知识库,数量: {len(kb_names)}, top_k={top_k}")
108
+ kb_context = await kb_mgr.retrieve(
109
+ query=query,
110
+ kb_names=kb_names,
111
+ top_k_fusion=top_k_fusion,
112
+ top_m_final=top_k,
113
+ )
114
+
115
+ if not kb_context:
116
+ return
117
+
118
+ formatted = kb_context.get("context_text", "")
119
+ if formatted:
120
+ results = kb_context.get("results", [])
121
+ logger.debug(f"[知识库] 为会话 {umo} 注入了 {len(results)} 条相关知识块")
122
+ return formatted
123
+
124
+
125
+ KNOWLEDGE_BASE_QUERY_TOOL = KnowledgeBaseQueryTool()
@@ -1,18 +1,19 @@
1
1
  import asyncio
2
- from datetime import datetime, timedelta
3
2
  from collections import defaultdict, deque
4
- from typing import DefaultDict, Deque, Union, AsyncGenerator
5
- from ..stage import Stage, register_stage
6
- from ..context import PipelineContext
7
- from astrbot.core.platform.astr_message_event import AstrMessageEvent
3
+ from collections.abc import AsyncGenerator
4
+ from datetime import datetime, timedelta
5
+
8
6
  from astrbot.core import logger
9
7
  from astrbot.core.config.astrbot_config import RateLimitStrategy
8
+ from astrbot.core.platform.astr_message_event import AstrMessageEvent
9
+
10
+ from ..context import PipelineContext
11
+ from ..stage import Stage, register_stage
10
12
 
11
13
 
12
14
  @register_stage
13
15
  class RateLimitStage(Stage):
14
- """
15
- 检查是否需要限制消息发送的限流器。
16
+ """检查是否需要限制消息发送的限流器。
16
17
 
17
18
  使用 Fixed Window 算法。
18
19
  如果触发限流,将 stall 流水线,直到下一个时间窗口来临时自动唤醒。
@@ -20,32 +21,30 @@ class RateLimitStage(Stage):
20
21
 
21
22
  def __init__(self):
22
23
  # 存储每个会话的请求时间队列
23
- self.event_timestamps: DefaultDict[str, Deque[datetime]] = defaultdict(deque)
24
+ self.event_timestamps: defaultdict[str, deque[datetime]] = defaultdict(deque)
24
25
  # 为每个会话设置一个锁,避免并发冲突
25
- self.locks: DefaultDict[str, asyncio.Lock] = defaultdict(asyncio.Lock)
26
+ self.locks: defaultdict[str, asyncio.Lock] = defaultdict(asyncio.Lock)
26
27
  # 限流参数
27
28
  self.rate_limit_count: int = 0
28
29
  self.rate_limit_time: timedelta = timedelta(0)
29
30
 
30
31
  async def initialize(self, ctx: PipelineContext) -> None:
31
- """
32
- 初始化限流器,根据配置设置限流参数。
33
- """
32
+ """初始化限流器,根据配置设置限流参数。"""
34
33
  self.rate_limit_count = ctx.astrbot_config["platform_settings"]["rate_limit"][
35
34
  "count"
36
35
  ]
37
36
  self.rate_limit_time = timedelta(
38
- seconds=ctx.astrbot_config["platform_settings"]["rate_limit"]["time"]
37
+ seconds=ctx.astrbot_config["platform_settings"]["rate_limit"]["time"],
39
38
  )
40
39
  self.rl_strategy = ctx.astrbot_config["platform_settings"]["rate_limit"][
41
40
  "strategy"
42
41
  ] # stall or discard
43
42
 
44
43
  async def process(
45
- self, event: AstrMessageEvent
46
- ) -> Union[None, AsyncGenerator[None, None]]:
47
- """
48
- 检查并处理限流逻辑。如果触发限流,流水线会 stall 并在窗口期后自动恢复。
44
+ self,
45
+ event: AstrMessageEvent,
46
+ ) -> None | AsyncGenerator[None, None]:
47
+ """检查并处理限流逻辑。如果触发限流,流水线会 stall 并在窗口期后自动恢复。
49
48
 
50
49
  Args:
51
50
  event (AstrMessageEvent): 当前消息事件。
@@ -53,48 +52,47 @@ class RateLimitStage(Stage):
53
52
 
54
53
  Returns:
55
54
  MessageEventResult: 继续或停止事件处理的结果。
55
+
56
56
  """
57
57
  session_id = event.session_id
58
58
  now = datetime.now()
59
59
 
60
60
  async with self.locks[session_id]: # 确保同一会话不会并发修改队列
61
- timestamps = self.event_timestamps[session_id]
62
-
63
- self._remove_expired_timestamps(timestamps, now)
64
-
65
- if len(timestamps) >= self.rate_limit_count:
66
- # 达到限流阈值,计算下一个窗口的时间
61
+ # 检查并处理限流,可能需要多次检查直到满足条件
62
+ while True:
63
+ timestamps = self.event_timestamps[session_id]
64
+ self._remove_expired_timestamps(timestamps, now)
65
+
66
+ if len(timestamps) < self.rate_limit_count:
67
+ timestamps.append(now)
68
+ break
67
69
  next_window_time = timestamps[0] + self.rate_limit_time
68
- stall_duration = (next_window_time - now).total_seconds()
70
+ stall_duration = (next_window_time - now).total_seconds() + 0.3
69
71
 
70
72
  match self.rl_strategy:
71
73
  case RateLimitStrategy.STALL.value:
72
74
  logger.info(
73
- f"会话 {session_id} 被限流。根据限流策略,此会话处理将被暂停 {stall_duration:.2f} 秒。"
75
+ f"会话 {session_id} 被限流。根据限流策略,此会话处理将被暂停 {stall_duration:.2f} 秒。",
74
76
  )
75
77
  await asyncio.sleep(stall_duration)
78
+ now = datetime.now()
76
79
  case RateLimitStrategy.DISCARD.value:
77
- # event.set_result(MessageEventResult().message(f"会话 {session_id} 被限流。根据限流策略,此请求已被丢弃,直到您的限额于 {stall_duration:.2f} 秒后重置。"))
78
80
  logger.info(
79
- f"会话 {session_id} 被限流。根据限流策略,此请求已被丢弃,直到限额于 {stall_duration:.2f} 秒后重置。"
81
+ f"会话 {session_id} 被限流。根据限流策略,此请求已被丢弃,直到限额于 {stall_duration:.2f} 秒后重置。",
80
82
  )
81
83
  return event.stop_event()
82
84
 
83
- self._remove_expired_timestamps(
84
- timestamps, now + timedelta(seconds=stall_duration)
85
- )
86
-
87
- timestamps.append(now)
88
-
89
85
  def _remove_expired_timestamps(
90
- self, timestamps: Deque[datetime], now: datetime
86
+ self,
87
+ timestamps: deque[datetime],
88
+ now: datetime,
91
89
  ) -> None:
92
- """
93
- 移除时间窗口外的时间戳。
90
+ """移除时间窗口外的时间戳。
94
91
 
95
92
  Args:
96
93
  timestamps (Deque[datetime]): 当前会话的时间戳队列。
97
94
  now (datetime): 当前时间,用于计算过期时间。
95
+
98
96
  """
99
97
  expiry_threshold: datetime = now - self.rate_limit_time
100
98
  while timestamps and timestamps[0] < expiry_threshold: