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,11 +1,13 @@
1
1
  # astrbot/dashboard/routes/t2i.py
2
2
 
3
3
  from dataclasses import asdict
4
+
4
5
  from quart import jsonify, request
5
6
 
6
7
  from astrbot.core import logger
7
8
  from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
8
9
  from astrbot.core.utils.t2i.template_manager import TemplateManager
10
+
9
11
  from .route import Response, Route, RouteContext
10
12
 
11
13
 
@@ -49,7 +51,7 @@ class T2iRoute(Route):
49
51
  try:
50
52
  active_template = self.config.get("t2i_active_template", "base")
51
53
  return jsonify(
52
- asdict(Response().ok(data={"active_template": active_template}))
54
+ asdict(Response().ok(data={"active_template": active_template})),
53
55
  )
54
56
  except Exception as e:
55
57
  logger.error("Error in get_active_template", exc_info=True)
@@ -62,7 +64,7 @@ class T2iRoute(Route):
62
64
  try:
63
65
  content = self.manager.get_template(name)
64
66
  return jsonify(
65
- asdict(Response().ok(data={"name": name, "content": content}))
67
+ asdict(Response().ok(data={"name": name, "content": content})),
66
68
  )
67
69
  except FileNotFoundError:
68
70
  response = jsonify(asdict(Response().error("Template not found")))
@@ -81,7 +83,7 @@ class T2iRoute(Route):
81
83
  content = data.get("content")
82
84
  if not name or not content:
83
85
  response = jsonify(
84
- asdict(Response().error("Name and content are required."))
86
+ asdict(Response().error("Name and content are required.")),
85
87
  )
86
88
  response.status_code = 400
87
89
  return response
@@ -91,15 +93,16 @@ class T2iRoute(Route):
91
93
  response = jsonify(
92
94
  asdict(
93
95
  Response().ok(
94
- data={"name": name}, message="Template created successfully."
95
- )
96
- )
96
+ data={"name": name},
97
+ message="Template created successfully.",
98
+ ),
99
+ ),
97
100
  )
98
101
  response.status_code = 201
99
102
  return response
100
103
  except FileExistsError:
101
104
  response = jsonify(
102
- asdict(Response().error("Template with this name already exists."))
105
+ asdict(Response().error("Template with this name already exists.")),
103
106
  )
104
107
  response.status_code = 409
105
108
  return response
@@ -149,7 +152,7 @@ class T2iRoute(Route):
149
152
  name = name.strip()
150
153
  self.manager.delete_template(name)
151
154
  return jsonify(
152
- asdict(Response().ok(message="Template deleted successfully."))
155
+ asdict(Response().ok(message="Template deleted successfully.")),
153
156
  )
154
157
  except FileNotFoundError:
155
158
  response = jsonify(asdict(Response().error("Template not found.")))
@@ -189,7 +192,7 @@ class T2iRoute(Route):
189
192
 
190
193
  except FileNotFoundError:
191
194
  response = jsonify(
192
- asdict(Response().error(f"模板 '{name}' 不存在,无法应用。"))
195
+ asdict(Response().error(f"模板 '{name}' 不存在,无法应用。")),
193
196
  )
194
197
  response.status_code = 404
195
198
  return response
@@ -215,9 +218,9 @@ class T2iRoute(Route):
215
218
  return jsonify(
216
219
  asdict(
217
220
  Response().ok(
218
- message="Default template has been reset and activated."
219
- )
220
- )
221
+ message="Default template has been reset and activated.",
222
+ ),
223
+ ),
221
224
  )
222
225
  except FileNotFoundError as e:
223
226
  response = jsonify(asdict(Response().error(str(e))))
@@ -13,7 +13,9 @@ DEFAULT_MCP_CONFIG = {"mcpServers": {}}
13
13
 
14
14
  class ToolsRoute(Route):
15
15
  def __init__(
16
- self, context: RouteContext, core_lifecycle: AstrBotCoreLifecycle
16
+ self,
17
+ context: RouteContext,
18
+ core_lifecycle: AstrBotCoreLifecycle,
17
19
  ) -> None:
18
20
  super().__init__(context)
19
21
  self.core_lifecycle = core_lifecycle
@@ -64,7 +66,7 @@ class ToolsRoute(Route):
64
66
  return Response().ok(servers).__dict__
