AstrBot 4.5.1__py3-none-any.whl → 4.5.3__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 +56 -53
  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.3.dist-info}/METADATA +2 -1
  240. astrbot-4.5.3.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.3.dist-info}/WHEEL +0 -0
  243. {astrbot-4.5.1.dist-info → astrbot-4.5.3.dist-info}/entry_points.txt +0 -0
  244. {astrbot-4.5.1.dist-info → astrbot-4.5.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,4 @@
1
- """
2
- 插件的重载、启停、安装、卸载等操作。
3
- """
1
+ """插件的重载、启停、安装、卸载等操作。"""
4
2
 
5
3
  import asyncio
6
4
  import functools
@@ -15,6 +13,7 @@ from types import ModuleType
15
13
  import yaml
16
14
 
17
15
  from astrbot.core import logger, pip_installer, sp
16
+ from astrbot.core.agent.handoff import FunctionTool, HandoffTool
18
17
  from astrbot.core.config.astrbot_config import AstrBotConfig
19
18
  from astrbot.core.provider.register import llm_tools
20
19
  from astrbot.core.utils.astrbot_path import (
@@ -22,7 +21,6 @@ from astrbot.core.utils.astrbot_path import (
22
21
  get_astrbot_plugin_path,
23
22
  )
24
23
  from astrbot.core.utils.io import remove_dir
25
- from astrbot.core.agent.handoff import HandoffTool, FunctionTool
26
24
 
27
25
  from . import StarMetadata
28
26
  from .context import Context
@@ -52,8 +50,9 @@ class PluginManager:
52
50
  """存储插件配置的路径。data/config"""
53
51
  self.reserved_plugin_path = os.path.abspath(
54
52
  os.path.join(
55
- os.path.dirname(os.path.abspath(__file__)), "../../../packages"
56
- )
53
+ os.path.dirname(os.path.abspath(__file__)),
54
+ "../../../packages",
55
+ ),
57
56
  )
58
57
  """保留插件的路径。在 packages 目录下"""
59
58
  self.conf_schema_fname = "_conf_schema.json"
@@ -80,7 +79,7 @@ class PluginManager:
80
79
  except asyncio.CancelledError:
81
80
  pass
82
81
  except Exception as e:
83
- logger.error(f"插件热重载监视任务异常: {str(e)}")
82
+ logger.error(f"插件热重载监视任务异常: {e!s}")
84
83
  logger.error(traceback.format_exc())
85
84
 
86
85
  async def _handle_file_changes(self, changes):
@@ -95,11 +94,13 @@ class PluginManager:
95
94
  continue
96
95
  if star.reserved:
97
96
  plugin_dir_path = os.path.join(
98
- self.reserved_plugin_path, star.root_dir_name
97
+ self.reserved_plugin_path,
98
+ star.root_dir_name,
99
99
  )
100
100
  else:
101
101
  plugin_dir_path = os.path.join(
102
- self.plugin_store_path, star.root_dir_name
102
+ self.plugin_store_path,
103
+ star.root_dir_name,
103
104
  )
104
105
  plugins_to_check.append((plugin_dir_path, star.name))
105
106
  reloaded_plugins = set()
@@ -143,14 +144,14 @@ class PluginManager:
143
144
  logger.info(f"插件 {d} 未找到 main.py 或者 {d}.py,跳过。")
144
145
  continue
145
146
  if os.path.exists(os.path.join(path, d, "main.py")) or os.path.exists(
146
- os.path.join(path, d, d + ".py")
147
+ os.path.join(path, d, d + ".py"),
147
148
  ):
148
149
  modules.append(
149
150
  {
150
151
  "pname": d,
151
152
  "module": module_str,
152
153
  "module_path": os.path.join(path, d, module_str),
153
- }
154
+ },
154
155
  )
155
156
  return modules
156
157
 
@@ -186,7 +187,7 @@ class PluginManager:
186
187
  try:
187
188
  await pip_installer.install(requirements_path=pth)
