AstrBot 4.5.1__py3-none-any.whl → 4.5.2__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 (244) hide show
  1. astrbot/api/__init__.py +10 -11
  2. astrbot/api/event/__init__.py +5 -6
  3. astrbot/api/event/filter/__init__.py +37 -36
  4. astrbot/api/platform/__init__.py +7 -8
  5. astrbot/api/provider/__init__.py +7 -7
  6. astrbot/api/star/__init__.py +3 -4
  7. astrbot/api/util/__init__.py +2 -2
  8. astrbot/cli/__main__.py +5 -5
  9. astrbot/cli/commands/__init__.py +3 -3
  10. astrbot/cli/commands/cmd_conf.py +19 -16
  11. astrbot/cli/commands/cmd_init.py +3 -2
  12. astrbot/cli/commands/cmd_plug.py +8 -10
  13. astrbot/cli/commands/cmd_run.py +5 -6
  14. astrbot/cli/utils/__init__.py +6 -6
  15. astrbot/cli/utils/basic.py +14 -14
  16. astrbot/cli/utils/plugin.py +24 -15
  17. astrbot/cli/utils/version_comparator.py +10 -12
  18. astrbot/core/__init__.py +8 -6
  19. astrbot/core/agent/agent.py +3 -2
  20. astrbot/core/agent/handoff.py +6 -2
  21. astrbot/core/agent/hooks.py +9 -6
  22. astrbot/core/agent/mcp_client.py +50 -15
  23. astrbot/core/agent/message.py +168 -0
  24. astrbot/core/agent/response.py +2 -1
  25. astrbot/core/agent/run_context.py +2 -3
  26. astrbot/core/agent/runners/base.py +10 -13
  27. astrbot/core/agent/runners/tool_loop_agent_runner.py +52 -51
  28. astrbot/core/agent/tool.py +60 -41
  29. astrbot/core/agent/tool_executor.py +9 -3
  30. astrbot/core/astr_agent_context.py +3 -1
  31. astrbot/core/astrbot_config_mgr.py +29 -9
  32. astrbot/core/config/__init__.py +2 -2
  33. astrbot/core/config/astrbot_config.py +28 -26
  34. astrbot/core/config/default.py +4 -6
  35. astrbot/core/conversation_mgr.py +105 -36
  36. astrbot/core/core_lifecycle.py +68 -54
  37. astrbot/core/db/__init__.py +33 -18
  38. astrbot/core/db/migration/helper.py +12 -10
  39. astrbot/core/db/migration/migra_3_to_4.py +53 -34
  40. astrbot/core/db/migration/migra_45_to_46.py +1 -1
  41. astrbot/core/db/migration/shared_preferences_v3.py +2 -1
  42. astrbot/core/db/migration/sqlite_v3.py +26 -23
  43. astrbot/core/db/po.py +27 -18
  44. astrbot/core/db/sqlite.py +74 -45
  45. astrbot/core/db/vec_db/base.py +10 -14
  46. astrbot/core/db/vec_db/faiss_impl/document_storage.py +90 -77
  47. astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +9 -3
  48. astrbot/core/db/vec_db/faiss_impl/vec_db.py +36 -31
  49. astrbot/core/event_bus.py +8 -6
  50. astrbot/core/file_token_service.py +6 -5
  51. astrbot/core/initial_loader.py +7 -5
  52. astrbot/core/knowledge_base/chunking/__init__.py +1 -3
  53. astrbot/core/knowledge_base/chunking/base.py +1 -0
  54. astrbot/core/knowledge_base/chunking/fixed_size.py +2 -0
  55. astrbot/core/knowledge_base/chunking/recursive.py +16 -10
  56. astrbot/core/knowledge_base/kb_db_sqlite.py +50 -48
  57. astrbot/core/knowledge_base/kb_helper.py +30 -17
  58. astrbot/core/knowledge_base/kb_mgr.py +6 -7
  59. astrbot/core/knowledge_base/models.py +10 -4
  60. astrbot/core/knowledge_base/parsers/__init__.py +3 -5
  61. astrbot/core/knowledge_base/parsers/base.py +1 -0
  62. astrbot/core/knowledge_base/parsers/markitdown_parser.py +2 -1
  63. astrbot/core/knowledge_base/parsers/pdf_parser.py +2 -1
  64. astrbot/core/knowledge_base/parsers/text_parser.py +1 -0
  65. astrbot/core/knowledge_base/parsers/util.py +1 -1
  66. astrbot/core/knowledge_base/retrieval/__init__.py +6 -8
  67. astrbot/core/knowledge_base/retrieval/manager.py +17 -14
  68. astrbot/core/knowledge_base/retrieval/rank_fusion.py +7 -3
  69. astrbot/core/knowledge_base/retrieval/sparse_retriever.py +11 -5
  70. astrbot/core/log.py +21 -13
  71. astrbot/core/message/components.py +123 -217
  72. astrbot/core/message/message_event_result.py +24 -24
  73. astrbot/core/persona_mgr.py +20 -11
  74. astrbot/core/pipeline/__init__.py +7 -7
  75. astrbot/core/pipeline/content_safety_check/stage.py +13 -9
  76. astrbot/core/pipeline/content_safety_check/strategies/__init__.py +1 -2
  77. astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +12 -13
  78. astrbot/core/pipeline/content_safety_check/strategies/keywords.py +1 -0
  79. astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
  80. astrbot/core/pipeline/context.py +4 -1
  81. astrbot/core/pipeline/context_utils.py +77 -7
  82. astrbot/core/pipeline/preprocess_stage/stage.py +12 -9
  83. astrbot/core/pipeline/process_stage/method/llm_request.py +125 -72
  84. astrbot/core/pipeline/process_stage/method/star_request.py +19 -17
  85. astrbot/core/pipeline/process_stage/stage.py +13 -10
  86. astrbot/core/pipeline/process_stage/utils.py +6 -5
  87. astrbot/core/pipeline/rate_limit_check/stage.py +37 -36
  88. astrbot/core/pipeline/respond/stage.py +23 -20
  89. astrbot/core/pipeline/result_decorate/stage.py +31 -23
  90. astrbot/core/pipeline/scheduler.py +12 -8
  91. astrbot/core/pipeline/session_status_check/stage.py +12 -8
  92. astrbot/core/pipeline/stage.py +10 -4
  93. astrbot/core/pipeline/waking_check/stage.py +24 -18
  94. astrbot/core/pipeline/whitelist_check/stage.py +10 -7
  95. astrbot/core/platform/__init__.py +6 -6
  96. astrbot/core/platform/astr_message_event.py +76 -110
  97. astrbot/core/platform/astrbot_message.py +11 -13
  98. astrbot/core/platform/manager.py +16 -15
  99. astrbot/core/platform/message_session.py +5 -3
  100. astrbot/core/platform/platform.py +16 -24
  101. astrbot/core/platform/platform_metadata.py +4 -4
  102. astrbot/core/platform/register.py +8 -8
  103. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +23 -15
  104. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +51 -33
  105. astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +42 -27
  106. astrbot/core/platform/sources/dingtalk/dingtalk_event.py +7 -3
  107. astrbot/core/platform/sources/discord/client.py +9 -6
  108. astrbot/core/platform/sources/discord/components.py +18 -14
  109. astrbot/core/platform/sources/discord/discord_platform_adapter.py +45 -30
  110. astrbot/core/platform/sources/discord/discord_platform_event.py +38 -30
  111. astrbot/core/platform/sources/lark/lark_adapter.py +23 -17
  112. astrbot/core/platform/sources/lark/lark_event.py +21 -14
  113. astrbot/core/platform/sources/misskey/misskey_adapter.py +107 -67
  114. astrbot/core/platform/sources/misskey/misskey_api.py +153 -129
  115. astrbot/core/platform/sources/misskey/misskey_event.py +20 -15
  116. astrbot/core/platform/sources/misskey/misskey_utils.py +74 -62
  117. astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +63 -44
  118. astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +41 -26
  119. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +36 -17
  120. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_event.py +3 -1
  121. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +12 -7
  122. astrbot/core/platform/sources/satori/satori_adapter.py +56 -38
  123. astrbot/core/platform/sources/satori/satori_event.py +34 -25
  124. astrbot/core/platform/sources/slack/client.py +11 -9
  125. astrbot/core/platform/sources/slack/slack_adapter.py +52 -36
  126. astrbot/core/platform/sources/slack/slack_event.py +34 -24
  127. astrbot/core/platform/sources/telegram/tg_adapter.py +38 -18
  128. astrbot/core/platform/sources/telegram/tg_event.py +32 -18
  129. astrbot/core/platform/sources/webchat/webchat_adapter.py +27 -17
  130. astrbot/core/platform/sources/webchat/webchat_event.py +14 -10
  131. astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +115 -120
  132. astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +9 -8
  133. astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +15 -16
  134. astrbot/core/platform/sources/wecom/wecom_adapter.py +35 -18
  135. astrbot/core/platform/sources/wecom/wecom_event.py +55 -48
  136. astrbot/core/platform/sources/wecom/wecom_kf.py +34 -44
  137. astrbot/core/platform/sources/wecom/wecom_kf_message.py +26 -10
  138. astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +18 -10
  139. astrbot/core/platform/sources/wecom_ai_bot/__init__.py +3 -5
  140. astrbot/core/platform/sources/wecom_ai_bot/ierror.py +0 -1
  141. astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +61 -37
  142. astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +67 -28
  143. astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +8 -9
  144. astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +18 -9
  145. astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +14 -12
  146. astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +22 -12
  147. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +40 -26
  148. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +47 -45
  149. astrbot/core/platform_message_history_mgr.py +5 -3
  150. astrbot/core/provider/__init__.py +2 -3
  151. astrbot/core/provider/entites.py +8 -8
  152. astrbot/core/provider/entities.py +61 -75
  153. astrbot/core/provider/func_tool_manager.py +59 -55
  154. astrbot/core/provider/manager.py +32 -22
  155. astrbot/core/provider/provider.py +72 -46
  156. astrbot/core/provider/register.py +7 -7
  157. astrbot/core/provider/sources/anthropic_source.py +48 -30
  158. astrbot/core/provider/sources/azure_tts_source.py +17 -13
  159. astrbot/core/provider/sources/coze_api_client.py +27 -17
  160. astrbot/core/provider/sources/coze_source.py +104 -87
  161. astrbot/core/provider/sources/dashscope_source.py +18 -11
  162. astrbot/core/provider/sources/dashscope_tts.py +36 -23
  163. astrbot/core/provider/sources/dify_source.py +25 -20
  164. astrbot/core/provider/sources/edge_tts_source.py +21 -17
  165. astrbot/core/provider/sources/fishaudio_tts_api_source.py +22 -14
  166. astrbot/core/provider/sources/gemini_embedding_source.py +12 -13
  167. astrbot/core/provider/sources/gemini_source.py +72 -58
  168. astrbot/core/provider/sources/gemini_tts_source.py +8 -6
  169. astrbot/core/provider/sources/gsv_selfhosted_source.py +17 -14
  170. astrbot/core/provider/sources/gsvi_tts_source.py +11 -7
  171. astrbot/core/provider/sources/minimax_tts_api_source.py +50 -40
  172. astrbot/core/provider/sources/openai_embedding_source.py +6 -8
  173. astrbot/core/provider/sources/openai_source.py +77 -69
  174. astrbot/core/provider/sources/openai_tts_api_source.py +14 -6
  175. astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
  176. astrbot/core/provider/sources/vllm_rerank_source.py +10 -4
  177. astrbot/core/provider/sources/volcengine_tts.py +38 -31
  178. astrbot/core/provider/sources/whisper_api_source.py +14 -12
  179. astrbot/core/provider/sources/whisper_selfhosted_source.py +15 -11
  180. astrbot/core/provider/sources/xinference_rerank_source.py +16 -8
  181. astrbot/core/provider/sources/xinference_stt_provider.py +35 -25
  182. astrbot/core/star/__init__.py +16 -11
  183. astrbot/core/star/config.py +10 -15
  184. astrbot/core/star/context.py +97 -75
  185. astrbot/core/star/filter/__init__.py +4 -3
  186. astrbot/core/star/filter/command.py +30 -28
  187. astrbot/core/star/filter/command_group.py +27 -24
  188. astrbot/core/star/filter/custom_filter.py +6 -5
  189. astrbot/core/star/filter/event_message_type.py +4 -2
  190. astrbot/core/star/filter/permission.py +4 -2
  191. astrbot/core/star/filter/platform_adapter_type.py +4 -2
  192. astrbot/core/star/filter/regex.py +4 -2
  193. astrbot/core/star/register/__init__.py +19 -19
  194. astrbot/core/star/register/star.py +6 -2
  195. astrbot/core/star/register/star_handler.py +96 -73
  196. astrbot/core/star/session_llm_manager.py +48 -14
  197. astrbot/core/star/session_plugin_manager.py +29 -15
  198. astrbot/core/star/star.py +1 -2
  199. astrbot/core/star/star_handler.py +13 -8
  200. astrbot/core/star/star_manager.py +151 -59
  201. astrbot/core/star/star_tools.py +44 -37
  202. astrbot/core/star/updator.py +10 -10
  203. astrbot/core/umop_config_router.py +10 -4
  204. astrbot/core/updator.py +13 -5
  205. astrbot/core/utils/astrbot_path.py +3 -5
  206. astrbot/core/utils/dify_api_client.py +33 -15
  207. astrbot/core/utils/io.py +66 -42
  208. astrbot/core/utils/log_pipe.py +1 -1
  209. astrbot/core/utils/metrics.py +7 -7
  210. astrbot/core/utils/path_util.py +15 -16
  211. astrbot/core/utils/pip_installer.py +5 -5
  212. astrbot/core/utils/session_waiter.py +19 -20
  213. astrbot/core/utils/shared_preferences.py +45 -20
  214. astrbot/core/utils/t2i/__init__.py +4 -1
  215. astrbot/core/utils/t2i/network_strategy.py +35 -26
  216. astrbot/core/utils/t2i/renderer.py +11 -5
  217. astrbot/core/utils/t2i/template_manager.py +14 -15
  218. astrbot/core/utils/tencent_record_helper.py +19 -13
  219. astrbot/core/utils/version_comparator.py +10 -13
  220. astrbot/core/zip_updator.py +43 -40
  221. astrbot/dashboard/routes/__init__.py +18 -18
  222. astrbot/dashboard/routes/auth.py +10 -8
  223. astrbot/dashboard/routes/chat.py +30 -21
  224. astrbot/dashboard/routes/config.py +92 -75
  225. astrbot/dashboard/routes/conversation.py +46 -39
  226. astrbot/dashboard/routes/file.py +4 -2
  227. astrbot/dashboard/routes/knowledge_base.py +47 -40
  228. astrbot/dashboard/routes/log.py +9 -4
  229. astrbot/dashboard/routes/persona.py +19 -16
  230. astrbot/dashboard/routes/plugin.py +69 -55
  231. astrbot/dashboard/routes/route.py +3 -1
  232. astrbot/dashboard/routes/session_management.py +130 -116
  233. astrbot/dashboard/routes/stat.py +34 -34
  234. astrbot/dashboard/routes/t2i.py +15 -12
  235. astrbot/dashboard/routes/tools.py +47 -52
  236. astrbot/dashboard/routes/update.py +32 -28
  237. astrbot/dashboard/server.py +30 -26
  238. astrbot/dashboard/utils.py +8 -4
  239. {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/METADATA +2 -1
  240. astrbot-4.5.2.dist-info/RECORD +261 -0
  241. astrbot-4.5.1.dist-info/RECORD +0 -260
  242. {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/WHEEL +0 -0
  243. {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/entry_points.txt +0 -0
  244. {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,12 +1,10 @@
1
- """
2
- 如需修改配置,请在 `data/cmd_config.json` 中修改或者在管理面板中可视化修改。
3
- """
1
+ """如需修改配置,请在 `data/cmd_config.json` 中修改或者在管理面板中可视化修改。"""
4
2
 
5
3
  import os
6
4
 
7
5
  from astrbot.core.utils.astrbot_path import get_astrbot_data_path
8
6
 
9
- VERSION = "4.5.1"
7
+ VERSION = "4.5.2"
10
8
  DB_PATH = os.path.join(get_astrbot_data_path(), "data_v4.db")
11
9
 
12
10
  # 默认配置
@@ -2707,9 +2705,9 @@ CONFIG_METADATA_3_SYSTEM = {
2707
2705
  "items": {"type": "string"},
2708
2706
  },
2709
2707
  },
2710
- }
2708
+ },
2711
2709
  },
2712
- }
2710
+ },
2713
2711
  }
2714
2712
 
2715
2713
 
@@ -1,13 +1,14 @@
1
- """
2
- AstrBot 会话-对话管理器, 维护两个本地存储, 其中一个是 json 格式的shared_preferences, 另外一个是数据库
1
+ """AstrBot 会话-对话管理器, 维护两个本地存储, 其中一个是 json 格式的shared_preferences, 另外一个是数据库.
3
2
 
4
3
  在 AstrBot 中, 会话和对话是独立的, 会话用于标记对话窗口, 例如群聊"123456789"可以建立一个会话,
5
4
  在一个会话中可以建立多个对话, 并且支持对话的切换和删除
6
5
  """
7
6
 
8
7
  import json
8
+ from collections.abc import Awaitable, Callable
9
+
9
10
  from astrbot.core import sp
10
- from typing import Dict, List, Callable, Awaitable
11
+ from astrbot.core.agent.message import AssistantMessageSegment, UserMessageSegment
11
12
  from astrbot.core.db import BaseDatabase
12
13
  from astrbot.core.db.po import Conversation, ConversationV2
13
14
 
@@ -16,31 +17,34 @@ class ConversationManager:
16
17
  """负责管理会话与 LLM 的对话,某个会话当前正在用哪个对话。"""
17
18
 
18
19
  def __init__(self, db_helper: BaseDatabase):
19
- self.session_conversations: Dict[str, str] = {}
20
+ self.session_conversations: dict[str, str] = {}
20
21
  self.db = db_helper
21
22
  self.save_interval = 60 # 每 60 秒保存一次
22
23
 
23
24
  # 会话删除回调函数列表(用于级联清理,如知识库配置)
24
- self._on_session_deleted_callbacks: List[Callable[[str], Awaitable[None]]] = []
25
+ self._on_session_deleted_callbacks: list[Callable[[str], Awaitable[None]]] = []
25
26
 
26
27
  def register_on_session_deleted(
27
- self, callback: Callable[[str], Awaitable[None]]
28
+ self,
29
+ callback: Callable[[str], Awaitable[None]],
28
30
  ) -> None:
29
- """注册会话删除回调函数
31
+ """注册会话删除回调函数.
30
32
 
31
33
  其他模块可以注册回调来响应会话删除事件,实现级联清理。
32
34
  例如:知识库模块可以注册回调来清理会话的知识库配置。
33
35
 
34
36
  Args:
35
37
  callback: 回调函数,接收会话ID (unified_msg_origin) 作为参数
38
+
36
39
  """
37
40
  self._on_session_deleted_callbacks.append(callback)
38
41
 
39
42
  async def _trigger_session_deleted(self, unified_msg_origin: str) -> None:
40
- """触发会话删除回调
43
+ """触发会话删除回调.
41
44
 
42
45
  Args:
43
46
  unified_msg_origin: 会话ID
47
+
44
48
  """
45
49
  for callback in self._on_session_deleted_callbacks:
46
50
  try:
@@ -49,7 +53,7 @@ class ConversationManager:
49
53
  from astrbot.core import logger
50
54
 
51
55
  logger.error(
52
- f"会话删除回调执行失败 (session: {unified_msg_origin}): {e}"
56
+ f"会话删除回调执行失败 (session: {unified_msg_origin}): {e}",
53
57
  )
54
58
 
55
59
  def _convert_conv_from_v2_to_v1(self, conv_v2: ConversationV2) -> Conversation:
@@ -75,12 +79,13 @@ class ConversationManager:
75
79
  title: str | None = None,
76
80
  persona_id: str | None = None,
77
81
  ) -> str:
78
- """新建对话,并将当前会话的对话转移到新对话
82
+ """新建对话,并将当前会话的对话转移到新对话.
79
83
 
80
84
  Args:
81
85
  unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
82
86
  Returns:
83
87
  conversation_id (str): 对话 ID, 是 uuid 格式的字符串
88
+
84
89
  """
85
90
  if not platform_id:
86
91
  # 如果没有提供 platform_id,则从 unified_msg_origin 中解析
@@ -106,18 +111,22 @@ class ConversationManager:
106
111
  Args:
107
112
  unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
108
113
  conversation_id (str): 对话 ID, 是 uuid 格式的字符串
114
+
109
115
  """
110
116
  self.session_conversations[unified_msg_origin] = conversation_id
111
117
  await sp.session_put(unified_msg_origin, "sel_conv_id", conversation_id)
112
118
 
113
119
  async def delete_conversation(
114
- self, unified_msg_origin: str, conversation_id: str | None = None
120
+ self,
121
+ unified_msg_origin: str,
122
+ conversation_id: str | None = None,
115
123
  ):
116
124
  """删除会话的对话,当 conversation_id 为 None 时删除会话当前的对话
117
125
 
118
126
  Args:
119
127
  unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
120
128
  conversation_id (str): 对话 ID, 是 uuid 格式的字符串
129
+
121
130
  """
122
131
  if not conversation_id:
123
132
  conversation_id = self.session_conversations.get(unified_msg_origin)
@@ -133,6 +142,7 @@ class ConversationManager:
133
142
 
134
143
  Args:
135
144
  unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
145
+
136
146
  """
137
147
  await self.db.delete_conversations_by_user_id(user_id=unified_msg_origin)
138
148
  self.session_conversations.pop(unified_msg_origin, None)
@@ -148,6 +158,7 @@ class ConversationManager:
148
158
  unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
149
159
  Returns:
150
160
  conversation_id (str): 对话 ID, 是 uuid 格式的字符串
