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,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,16 +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
+ chain: list[BaseMessageComponent] = field(default_factory=list)
28
+ use_t2i_: bool | None = None # None 为跟随用户设置
29
+ type: str | None = None
30
+ """消息链承载的消息的类型。可选,用于让消息平台区分不同业务场景的消息链。"""
27
31
 
28
32
  def message(self, message: str):
29
33
  """添加一条文本消息到消息链 `chain` 中。
30
34
 
31
35
  Example:
32
-
33
36
  CommandResult().message("Hello ").message("world!")
34
37
  # 输出 Hello world!
35
38
 
@@ -37,11 +40,10 @@ class MessageChain:
37
40
  self.chain.append(Plain(message))
38
41
  return self
39
42
 
40
- def at(self, name: str, qq: Union[str, int]):
43
+ def at(self, name: str, qq: str | int):
41
44
  """添加一条 At 消息到消息链 `chain` 中。
42
45
 
43
46
  Example:
44
-
45
47
  CommandResult().at("张三", "12345678910")
46
48
  # 输出 @张三
47
49
 
@@ -53,7 +55,6 @@ class MessageChain:
53
55
  """添加一条 AtAll 消息到消息链 `chain` 中。
54
56
 
55
57
  Example:
56
-
57
58
  CommandResult().at_all()
58
59
  # 输出 @所有人
59
60
 
@@ -66,7 +67,6 @@ class MessageChain:
66
67
  """添加一条错误消息到消息链 `chain` 中
67
68
 
68
69
  Example:
69
-
70
70
  CommandResult().error("解析失败")
71
71
 
72
72
  """
@@ -80,7 +80,6 @@ class MessageChain:
80
80
  如果需要发送本地图片,请使用 `file_image` 方法。
81
81
 
82
82
  Example:
83
-
84
83
  CommandResult().image("https://example.com/image.jpg")
85
84
 
86
85
  """
@@ -94,15 +93,26 @@ class MessageChain:
94
93
  如果需要发送网络图片,请使用 `url_image` 方法。
95
94
 
96
95
  CommandResult().image("image.jpg")
96
+
97
97
  """
98
98
  self.chain.append(Image.fromFileSystem(path))
99
99
  return self
100
100
 
101
+ def base64_image(self, base64_str: str):
102
+ """添加一条图片消息(base64 编码字符串)到消息链 `chain` 中。
103
+ Example:
104
+
105
+ CommandResult().base64_image("iVBORw0KGgoAAAANSUhEUgAAAAUA...")
106
+ """
107
+ self.chain.append(Image.fromBase64(base64_str))
108
+ return self
109
+
101
110
  def use_t2i(self, use_t2i: bool):
102
111
  """设置是否使用文本转图片服务。
103
112
 
104
113
  Args:
105
114
  use_t2i (bool): 是否使用文本转图片服务。默认为 None,即跟随用户的设置。当设置为 True 时,将会使用文本转图片服务。
115
+
106
116
  """
107
117
  self.use_t2i_ = use_t2i
108
118
  return self
@@ -114,7 +124,7 @@ class MessageChain:
114
124
  def squash_plain(self):
115
125
  """将消息链中的所有 Plain 消息段聚合到第一个 Plain 消息段中。"""
116
126
  if not self.chain:
117
- return
127
+ return None
118
128
 
119
129
  new_chain = []
120
130
  first_plain = None
@@ -142,6 +152,7 @@ class EventResultType(enum.Enum):
142
152
  Attributes:
143
153
  CONTINUE: 事件将会继续传播
144
154
  STOP: 事件将会终止传播
155
+
145
156
  """
146
157
 
147
158
  CONTINUE = enum.auto()
@@ -157,7 +168,7 @@ class ResultContentType(enum.Enum):
157
168
  """普通的消息结果"""
158
169
  STREAMING_RESULT = enum.auto()
159
170
  """调用 LLM 产生的流式结果"""
160
- STREAMING_FINISH= enum.auto()
171
+ STREAMING_FINISH = enum.auto()
161
172
  """流式输出完成"""
162
173
 
163
174
 
@@ -170,17 +181,18 @@ class MessageEventResult(MessageChain):
170
181
  `chain` (list): 用于顺序存储各个组件。
171
182
  `use_t2i_` (bool): 用于标记是否使用文本转图片服务。默认为 None,即跟随用户的设置。当设置为 True 时,将会使用文本转图片服务。
172
183
  `result_type` (EventResultType): 事件处理的结果类型。
184
+
173
185
  """
174
186
 
175
- result_type: Optional[EventResultType] = field(
176
- default_factory=lambda: EventResultType.CONTINUE
187
+ result_type: EventResultType | None = field(
188
+ default_factory=lambda: EventResultType.CONTINUE,
177
189
  )
178
190
 
179
- result_content_type: Optional[ResultContentType] = field(
180
- default_factory=lambda: ResultContentType.GENERAL_RESULT
191
+ result_content_type: ResultContentType | None = field(
192
+ default_factory=lambda: ResultContentType.GENERAL_RESULT,
181
193
  )
182
194
 
183
- async_stream: Optional[AsyncGenerator] = None
195
+ async_stream: AsyncGenerator | None = None
184
196
  """异步流"""
185
197
 
186
198
  def stop_event(self) -> "MessageEventResult":
@@ -194,9 +206,7 @@ class MessageEventResult(MessageChain):
194
206
  return self
195
207
 
196
208
  def is_stopped(self) -> bool:
197
- """
198
- 是否终止事件传播。
199
- """
209
+ """是否终止事件传播。"""
200
210
  return self.result_type == EventResultType.STOP
201
211
 
202
212
  def set_async_stream(self, stream: AsyncGenerator) -> "MessageEventResult":
@@ -209,6 +219,7 @@ class MessageEventResult(MessageChain):
209
219
 
210
220
  Args:
211
221
  result_type (EventResultType): 事件处理的结果类型。
222
+
212
223
  """
213
224
  self.result_content_type = typ
214
225
  return self
@@ -0,0 +1,192 @@
1
+ from astrbot import logger
2
+ from astrbot.core.astrbot_config_mgr import AstrBotConfigManager
3
+ from astrbot.core.db import BaseDatabase
4
+ from astrbot.core.db.po import Persona, Personality
5
+ from astrbot.core.platform.message_session import MessageSession
6
+
7
+ DEFAULT_PERSONALITY = Personality(
8
+ prompt="You are a helpful and friendly assistant.",
9
+ name="default",
10
+ begin_dialogs=[],
11
+ mood_imitation_dialogs=[],
12
+ tools=None,
13
+ _begin_dialogs_processed=[],
14
+ _mood_imitation_dialogs_processed="",
15
+ )
16
+
17
+
18
+ class PersonaManager:
19
+ def __init__(self, db_helper: BaseDatabase, acm: AstrBotConfigManager):
20
+ self.db = db_helper
21
+ self.acm = acm
22
+ default_ps = acm.default_conf.get("provider_settings", {})
23
+ self.default_persona: str = default_ps.get("default_personality", "default")
24
+ self.personas: list[Persona] = []
25
+ self.selected_default_persona: Persona | None = None
26
+
27
+ self.personas_v3: list[Personality] = []
28
+ self.selected_default_persona_v3: Personality | None = None
29
+ self.persona_v3_config: list[dict] = []
30
+
31
+ async def initialize(self):
32
+ self.personas = await self.get_all_personas()
33
+ self.get_v3_persona_data()
34
+ logger.info(f"已加载 {len(self.personas)} 个人格。")
35
+
36
+ async def get_persona(self, persona_id: str):
37
+ """获取指定 persona 的信息"""
38
+ persona = await self.db.get_persona_by_id(persona_id)
39
+ if not persona:
40
+ raise ValueError(f"Persona with ID {persona_id} does not exist.")
41
+ return persona
42
+
43
+ async def get_default_persona_v3(
44
+ self,
45
+ umo: str | MessageSession | None = None,
46
+ ) -> Personality:
47
+ """获取默认 persona"""
48
+ cfg = self.acm.get_conf(umo)
49
+ default_persona_id = cfg.get("provider_settings", {}).get(
50
+ "default_personality",
51
+ "default",
52
+ )
53
+ if not default_persona_id or default_persona_id == "default":
54
+ return DEFAULT_PERSONALITY
55
+ try:
56
+ return next(p for p in self.personas_v3 if p["name"] == default_persona_id)
57
+ except Exception:
58
+ return DEFAULT_PERSONALITY
59
+
60
+ async def delete_persona(self, persona_id: str):
61
+ """删除指定 persona"""
62
+ if not await self.db.get_persona_by_id(persona_id):
63
+ raise ValueError(f"Persona with ID {persona_id} does not exist.")
64
+ await self.db.delete_persona(persona_id)
65
+ self.personas = [p for p in self.personas if p.persona_id != persona_id]
66
+ self.get_v3_persona_data()
67
+
68
+ async def update_persona(
69
+ self,
70
+ persona_id: str,
71
+ system_prompt: str | None = None,
72
+ begin_dialogs: list[str] | None = None,
73
+ tools: list[str] | None = None,
74
+ ):
75
+ """更新指定 persona 的信息。tools 参数为 None 时表示使用所有工具,空列表表示不使用任何工具"""
76
+ existing_persona = await self.db.get_persona_by_id(persona_id)
77
+ if not existing_persona:
78
+ raise ValueError(f"Persona with ID {persona_id} does not exist.")
79
+ persona = await self.db.update_persona(
80
+ persona_id,
81
+ system_prompt,
82
+ begin_dialogs,
83
+ tools=tools,
84
+ )
85
+ if persona:
86
+ for i, p in enumerate(self.personas):
87
+ if p.persona_id == persona_id:
88
+ self.personas[i] = persona
89
+ break
90
+ self.get_v3_persona_data()
91
+ return persona
92
+
93
+ async def get_all_personas(self) -> list[Persona]:
94
+ """获取所有 personas"""
95
+ return await self.db.get_personas()
96
+
97
+ async def create_persona(
98
+ self,
99
+ persona_id: str,
100
+ system_prompt: str,
101
+ begin_dialogs: list[str] = None,
102
+ tools: list[str] = None,
103
+ ) -> Persona:
104
+ """创建新的 persona。tools 参数为 None 时表示使用所有工具,空列表表示不使用任何工具"""
105
+ if await self.db.get_persona_by_id(persona_id):
106
+ raise ValueError(f"Persona with ID {persona_id} already exists.")
107
+ new_persona = await self.db.insert_persona(
108
+ persona_id,
109
+ system_prompt,
110
+ begin_dialogs,
111
+ tools=tools,
112
+ )
113
+ self.personas.append(new_persona)
114
+ self.get_v3_persona_data()
115
+ return new_persona
116
+
117
+ def get_v3_persona_data(
118
+ self,
119
+ ) -> tuple[list[dict], list[Personality], Personality]:
120
+ """获取 AstrBot <4.0.0 版本的 persona 数据。
121
+
122
+ Returns:
123
+ - list[dict]: 包含 persona 配置的字典列表。
124
+ - list[Personality]: 包含 Personality 对象的列表。
125
+ - Personality: 默认选择的 Personality 对象。
126
+
127
+ """
128
+ v3_persona_config = [
129
+ {
130
+ "prompt": persona.system_prompt,
131
+ "name": persona.persona_id,
132
+ "begin_dialogs": persona.begin_dialogs or [],
133
+ "mood_imitation_dialogs": [], # deprecated
134
+ "tools": persona.tools,
135
+ }
136
+ for persona in self.personas
137
+ ]
138
+
139
+ personas_v3: list[Personality] = []
140
+ selected_default_persona: Personality | None = None
141
+
142
+ for persona_cfg in v3_persona_config:
143
+ begin_dialogs = persona_cfg.get("begin_dialogs", [])
144
+ bd_processed = []
145
+ if begin_dialogs:
146
+ if len(begin_dialogs) % 2 != 0:
147
+ logger.error(
148
+ f"{persona_cfg['name']} 人格情景预设对话格式不对,条数应该为偶数。",
149
+ )
150
+ begin_dialogs = []
151
+ user_turn = True
152
+ for dialog in begin_dialogs:
153
+ bd_processed.append(
154
+ {
155
+ "role": "user" if user_turn else "assistant",
156
+ "content": dialog,
157
+ "_no_save": None, # 不持久化到 db
158
+ },
159
+ )
160
+ user_turn = not user_turn
161
+
162
+ try:
163
+ persona = Personality(
164
+ **persona_cfg,
165
+ _begin_dialogs_processed=bd_processed,
166
+ _mood_imitation_dialogs_processed="", # deprecated
167
+ )
168
+ if persona["name"] == self.default_persona:
169
+ selected_default_persona = persona
170
+ personas_v3.append(persona)
171
+ except Exception as e:
172
+ logger.error(f"解析 Persona 配置失败:{e}")
173
+
174
+ if not selected_default_persona and len(personas_v3) > 0:
175
+ # 默认选择第一个
176
+ selected_default_persona = personas_v3[0]
177
+
178
+ if not selected_default_persona:
179
+ selected_default_persona = DEFAULT_PERSONALITY
180
+ personas_v3.append(selected_default_persona)
181
+
182
+ self.personas_v3 = personas_v3
183
+ self.selected_default_persona_v3 = selected_default_persona
184
+ self.persona_v3_config = v3_persona_config
185
+ self.selected_default_persona = Persona(
186
+ persona_id=selected_default_persona["name"],
187
+ system_prompt=selected_default_persona["prompt"],
188
+ begin_dialogs=selected_default_persona["begin_dialogs"],
189
+ tools=selected_default_persona["tools"] or None,
190
+ )
191
+
192
+ return v3_persona_config, personas_v3, selected_default_persona
@@ -1,25 +1,25 @@
1
1
  from astrbot.core.message.message_event_result import (
2
- MessageEventResult,
3
2
  EventResultType,
3
+ MessageEventResult,
4
4
  )
5
5
 
6
- from .waking_check.stage import WakingCheckStage
7
- from .whitelist_check.stage import WhitelistCheckStage
8
- from .rate_limit_check.stage import RateLimitStage
9
6
  from .content_safety_check.stage import ContentSafetyCheckStage
10
- from .platform_compatibility.stage import PlatformCompatibilityStage
11
7
  from .preprocess_stage.stage import PreProcessStage
12
8
  from .process_stage.stage import ProcessStage
13
- from .result_decorate.stage import ResultDecorateStage
9
+ from .rate_limit_check.stage import RateLimitStage
14
10
  from .respond.stage import RespondStage
11
+ from .result_decorate.stage import ResultDecorateStage
12
+ from .session_status_check.stage import SessionStatusCheckStage
13
+ from .waking_check.stage import WakingCheckStage
14
+ from .whitelist_check.stage import WhitelistCheckStage
15
15
 
16
16
  # 管道阶段顺序
17
17
  STAGES_ORDER = [
18
18
  "WakingCheckStage", # 检查是否需要唤醒
19
19
  "WhitelistCheckStage", # 检查是否在群聊/私聊白名单
20
+ "SessionStatusCheckStage", # 检查会话是否整体启用
20
21
  "RateLimitStage", # 检查会话是否超过频率限制
21
22
  "ContentSafetyCheckStage", # 检查内容安全
22
- "PlatformCompatibilityStage", # 检查所有处理器的平台兼容性
23
23
  "PreProcessStage", # 预处理
24
24
  "ProcessStage", # 交由 Stars 处理(a.k.a 插件),或者 LLM 调用
25
25
  "ResultDecorateStage", # 处理结果,比如添加回复前缀、t2i、转换为语音 等
@@ -27,15 +27,15 @@ STAGES_ORDER = [
27
27
  ]
28
28
 
29
29
  __all__ = [
30
- "WakingCheckStage",
31
- "WhitelistCheckStage",
32
- "RateLimitStage",
33
30
  "ContentSafetyCheckStage",
34
- "PlatformCompatibilityStage",
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
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:
@@ -13,18 +12,18 @@ class BaiduAipStrategy(ContentSafetyStrategy):
13
12
  self.secret_key = sk
14
13
  self.client = AipContentCensor(self.app_id, self.api_key, self.secret_key)
15
14
 
16
- def check(self, content: str):
15
+ def check(self, content: str) -> tuple[bool, str]:
17
16
  res = self.client.textCensorUserDefined(content)
18
17
  if "conclusionType" not in res:
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
 
@@ -16,7 +17,7 @@ class KeywordsStrategy(ContentSafetyStrategy):
16
17
  # json.loads(base64.b64decode(f.read()).decode("utf-8"))["keywords"]
17
18
  # )
18
19
 
19
- def check(self, content: str) -> bool:
20
+ def check(self, content: str) -> tuple[bool, str]:
20
21
  for keyword in self.keywords:
21
22
  if re.search(keyword, content):
22
23
  return False, "内容安全检查不通过,匹配到敏感词。"
@@ -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,10 @@
1
1
  from dataclasses import dataclass
2
- from astrbot.core.config.astrbot_config import AstrBotConfig
2
+
3
+ from astrbot.core.config import AstrBotConfig
3
4
  from astrbot.core.star import PluginManager
4
5
 
6
+ from .context_utils import call_event_hook, call_handler
7
+
5
8
 
6
9
  @dataclass
7
10
  class PipelineContext:
@@ -9,3 +12,6 @@ class PipelineContext:
9
12
 
10
13
  astrbot_config: AstrBotConfig # AstrBot 配置对象
11
14
  plugin_manager: PluginManager # 插件管理器对象
15
+ astrbot_config_id: str
16
+ call_handler = call_handler
17
+ call_event_hook = call_event_hook