188
189
  except Exception as e:
189
- logger.error(f"更新插件 {p} 的依赖失败。Code: {str(e)}")
190
+ logger.error(f"更新插件 {p} 的依赖失败。Code: {e!s}")
190
191
 
191
192
  @staticmethod
192
193
  def _load_plugin_metadata(plugin_path: str, plugin_obj=None) -> StarMetadata | None:
@@ -201,7 +202,8 @@ class PluginManager:
201
202
 
202
203
  if os.path.exists(os.path.join(plugin_path, "metadata.yaml")):
203
204
  with open(
204
- os.path.join(plugin_path, "metadata.yaml"), encoding="utf-8"
205
+ os.path.join(plugin_path, "metadata.yaml"),
206
+ encoding="utf-8",
205
207
  ) as f:
206
208
  metadata = yaml.safe_load(f)
207
209
  elif plugin_obj and hasattr(plugin_obj, "info"):
@@ -219,7 +221,7 @@ class PluginManager:
219
221
  or "author" not in metadata
220
222
  ):
221
223
  raise Exception(
222
- "插件元数据信息不完整。name, desc, version, author 是必须的字段。"
224
+ "插件元数据信息不完整。name, desc, version, author 是必须的字段。",
223
225
  )
224
226
  metadata = StarMetadata(
225
227
  name=metadata["name"],
@@ -234,7 +236,8 @@ class PluginManager:
234
236
 
235
237
  @staticmethod
236
238
  def _get_plugin_related_modules(
237
- plugin_root_dir: str, is_reserved: bool = False
239
+ plugin_root_dir: str,
240
+ is_reserved: bool = False,
238
241
  ) -> list[str]:
239
242
  """获取与指定插件相关的所有已加载模块名
240
243
 
@@ -246,6 +249,7 @@ class PluginManager:
246
249
 
247
250
  Returns:
248
251
  list[str]: 与该插件相关的模块名列表
252
+
249
253
  """
250
254
  prefix = "packages." if is_reserved else "data.plugins."
251
255
  return [
@@ -268,6 +272,7 @@ class PluginManager:
268
272
  module_patterns: 要移除的模块名模式列表(例如 ["data.plugins", "packages"])
269
273
  root_dir_name: 插件根目录名,用于移除与该插件相关的所有模块
270
274
  is_reserved: 插件是否为保留插件(影响模块路径前缀)
275
+
271
276
  """
272
277
  if module_patterns:
273
278
  for pattern in module_patterns:
@@ -278,7 +283,8 @@ class PluginManager:
278
283
 
279
284
  if root_dir_name:
280
285
  for module_name in self._get_plugin_related_modules(
281
- root_dir_name, is_reserved
286
+ root_dir_name,
287
+ is_reserved,
282
288
  ):
283
289
  try:
284
290
  del sys.modules[module_name]
@@ -297,6 +303,7 @@ class PluginManager:
297
303
  tuple: 返回 load() 方法的结果,包含 (success, error_message)
298
304
  - success (bool): 重载是否成功
299
305
  - error_message (str|None): 错误信息,成功时为 None
306
+
300
307
  """
301
308
  async with self._pm_lock:
302
309
  specified_module_path = None
@@ -315,7 +322,7 @@ class PluginManager:
315
322
  except Exception as e:
316
323
  logger.warning(traceback.format_exc())
317
324
  logger.warning(
318
- f"插件 {smd.name} 未被正常终止: {str(e)}, 可能会导致该插件运行不正常。"
325
+ f"插件 {smd.name} 未被正常终止: {e!s}, 可能会导致该插件运行不正常。",
319
326
  )
320
327
  if smd.name and smd.module_path:
321
328
  await self._unbind_plugin(smd.name, smd.module_path)
@@ -332,7 +339,7 @@ class PluginManager:
332
339
  except Exception as e:
333
340
  logger.warning(traceback.format_exc())
334
341
  logger.warning(
335
- f"插件 {smd.name} 未被正常终止: {str(e)}, 可能会导致该插件运行不正常。"
342
+ f"插件 {smd.name} 未被正常终止: {e!s}, 可能会导致该插件运行不正常。",
336
343
  )
337
344
  if smd.name:
338
345
  await self._unbind_plugin(smd.name, specified_module_path)
@@ -353,6 +360,7 @@ class PluginManager:
353
360
  tuple: (success, error_message)
354
361
  - success (bool): 是否全部加载成功
355
362
  - error_message (str|None): 错误信息,成功时为 None
363
+
356
364
  """
357
365
  inactivated_plugins = await sp.global_get("inactivated_plugins", [])
358
366
  inactivated_llm_tools = await sp.global_get("inactivated_llm_tools", [])
@@ -371,7 +379,8 @@ class PluginManager:
371
379
  # module_path = plugin_module['module_path']
372
380
  root_dir_name = plugin_module["pname"] # 插件的目录名
373
381
  reserved = plugin_module.get(
374
- "reserved", False
382
+ "reserved",
383
+ False,
375
384
  ) # 是否是保留插件。目前在 packages/ 目录下的都是保留插件。保留插件不可以卸载。
376
385
 
377
386
  path = "data.plugins." if not reserved else "packages."
@@ -394,7 +403,7 @@ class PluginManager:
394
403
  module = __import__(path, fromlist=[module_str])
395
404
  except Exception as e:
396
405
  logger.error(traceback.format_exc())
397
- logger.error(f"插件 {root_dir_name} 导入失败。原因:{str(e)}")
406
+ logger.error(f"插件 {root_dir_name} 导入失败。原因:{e!s}")
398
407
  continue
399
408
 
400
409
  # 检查 _conf_schema.json
@@ -405,14 +414,16 @@ class PluginManager:
405
414
  else os.path.join(self.reserved_plugin_path, root_dir_name)
406
415
  )
407
416
  plugin_schema_path = os.path.join(
408
- plugin_dir_path, self.conf_schema_fname
417
+ plugin_dir_path,
418
+ self.conf_schema_fname,
409
419
  )
410
420
  if os.path.exists(plugin_schema_path):
411
421
  # 加载插件配置
412
422
  with open(plugin_schema_path, encoding="utf-8") as f:
413
423
  plugin_config = AstrBotConfig(
414
424
  config_path=os.path.join(
415
- self.plugin_config_path, f"{root_dir_name}_config.json"
425
+ self.plugin_config_path,
426
+ f"{root_dir_name}_config.json",
416
427
  ),
417
428
  schema=json.loads(f.read()),
418
429
  )
@@ -425,7 +436,7 @@ class PluginManager:
425
436
  try:
426
437
  # yaml 文件的元数据优先
427
438
  metadata_yaml = self._load_plugin_metadata(
428
- plugin_path=plugin_dir_path
439
+ plugin_path=plugin_dir_path,
429
440
  )
430
441
  if metadata_yaml:
431
442
  metadata.name = metadata_yaml.name
@@ -436,7 +447,7 @@ class PluginManager:
436
447
  metadata.display_name = metadata_yaml.display_name
437
448
  except Exception as e:
438
449
  logger.warning(
439
- f"插件 {root_dir_name} 元数据载入失败: {str(e)}。使用默认元数据。"
450
+ f"插件 {root_dir_name} 元数据载入失败: {e!s}。使用默认元数据。",
440
451
  )
441
452
  logger.info(metadata)
442
453
  metadata.config = plugin_config
@@ -445,15 +456,16 @@ class PluginManager:
445
456
  if plugin_config and metadata.star_cls_type:
446
457
  try:
447
458
  metadata.star_cls = metadata.star_cls_type(
448
- context=self.context, config=plugin_config
459
+ context=self.context,
460
+ config=plugin_config,
449
461
  )
450
462
  except TypeError as _:
451
463
  metadata.star_cls = metadata.star_cls_type(
452
- context=self.context
464
+ context=self.context,
453
465
  )
454
466
  elif metadata.star_cls_type:
455
467
  metadata.star_cls = metadata.star_cls_type(
456
- context=self.context
468
+ context=self.context,
457
469
  )
458
470
  else:
459
471
  logger.info(f"插件 {metadata.name} 已被禁用。")
@@ -469,7 +481,7 @@ class PluginManager:
469
481
  # 绑定 handler
470
482
  related_handlers = (
471
483
  star_handlers_registry.get_handlers_by_module_name(
472
- metadata.module_path
484
+ metadata.module_path,
473
485
  )
474
486
  )
475
487
  for handler in related_handlers:
@@ -505,7 +517,7 @@ class PluginManager:
505
517
  else:
506
518
  # v3.4.0 以前的方式注册插件
507
519
  logger.debug(
508
- f"插件 {path} 未通过装饰器注册。尝试通过旧版本方式载入。"
520
+ f"插件 {path} 未通过装饰器注册。尝试通过旧版本方式载入。",
509
521
  )
510
522
  classes = self._get_classes(module)
511
523
 
@@ -514,19 +526,21 @@ class PluginManager:
514
526
  if plugin_config:
515
527
  try:
516
528
  obj = getattr(module, classes[0])(
517
- context=self.context, config=plugin_config
529
+ context=self.context,
530
+ config=plugin_config,
518
531
  ) # 实例化插件类
519
532
  except TypeError as _:
520
533
  obj = getattr(module, classes[0])(
521
- context=self.context
534
+ context=self.context,
522
535
  ) # 实例化插件类
523
536
  else:
524
537
  obj = getattr(module, classes[0])(
525
- context=self.context
538
+ context=self.context,
526
539
  ) # 实例化插件类
527
540
 
528
541
  metadata = self._load_plugin_metadata(
529
- plugin_path=plugin_dir_path, plugin_obj=obj
542
+ plugin_path=plugin_dir_path,
543
+ plugin_obj=obj,
530
544
  )
531
545
  if not metadata:
532
546
  raise Exception(f"无法找到插件 {plugin_dir_path} 的元数据。")
@@ -552,7 +566,7 @@ class PluginManager:
552
566
 
553
567
  full_names = []
554
568
  for handler in star_handlers_registry.get_handlers_by_module_name(
555
- metadata.module_path
569
+ metadata.module_path,
556
570
  ):
557
571
  full_names.append(handler.handler_full_name)
558
572
 
@@ -562,7 +576,8 @@ class PluginManager:
562
576
  and handler.handler_name in alter_cmd[metadata.name]
563
577
  ):
564
578
  cmd_type = alter_cmd[metadata.name][handler.handler_name].get(
565
- "permission", "member"
579
+ "permission",
580
+ "member",
566
581
  )
567
582
  found_permission_filter = False
568
583
  for filter_ in handler.event_filters:
@@ -578,12 +593,12 @@ class PluginManager:
578
593
  PermissionTypeFilter(
579
594
  PermissionType.ADMIN
580
595
  if cmd_type == "admin"
581
- else PermissionType.MEMBER
582
- )
596
+ else PermissionType.MEMBER,
597
+ ),
583
598
  )
584
599
 
585
600
  logger.debug(
586
- f"插入权限过滤器 {cmd_type} 到 {metadata.name} 的 {handler.handler_name} 方法。"
601
+ f"插入权限过滤器 {cmd_type} 到 {metadata.name} 的 {handler.handler_name} 方法。",
587
602
  )
588
603
 
589
604
  metadata.star_handler_full_names = full_names
@@ -598,7 +613,7 @@ class PluginManager:
598
613
  for line in errors.split("\n"):
599
614
  logger.error(f"| {line}")
600
615
  logger.error("----------------------------------")
601
- fail_rec += f"加载 {root_dir_name} 插件时出现问题,原因 {str(e)}。\n"
616
+ fail_rec += f"加载 {root_dir_name} 插件时出现问题,原因 {e!s}。\n"
602
617
 
603
618
  # 清除 pip.main 导致的多余的 logging handlers
604
619
  for handler in logging.root.handlers[:]:
@@ -606,9 +621,8 @@ class PluginManager:
606
621
 
607
622
  if not fail_rec:
608
623
  return True, None
609
- else:
610
- self.failed_plugin_info = fail_rec
611
- return False, fail_rec
624
+ self.failed_plugin_info = fail_rec
625
+ return False, fail_rec
612
626
 
613
627
  async def install_plugin(self, repo_url: str, proxy=""):
614
628
  """从仓库 URL 安装插件
@@ -624,6 +638,7 @@ class PluginManager:
624
638
  - repo: 插件的仓库 URL
625
639
  - readme: README.md 文件的内容(如果存在)
626
640
  如果找不到插件元数据则返回 None。
641
+
627
642
  """
628
643
  async with self._pm_lock:
629
644
  plugin_path = await self.updator.install(repo_url, proxy)
@@ -652,7 +667,7 @@ class PluginManager:
652
667
  readme_content = f.read()
653
668
  except Exception as e:
654
669
  logger.warning(
655
- f"读取插件 {dir_name} 的 README.md 文件失败: {str(e)}"
670
+ f"读取插件 {dir_name} 的 README.md 文件失败: {e!s}",
656
671
  )
657
672
 
658
673
  plugin_info = None
@@ -665,14 +680,22 @@ class PluginManager:
665
680
 
666
681
  return plugin_info
667
682
 
668
- async def uninstall_plugin(self, plugin_name: str):
683
+ async def uninstall_plugin(
684
+ self,
685
+ plugin_name: str,
686
+ delete_config: bool = False,
687
+ delete_data: bool = False,
688
+ ):
669
689
  """卸载指定的插件。
670
690
 
671
691
  Args:
672
692
  plugin_name (str): 要卸载的插件名称
693
+ delete_config (bool): 是否删除插件配置文件,默认为 False
694
+ delete_data (bool): 是否删除插件数据,默认为 False
673
695
 
674
696
  Raises:
675
697
  Exception: 当插件不存在、是保留插件时,或删除插件文件夹失败时抛出异常
698
+
676
699
  """
677
700
  async with self._pm_lock:
678
701
  plugin = self.context.get_registered_star(plugin_name)
@@ -689,7 +712,7 @@ class PluginManager:
689
712
  except Exception as e:
690
713
  logger.warning(traceback.format_exc())
691
714
  logger.warning(
692
- f"插件 {plugin_name} 未被正常终止 {str(e)}, 可能会导致资源泄露等问题。"
715
+ f"插件 {plugin_name} 未被正常终止 {e!s}, 可能会导致资源泄露等问题。",
693
716
  )
694
717
 
695
718
  # 从 star_registry 和 star_map 中删除
@@ -698,12 +721,58 @@ class PluginManager:
698
721
 
699
722
  await self._unbind_plugin(plugin_name, plugin.module_path)
700
723
 
724
+ # 删除插件文件夹
701
725
  try:
702
726
  remove_dir(os.path.join(ppath, root_dir_name))
703
727
  except Exception as e:
704
728
  raise Exception(
705
- f"移除插件成功,但是删除插件文件夹失败: {str(e)}。您可以手动删除该文件夹,位于 addons/plugins/ 下。"
729
+ f"移除插件成功,但是删除插件文件夹失败: {e!s}。您可以手动删除该文件夹,位于 addons/plugins/ 下。",
730
+ )
731
+
732
+ # 删除插件配置文件
733
+ if delete_config and root_dir_name:
734
+ config_file = os.path.join(
735
+ self.plugin_config_path,
736
+ f"{root_dir_name}_config.json",
737
+ )
738
+ if os.path.exists(config_file):
739
+ try:
740
+ os.remove(config_file)
741
+ logger.info(f"已删除插件 {plugin_name} 的配置文件")
742
+ except Exception as e:
743
+ logger.warning(f"删除插件配置文件失败: {e!s}")
744
+
745
+ # 删除插件持久化数据
746
+ # 注意:需要检查两个可能的目录名(plugin_data 和 plugins_data)
747
+ # data/temp 目录可能被多个插件共享,不自动删除以防误删
748
+ if delete_data and root_dir_name:
749
+ data_base_dir = os.path.dirname(ppath) # data/
750
+
751
+ # 删除 data/plugin_data 下的插件持久化数据(单数形式,新版本)
752
+ plugin_data_dir = os.path.join(
753
+ data_base_dir, "plugin_data", root_dir_name
706
754
  )
755
+ if os.path.exists(plugin_data_dir):
756
+ try:
757
+ remove_dir(plugin_data_dir)
758
+ logger.info(
759
+ f"已删除插件 {plugin_name} 的持久化数据 (plugin_data)"
760
+ )
761
+ except Exception as e:
762
+ logger.warning(f"删除插件持久化数据失败 (plugin_data): {e!s}")
763
+
764
+ # 删除 data/plugins_data 下的插件持久化数据(复数形式,旧版本兼容)
765
+ plugins_data_dir = os.path.join(
766
+ data_base_dir, "plugins_data", root_dir_name
767
+ )
768
+ if os.path.exists(plugins_data_dir):
769
+ try:
770
+ remove_dir(plugins_data_dir)
771
+ logger.info(
772
+ f"已删除插件 {plugin_name} 的持久化数据 (plugins_data)"
773
+ )
774
+ except Exception as e:
775
+ logger.warning(f"删除插件持久化数据失败 (plugins_data): {e!s}")
707
776
 
708
777
  async def _unbind_plugin(self, plugin_name: str, plugin_module_path: str):
709
778
  """解绑并移除一个插件。
@@ -711,6 +780,7 @@ class PluginManager:
711
780
  Args:
712
781
  plugin_name: 要解绑的插件名称
713
782
  plugin_module_path: 插件的完整模块路径
783
+
714
784
  """
715
785
  plugin = None
716
786
  del star_map[plugin_module_path]
@@ -720,10 +790,10 @@ class PluginManager:
720
790
  del star_registry[i]
721
791
  break
722
792
  for handler in star_handlers_registry.get_handlers_by_module_name(
723
- plugin_module_path
793
+ plugin_module_path,
724
794
  ):
725
795
  logger.info(
726
- f"移除了插件 {plugin_name} 的处理函数 {handler.handler_name} ({len(star_handlers_registry)})"
796
+ f"移除了插件 {plugin_name} 的处理函数 {handler.handler_name} ({len(star_handlers_registry)})",
727
797
  )
728
798
  star_handlers_registry.remove(handler)
729
799
 
@@ -734,11 +804,25 @@ class PluginManager:
734
804
  ]:
735
805
  del star_handlers_registry.star_handlers_map[k]
736
806
 
807
+ # llm_tools 中移除该插件的工具函数绑定
808
+ to_remove = []
809
+ for func_tool in llm_tools.func_list:
810
+ mp = func_tool.handler_module_path
811
+ if (
812
+ mp
813
+ and mp.startswith(plugin_module_path)
814
+ and not mp.endswith(("packages", "data.plugins"))
815
+ ):
816
+ to_remove.append(func_tool)
817
+ for func_tool in to_remove:
818
+ llm_tools.func_list.remove(func_tool)
819
+
737
820
  if plugin is None:
738
821
  return
739
822
 
740
823
  self._purge_modules(
741
- root_dir_name=plugin.root_dir_name, is_reserved=plugin.reserved
824
+ root_dir_name=plugin.root_dir_name,
825
+ is_reserved=plugin.reserved,
742
826
  )
743
827
 
744
828
  async def update_plugin(self, plugin_name: str, proxy=""):
@@ -753,8 +837,7 @@ class PluginManager:
753
837
  await self.reload(plugin_name)
754
838
 
755
839
  async def turn_off_plugin(self, plugin_name: str):
756
- """
757
- 禁用一个插件。
840
+ """禁用一个插件。
758
841
  调用插件的 terminate() 方法,
759
842
  将插件的 module_path 加入到 data/shared_preferences.json 的 inactivated_plugins 列表中。
760
843
  并且同时将插件启用的 llm_tool 禁用。
@@ -773,12 +856,18 @@ class PluginManager:
773
856
  inactivated_plugins.append(plugin.module_path)
774
857
 
775
858
  inactivated_llm_tools: list = list(
776
- set(await sp.global_get("inactivated_llm_tools", []))
859
+ set(await sp.global_get("inactivated_llm_tools", [])),
777
860
  ) # 后向兼容
778
861
 
779
862
  # 禁用插件启用的 llm_tool
780
863
  for func_tool in llm_tools.func_list:
781
- if func_tool.handler_module_path == plugin.module_path:
864
+ mp = func_tool.handler_module_path
865
+ if (
866
+ plugin.module_path
867
+ and mp
868
+ and plugin.module_path.startswith(mp)
869
+ and not mp.endswith(("packages", "data.plugins"))
870
+ ):
782
871
  func_tool.active = False
783
872
  if func_tool.name not in inactivated_llm_tools:
784
873
  inactivated_llm_tools.append(func_tool.name)
@@ -803,7 +892,8 @@ class PluginManager:
803
892
 
804
893
  if "__del__" in star_metadata.star_cls_type.__dict__:
805
894
  asyncio.get_event_loop().run_in_executor(
806
- None, star_metadata.star_cls.__del__
895
+ None,
896
+ star_metadata.star_cls.__del__,
807
897
  )
808
898
  elif "terminate" in star_metadata.star_cls_type.__dict__:
809
899
  await star_metadata.star_cls.terminate()
@@ -820,8 +910,12 @@ class PluginManager:
820
910
 
821
911
  # 启用插件启用的 llm_tool
822
912
  for func_tool in llm_tools.func_list:
913
+ mp = func_tool.handler_module_path
823
914
  if (
824
- func_tool.handler_module_path == plugin.module_path
915
+ plugin.module_path
916
+ and mp
917
+ and plugin.module_path.startswith(mp)
918
+ and not mp.endswith(("packages", "data.plugins"))
825
919
  and func_tool.name in inactivated_llm_tools
826
920
  ):
827
921
  inactivated_llm_tools.remove(func_tool.name)
@@ -830,8 +924,6 @@ class PluginManager:
830
924
 
831
925
  await self.reload(plugin_name)
832
926
 
833
- # plugin.activated = True
834
-
835
927
  async def install_plugin_from_file(self, zip_file_path: str):
836
928
  dir_name = os.path.basename(zip_file_path).replace(".zip", "")
837
929
  dir_name = dir_name.removesuffix("-master").removesuffix("-main").lower()
@@ -842,7 +934,7 @@ class PluginManager:
842
934
  try:
843
935
  os.remove(zip_file_path)
844
936
  except BaseException as e:
845
- logger.warning(f"删除插件压缩包失败: {str(e)}")
937
+ logger.warning(f"删除插件压缩包失败: {e!s}")
846
938
  # await self.reload()
847
939
  await self.load(specified_dir_name=dir_name)
848
940
 
@@ -866,7 +958,7 @@ class PluginManager:
866
958
  with open(readme_path, encoding="utf-8") as f:
867
959
  readme_content = f.read()
868
960
  except Exception as e:
869
- logger.warning(f"读取插件 {dir_name} 的 README.md 文件失败: {str(e)}")
961
+ logger.warning(f"读取插件 {dir_name} 的 README.md 文件失败: {e!s}")
870
962
 
871
963
  plugin_info = None
872
964
  if plugin: