AstrBot 4.5.0__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 +44 -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 +18 -13
  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 +47 -29
  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 +40 -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 +102 -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 +116 -0
  181. astrbot/core/provider/sources/xinference_stt_provider.py +197 -0
  182. astrbot/core/star/__init__.py +16 -11
  183. astrbot/core/star/config.py +10 -15
  184. astrbot/core/star/context.py +109 -84
  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.0.dist-info → astrbot-4.5.2.dist-info}/METADATA +4 -2
  240. astrbot-4.5.2.dist-info/RECORD +261 -0
  241. astrbot-4.5.0.dist-info/RECORD +0 -258
  242. {astrbot-4.5.0.dist-info → astrbot-4.5.2.dist-info}/WHEEL +0 -0
  243. {astrbot-4.5.0.dist-info → astrbot-4.5.2.dist-info}/entry_points.txt +0 -0
  244. {astrbot-4.5.0.dist-info → astrbot-4.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,15 +1,16 @@
1
1
  import enum
2
-
3
- from typing import List, Optional, Union, AsyncGenerator
2
+ from collections.abc import AsyncGenerator
4
3
  from dataclasses import dataclass, field
4
+
5
+ from typing_extensions import deprecated
6
+
5
7
  from astrbot.core.message.components import (
6
- BaseMessageComponent,
7
- Plain,
8
- Image,
9
8
  At,
10
9
  AtAll,
10
+ BaseMessageComponent,
11
+ Image,
12
+ Plain,
11
13
  )
12
- from typing_extensions import deprecated
13
14
 
14
15
 
15
16
  @dataclass
@@ -20,18 +21,18 @@ class MessageChain:
20
21
  Attributes:
21
22
  `chain` (list): 用于顺序存储各个组件。
22
23
  `use_t2i_` (bool): 用于标记是否使用文本转图片服务。默认为 None,即跟随用户的设置。当设置为 True 时,将会使用文本转图片服务。
24
+
23
25
  """
24
26
 
25
- chain: List[BaseMessageComponent] = field(default_factory=list)
26
- use_t2i_: Optional[bool] = None # None 为跟随用户设置
27
- type: Optional[str] = None
27
+ chain: list[BaseMessageComponent] = field(default_factory=list)
28
+ use_t2i_: bool | None = None # None 为跟随用户设置
29
+ type: str | None = None
28
30
  """消息链承载的消息的类型。可选,用于让消息平台区分不同业务场景的消息链。"""
29
31
 
30
32
  def message(self, message: str):
31
33
  """添加一条文本消息到消息链 `chain` 中。
32
34
 
33
35
  Example:
34
-
35
36
  CommandResult().message("Hello ").message("world!")
36
37
  # 输出 Hello world!
37
38
 
@@ -39,11 +40,10 @@ class MessageChain:
39
40
  self.chain.append(Plain(message))
40
41
  return self
41
42
 
42
- def at(self, name: str, qq: Union[str, int]):
43
+ def at(self, name: str, qq: str | int):
43
44
  """添加一条 At 消息到消息链 `chain` 中。
44
45
 
45
46
  Example:
46
-
47
47
  CommandResult().at("张三", "12345678910")
48
48
  # 输出 @张三
49
49
 
@@ -55,7 +55,6 @@ class MessageChain:
55
55
  """添加一条 AtAll 消息到消息链 `chain` 中。
56
56
 
57
57
  Example:
58
-
59
58
  CommandResult().at_all()
60
59
  # 输出 @所有人
61
60
 
@@ -68,7 +67,6 @@ class MessageChain:
68
67
  """添加一条错误消息到消息链 `chain` 中
69
68
 
70
69
  Example:
71
-
72
70
  CommandResult().error("解析失败")
73
71
 
74
72
  """
@@ -82,7 +80,6 @@ class MessageChain:
82
80
  如果需要发送本地图片,请使用 `file_image` 方法。
83
81
 
84
82
  Example:
85
-
86
83
  CommandResult().image("https://example.com/image.jpg")
87
84
 
88
85
  """
@@ -96,6 +93,7 @@ class MessageChain:
96
93
  如果需要发送网络图片,请使用 `url_image` 方法。
97
94
 
98
95
  CommandResult().image("image.jpg")
96
+
99
97
  """
100
98
  self.chain.append(Image.fromFileSystem(path))
101
99
  return self
@@ -114,6 +112,7 @@ class MessageChain:
114
112
 
115
113
  Args:
116
114
  use_t2i (bool): 是否使用文本转图片服务。默认为 None,即跟随用户的设置。当设置为 True 时,将会使用文本转图片服务。
115
+
117
116
  """
118
117
  self.use_t2i_ = use_t2i
119
118
  return self
@@ -125,7 +124,7 @@ class MessageChain:
125
124
  def squash_plain(self):
126
125
  """将消息链中的所有 Plain 消息段聚合到第一个 Plain 消息段中。"""
127
126
  if not self.chain:
128
- return
127
+ return None
129
128
 
130
129
  new_chain = []
131
130
  first_plain = None
@@ -153,6 +152,7 @@ class EventResultType(enum.Enum):
153
152
  Attributes:
154
153
  CONTINUE: 事件将会继续传播
155
154
  STOP: 事件将会终止传播
155
+
156
156
  """
157
157
 
158
158
  CONTINUE = enum.auto()
@@ -181,17 +181,18 @@ class MessageEventResult(MessageChain):
181
181
  `chain` (list): 用于顺序存储各个组件。
182
182
  `use_t2i_` (bool): 用于标记是否使用文本转图片服务。默认为 None,即跟随用户的设置。当设置为 True 时,将会使用文本转图片服务。
183
183
  `result_type` (EventResultType): 事件处理的结果类型。
184
+
184
185
  """
185
186
 
186
- result_type: Optional[EventResultType] = field(
187
- default_factory=lambda: EventResultType.CONTINUE
187
+ result_type: EventResultType | None = field(
188
+ default_factory=lambda: EventResultType.CONTINUE,
188
189
  )
189
190
 
190
- result_content_type: Optional[ResultContentType] = field(
191
- default_factory=lambda: ResultContentType.GENERAL_RESULT
191
+ result_content_type: ResultContentType | None = field(
192
+ default_factory=lambda: ResultContentType.GENERAL_RESULT,
192
193
  )
193
194
 
194
- async_stream: Optional[AsyncGenerator] = None
195
+ async_stream: AsyncGenerator | None = None
195
196
  """异步流"""
196
197
 
197
198
  def stop_event(self) -> "MessageEventResult":
@@ -205,9 +206,7 @@ class MessageEventResult(MessageChain):
205
206
  return self
206
207
 
207
208
  def is_stopped(self) -> bool:
208
- """
209
- 是否终止事件传播。
210
- """
209
+ """是否终止事件传播。"""
211
210
  return self.result_type == EventResultType.STOP
212
211
 
213
212
  def set_async_stream(self, stream: AsyncGenerator) -> "MessageEventResult":
@@ -220,6 +219,7 @@ class MessageEventResult(MessageChain):
220
219
 
221
220
  Args:
222
221
  result_type (EventResultType): 事件处理的结果类型。
222
+
223
223
  """
224
224
  self.result_content_type = typ
225
225
  return self
@@ -1,8 +1,8 @@
1
+ from astrbot import logger
2
+ from astrbot.core.astrbot_config_mgr import AstrBotConfigManager
1
3
  from astrbot.core.db import BaseDatabase
2
4
  from astrbot.core.db.po import Persona, Personality
3
- from astrbot.core.astrbot_config_mgr import AstrBotConfigManager
4
5
  from astrbot.core.platform.message_session import MessageSession
5
- from astrbot import logger
6
6
 
7
7
  DEFAULT_PERSONALITY = Personality(
8
8
  prompt="You are a helpful and friendly assistant.",
@@ -41,12 +41,14 @@ class PersonaManager:
41
41
  return persona
42
42
 
43
43
  async def get_default_persona_v3(
44
- self, umo: str | MessageSession | None = None
44
+ self,
45
+ umo: str | MessageSession | None = None,
45
46
  ) -> Personality:
46
47
  """获取默认 persona"""
47
48
  cfg = self.acm.get_conf(umo)
48
49
  default_persona_id = cfg.get("provider_settings", {}).get(
49
- "default_personality", "default"
50
+ "default_personality",
51
+ "default",
50
52
  )
51
53
  if not default_persona_id or default_persona_id == "default":
52
54
  return DEFAULT_PERSONALITY
@@ -66,16 +68,19 @@ class PersonaManager:
66
68
  async def update_persona(
67
69
  self,
68
70
  persona_id: str,
69
- system_prompt: str = None,
70
- begin_dialogs: list[str] = None,
71
- tools: list[str] = None,
71
+ system_prompt: str | None = None,
72
+ begin_dialogs: list[str] | None = None,
73
+ tools: list[str] | None = None,
72
74
  ):
73
75
  """更新指定 persona 的信息。tools 参数为 None 时表示使用所有工具,空列表表示不使用任何工具"""
74
76
  existing_persona = await self.db.get_persona_by_id(persona_id)
75
77
  if not existing_persona:
76
78
  raise ValueError(f"Persona with ID {persona_id} does not exist.")
77
79
  persona = await self.db.update_persona(
78
- persona_id, system_prompt, begin_dialogs, tools=tools
80
+ persona_id,
81
+ system_prompt,
82
+ begin_dialogs,
83
+ tools=tools,
79
84
  )
80
85
  if persona:
81
86
  for i, p in enumerate(self.personas):
@@ -100,7 +105,10 @@ class PersonaManager:
100
105
  if await self.db.get_persona_by_id(persona_id):
101
106
  raise ValueError(f"Persona with ID {persona_id} already exists.")
102
107
  new_persona = await self.db.insert_persona(
103
- persona_id, system_prompt, begin_dialogs, tools=tools
108
+ persona_id,
109
+ system_prompt,
110
+ begin_dialogs,
111
+ tools=tools,
104
112
  )
105
113
  self.personas.append(new_persona)
106
114
  self.get_v3_persona_data()
@@ -115,6 +123,7 @@ class PersonaManager:
115
123
  - list[dict]: 包含 persona 配置的字典列表。
116
124
  - list[Personality]: 包含 Personality 对象的列表。
117
125
  - Personality: 默认选择的 Personality 对象。
126
+
118
127
  """
119
128
  v3_persona_config = [
120
129
  {
@@ -136,7 +145,7 @@ class PersonaManager:
136
145
  if begin_dialogs:
137
146
  if len(begin_dialogs) % 2 != 0:
138
147
  logger.error(
139
- f"{persona_cfg['name']} 人格情景预设对话格式不对,条数应该为偶数。"
148
+ f"{persona_cfg['name']} 人格情景预设对话格式不对,条数应该为偶数。",
140
149
  )
141
150
  begin_dialogs = []
142
151
  user_turn = True
@@ -146,7 +155,7 @@ class PersonaManager:
146
155
  "role": "user" if user_turn else "assistant",
147
156
  "content": dialog,
148
157
  "_no_save": None, # 不持久化到 db
149
- }
158
+ },
150
159
  )
151
160
  user_turn = not user_turn
152
161
 
@@ -27,15 +27,15 @@ STAGES_ORDER = [
27
27
  ]
28
28
 
29
29
  __all__ = [
30
- "WakingCheckStage",
31
- "WhitelistCheckStage",
32
- "SessionStatusCheckStage",
33
- "RateLimitStage",
34
30
  "ContentSafetyCheckStage",
31
+ "EventResultType",
32
+ "MessageEventResult",
35
33
  "PreProcessStage",
36
34
  "ProcessStage",
37
- "ResultDecorateStage",
35
+ "RateLimitStage",
38
36
  "RespondStage",
39
- "MessageEventResult",
40
- "EventResultType",
37
+ "ResultDecorateStage",
38
+ "SessionStatusCheckStage",
39
+ "WakingCheckStage",
40
+ "WhitelistCheckStage",
41
41
  ]
@@ -1,9 +1,11 @@
1
- from typing import Union, AsyncGenerator
2
- from ..stage import Stage, register_stage
3
- from ..context import PipelineContext
4
- from astrbot.core.platform.astr_message_event import AstrMessageEvent
5
- from astrbot.core.message.message_event_result import MessageEventResult
1
+ from collections.abc import AsyncGenerator
2
+
6
3
  from astrbot.core import logger
4
+ from astrbot.core.message.message_event_result import MessageEventResult
5
+ from astrbot.core.platform.astr_message_event import AstrMessageEvent
6
+
7
+ from ..context import PipelineContext
8
+ from ..stage import Stage, register_stage
7
9
  from .strategies.strategy import StrategySelector
8
10
 
9
11
 
@@ -19,8 +21,10 @@ class ContentSafetyCheckStage(Stage):
19
21
  self.strategy_selector = StrategySelector(config)
20
22
 
21
23
  async def process(
22
- self, event: AstrMessageEvent, check_text: str | None = None
23
- ) -> Union[None, AsyncGenerator[None, None]]:
24
+ self,
25
+ event: AstrMessageEvent,
26
+ check_text: str | None = None,
27
+ ) -> None | AsyncGenerator[None, None]:
24
28
  """检查内容安全"""
25
29
  text = check_text if check_text else event.get_message_str()
26
30
  ok, info = self.strategy_selector.check(text)
@@ -28,8 +32,8 @@ class ContentSafetyCheckStage(Stage):
28
32
  if event.is_at_or_wake_command:
29
33
  event.set_result(
30
34
  MessageEventResult().message(
31
- "你的消息或者大模型的响应中包含不适当的内容,已被屏蔽。"
32
- )
35
+ "你的消息或者大模型的响应中包含不适当的内容,已被屏蔽。",
36
+ ),
33
37
  )
34
38
  yield
35
39
  event.stop_event()
@@ -1,8 +1,7 @@
1
1
  import abc
2
- from typing import Tuple
3
2
 
4
3
 
5
4
  class ContentSafetyStrategy(abc.ABC):
6
5
  @abc.abstractmethod
7
- def check(self, content: str) -> Tuple[bool, str]:
6
+ def check(self, content: str) -> tuple[bool, str]:
8
7
  raise NotImplementedError
@@ -1,10 +1,9 @@
1
- """
2
- 使用此功能应该先 pip install baidu-aip
3
- """
1
+ """使用此功能应该先 pip install baidu-aip"""
4
2
 
5
- from . import ContentSafetyStrategy
6
3
  from aip import AipContentCensor
7
4
 
5
+ from . import ContentSafetyStrategy
6
+
8
7
 
9
8
  class BaiduAipStrategy(ContentSafetyStrategy):
10
9
  def __init__(self, appid: str, ak: str, sk: str) -> None:
@@ -19,12 +18,12 @@ class BaiduAipStrategy(ContentSafetyStrategy):
19
18
  return False, ""
20
19
  if res["conclusionType"] == 1:
21
20
  return True, ""
22
- else:
23
- if "data" not in res:
24
- return False, ""
25
- count = len(res["data"])
26
- info = f"百度审核服务发现 {count} 处违规:\n"
27
- for i in res["data"]:
28
- info += f"{i['msg']};\n"
29
- info += "\n判断结果:" + res["conclusion"]
30
- return False, info
21
+ if "data" not in res:
22
+ return False, ""
23
+ count = len(res["data"])
24
+ parts = [f"百度审核服务发现 {count} 处违规:\n"]
25
+ for i in res["data"]:
26
+ parts.append(f"{i['msg']};\n")
27
+ parts.append("\n判断结果:" + res["conclusion"])
28
+ info = "".join(parts)
29
+ return False, info
@@ -1,4 +1,5 @@
1
1
  import re
2
+
2
3
  from . import ContentSafetyStrategy
3
4
 
4
5
 
@@ -1,16 +1,16 @@
1
- from . import ContentSafetyStrategy
2
- from typing import List, Tuple
3
1
  from astrbot import logger
4
2
 
3
+ from . import ContentSafetyStrategy
4
+
5
5
 
6
6
  class StrategySelector:
7
7
  def __init__(self, config: dict) -> None:
8
- self.enabled_strategies: List[ContentSafetyStrategy] = []
8
+ self.enabled_strategies: list[ContentSafetyStrategy] = []
9
9
  if config["internal_keywords"]["enable"]:
10
10
  from .keywords import KeywordsStrategy
11
11
 
12
12
  self.enabled_strategies.append(
13
- KeywordsStrategy(config["internal_keywords"]["extra_keywords"])
13
+ KeywordsStrategy(config["internal_keywords"]["extra_keywords"]),
14
14
  )
15
15
  if config["baidu_aip"]["enable"]:
16
16
  try:
@@ -23,10 +23,10 @@ class StrategySelector:
23
23
  config["baidu_aip"]["app_id"],
24
24
  config["baidu_aip"]["api_key"],
25
25
  config["baidu_aip"]["secret_key"],
26
- )
26
+ ),
27
27
  )
28
28
 
29
- def check(self, content: str) -> Tuple[bool, str]:
29
+ def check(self, content: str) -> tuple[bool, str]:
30
30
  for strategy in self.enabled_strategies:
31
31
  ok, info = strategy.check(content)
32
32
  if not ok:
@@ -1,7 +1,9 @@
1
1
  from dataclasses import dataclass
2
+
2
3
  from astrbot.core.config import AstrBotConfig
3
4
  from astrbot.core.star import PluginManager
4
- from .context_utils import call_handler, call_event_hook
5
+
6
+ from .context_utils import call_event_hook, call_handler, call_local_llm_tool
5
7
 
6
8
 
7
9
  @dataclass
@@ -13,3 +15,4 @@ class PipelineContext:
13
15
  astrbot_config_id: str
14
16
  call_handler = call_handler
15
17
  call_event_hook = call_event_hook
18
+ call_local_llm_tool = call_local_llm_tool
@@ -1,11 +1,14 @@
1
1
  import inspect
2
2
  import traceback
3
3
  import typing as T
4
+
4
5
  from astrbot import logger
5
- from astrbot.core.star.star_handler import star_handlers_registry, EventType
6
- from astrbot.core.star.star import star_map
7
- from astrbot.core.message.message_event_result import MessageEventResult, CommandResult
6
+ from astrbot.core.agent.run_context import ContextWrapper
7
+ from astrbot.core.astr_agent_context import AstrAgentContext
8
+ from astrbot.core.message.message_event_result import CommandResult, MessageEventResult
8
9
  from astrbot.core.platform.astr_message_event import AstrMessageEvent
10
+ from astrbot.core.star.star import star_map
11
+ from astrbot.core.star.star_handler import EventType, star_handlers_registry
9
12
 
10
13
 
11
14
  async def call_handler(
@@ -26,6 +29,7 @@ async def call_handler(
26
29
 
27
30
  Returns:
28
31
  AsyncGenerator[None, None]: 异步生成器,用于在管道中传递控制流
32
+
29
33
  """
30
34
  ready_to_call = None # 一个协程或者异步生成器
31
35
 
@@ -80,14 +84,17 @@ async def call_event_hook(
80
84
 
81
85
  Returns:
82
86
  bool: 如果事件被终止,返回 True
83
- #"""
87
+ #
88
+
89
+ """
84
90
  handlers = star_handlers_registry.get_handlers_by_event_type(
85
- hook_type, plugins_name=event.plugins_name
91
+ hook_type,
92
+ plugins_name=event.plugins_name,
86
93
  )
87
94
  for handler in handlers:
88
95
  try:
89
96
  logger.debug(
90
- f"hook({hook_type.name}) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}"
97
+ f"hook({hook_type.name}) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}",
91
98
  )
92
99
  await handler.handler(event, *args, **kwargs)
93
100
  except BaseException:
@@ -95,8 +102,71 @@ async def call_event_hook(
95
102
 
96
103
  if event.is_stopped():
97
104
  logger.info(
98
- f"{star_map[handler.handler_module_path].name} - {handler.handler_name} 终止了事件传播。"
105
+ f"{star_map[handler.handler_module_path].name} - {handler.handler_name} 终止了事件传播。",
99
106
  )
100
107
  return True
101
108
 
102
109
  return event.is_stopped()
110
+
111
+
112
+ async def call_local_llm_tool(
113
+ context: ContextWrapper[AstrAgentContext],
114
+ handler: T.Callable[..., T.Awaitable[T.Any]],
115
+ method_name: str,
116
+ *args,
117
+ **kwargs,
118
+ ) -> T.AsyncGenerator[T.Any, None]:
119
+ """执行本地 LLM 工具的处理函数并处理其返回结果"""
120
+ ready_to_call = None # 一个协程或者异步生成器
121
+
122
+ trace_ = None
123
+
124
+ event = context.context.event
125
+
126
+ try:
127
+ if method_name == "run" or method_name == "decorator_handler":
128
+ ready_to_call = handler(event, *args, **kwargs)
129
+ elif method_name == "call":
130
+ ready_to_call = handler(context, *args, **kwargs)
131
+ else:
132
+ raise ValueError(f"未知的方法名: {method_name}")
133
+ except ValueError as e:
134
+ logger.error(f"调用本地 LLM 工具时出错: {e}", exc_info=True)
135
+ except TypeError:
136
+ logger.error("处理函数参数不匹配,请检查 handler 的定义。", exc_info=True)
137
+ except Exception as e:
138
+ trace_ = traceback.format_exc()
139
+ logger.error(f"调用本地 LLM 工具时出错: {e}\n{trace_}")
140
+
141
+ if not ready_to_call:
142
+ return
143
+
144
+ if inspect.isasyncgen(ready_to_call):
145
+ _has_yielded = False
146
+ try:
147
+ async for ret in ready_to_call:
148
+ # 这里逐步执行异步生成器, 对于每个 yield 返回的 ret, 执行下面的代码
149
+ # 返回值只能是 MessageEventResult 或者 None(无返回值)
150
+ _has_yielded = True
151
+ if isinstance(ret, (MessageEventResult, CommandResult)):
152
+ # 如果返回值是 MessageEventResult, 设置结果并继续
153
+ event.set_result(ret)
154
+ yield
155
+ else:
156
+ # 如果返回值是 None, 则不设置结果并继续
157
+ # 继续执行后续阶段
158
+ yield ret
159
+ if not _has_yielded:
160
+ # 如果这个异步生成器没有执行到 yield 分支
161
+ yield
162
+ except Exception as e:
163
+ logger.error(f"Previous Error: {trace_}")
164
+ raise e
165
+ elif inspect.iscoroutine(ready_to_call):
166
+ # 如果只是一个协程, 直接执行
167
+ ret = await ready_to_call
168
+ if isinstance(ret, (MessageEventResult, CommandResult)):
169
+ event.set_result(ret)
170
+ yield
171
+ else:
172
+ yield ret
@@ -1,12 +1,14 @@
1
- import traceback
2
1
  import asyncio
3
2
  import random
4
- from typing import 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
+ import traceback
4
+ from collections.abc import AsyncGenerator
5
+
8
6
  from astrbot.core import logger
9
- from astrbot.core.message.components import Plain, Record, Image
7
+ from astrbot.core.message.components import Image, Plain, Record
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
@@ -20,8 +22,9 @@ class PreProcessStage(Stage):
20
22
  self.platform_settings: dict = self.config.get("platform_settings", {})
21
23
 
22
24
  async def process(
23
- self, event: AstrMessageEvent
24
- ) -> Union[None, AsyncGenerator[None, None]]:
25
+ self,
26
+ event: AstrMessageEvent,
27
+ ) -> None | AsyncGenerator[None, None]:
25
28
  """在处理事件之前的预处理"""
26
29
  # 平台特异配置:platform_specific.<platform>.pre_ack_emoji
27
30
  supported = {"telegram", "lark"}
@@ -68,7 +71,7 @@ class PreProcessStage(Stage):
68
71
  stt_provider = ctx.get_using_stt_provider(event.unified_msg_origin)
69
72
  if not stt_provider:
70
73
  logger.warning(
71
- f"会话 {event.unified_msg_origin} 未配置语音转文本模型。"
74
+ f"会话 {event.unified_msg_origin} 未配置语音转文本模型。",
72
75
  )
73
76
  return
74
77
  message_chain = event.get_messages()