161
+
151
162
  """
152
163
  ret = self.session_conversations.get(unified_msg_origin, None)
153
164
  if not ret:
@@ -162,13 +173,15 @@ class ConversationManager:
162
173
  conversation_id: str,
163
174
  create_if_not_exists: bool = False,
164
175
  ) -> Conversation | None:
165
- """获取会话的对话
176
+ """获取会话的对话.
166
177
 
167
178
  Args:
168
179
  unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
169
180
  conversation_id (str): 对话 ID, 是 uuid 格式的字符串
181
+ create_if_not_exists (bool): 如果对话不存在,是否创建一个新的对话
170
182
  Returns:
171
183
  conversation (Conversation): 对话对象
184
+
172
185
  """
173
186
  conv = await self.db.get_conversation_by_id(cid=conversation_id)
174
187
  if not conv and create_if_not_exists:
@@ -181,18 +194,22 @@ class ConversationManager:
181
194
  return conv_res
182
195
 
183
196
  async def get_conversations(
184
- self, unified_msg_origin: str | None = None, platform_id: str | None = None
185
- ) -> List[Conversation]:
186
- """获取对话列表
197
+ self,
198
+ unified_msg_origin: str | None = None,
199
+ platform_id: str | None = None,
200
+ ) -> list[Conversation]:
201
+ """获取对话列表.
187
202
 
188
203
  Args:
189
204
  unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id,可选
190
205
  platform_id (str): 平台 ID, 可选参数, 用于过滤对话
191
206
  Returns:
192
207
  conversations (List[Conversation]): 对话对象列表
208
+
193
209
  """
194
210
  convs = await self.db.get_conversations(
195
- user_id=unified_msg_origin, platform_id=platform_id
211
+ user_id=unified_msg_origin,
212
+ platform_id=platform_id,
196
213
  )
197
214
  convs_res = []
198
215
  for conv in convs:
@@ -208,7 +225,7 @@ class ConversationManager:
208
225
  search_query: str = "",
209
226
  **kwargs,
210
227
  ) -> tuple[list[Conversation], int]:
211
- """获取过滤后的对话列表
228
+ """获取过滤后的对话列表.
212
229
 
213
230
  Args:
214
231
  page (int): 页码, 默认为 1
@@ -217,6 +234,7 @@ class ConversationManager:
217
234
  search_query (str): 搜索查询字符串, 可选
218
235
  Returns:
219
236
  conversations (list[Conversation]): 对话对象列表
237
+
220
238
  """
221
239
  convs, cnt = await self.db.get_filtered_conversations(
222
240
  page=page,
@@ -238,13 +256,14 @@ class ConversationManager:
238
256
  history: list[dict] | None = None,
239
257
  title: str | None = None,
240
258
  persona_id: str | None = None,
241
- ):
242
- """更新会话的对话
259
+ ) -> None:
260
+ """更新会话的对话.
243
261
 
244
262
  Args:
245
263
  unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
246
264
  conversation_id (str): 对话 ID, 是 uuid 格式的字符串
247
265
  history (List[Dict]): 对话历史记录, 是一个字典列表, 每个字典包含 role 和 content 字段
266
+
248
267
  """
249
268
  if not conversation_id:
250
269
  # 如果没有提供 conversation_id,则获取当前的
@@ -258,16 +277,20 @@ class ConversationManager:
258
277
  )
259
278
 
260
279
  async def update_conversation_title(
261
- self, unified_msg_origin: str, title: str, conversation_id: str | None = None
262
- ):
263
- """更新会话的对话标题
280
+ self,
281
+ unified_msg_origin: str,
282
+ title: str,
283
+ conversation_id: str | None = None,
284
+ ) -> None:
285
+ """更新会话的对话标题.
264
286
 
265
287
  Args:
266
288
  unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
267
289
  title (str): 对话标题
268
-
290
+ conversation_id (str): 对话 ID, 是 uuid 格式的字符串
269
291
  Deprecated:
270
292
  Use `update_conversation` with `title` parameter instead.
293
+
271
294
  """
272
295
  await self.update_conversation(
273
296
  unified_msg_origin=unified_msg_origin,
@@ -280,15 +303,16 @@ class ConversationManager:
280
303
  unified_msg_origin: str,
281
304
  persona_id: str,
282
305
  conversation_id: str | None = None,
283
- ):
284
- """更新会话的对话 Persona ID
306
+ ) -> None:
307
+ """更新会话的对话 Persona ID.
285
308
 
286
309
  Args:
287
310
  unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
288
311
  persona_id (str): 对话 Persona ID
289
-
312
+ conversation_id (str): 对话 ID, 是 uuid 格式的字符串
290
313
  Deprecated:
291
314
  Use `update_conversation` with `persona_id` parameter instead.
315
+
292
316
  """
293
317
  await self.update_conversation(
294
318
  unified_msg_origin=unified_msg_origin,
@@ -296,40 +320,85 @@ class ConversationManager:
296
320
  persona_id=persona_id,
297
321
  )
298
322
 
323
+ async def add_message_pair(
324
+ self,
325
+ cid: str,
326
+ user_message: UserMessageSegment | dict,
327
+ assistant_message: AssistantMessageSegment | dict,
328
+ ) -> None:
329
+ """Add a user-assistant message pair to the conversation history.
330
+
331
+ Args:
332
+ cid (str): Conversation ID
333
+ user_message (UserMessageSegment | dict): OpenAI-format user message object or dict
334
+ assistant_message (AssistantMessageSegment | dict): OpenAI-format assistant message object or dict
335
+
336
+ Raises:
337
+ Exception: If the conversation with the given ID is not found
338
+ """
339
+ conv = await self.db.get_conversation_by_id(cid=cid)
340
+ if not conv:
341
+ raise Exception(f"Conversation with id {cid} not found")
342
+ history = conv.content or []
343
+ if isinstance(user_message, UserMessageSegment):
344
+ user_msg_dict = user_message.model_dump()
345
+ else:
346
+ user_msg_dict = user_message
347
+ if isinstance(assistant_message, AssistantMessageSegment):
348
+ assistant_msg_dict = assistant_message.model_dump()
349
+ else:
350
+ assistant_msg_dict = assistant_message
351
+ history.append(user_msg_dict)
352
+ history.append(assistant_msg_dict)
353
+ await self.db.update_conversation(
354
+ cid=cid,
355
+ content=history,
356
+ )
357
+
299
358
  async def get_human_readable_context(
300
- self, unified_msg_origin, conversation_id, page=1, page_size=10
301
- ):
302
- """获取人类可读的上下文
359
+ self,
360
+ unified_msg_origin: str,
361
+ conversation_id: str,
362
+ page: int = 1,
363
+ page_size: int = 10,
364
+ ) -> tuple[list[str], int]:
365
+ """获取人类可读的上下文.
303
366
 
304
367
  Args:
305
368
  unified_msg_origin (str): 统一的消息来源字符串。格式为 platform_name:message_type:session_id
306
369
  conversation_id (str): 对话 ID, 是 uuid 格式的字符串
307
370
  page (int): 页码
308
371
  page_size (int): 每页大小
372
+
309
373
  """
310
374
  conversation = await self.get_conversation(unified_msg_origin, conversation_id)
375
+ if not conversation:
376
+ return [], 0
311
377
  history = json.loads(conversation.history)
312
378
 
313
- contexts = []
314
- temp_contexts = []
379
+ # contexts_groups 存放按顺序的段落(每个段落是一个 str 列表),
380
+ # 之后会被展平成一个扁平的 str 列表返回。
381
+ contexts_groups: list[list[str]] = []
382
+ temp_contexts: list[str] = []
315
383
  for record in history:
316
384
  if record["role"] == "user":
317
385
  temp_contexts.append(f"User: {record['content']}")
318
386
  elif record["role"] == "assistant":
319
- if "content" in record and record["content"]:
387
+ if record.get("content"):
320
388
  temp_contexts.append(f"Assistant: {record['content']}")
321
389
  elif "tool_calls" in record:
322
390
  tool_calls_str = json.dumps(
323
- record["tool_calls"], ensure_ascii=False
391
+ record["tool_calls"],
392
+ ensure_ascii=False,
324
393
  )
325
394
  temp_contexts.append(f"Assistant: [函数调用] {tool_calls_str}")
326
395
  else:
327
396
  temp_contexts.append("Assistant: [未知的内容]")
328
- contexts.insert(0, temp_contexts)
397
+ contexts_groups.insert(0, temp_contexts)
329
398
  temp_contexts = []
330
399
 
331
- # 展平 contexts 列表
332
- contexts = [item for sublist in contexts for item in sublist]
400
+ # 展平分组后的 contexts 列表为单层字符串列表
401
+ contexts = [item for sublist in contexts_groups for item in sublist]
333
402
 
334
403
  # 计算分页
335
404
  paged_contexts = contexts[(page - 1) * page_size : page * page_size]
@@ -1,5 +1,5 @@
1
- """
2
- Astrbot 核心生命周期管理类, 负责管理 AstrBot 的启动、停止、重启等操作。
1
+ """Astrbot 核心生命周期管理类, 负责管理 AstrBot 的启动、停止、重启等操作.
2
+
3
3
  该类负责初始化各个组件, 包括 ProviderManager、PlatformManager、ConversationManager、PluginManager、PipelineScheduler、EventBus等。
4
4
  该类还负责加载和执行插件, 以及处理事件总线的分发。
5
5
 
@@ -9,44 +9,44 @@ Astrbot 核心生命周期管理类, 负责管理 AstrBot 的启动、停止、
9
9
  3. 执行启动完成事件钩子
10
10
  """
11
11
 
12
- import traceback
13
12
  import asyncio
14
- import time
15
- import threading
16
13
  import os
17
- from .event_bus import EventBus
18
- from . import astrbot_config, html_renderer
14
+ import threading
15
+ import time
16
+ import traceback
19
17
  from asyncio import Queue
20
- from astrbot.core.pipeline.scheduler import PipelineScheduler, PipelineContext
21
- from astrbot.core.star import PluginManager
22
- from astrbot.core.platform.manager import PlatformManager
23
- from astrbot.core.star.context import Context
24
- from astrbot.core.persona_mgr import PersonaManager
25
- from astrbot.core.provider.manager import ProviderManager
26
- from astrbot.core import LogBroker
27
- from astrbot.core.db import BaseDatabase
28
- from astrbot.core.db.migration.migra_45_to_46 import migrate_45_to_46
29
- from astrbot.core.updator import AstrBotUpdator
30
- from astrbot.core import logger, sp
18
+
19
+ from astrbot.core import LogBroker, logger, sp
20
+ from astrbot.core.astrbot_config_mgr import AstrBotConfigManager
31
21
  from astrbot.core.config.default import VERSION
32
22
  from astrbot.core.conversation_mgr import ConversationManager
23
+ from astrbot.core.db import BaseDatabase
24
+ from astrbot.core.db.migration.migra_45_to_46 import migrate_45_to_46
25
+ from astrbot.core.knowledge_base.kb_mgr import KnowledgeBaseManager
26
+ from astrbot.core.persona_mgr import PersonaManager
27
+ from astrbot.core.pipeline.scheduler import PipelineContext, PipelineScheduler
28
+ from astrbot.core.platform.manager import PlatformManager
33
29
  from astrbot.core.platform_message_history_mgr import PlatformMessageHistoryManager
30
+ from astrbot.core.provider.manager import ProviderManager
31
+ from astrbot.core.star import PluginManager
32
+ from astrbot.core.star.context import Context
33
+ from astrbot.core.star.star_handler import EventType, star_handlers_registry, star_map
34
34
  from astrbot.core.umop_config_router import UmopConfigRouter
35
- from astrbot.core.astrbot_config_mgr import AstrBotConfigManager
36
- from astrbot.core.star.star_handler import star_handlers_registry, EventType
37
- from astrbot.core.star.star_handler import star_map
38
- from astrbot.core.knowledge_base.kb_mgr import KnowledgeBaseManager
35
+ from astrbot.core.updator import AstrBotUpdator
36
+
37
+ from . import astrbot_config, html_renderer
38
+ from .event_bus import EventBus
39
39
 
40
40
 
41
41
  class AstrBotCoreLifecycle:
42
- """
43
- AstrBot 核心生命周期管理类, 负责管理 AstrBot 的启动、停止、重启等操作。
42
+ """AstrBot 核心生命周期管理类, 负责管理 AstrBot 的启动、停止、重启等操作.
43
+
44
44
  该类负责初始化各个组件, 包括 ProviderManager、PlatformManager、ConversationManager、PluginManager、PipelineScheduler、
45
45
  EventBus 等。
46
46
  该类还负责加载和执行插件, 以及处理事件总线的分发。
47
47
  """
