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,12 +1,17 @@
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
11
+
12
+ from .route import Response, Route, RouteContext
13
+
14
+ CLEAR_SITE_DATA_HEADERS = {"Clear-Site-Data": '"cache"'}
10
15
 
11
16
 
12
17
  class UpdateRoute(Route):
@@ -23,11 +28,29 @@ class UpdateRoute(Route):
23
28
  "/update/do": ("POST", self.update_project),
24
29
  "/update/dashboard": ("POST", self.update_dashboard),
25
30
  "/update/pip-install": ("POST", self.install_pip_package),
31
+ "/update/migration": ("POST", self.do_migration),
26
32
  }
27
33
  self.astrbot_updator = astrbot_updator
28
34
  self.core_lifecycle = core_lifecycle
29
35
  self.register_routes()
30
36
 
37
+ async def do_migration(self):
38
+ need_migration = await check_migration_needed_v4(self.core_lifecycle.db)
39
+ if not need_migration:
40
+ return Response().ok(None, "不需要进行迁移。").__dict__
41
+ try:
42
+ data = await request.json
43
+ pim = data.get("platform_id_map", {})
44
+ await do_migration_v4(
45
+ self.core_lifecycle.db,
46
+ pim,
47
+ self.core_lifecycle.astrbot_config,
48
+ )
49
+ return Response().ok(None, "迁移成功。").__dict__
50
+ except Exception as e:
51
+ logger.error(f"迁移失败: {traceback.format_exc()}")
52
+ return Response().error(f"迁移失败: {e!s}").__dict__
53
+
31
54
  async def check_update(self):
32
55
  type_ = request.args.get("type", None)
33
56
 
@@ -39,20 +62,19 @@ class UpdateRoute(Route):
39
62
  .ok({"has_new_version": dv != f"v{VERSION}", "current_version": dv})
40
63
  .__dict__
41
64
  )
42
- else:
43
- ret = await self.astrbot_updator.check_update(None, None)
44
- return Response(
45
- status="success",
46
- message=str(ret) if ret is not None else "已经是最新版本了。",
47
- data={
48
- "version": f"v{VERSION}",
49
- "has_new_version": ret is not None,
50
- "dashboard_version": dv,
51
- "dashboard_has_new_version": dv != f"v{VERSION}",
52
- },
53
- ).__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__
54
76
  except Exception as e:
55
- logger.warning(f"检查更新失败: {str(e)} (不影响除项目更新外的正常使用)")
77
+ logger.warning(f"检查更新失败: {e!s} (不影响除项目更新外的正常使用)")
56
78
  return Response().error(e.__str__()).__dict__
57
79
 
58
80
  async def get_releases(self):
@@ -79,35 +101,37 @@ class UpdateRoute(Route):
79
101
 
80
102
  try:
81
103
  await self.astrbot_updator.update(
82
- latest=latest, version=version, proxy=proxy
104
+ latest=latest,
105
+ version=version,
106
+ proxy=proxy,
83
107
  )
84
108
 
85
- if latest:
86
- try:
87
- await download_dashboard()
88
- except Exception as e:
89
- logger.error(f"下载管理面板文件失败: {e}。")
109
+ try:
110
+ await download_dashboard(latest=latest, version=version, proxy=proxy)
111
+ except Exception as e:
112
+ logger.error(f"下载管理面板文件失败: {e}。")
90
113
 
91
114
  # pip 更新依赖
92
115
  logger.info("更新依赖中...")
93
116
  try:
94
- pip_installer.install(requirements_path="requirements.txt")
117
+ await pip_installer.install(requirements_path="requirements.txt")
95
118
  except Exception as e:
96
119
  logger.error(f"更新依赖失败: {e}")
97
120
 
98
121
  if reboot:
99
122
  await self.core_lifecycle.restart()
100
- return (
123
+ ret = (
101
124
  Response()
102
125
  .ok(None, "更新成功,AstrBot 将在 2 秒内全量重启以应用新的代码。")
103
126
  .__dict__
104
127
  )
105
- else:
106
- return (
107
- Response()
108
- .ok(None, "更新成功,AstrBot 将在下次启动时应用新的代码。")
109
- .__dict__
110
- )
128
+ 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
111
135
  except Exception as e:
112
136
  logger.error(f"/api/update_project: {traceback.format_exc()}")
113
137
  return Response().error(e.__str__()).__dict__
@@ -115,13 +139,12 @@ class UpdateRoute(Route):
115
139
  async def update_dashboard(self):
116
140
  try:
117
141
  try:
118
- await download_dashboard()
142
+ await download_dashboard(version=f"v{VERSION}", latest=False)
119
143
  except Exception as e:
120
144
  logger.error(f"下载管理面板文件失败: {e}。")
121
145
  return Response().error(f"下载管理面板文件失败: {e}").__dict__
122
- return (
123
- Response().ok(None, "更新成功。刷新页面即可应用新版本面板。").__dict__
124
- )
146
+ ret = Response().ok(None, "更新成功。刷新页面即可应用新版本面板。").__dict__
147
+ return ret, 200, CLEAR_SITE_DATA_HEADERS
125
148
  except Exception as e:
126
149
  logger.error(f"/api/update_dashboard: {traceback.format_exc()}")
127
150
  return Response().error(e.__str__()).__dict__
@@ -140,7 +163,7 @@ class UpdateRoute(Route):
140
163
  if not package:
141
164
  return Response().error("缺少参数 package 或不合法。").__dict__
142
165
  try:
143
- pip_installer.install(package, mirror=mirror)
166
+ await pip_installer.install(package, mirror=mirror)
144
167
  return Response().ok(None, "安装成功。").__dict__
145
168
  except Exception as e:
146
169
  logger.error(f"/api/update_pip: {traceback.format_exc()}")
@@ -1,22 +1,26 @@
1
- import logging
2
- import jwt
3
1
  import asyncio
2
+ import logging
4
3
  import os
5
4
  import socket
5
+
6
+ import jwt
6
7
  import psutil
7
- from astrbot.core.config.default import VERSION
8
- from quart import Quart, request, jsonify, g
8
+ from quart import Quart, g, jsonify, request
9
9
  from quart.logging import default_handler
10
+
11
+ from astrbot.core import logger
12
+ from astrbot.core.config.default import VERSION
10
13
  from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
11
- from .routes import *
12
- from .routes.route import RouteContext, Response
13
- from astrbot.core import logger, WEBUI_SK
14
14
  from astrbot.core.db import BaseDatabase
15
+ from astrbot.core.utils.astrbot_path import get_astrbot_data_path
15
16
  from astrbot.core.utils.io import get_local_ip_addresses
16
17
 
17
- DATAPATH = os.path.abspath(
18
- os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../data")
19
- )
18
+ from .routes import *
19
+ from .routes.route import Response, RouteContext
20
+ from .routes.session_management import SessionManagementRoute
21
+ from .routes.t2i import T2iRoute
22
+
23
+ APP: Quart = None
20
24
 
21
25
 
22
26
  class AstrBotDashboard:
@@ -25,11 +29,21 @@ class AstrBotDashboard:
25
29
  core_lifecycle: AstrBotCoreLifecycle,
26
30
  db: BaseDatabase,
27
31
  shutdown_event: asyncio.Event,
32
+ webui_dir: str | None = None,
28
33
  ) -> None:
29
34
  self.core_lifecycle = core_lifecycle
30
35
  self.config = core_lifecycle.astrbot_config
31
- self.data_path = os.path.abspath(os.path.join(DATAPATH, "dist"))
36
+
37
+ # 参数指定webui目录
38
+ if webui_dir and os.path.exists(webui_dir):
39
+ self.data_path = os.path.abspath(webui_dir)
40
+ else:
41
+ self.data_path = os.path.abspath(
42
+ os.path.join(get_astrbot_data_path(), "dist"),
43
+ )
44
+
32
45
  self.app = Quart("dashboard", static_folder=self.data_path, static_url_path="/")
46
+ APP = self.app # noqa
33
47
  self.app.config["MAX_CONTENT_LENGTH"] = (
34
48
  128 * 1024 * 1024
35
49
  ) # 将 Flask 允许的最大上传文件体大小设置为 128 MB
@@ -39,11 +53,15 @@ class AstrBotDashboard:
39
53
  logging.getLogger(self.app.name).removeHandler(default_handler)
40
54
  self.context = RouteContext(self.config, self.app)
41
55
  self.ur = UpdateRoute(
42
- self.context, core_lifecycle.astrbot_updator, core_lifecycle
56
+ self.context,
57
+ core_lifecycle.astrbot_updator,
58
+ core_lifecycle,
43
59
  )
44
60
  self.sr = StatRoute(self.context, db, core_lifecycle)
45
61
  self.pr = PluginRoute(
46
- self.context, core_lifecycle, core_lifecycle.plugin_manager
62
+ self.context,
63
+ core_lifecycle,
64
+ core_lifecycle.plugin_manager,
47
65
  )
48
66
  self.cr = ConfigRoute(self.context, core_lifecycle)
49
67
  self.lr = LogRoute(self.context, core_lifecycle.log_broker)
@@ -52,26 +70,50 @@ class AstrBotDashboard:
52
70
  self.chat_route = ChatRoute(self.context, db, core_lifecycle)
53
71
  self.tools_root = ToolsRoute(self.context, core_lifecycle)
54
72
  self.conversation_route = ConversationRoute(self.context, db, core_lifecycle)
73
+ self.file_route = FileRoute(self.context)
74
+ self.session_management_route = SessionManagementRoute(
75
+ self.context,
76
+ db,
77
+ core_lifecycle,
78
+ )
79
+ self.persona_route = PersonaRoute(self.context, db, core_lifecycle)
80
+ self.t2i_route = T2iRoute(self.context, core_lifecycle)
81
+ self.kb_route = KnowledgeBaseRoute(self.context, core_lifecycle)
82
+
83
+ self.app.add_url_rule(
84
+ "/api/plug/<path:subpath>",
85
+ view_func=self.srv_plug_route,
86
+ methods=["GET", "POST"],
87
+ )
55
88
 
56
89
  self.shutdown_event = shutdown_event
57
90
 
91
+ self._init_jwt_secret()
92
+
93
+ async def srv_plug_route(self, subpath, *args, **kwargs):
94
+ """插件路由"""
95
+ registered_web_apis = self.core_lifecycle.star_context.registered_web_apis
96
+ for api in registered_web_apis:
97
+ route, view_handler, methods, _ = api
98
+ if route == f"/{subpath}" and request.method in methods:
99
+ return await view_handler(*args, **kwargs)
100
+ return jsonify(Response().error("未找到该路由").__dict__)
101
+
58
102
  async def auth_middleware(self):
59
103
  if not request.path.startswith("/api"):
60
- return
61
- if request.path == "/api/auth/login":
62
- return
63
- if request.path == "/api/chat/get_file":
64
- return
65
- # claim jwt
104
+ return None
105
+ allowed_endpoints = ["/api/auth/login", "/api/file"]
106
+ if any(request.path.startswith(prefix) for prefix in allowed_endpoints):
107
+ return None
108
+ # 声明 JWT
66
109
  token = request.headers.get("Authorization")
67
110
  if not token:
68
111
  r = jsonify(Response().error("未授权").__dict__)
69
112
  r.status_code = 401
70
113
  return r
71
- if token.startswith("Bearer "):
72
- token = token[7:]
114
+ token = token.removeprefix("Bearer ")
73
115
  try:
74
- payload = jwt.decode(token, WEBUI_SK, algorithms=["HS256"])
116
+ payload = jwt.decode(token, self._jwt_secret, algorithms=["HS256"])
75
117
  g.username = payload["username"]