65
67
  except Exception as e:
66
68
  logger.error(traceback.format_exc())
67
- return Response().error(f"获取 MCP 服务器列表失败: {str(e)}").__dict__
69
+ return Response().error(f"获取 MCP 服务器列表失败: {e!s}").__dict__
68
70
 
69
71
  async def add_mcp_server(self):
70
72
  try:
@@ -105,23 +107,22 @@ class ToolsRoute(Route):
105
107
  if self.tool_mgr.save_mcp_config(config):
106
108
  try:
107
109
  await self.tool_mgr.enable_mcp_server(
108
- name, server_config, timeout=30
110
+ name,
111
+ server_config,
112
+ timeout=30,
109
113
  )
110
114
  except TimeoutError:
111
115
  return Response().error(f"启用 MCP 服务器 {name} 超时。").__dict__
112
116
  except Exception as e:
113
117
  logger.error(traceback.format_exc())
114
118
  return (
115
- Response()
116
- .error(f"启用 MCP 服务器 {name} 失败: {str(e)}")
117
- .__dict__
119
+ Response().error(f"启用 MCP 服务器 {name} 失败: {e!s}").__dict__
118
120
  )
119
121
  return Response().ok(None, f"成功添加 MCP 服务器 {name}").__dict__
120
- else:
121
- return Response().error("保存配置失败").__dict__
122
+ return Response().error("保存配置失败").__dict__
122
123
  except Exception as e:
123
124
  logger.error(traceback.format_exc())
124
- return Response().error(f"添加 MCP 服务器失败: {str(e)}").__dict__
125
+ return Response().error(f"添加 MCP 服务器失败: {e!s}").__dict__
125
126
 
126
127
  async def update_mcp_server(self):
127
128
  try:
@@ -139,7 +140,8 @@ class ToolsRoute(Route):
139
140
 
140
141
  # 获取活动状态
141
142
  active = server_data.get(
142
- "active", config["mcpServers"][name].get("active", True)
143
+ "active",
144
+ config["mcpServers"][name].get("active", True),
143
145
  )
144
146
 
145
147
  # 创建新的配置对象
@@ -177,19 +179,21 @@ class ToolsRoute(Route):
177
179
  except TimeoutError as e:
178
180
  return (
179
181
  Response()
180
- .error(f"启用前停用 MCP 服务器时 {name} 超时: {str(e)}")
182
+ .error(f"启用前停用 MCP 服务器时 {name} 超时: {e!s}")
181
183
  .__dict__
182
184
  )
183
185
  except Exception as e:
184
186
  logger.error(traceback.format_exc())
185
187
  return (
186
188
  Response()
187
- .error(f"启用前停用 MCP 服务器时 {name} 失败: {str(e)}")
189
+ .error(f"启用前停用 MCP 服务器时 {name} 失败: {e!s}")
188
190
  .__dict__
189
191
  )
190
192
  try:
191
193
  await self.tool_mgr.enable_mcp_server(
192
- name, config["mcpServers"][name], timeout=30
194
+ name,
195
+ config["mcpServers"][name],
196
+ timeout=30,
193
197
  )
194
198
  except TimeoutError:
195
199
  return (
@@ -199,34 +203,30 @@ class ToolsRoute(Route):
199
203
  logger.error(traceback.format_exc())
200
204
  return (
201
205
  Response()
202
- .error(f"启用 MCP 服务器 {name} 失败: {str(e)}")
206
+ .error(f"启用 MCP 服务器 {name} 失败: {e!s}")
207
+ .__dict__
208
+ )
209
+ # 如果要停用服务器
210
+ elif name in self.tool_mgr.mcp_client_dict:
211
+ try:
212
+ await self.tool_mgr.disable_mcp_server(name, timeout=10)
213
+ except TimeoutError:
214
+ return (
215
+ Response().error(f"停用 MCP 服务器 {name} 超时。").__dict__
216
+ )
217
+ except Exception as e:
218
+ logger.error(traceback.format_exc())
219
+ return (
220
+ Response()
221
+ .error(f"停用 MCP 服务器 {name} 失败: {e!s}")
203
222
  .__dict__
204
223
  )
205
- else:
206
- # 如果要停用服务器
207
- if name in self.tool_mgr.mcp_client_dict:
208
- try:
209
- await self.tool_mgr.disable_mcp_server(name, timeout=10)
210
- except TimeoutError:
211
- return (
212
- Response()
213
- .error(f"停用 MCP 服务器 {name} 超时。")
214
- .__dict__
215
- )
216
- except Exception as e:
217
- logger.error(traceback.format_exc())
218
- return (
219
- Response()
220
- .error(f"停用 MCP 服务器 {name} 失败: {str(e)}")
221
- .__dict__
222
- )
223
224
 
224
225
  return Response().ok(None, f"成功更新 MCP 服务器 {name}").__dict__
225
- else:
226
- return Response().error("保存配置失败").__dict__
226
+ return Response().error("保存配置失败").__dict__
227
227
  except Exception as e:
228
228
  logger.error(traceback.format_exc())
229
- return Response().error(f"更新 MCP 服务器失败: {str(e)}").__dict__
229
+ return Response().error(f"更新 MCP 服务器失败: {e!s}").__dict__
230
230
 
231
231
  async def delete_mcp_server(self):
232
232
  try:
@@ -255,20 +255,17 @@ class ToolsRoute(Route):
255
255
  logger.error(traceback.format_exc())
256
256
  return (
257
257
  Response()
258
- .error(f"停用 MCP 服务器 {name} 失败: {str(e)}")
258
+ .error(f"停用 MCP 服务器 {name} 失败: {e!s}")
259
259
  .__dict__
260
260
  )
261
261
  return Response().ok(None, f"成功删除 MCP 服务器 {name}").__dict__
262
- else:
263
- return Response().error("保存配置失败").__dict__
262
+ return Response().error("保存配置失败").__dict__
264
263
  except Exception as e:
265
264
  logger.error(traceback.format_exc())
266
- return Response().error(f"删除 MCP 服务器失败: {str(e)}").__dict__
265
+ return Response().error(f"删除 MCP 服务器失败: {e!s}").__dict__
267
266
 
268
267
  async def test_mcp_connection(self):
269
- """
270
- 测试 MCP 服务器连接
271
- """
268
+ """测试 MCP 服务器连接"""
272
269
  try:
273
270
  server_data = await request.json
274
271
  config = server_data.get("mcp_server_config", None)
@@ -283,9 +280,8 @@ class ToolsRoute(Route):
283
280
  if len(keys) > 1:
284
281
  return Response().error("一次只能配置一个 MCP 服务器配置").__dict__
285
282
  config = config["mcpServers"][keys[0]]
286
- else:
287
- if not config:
288
- return Response().error("MCP 服务器配置不能为空").__dict__
283
+ elif not config:
284
+ return Response().error("MCP 服务器配置不能为空").__dict__
289
285
 
290
286
  tools_name = await self.tool_mgr.test_mcp_server_connection(config)
291
287
  return (
@@ -294,7 +290,7 @@ class ToolsRoute(Route):
294
290
 
295
291
  except Exception as e:
296
292
  logger.error(traceback.format_exc())
297
- return Response().error(f"测试 MCP 连接失败: {str(e)}").__dict__
293
+ return Response().error(f"测试 MCP 连接失败: {e!s}").__dict__
298
294
 
299
295
  async def get_tool_list(self):
300
296
  """获取所有注册的工具列表"""
@@ -304,7 +300,7 @@ class ToolsRoute(Route):
304
300
  return Response().ok(data=tools_dict).__dict__
305
301
  except Exception as e:
306
302
  logger.error(traceback.format_exc())
307
- return Response().error(f"获取工具列表失败: {str(e)}").__dict__
303
+ return Response().error(f"获取工具列表失败: {e!s}").__dict__
308
304
 
309
305
  async def toggle_tool(self):
310
306
  """启用或停用指定的工具"""
@@ -320,18 +316,17 @@ class ToolsRoute(Route):
320
316
  try:
321
317
  ok = self.tool_mgr.activate_llm_tool(tool_name, star_map=star_map)
322
318
  except ValueError as e:
323
- return Response().error(f"启用工具失败: {str(e)}").__dict__
319
+ return Response().error(f"启用工具失败: {e!s}").__dict__
324
320
  else:
325
321
  ok = self.tool_mgr.deactivate_llm_tool(tool_name)
326
322
 
327
323
  if ok:
328
324
  return Response().ok(None, "操作成功。").__dict__
329
- else:
330
- return Response().error(f"工具 {tool_name} 不存在或操作失败。").__dict__
325
+ return Response().error(f"工具 {tool_name} 不存在或操作失败。").__dict__
331
326
 
332
327
  except Exception as e:
333
328
  logger.error(traceback.format_exc())
334
- return Response().error(f"操作工具失败: {str(e)}").__dict__
329
+ return Response().error(f"操作工具失败: {e!s}").__dict__
335
330
 
336
331
  async def sync_provider(self):
337
332
  """同步 MCP 提供者配置"""
@@ -348,4 +343,4 @@ class ToolsRoute(Route):
348
343
  return Response().ok(message="同步成功").__dict__
349
344
  except Exception as e:
350
345
  logger.error(traceback.format_exc())
351
- return Response().error(f"同步失败: {str(e)}").__dict__
346
+ return Response().error(f"同步失败: {e!s}").__dict__
@@ -1,13 +1,15 @@
1
1
  import traceback
2
- from .route import Route, Response, RouteContext
2
+
3
3
  from quart import request
4
+
5
+ from astrbot.core import DEMO_MODE, logger, pip_installer
6
+ from astrbot.core.config.default import VERSION
4
7
  from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
8
+ from astrbot.core.db.migration.helper import check_migration_needed_v4, do_migration_v4
5
9
  from astrbot.core.updator import AstrBotUpdator
6
- from astrbot.core import logger, pip_installer
7
10
  from astrbot.core.utils.io import download_dashboard, get_dashboard_version
8
- from astrbot.core.config.default import VERSION
9
- from astrbot.core import DEMO_MODE
10
- from astrbot.core.db.migration.helper import do_migration_v4, check_migration_needed_v4
11
+
12
+ from .route import Response, Route, RouteContext
11
13
 
12
14
  CLEAR_SITE_DATA_HEADERS = {"Clear-Site-Data": '"cache"'}
13
15
 
@@ -40,12 +42,14 @@ class UpdateRoute(Route):
40
42
  data = await request.json
41
43
  pim = data.get("platform_id_map", {})
42
44
  await do_migration_v4(
43
- self.core_lifecycle.db, pim, self.core_lifecycle.astrbot_config
45
+ self.core_lifecycle.db,
46
+ pim,
47
+ self.core_lifecycle.astrbot_config,
44
48
  )
45
49
  return Response().ok(None, "迁移成功。").__dict__
46
50
  except Exception as e:
47
51
  logger.error(f"迁移失败: {traceback.format_exc()}")
48
- return Response().error(f"迁移失败: {str(e)}").__dict__
52
+ return Response().error(f"迁移失败: {e!s}").__dict__
49
53
 
50
54
  async def check_update(self):
51
55
  type_ = request.args.get("type", None)
@@ -58,20 +62,19 @@ class UpdateRoute(Route):
58
62
  .ok({"has_new_version": dv != f"v{VERSION}", "current_version": dv})
59
63
  .__dict__
60
64
  )
61
- else:
62
- ret = await self.astrbot_updator.check_update(None, None, False)
63
- return Response(
64
- status="success",
65
- message=str(ret) if ret is not None else "已经是最新版本了。",
66
- data={
67
- "version": f"v{VERSION}",
68
- "has_new_version": ret is not None,
69
- "dashboard_version": dv,
70
- "dashboard_has_new_version": bool(dv and dv != f"v{VERSION}"),
71
- },
72
- ).__dict__
65
+ ret = await self.astrbot_updator.check_update(None, None, False)
66
+ return Response(
67
+ status="success",
68
+ message=str(ret) if ret is not None else "已经是最新版本了。",
69
+ data={
70
+ "version": f"v{VERSION}",
71
+ "has_new_version": ret is not None,
72
+ "dashboard_version": dv,
73
+ "dashboard_has_new_version": bool(dv and dv != f"v{VERSION}"),
74
+ },
75
+ ).__dict__
73
76
  except Exception as e:
74
- logger.warning(f"检查更新失败: {str(e)} (不影响除项目更新外的正常使用)")
77
+ logger.warning(f"检查更新失败: {e!s} (不影响除项目更新外的正常使用)")
75
78
  return Response().error(e.__str__()).__dict__
76
79
 
77
80
  async def get_releases(self):
@@ -98,7 +101,9 @@ class UpdateRoute(Route):
98
101
 
99
102
  try:
100
103
  await self.astrbot_updator.update(
101
- latest=latest, version=version, proxy=proxy
104
+ latest=latest,
105
+ version=version,
106
+ proxy=proxy,
102
107
  )
103
108
 
104
109
  try:
@@ -121,13 +126,12 @@ class UpdateRoute(Route):
121
126
  .__dict__
122
127
  )
123
128
  return ret, 200, CLEAR_SITE_DATA_HEADERS
124
- else:
125
- ret = (
126
- Response()
127
- .ok(None, "更新成功,AstrBot 将在下次启动时应用新的代码。")
128
- .__dict__
129
- )
130
- return ret, 200, CLEAR_SITE_DATA_HEADERS
129
+ ret = (
130
+ Response()
131
+ .ok(None, "更新成功,AstrBot 将在下次启动时应用新的代码。")
132
+ .__dict__
133
+ )
134
+ return ret, 200, CLEAR_SITE_DATA_HEADERS
131
135
  except Exception as e:
132
136
  logger.error(f"/api/update_project: {traceback.format_exc()}")
133
137
  return Response().error(e.__str__()).__dict__
@@ -39,7 +39,7 @@ class AstrBotDashboard:
39
39
  self.data_path = os.path.abspath(webui_dir)
40
40
  else:
41
41
  self.data_path = os.path.abspath(
42
- os.path.join(get_astrbot_data_path(), "dist")
42
+ os.path.join(get_astrbot_data_path(), "dist"),
43
43
  )
44
44
 
45
45
  self.app = Quart("dashboard", static_folder=self.data_path, static_url_path="/")
@@ -53,11 +53,15 @@ class AstrBotDashboard:
53
53
  logging.getLogger(self.app.name).removeHandler(default_handler)
54
54
  self.context = RouteContext(self.config, self.app)
55
55
  self.ur = UpdateRoute(
56
- self.context, core_lifecycle.astrbot_updator, core_lifecycle
56
+ self.context,
57
+ core_lifecycle.astrbot_updator,
58
+ core_lifecycle,
57
59
  )
58
60
  self.sr = StatRoute(self.context, db, core_lifecycle)
59
61
  self.pr = PluginRoute(
60
- self.context, core_lifecycle, core_lifecycle.plugin_manager
62
+ self.context,
63
+ core_lifecycle,
64
+ core_lifecycle.plugin_manager,
61
65
  )
62
66
  self.cr = ConfigRoute(self.context, core_lifecycle)
63
67
  self.lr = LogRoute(self.context, core_lifecycle.log_broker)
@@ -68,7 +72,9 @@ class AstrBotDashboard:
68
72
  self.conversation_route = ConversationRoute(self.context, db, core_lifecycle)
69
73
  self.file_route = FileRoute(self.context)
70
74
  self.session_management_route = SessionManagementRoute(
71
- self.context, db, core_lifecycle
75
+ self.context,
76
+ db,
77
+ core_lifecycle,
72
78
  )
73
79
  self.persona_route = PersonaRoute(self.context, db, core_lifecycle)
74
80
  self.t2i_route = T2iRoute(self.context, core_lifecycle)
@@ -85,9 +91,7 @@ class AstrBotDashboard:
85
91
  self._init_jwt_secret()
86
92
 
87
93
  async def srv_plug_route(self, subpath, *args, **kwargs):
88
- """
89
- 插件路由
90
- """
94
+ """插件路由"""
91
95
  registered_web_apis = self.core_lifecycle.star_context.registered_web_apis
92
96
  for api in registered_web_apis:
93
97
  route, view_handler, methods, _ = api
@@ -97,18 +101,17 @@ class AstrBotDashboard:
97
101
 
98
102
  async def auth_middleware(self):
99
103
  if not request.path.startswith("/api"):
100
- return
104
+ return None
101
105
  allowed_endpoints = ["/api/auth/login", "/api/file"]
102
106
  if any(request.path.startswith(prefix) for prefix in allowed_endpoints):
103
- return
104
- # claim jwt
107
+ return None
108
+ # 声明 JWT
105
109
  token = request.headers.get("Authorization")
106
110
  if not token:
107
111
  r = jsonify(Response().error("未授权").__dict__)
108
112
  r.status_code = 401
109
113
  return r
110
- if token.startswith("Bearer "):
111
- token = token[7:]
114
+ token = token.removeprefix("Bearer ")
112
115
  try:
113
116
  payload = jwt.decode(token, self._jwt_secret, algorithms=["HS256"])
114
117
  g.username = payload["username"]
@@ -122,9 +125,7 @@ class AstrBotDashboard:
122
125
  return r
123
126
 
124
127
  def check_port_in_use(self, port: int) -> bool:
125
- """
126
- 跨平台检测端口是否被占用
127
- """
128
+ """跨平台检测端口是否被占用"""
128
129
  try:
129
130
  # 创建 IPv4 TCP Socket
130
131
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -136,7 +137,7 @@ class AstrBotDashboard:
136
137
  # result 为 0 表示端口被占用
137
138
  return result == 0
138
139
  except Exception as e:
139
- logger.warning(f"检查端口 {port} 时发生错误: {str(e)}")
140
+ logger.warning(f"检查端口 {port} 时发生错误: {e!s}")
140
141
  # 如果出现异常,保守起见认为端口可能被占用
141
142
  return True
142
143
 
@@ -157,10 +158,10 @@ class AstrBotDashboard:
157
158
  ]
158
159
  return "\n ".join(proc_info)
159
160
  except (psutil.NoSuchProcess, psutil.AccessDenied) as e:
160
- return f"无法获取进程详细信息(可能需要管理员权限): {str(e)}"
161
+ return f"无法获取进程详细信息(可能需要管理员权限): {e!s}"
161
162
  return "未找到占用进程"
162
163
  except Exception as e:
163
- return f"获取进程信息失败: {str(e)}"
164
+ return f"获取进程信息失败: {e!s}"
164
165
 
165
166
  def _init_jwt_secret(self):
166
167
  if not self.config.get("dashboard", {}).get("jwt_secret", None):
@@ -182,13 +183,13 @@ class AstrBotDashboard:
182
183
 
183
184
  if not enable:
184
185
  logger.info("WebUI 已被禁用")
185
- return
186
+ return None
186
187
 
187
188
  logger.info(f"正在启动 WebUI, 监听地址: http://{host}:{port}")
188
189
 
189
190
  if host == "0.0.0.0":
190
191
  logger.info(
191
- "提示: WebUI 将监听所有网络接口,请注意安全。(可在 data/cmd_config.json 中配置 dashboard.host 以修改 host)"
192
+ "提示: WebUI 将监听所有网络接口,请注意安全。(可在 data/cmd_config.json 中配置 dashboard.host 以修改 host)",
192
193
  )
193
194
 
194
195
  if host not in ["localhost", "127.0.0.1"]:
@@ -207,16 +208,17 @@ class AstrBotDashboard:
207
208
  f"请确保:\n"
208
209
  f"1. 没有其他 AstrBot 实例正在运行\n"
209
210
  f"2. 端口 {port} 没有被其他程序占用\n"
210
- f"3. 如需使用其他端口,请修改配置文件"
211
+ f"3. 如需使用其他端口,请修改配置文件",
211
212
  )
212
213
 
213
214
  raise Exception(f"端口 {port} 已被占用")
214
215
 
215
- display = f"\n ✨✨✨\n AstrBot v{VERSION} WebUI 已启动,可访问\n\n"
216
- display += f" ➜ 本地: http://localhost:{port}\n"
216
+ parts = [f"\n ✨✨✨\n AstrBot v{VERSION} WebUI 已启动,可访问\n\n"]
217
+ parts.append(f" ➜ 本地: http://localhost:{port}\n")
217
218
  for ip in ip_addr:
218
- display += f" ➜ 网络: http://{ip}:{port}\n"
219
- display += " ➜ 默认用户名和密码: astrbot\n ✨✨✨\n"
219
+ parts.append(f" ➜ 网络: http://{ip}:{port}\n")
220
+ parts.append(" ➜ 默认用户名和密码: astrbot\n ✨✨✨\n")
221
+ display = "".join(parts)
220
222
 
221
223
  if not ip_addr:
222
224
  display += (
@@ -226,7 +228,9 @@ class AstrBotDashboard:
226
228
  logger.info(display)
227
229
 
228
230
  return self.app.run_task(
229
- host=host, port=port, shutdown_trigger=self.shutdown_trigger
231
+ host=host,
232
+ port=port,
233
+ shutdown_trigger=self.shutdown_trigger,
230
234
  )
231
235
 
232
236
  async def shutdown_trigger(self):
@@ -2,14 +2,17 @@ import base64
2
2
  import os
3
3
  import traceback
4
4
  from io import BytesIO
5
+
5
6
  from astrbot.api import logger
7
+ from astrbot.core.db.vec_db.faiss_impl import FaissVecDB
6
8
  from astrbot.core.knowledge_base.kb_helper import KBHelper
7
9
  from astrbot.core.knowledge_base.kb_mgr import KnowledgeBaseManager
8
- from astrbot.core.db.vec_db.faiss_impl import FaissVecDB
9
10
 
10
11
 
11
12
  async def generate_tsne_visualization(
12
- query: str, kb_names: list[str], kb_manager: KnowledgeBaseManager
13
+ query: str,
14
+ kb_names: list[str],
15
+ kb_manager: KnowledgeBaseManager,
13
16
  ) -> str | None:
14
17
  """生成 t-SNE 可视化图片
15
18
 
@@ -20,18 +23,19 @@ async def generate_tsne_visualization(
20
23
 
21
24
  Returns:
22
25
  图片路径或 None
26
+
23
27
  """
24
28
  try:
25
29
  import faiss
26
- import numpy as np
27
30
  import matplotlib
31
+ import numpy as np
28
32
 
29
33
  matplotlib.use("Agg") # 使用非交互式后端
30
34
  import matplotlib.pyplot as plt
31
35
  from sklearn.manifold import TSNE
32
36
  except ImportError as e:
33
37
  raise Exception(
34
- "缺少必要的库以生成 t-SNE 可视化。请安装 matplotlib 和 scikit-learn: {e}"
38
+ "缺少必要的库以生成 t-SNE 可视化。请安装 matplotlib 和 scikit-learn: {e}",
35
39
  ) from e
36
40
 
37
41
  try:
@@ -1,8 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AstrBot
3
- Version: 4.5.0
3
+ Version: 4.5.2
4
4
  Summary: 易上手的多平台 LLM 聊天机器人及开发框架
5
5
  License-File: LICENSE
6
+ Keywords: Astrbot,Astrbot Module,Astrbot Plugin
6
7
  Requires-Python: >=3.10
7
8
  Requires-Dist: aiocqhttp>=1.4.4
8
9
  Requires-Dist: aiodocker>=0.24.0
@@ -54,6 +55,7 @@ Requires-Dist: telegramify-markdown>=0.5.1
54
55
  Requires-Dist: watchfiles>=1.0.5
55
56
  Requires-Dist: websockets>=15.0.1
56
57
  Requires-Dist: wechatpy>=1.8.18
58
+ Requires-Dist: xinference-client
57
59
  Description-Content-Type: text/markdown
58
60
 
59
61
  ![AstrBot-Logo-Simplified](https://github.com/user-attachments/assets/ffd99b6b-3272-4682-beaa-6fe74250f7d9)
@@ -66,7 +68,7 @@ Description-Content-Type: text/markdown
66
68
 
67
69
  <div>
68
70
  <a href="https://trendshift.io/repositories/12875" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12875" alt="Soulter%2FAstrBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
69
- <a href="https://hellogithub.com/repository/AstrBotDevs/AstrBot" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=d127d50cd5e54c5382328acc3bb25483&claim_uid=ZO9by7qCXgSd6Lp" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
71
+ <a href="https://hellogithub.com/repository/AstrBotDevs/AstrBot" target="_blank"><img src="https://api.hellogithub.com/v1/widgets/recommend.svg?rid=d127d50cd5e54c5382328acc3bb25483&claim_uid=ZO9by7qCXgSd6Lp&t=1" alt="Featured|HelloGitHub" style="width: 250px; height: 54px;" width="250" height="54" /></a>
70
72
  </div>
71
73
 
72
74
  <br>