48
48
 
49
- def __init__(self, log_broker: LogBroker, db: BaseDatabase):
49
+ def __init__(self, log_broker: LogBroker, db: BaseDatabase) -> None:
50
50
  self.log_broker = log_broker # 初始化日志代理
51
51
  self.astrbot_config = astrbot_config # 初始化配置
52
52
  self.db = db # 初始化数据库
@@ -70,11 +70,11 @@ class AstrBotCoreLifecycle:
70
70
  del os.environ["no_proxy"]
71
71
  logger.debug("HTTP proxy cleared")
72
72
 
73
- async def initialize(self):
74
- """
75
- 初始化 AstrBot 核心生命周期管理类, 负责初始化各个组件, 包括 ProviderManager、PlatformManager、ConversationManager、PluginManager、PipelineScheduler、EventBus、AstrBotUpdator等。
76
- """
73
+ async def initialize(self) -> None:
74
+ """初始化 AstrBot 核心生命周期管理类.
77
75
 
76
+ 负责初始化各个组件, 包括 ProviderManager、PlatformManager、ConversationManager、PluginManager、PipelineScheduler、EventBus、AstrBotUpdator等。
77
+ """
78
78
  # 初始化日志代理
79
79
  logger.info("AstrBot v" + VERSION)
80
80
  if os.environ.get("TESTING", ""):
@@ -91,7 +91,9 @@ class AstrBotCoreLifecycle:
91
91
 
92
92
  # 初始化 AstrBot 配置管理器
93
93
  self.astrbot_config_mgr = AstrBotConfigManager(
94
- default_config=self.astrbot_config, ucr=self.umop_config_router, sp=sp
94
+ default_config=self.astrbot_config,
95
+ ucr=self.umop_config_router,
96
+ sp=sp,
95
97
  )
96
98
 
97
99
  # 4.5 to 4.6 migration for umop_config_router
@@ -110,7 +112,9 @@ class AstrBotCoreLifecycle:
110
112
 
111
113
  # 初始化供应商管理器
112
114
  self.provider_manager = ProviderManager(
113
- self.astrbot_config_mgr, self.db, self.persona_mgr
115
+ self.astrbot_config_mgr,
116
+ self.db,
117
+ self.persona_mgr,
114
118
  )
115
119
 
116
120
  # 初始化平台管理器
@@ -158,7 +162,9 @@ class AstrBotCoreLifecycle:
158
162
 
159
163
  # 初始化事件总线
160
164
  self.event_bus = EventBus(
161
- self.event_queue, self.pipeline_scheduler_mapping, self.astrbot_config_mgr
165
+ self.event_queue,
166
+ self.pipeline_scheduler_mapping,
167
+ self.astrbot_config_mgr,
162
168
  )
163
169
 
164
170
  # 记录启动时间
@@ -173,13 +179,13 @@ class AstrBotCoreLifecycle:
173
179
  # 初始化关闭控制面板的事件
174
180
  self.dashboard_shutdown_event = asyncio.Event()
175
181
 
176
- def _load(self):
177
- """加载事件总线和任务并初始化"""
178
-
182
+ def _load(self) -> None:
183
+ """加载事件总线和任务并初始化."""
179
184
  # 创建一个异步任务来执行事件总线的 dispatch() 方法
180
185
  # dispatch是一个无限循环的协程, 从事件队列中获取事件并处理
181
186
  event_bus_task = asyncio.create_task(
182
- self.event_bus.dispatch(), name="event_bus"
187
+ self.event_bus.dispatch(),
188
+ name="event_bus",
183
189
  )
184
190
 
185
191
  # 把插件中注册的所有协程函数注册到事件总线中并执行
@@ -190,16 +196,17 @@ class AstrBotCoreLifecycle:
190
196
  tasks_ = [event_bus_task, *extra_tasks]
191
197
  for task in tasks_:
192
198
  self.curr_tasks.append(
193
- asyncio.create_task(self._task_wrapper(task), name=task.get_name())
199
+ asyncio.create_task(self._task_wrapper(task), name=task.get_name()),
194
200
  )
195
201
 
196
202
  self.start_time = int(time.time())
197
203
 