76
118
  except jwt.ExpiredSignatureError:
77
119
  r = jsonify(Response().error("Token 过期").__dict__)
@@ -83,9 +125,7 @@ class AstrBotDashboard:
83
125
  return r
84
126
 
85
127
  def check_port_in_use(self, port: int) -> bool:
86
- """
87
- 跨平台检测端口是否被占用
88
- """
128
+ """跨平台检测端口是否被占用"""
89
129
  try:
90
130
  # 创建 IPv4 TCP Socket
91
131
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -97,7 +137,7 @@ class AstrBotDashboard:
97
137
  # result 为 0 表示端口被占用
98
138
  return result == 0
99
139
  except Exception as e:
100
- logger.warning(f"检查端口 {port} 时发生错误: {str(e)}")
140
+ logger.warning(f"检查端口 {port} 时发生错误: {e!s}")
101
141
  # 如果出现异常,保守起见认为端口可能被占用
102
142
  return True
103
143
 
@@ -118,21 +158,38 @@ class AstrBotDashboard:
118
158
  ]
119
159
  return "\n ".join(proc_info)
120
160
  except (psutil.NoSuchProcess, psutil.AccessDenied) as e:
121
- return f"无法获取进程详细信息(可能需要管理员权限): {str(e)}"
161
+ return f"无法获取进程详细信息(可能需要管理员权限): {e!s}"
122
162
  return "未找到占用进程"
123
163
  except Exception as e:
124
- return f"获取进程信息失败: {str(e)}"
164
+ return f"获取进程信息失败: {e!s}"
165
+
166
+ def _init_jwt_secret(self):
167
+ if not self.config.get("dashboard", {}).get("jwt_secret", None):
168
+ # 如果没有设置 JWT 密钥,则生成一个新的密钥
169
+ jwt_secret = os.urandom(32).hex()
170
+ self.config["dashboard"]["jwt_secret"] = jwt_secret
171
+ self.config.save_config()
172
+ logger.info("Initialized random JWT secret for dashboard.")
173
+ self._jwt_secret = self.config["dashboard"]["jwt_secret"]
125
174
 
126
175
  def run(self):
127
176
  ip_addr = []
128
- port = self.core_lifecycle.astrbot_config["dashboard"].get("port", 6185)
177
+ if p := os.environ.get("DASHBOARD_PORT"):
178
+ port = p
179
+ else:
180
+ port = self.core_lifecycle.astrbot_config["dashboard"].get("port", 6185)
129
181
  host = self.core_lifecycle.astrbot_config["dashboard"].get("host", "0.0.0.0")
182
+ enable = self.core_lifecycle.astrbot_config["dashboard"].get("enable", True)
183
+
184
+ if not enable:
185
+ logger.info("WebUI 已被禁用")
186
+ return None
130
187
 
131
188
  logger.info(f"正在启动 WebUI, 监听地址: http://{host}:{port}")
132
189
 
133
190
  if host == "0.0.0.0":
134
191
  logger.info(
135
- "提示: WebUI 将监听所有网络接口,请注意安全。(可在 data/cmd_config.json 中配置 dashboard.host 以修改 host)"
192
+ "提示: WebUI 将监听所有网络接口,请注意安全。(可在 data/cmd_config.json 中配置 dashboard.host 以修改 host)",
136
193
  )
137
194
 
138
195
  if host not in ["localhost", "127.0.0.1"]:
@@ -151,16 +208,17 @@ class AstrBotDashboard:
151
208
  f"请确保:\n"
152
209
  f"1. 没有其他 AstrBot 实例正在运行\n"
153
210
  f"2. 端口 {port} 没有被其他程序占用\n"
154
- f"3. 如需使用其他端口,请修改配置文件"
211
+ f"3. 如需使用其他端口,请修改配置文件",
155
212
  )
156
213
 
157
214
  raise Exception(f"端口 {port} 已被占用")
158
215
 
159
- display = f"\n ✨✨✨\n AstrBot v{VERSION} WebUI 已启动,可访问\n\n"
160
- 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")
161
218
  for ip in ip_addr:
162
- display += f" ➜ 网络: http://{ip}:{port}\n"
163
- display += " ➜ 默认用户名和密码: astrbot\n ✨✨✨\n"
219
+ parts.append(f" ➜ 网络: http://{ip}:{port}\n")
220
+ parts.append(" ➜ 默认用户名和密码: astrbot\n ✨✨✨\n")
221
+ display = "".join(parts)
164
222
 
165
223
  if not ip_addr:
166
224
  display += (
@@ -170,7 +228,9 @@ class AstrBotDashboard:
170
228
  logger.info(display)
171
229
 
172
230
  return self.app.run_task(
173
- host=host, port=port, shutdown_trigger=self.shutdown_trigger
231
+ host=host,
232
+ port=port,
233
+ shutdown_trigger=self.shutdown_trigger,
174
234
  )
175
235
 
176
236
  async def shutdown_trigger(self):
@@ -0,0 +1,165 @@
1
+ import base64
2
+ import os
3
+ import traceback
4
+ from io import BytesIO
5
+
6
+ from astrbot.api import logger
7
+ from astrbot.core.db.vec_db.faiss_impl import FaissVecDB
8
+ from astrbot.core.knowledge_base.kb_helper import KBHelper
9
+ from astrbot.core.knowledge_base.kb_mgr import KnowledgeBaseManager
10
+
11
+
12
+ async def generate_tsne_visualization(
13
+ query: str,
14
+ kb_names: list[str],
15
+ kb_manager: KnowledgeBaseManager,
16
+ ) -> str | None:
17
+ """生成 t-SNE 可视化图片
18
+
19
+ Args:
20
+ query: 查询文本
21
+ kb_names: 知识库名称列表
22
+ kb_manager: 知识库管理器
23
+
24
+ Returns:
25
+ 图片路径或 None
26
+
27
+ """
28
+ try:
29
+ import faiss
30
+ import matplotlib
31
+ import numpy as np
32
+
33
+ matplotlib.use("Agg") # 使用非交互式后端
34
+ import matplotlib.pyplot as plt
35
+ from sklearn.manifold import TSNE
36
+ except ImportError as e:
37
+ raise Exception(
38
+ "缺少必要的库以生成 t-SNE 可视化。请安装 matplotlib 和 scikit-learn: {e}",
39
+ ) from e
40
+
41
+ try:
42
+ # 获取第一个知识库的向量数据
43
+ kb_helper: KBHelper | None = None
44
+ for kb_name in kb_names:
45
+ kb_helper = await kb_manager.get_kb_by_name(kb_name)
46
+ if kb_helper:
47
+ break
48
+
49
+ if not kb_helper:
50
+ logger.warning("未找到知识库")
51
+ return None
52
+
53
+ kb = kb_helper.kb
54
+ index_path = f"data/knowledge_base/{kb.kb_id}/index.faiss"
55
+
56
+ # 读取 FAISS 索引
57
+ if not os.path.exists(index_path):
58
+ logger.warning(f"FAISS 索引不存在: {index_path}")
59
+ return None
60
+
61
+ index = faiss.read_index(index_path)
62
+
63
+ if index.ntotal == 0:
64
+ logger.warning("索引为空")
65
+ return None
66
+
67
+ # 提取所有向量
68
+ logger.info(f"提取 {index.ntotal} 个向量用于可视化...")
69
+ if isinstance(index, faiss.IndexIDMap):
70
+ base_index = faiss.downcast_index(index.index)
71
+ if hasattr(base_index, "reconstruct_n"):
72
+ vectors = base_index.reconstruct_n(0, index.ntotal)
73
+ else:
74
+ vectors = np.zeros((index.ntotal, index.d), dtype=np.float32)
75
+ for i in range(index.ntotal):
76
+ base_index.reconstruct(i, vectors[i])
77
+ elif hasattr(index, "reconstruct_n"):
78
+ vectors = index.reconstruct_n(0, index.ntotal)
79
+ else:
80
+ vectors = np.zeros((index.ntotal, index.d), dtype=np.float32)
81
+ for i in range(index.ntotal):
82
+ index.reconstruct(i, vectors[i])
83
+
84
+ # 获取查询向量
85
+ vec_db: FaissVecDB = kb_helper.vec_db # type: ignore
86
+ embedding_provider = vec_db.embedding_provider
87
+ query_embedding = await embedding_provider.get_embedding(query)
88
+ query_vector = np.array([query_embedding], dtype=np.float32)
89
+
90
+ # 合并所有向量和查询向量
91
+ all_vectors = np.vstack([vectors, query_vector])
92
+
93
+ # t-SNE 降维
94
+ logger.info("开始 t-SNE 降维...")
95
+ perplexity = min(30, all_vectors.shape[0] - 1)
96
+ tsne = TSNE(n_components=2, random_state=42, perplexity=perplexity)
97
+ vectors_2d = tsne.fit_transform(all_vectors)
98
+
99
+ # 分离知识库向量和查询向量
100
+ kb_vectors_2d = vectors_2d[:-1]
101
+ query_vector_2d = vectors_2d[-1]
102
+
103
+ # 可视化
104
+ logger.info("生成可视化图表...")
105
+ plt.figure(figsize=(14, 10))
106
+
107
+ # 绘制知识库向量
108
+ scatter = plt.scatter(
109
+ kb_vectors_2d[:, 0],
110
+ kb_vectors_2d[:, 1],
111
+ alpha=0.5,
112
+ s=40,
113
+ c=range(len(kb_vectors_2d)),
114
+ cmap="viridis",
115
+ label="Knowledge Base Vectors",
116
+ )
117
+
118
+ # 绘制查询向量(红色 X)
119
+ plt.scatter(
120
+ query_vector_2d[0],
121
+ query_vector_2d[1],
122
+ c="red",
123
+ s=300,
124
+ marker="X",
125
+ edgecolors="black",
126
+ linewidths=2,
127
+ label="Query",
128
+ zorder=5,
129
+ )
130
+
131
+ # 添加查询文本标注
132
+ plt.annotate(
133
+ "Query",
134
+ (query_vector_2d[0], query_vector_2d[1]),
135
+ xytext=(10, 10),
136
+ textcoords="offset points",
137
+ fontsize=10,
138
+ bbox={"boxstyle": "round,pad=0.5", "fc": "yellow", "alpha": 0.7},
139
+ arrowprops={"arrowstyle": "->", "connectionstyle": "arc3,rad=0"},
140
+ )
141
+
142
+ plt.colorbar(scatter, label="Vector Index")
143
+ plt.title(
144
+ f"t-SNE Visualization: Query in Knowledge Base\n"
145
+ f"({index.ntotal} vectors, {index.d} dimensions, KB: {kb.kb_name})",
146
+ fontsize=14,
147
+ pad=20,
148
+ )
149
+ plt.xlabel("t-SNE Dimension 1", fontsize=12)
150
+ plt.ylabel("t-SNE Dimension 2", fontsize=12)
151
+ plt.grid(True, alpha=0.3)
152
+ plt.legend(fontsize=10, loc="upper right")
153
+
154
+ # base64 编码图片返回
155
+ buffer = BytesIO()
156
+ plt.savefig(buffer, format="png", dpi=150, bbox_inches="tight")
157
+ plt.close()
158
+ buffer.seek(0)
159
+ img_base64 = base64.b64encode(buffer.read()).decode("utf-8")
160
+ return img_base64
161
+
162
+ except Exception as e:
163
+ logger.error(f"生成 t-SNE 可视化时出错: {e}")
164
+ logger.error(traceback.format_exc())
165
+ return None