198
- async def _task_wrapper(self, task: asyncio.Task):
199
- """异步任务包装器, 用于处理异步任务执行中出现的各种异常
204
+ async def _task_wrapper(self, task: asyncio.Task) -> None:
205
+ """异步任务包装器, 用于处理异步任务执行中出现的各种异常.
200
206
 
201
207
  Args:
202
208
  task (asyncio.Task): 要执行的异步任务
209
+
203
210
  """
204
211
  try:
205
212
  await task
@@ -212,19 +219,22 @@ class AstrBotCoreLifecycle:
212
219
  logger.error(f"| {line}")
213
220
  logger.error("-------")
214
221
 
215
- async def start(self):
216
- """启动 AstrBot 核心生命周期管理类, 用load加载事件总线和任务并初始化, 执行启动完成事件钩子"""
222
+ async def start(self) -> None:
223
+ """启动 AstrBot 核心生命周期管理类.
224
+
225
+ 用load加载事件总线和任务并初始化, 执行启动完成事件钩子
226
+ """
217
227
  self._load()
218
228
  logger.info("AstrBot 启动完成。")
219
229
 
220
230
  # 执行启动完成事件钩子
221
231
  handlers = star_handlers_registry.get_handlers_by_event_type(
222
- EventType.OnAstrBotLoadedEvent
232
+ EventType.OnAstrBotLoadedEvent,
223
233
  )
224
234
  for handler in handlers:
225
235
  try:
226
236
  logger.info(
227
- f"hook(on_astrbot_loaded) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}"
237
+ f"hook(on_astrbot_loaded) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}",
228
238
  )
229
239
  await handler.handler()
230
240
  except BaseException:
@@ -233,8 +243,8 @@ class AstrBotCoreLifecycle:
233
243
  # 同时运行curr_tasks中的所有任务
234
244
  await asyncio.gather(*self.curr_tasks, return_exceptions=True)
235
245
 
236
- async def stop(self):
237
- """停止 AstrBot 核心生命周期管理类, 取消所有当前任务并终止各个管理器"""
246
+ async def stop(self) -> None:
247
+ """停止 AstrBot 核心生命周期管理类, 取消所有当前任务并终止各个管理器."""
238
248
  # 请求停止所有正在运行的异步任务
239
249
  for task in self.curr_tasks:
240
250
  task.cancel()
@@ -245,7 +255,7 @@ class AstrBotCoreLifecycle:
245
255
  except Exception as e:
246
256
  logger.warning(traceback.format_exc())
247
257
  logger.warning(
248
- f"插件 {plugin.name} 未被正常终止 {e!s}, 可能会导致资源泄露等问题。"
258
+ f"插件 {plugin.name} 未被正常终止 {e!s}, 可能会导致资源泄露等问题。",
249
259
  )
250
260
 
251
261
  await self.provider_manager.terminate()
@@ -262,14 +272,16 @@ class AstrBotCoreLifecycle:
262
272
  except Exception as e:
263
273
  logger.error(f"任务 {task.get_name()} 发生错误: {e}")
264
274
 
265
- async def restart(self):
275
+ async def restart(self) -> None:
266
276
  """重启 AstrBot 核心生命周期管理类, 终止各个管理器并重新加载平台实例"""
267
277
  await self.provider_manager.terminate()
268
278
  await self.platform_manager.terminate()
269
279
  await self.kb_manager.terminate()
270
280
  self.dashboard_shutdown_event.set()
271
281
  threading.Thread(
272
- target=self.astrbot_updator._reboot, name="restart", daemon=True
282
+ target=self.astrbot_updator._reboot,
283
+ name="restart",
284
+ daemon=True,
273
285
  ).start()
274
286
 
275
287
  def load_platform(self) -> list[asyncio.Task]:
@@ -281,36 +293,38 @@ class AstrBotCoreLifecycle:
281
293
  asyncio.create_task(
282
294
  platform_inst.run(),
283
295
  name=f"{platform_inst.meta().id}({platform_inst.meta().name})",
284
- )
296
+ ),
285
297
  )
286
298
  return tasks
287
299
 
288
300
  async def load_pipeline_scheduler(self) -> dict[str, PipelineScheduler]:
289
- """加载消息事件流水线调度器
301
+ """加载消息事件流水线调度器.
290
302
 
291
303
  Returns:
292
304
  dict[str, PipelineScheduler]: 平台 ID 到流水线调度器的映射
305
+
293
306
  """
294
307
  mapping = {}
295
308
  for conf_id, ab_config in self.astrbot_config_mgr.confs.items():
296
309
  scheduler = PipelineScheduler(
297
- PipelineContext(ab_config, self.plugin_manager, conf_id)
310
+ PipelineContext(ab_config, self.plugin_manager, conf_id),
298
311
  )
299
312
  await scheduler.initialize()
300
313
  mapping[conf_id] = scheduler
301
314
  return mapping
302
315
 
303
- async def reload_pipeline_scheduler(self, conf_id: str):
304
- """重新加载消息事件流水线调度器
316
+ async def reload_pipeline_scheduler(self, conf_id: str) -> None:
317
+ """重新加载消息事件流水线调度器.
305
318
 
306
319
  Returns:
307
320
  dict[str, PipelineScheduler]: 平台 ID 到流水线调度器的映射
321
+
308
322
  """
309
323
  ab_config = self.astrbot_config_mgr.confs.get(conf_id)
310
324
  if not ab_config:
311
325
  raise ValueError(f"配置文件 {conf_id} 不存在")
312
326
  scheduler = PipelineScheduler(
313
- PipelineContext(ab_config, self.plugin_manager, conf_id)
327
+ PipelineContext(ab_config, self.plugin_manager, conf_id),
314
328
  )
315
329
  await scheduler.initialize()
316
330
  self.pipeline_scheduler_mapping[conf_id] = scheduler