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,14 +1,17 @@
1
- import aiohttp
2
1
  import asyncio
3
- import ssl
4
- import certifi
5
2
  import logging
6
3
  import random
7
- from . import RenderStrategy
4
+ import ssl
5
+
6
+ import aiohttp
7
+ import certifi
8
+
8
9
  from astrbot.core.config import VERSION
9
10
  from astrbot.core.utils.io import download_image_by_url
10
11
  from astrbot.core.utils.t2i.template_manager import TemplateManager
11
12
 
13
+ from . import RenderStrategy
14
+
12
15
  ASTRBOT_T2I_DEFAULT_ENDPOINT = "https://t2i.soulter.top/text2img"
13
16
 
14
17
  logger = logging.getLogger("astrbot")
@@ -38,7 +41,7 @@ class NetworkRenderStrategy(RenderStrategy):
38
41
  try:
39
42
  async with aiohttp.ClientSession() as session:
40
43
  async with session.get(
41
- "https://api.soulter.top/astrbot/t2i-endpoints"
44
+ "https://api.soulter.top/astrbot/t2i-endpoints",
42
45
  ) as resp:
43
46
  if resp.status == 200:
44
47
  data = await resp.json()
@@ -49,14 +52,13 @@ class NetworkRenderStrategy(RenderStrategy):
49
52
  if ep.get("active") and ep.get("url")
50
53
  ]
51
54
  logger.info(
52
- f"Successfully got {len(self.endpoints)} official T2I endpoints."
55
+ f"Successfully got {len(self.endpoints)} official T2I endpoints.",
53
56
  )
54
57
  except Exception as e:
55
58
  logger.error(f"Failed to get official endpoints: {e}")
56
59
 
57
60
  def _clean_url(self, url: str):
58
- if url.endswith("/"):
59
- url = url[:-1]
61
+ url = url.removesuffix("/")
60
62
  if not url.endswith("text2img"):
61
63
  url += "/text2img"
62
64
  return url
@@ -69,7 +71,6 @@ class NetworkRenderStrategy(RenderStrategy):
69
71
  options: dict | None = None,
70
72
  ) -> str:
71
73
  """使用自定义文转图模板"""
72
-
73
74
  default_options = {"full_page": True, "type": "jpeg", "quality": 40}
74
75
  if options:
75
76
  default_options |= options
@@ -89,21 +90,26 @@ class NetworkRenderStrategy(RenderStrategy):
89
90
  if return_url:
90
91
  ssl_context = ssl.create_default_context(cafile=certifi.where())
91
92
  connector = aiohttp.TCPConnector(ssl=ssl_context)
92
- async with aiohttp.ClientSession(
93
- trust_env=True, connector=connector
94
- ) as session:
95
- async with session.post(
96
- f"{endpoint}/generate", json=post_data
97
- ) as resp:
98
- if resp.status == 200:
99
- ret = await resp.json()
100
- return f"{endpoint}/{ret['data']['id']}"
101
- else:
102
- raise Exception(f"HTTP {resp.status}")
93
+ async with (
94
+ aiohttp.ClientSession(
95
+ trust_env=True,
96
+ connector=connector,
97
+ ) as session,
98
+ session.post(
99
+ f"{endpoint}/generate",
100
+ json=post_data,
101
+ ) as resp,
102
+ ):
103
+ if resp.status == 200:
104
+ ret = await resp.json()
105
+ return f"{endpoint}/{ret['data']['id']}"
106
+ raise Exception(f"HTTP {resp.status}")
103
107
  else:
104
108
  # download_image_by_url 失败时抛异常
105
109
  return await download_image_by_url(
106
- f"{endpoint}/generate", post=True, post_data=post_data
110
+ f"{endpoint}/generate",
111
+ post=True,
112
+ post_data=post_data,
107
113
  )
108
114
  except Exception as e:
109
115
  last_exception = e
@@ -114,15 +120,18 @@ class NetworkRenderStrategy(RenderStrategy):
114
120
  raise RuntimeError(f"All endpoints failed: {last_exception}")
115
121
 
116
122
  async def render(
117
- self, text: str, return_url: bool = False, template_name: str | None = "base"
123
+ self,
124
+ text: str,
125
+ return_url: bool = False,
126
+ template_name: str | None = "base",
118
127
  ) -> str:
119
- """
120
- 返回图像的文件路径
121
- """
128
+ """返回图像的文件路径"""
122
129
  if not template_name:
123
130
  template_name = "base"
124
131
  tmpl_str = await self.get_template(name=template_name)
125
132
  text = text.replace("`", "\\`")
126
133
  return await self.render_custom_template(
127
- tmpl_str, {"text": text, "version": f"v{VERSION}"}, return_url
134
+ tmpl_str,
135
+ {"text": text, "version": f"v{VERSION}"},
136
+ return_url,
128
137
  )
@@ -1,7 +1,8 @@
1
- from .network_strategy import NetworkRenderStrategy
2
- from .local_strategy import LocalRenderStrategy
3
1
  from astrbot.core.log import LogManager
4
2
 
3
+ from .local_strategy import LocalRenderStrategy
4
+ from .network_strategy import NetworkRenderStrategy
5
+
5
6
  logger = LogManager.GetLogger(log_name="astrbot")
6
7
 
7
8
 
@@ -30,7 +31,10 @@ class HtmlRenderer:
30
31
  @example: 参见 https://astrbot.app 插件开发部分。
31
32
  """
32
33
  return await self.network_strategy.render_custom_template(
33
- tmpl_str, tmpl_data, return_url, options
34
+ tmpl_str,
35
+ tmpl_data,
36
+ return_url,
37
+ options,
34
38
  )
35
39
 
36
40
  async def render_t2i(
@@ -44,11 +48,13 @@ class HtmlRenderer:
44
48
  if use_network:
45
49
  try:
46
50
  return await self.network_strategy.render(
47
- text, return_url=return_url, template_name=template_name
51
+ text,
52
+ return_url=return_url,
53
+ template_name=template_name,
48
54
  )
49
55
  except BaseException as e:
50
56
  logger.error(
51
- f"Failed to render image via AstrBot API: {e}. Falling back to local rendering."
57
+ f"Failed to render image via AstrBot API: {e}. Falling back to local rendering.",
52
58
  )
53
59
  return await self.local_strategy.render(text)
54
60
  else:
@@ -2,12 +2,12 @@
2
2
 
3
3
  import os
4
4
  import shutil
5
+
5
6
  from astrbot.core.utils.astrbot_path import get_astrbot_data_path, get_astrbot_path
6
7
 
7
8
 
8
9
  class TemplateManager:
9
- """
10
- 负责管理 t2i HTML 模板的 CRUD 和重置操作。
10
+ """负责管理 t2i HTML 模板的 CRUD 和重置操作。
11
11
  采用“用户覆盖内置”策略:用户模板存储在 data 目录中,并优先于内置模板加载。
12
12
  所有创建、更新、删除操作仅影响用户目录,以确保更新框架时用户数据安全。
13
13
  """
@@ -16,7 +16,12 @@ class TemplateManager:
16
16
 
17
17
  def __init__(self):
18
18
  self.builtin_template_dir = os.path.join(
19
- get_astrbot_path(), "astrbot", "core", "utils", "t2i", "template"
19
+ get_astrbot_path(),
20
+ "astrbot",
21
+ "core",
22
+ "utils",
23
+ "t2i",
24
+ "template",
20
25
  )
21
26
  self.user_template_dir = os.path.join(get_astrbot_data_path(), "t2i_templates")
22
27
 
@@ -43,12 +48,11 @@ class TemplateManager:
43
48
 
44
49
  def _read_file(self, path: str) -> str:
45
50
  """读取文件内容。"""
46
- with open(path, "r", encoding="utf-8") as f:
51
+ with open(path, encoding="utf-8") as f:
47
52
  return f.read()
48
53
 
49
54
  def list_templates(self) -> list[dict]:
50
- """
51
- 列出所有可用模板。
55
+ """列出所有可用模板。
52
56
  该列表是内置模板和用户模板的合并视图,用户模板将覆盖同名的内置模板。
53
57
  """
54
58
  dirs_to_scan = [self.builtin_template_dir, self.user_template_dir]
@@ -63,8 +67,7 @@ class TemplateManager:
63
67
  ]
64
68
 
65
69
  def get_template(self, name: str) -> str:
66
- """
67
- 获取指定模板的内容。
70
+ """获取指定模板的内容。
68
71
  优先从用户目录加载,如果不存在则回退到内置目录。
69
72
  """
70
73
  user_path = self._get_user_template_path(name)
@@ -86,8 +89,7 @@ class TemplateManager:
86
89
  f.write(content)
87
90
 
88
91
  def update_template(self, name: str, content: str):
89
- """
90
- 更新一个模板。此操作始终写入用户目录。
92
+ """更新一个模板。此操作始终写入用户目录。
91
93
  如果更新的是一个内置模板,此操作实际上会在用户目录中创建一个修改后的副本,
92
94
  从而实现对内置模板的“覆盖”。
93
95
  """
@@ -96,8 +98,7 @@ class TemplateManager:
96
98
  f.write(content)
97
99
 
98
100
  def delete_template(self, name: str):
99
- """
100
- 仅删除用户目录中的模板文件。
101
+ """仅删除用户目录中的模板文件。
101
102
  如果删除的是一个覆盖了内置模板的用户模板,这将有效地“恢复”到内置版本。
102
103
  """
103
104
  path = self._get_user_template_path(name)
@@ -106,7 +107,5 @@ class TemplateManager:
106
107
  os.remove(path)
107
108
 
108
109
  def reset_default_template(self):
109
- """
110
- 将核心模板从内置目录强制重置到用户目录。
111
- """
110
+ """将核心模板从内置目录强制重置到用户目录。"""
112
111
  self._copy_core_templates(overwrite=True)
@@ -1,10 +1,11 @@
1
+ import asyncio
1
2
  import base64
2
- import wave
3
3
  import os
4
4
  import subprocess
5
- from io import BytesIO
6
- import asyncio
7
5
  import tempfile
6
+ import wave
7
+ from io import BytesIO
8
+
8
9
  from astrbot.core import logger
9
10
  from astrbot.core.utils.astrbot_path import get_astrbot_data_path
10
11
 
@@ -35,7 +36,7 @@ async def wav_to_tencent_silk(wav_path: str, output_path: str) -> int:
35
36
  import pilk
36
37
  except (ImportError, ModuleNotFoundError) as _:
37
38
  raise Exception(
38
- "pilk 模块未安装,请前往管理面板->控制台->安装pip库 安装 pilk 这个库"
39
+ "pilk 模块未安装,请前往管理面板->控制台->安装pip库 安装 pilk 这个库",
39
40
  )
40
41
  # with wave.open(wav_path, 'rb') as wav:
41
42
  # wav_data = wav.readframes(wav.getnframes())
@@ -60,8 +61,7 @@ async def wav_to_tencent_silk(wav_path: str, output_path: str) -> int:
60
61
 
61
62
 
62
63
  async def convert_to_pcm_wav(input_path: str, output_path: str) -> str:
63
- """
64
- 将 MP3 或其他音频格式转换为 PCM 16bit WAV,采样率24000Hz,单声道。
64
+ """将 MP3 或其他音频格式转换为 PCM 16bit WAV,采样率24000Hz,单声道。
65
65
  若转换失败则抛出异常。
66
66
  """
67
67
  try:
@@ -99,13 +99,11 @@ async def convert_to_pcm_wav(input_path: str, output_path: str) -> str:
99
99
 
100
100
  if os.path.exists(output_path) and os.path.getsize(output_path) > 0:
101
101
  return output_path
102
- else:
103
- raise RuntimeError("生成的WAV文件不存在或为空")
102
+ raise RuntimeError("生成的WAV文件不存在或为空")
104
103
 
105
104
 
106
105
  async def audio_to_tencent_silk_base64(audio_path: str) -> tuple[str, float]:
107
- """
108
- 将 MP3/WAV 文件转为 Tencent Silk 并返回 base64 编码与时长(秒)。
106
+ """将 MP3/WAV 文件转为 Tencent Silk 并返回 base64 编码与时长(秒)。
109
107
 
110
108
  参数:
111
109
  - audio_path: 输入音频文件路径(.mp3 或 .wav)
@@ -125,7 +123,9 @@ async def audio_to_tencent_silk_base64(audio_path: str) -> tuple[str, float]:
125
123
  # 是否需要转换为 WAV
126
124
  ext = os.path.splitext(audio_path)[1].lower()
127
125
  temp_wav = tempfile.NamedTemporaryFile(
128
- suffix=".wav", delete=False, dir=temp_dir
126
+ suffix=".wav",
127
+ delete=False,
128
+ dir=temp_dir,
129
129
  ).name
130
130
 
131
131
  if ext != ".wav":
@@ -140,12 +140,18 @@ async def audio_to_tencent_silk_base64(audio_path: str) -> tuple[str, float]:
140
140
  rate = wav_file.getframerate()
141
141
 
142
142
  silk_path = tempfile.NamedTemporaryFile(
143
- suffix=".silk", delete=False, dir=temp_dir
143
+ suffix=".silk",
144
+ delete=False,
145
+ dir=temp_dir,
144
146
  ).name
145
147
 
146
148
  try:
147
149
  duration = await asyncio.to_thread(
148
- pilk.encode, wav_path, silk_path, pcm_rate=rate, tencent=True
150
+ pilk.encode,
151
+ wav_path,
152
+ silk_path,
153
+ pcm_rate=rate,
154
+ tencent=True,
149
155
  )
150
156
 
151
157
  with open(silk_path, "rb") as f:
@@ -38,15 +38,15 @@ class VersionComparator:
38
38
  for i in range(length):
39
39
  if v1_parts[i] > v2_parts[i]:
40
40
  return 1
41
- elif v1_parts[i] < v2_parts[i]:
41
+ if v1_parts[i] < v2_parts[i]:
42
42
  return -1
43
43
 
44
44
  # 比较预发布标签
45
45
  if v1_prerelease is None and v2_prerelease is not None:
46
46
  return 1 # 没有预发布标签的版本高于有预发布标签的版本
47
- elif v1_prerelease is not None and v2_prerelease is None:
47
+ if v1_prerelease is not None and v2_prerelease is None:
48
48
  return -1 # 有预发布标签的版本低于没有预发布标签的版本
49
- elif v1_prerelease is not None and v2_prerelease is not None:
49
+ if v1_prerelease is not None and v2_prerelease is not None:
50
50
  len_pre = max(len(v1_prerelease), len(v2_prerelease))
51
51
  for i in range(len_pre):
52
52
  p1 = v1_prerelease[i] if i < len(v1_prerelease) else None
@@ -54,21 +54,18 @@ class VersionComparator:
54
54
 
55
55
  if p1 is None and p2 is not None:
56
56
  return -1
57
- elif p1 is not None and p2 is None:
57
+ if p1 is not None and p2 is None:
58
58
  return 1
59
- elif isinstance(p1, int) and isinstance(p2, str):
59
+ if isinstance(p1, int) and isinstance(p2, str):
60
60
  return -1
61
- elif isinstance(p1, str) and isinstance(p2, int):
61
+ if isinstance(p1, str) and isinstance(p2, int):
62
62
  return 1
63
- elif isinstance(p1, int) and isinstance(p2, int):
63
+ if (isinstance(p1, int) and isinstance(p2, int)) or (
64
+ isinstance(p1, str) and isinstance(p2, str)
65
+ ):
64
66
  if p1 > p2:
65
67
  return 1
66
- elif p1 < p2:
67
- return -1
68
- elif isinstance(p1, str) and isinstance(p2, str):
69
- if p1 > p2:
70
- return 1
71
- elif p1 < p2:
68
+ if p1 < p2:
72
69
  return -1
73
70
  return 0 # 预发布标签完全相同
74
71
 
@@ -1,14 +1,14 @@
1
- import aiohttp
2
1
  import os
3
2
  import re
4
- import zipfile
5
3
  import shutil
6
-
7
4
  import ssl
5
+ import zipfile
6
+
7
+ import aiohttp
8
8
  import certifi
9
9
 
10
- from astrbot.core.utils.io import on_error, download_file
11
10
  from astrbot.core import logger
11
+ from astrbot.core.utils.io import download_file, on_error
12
12
  from astrbot.core.utils.version_comparator import VersionComparator
13
13
 
14
14
 
@@ -18,7 +18,10 @@ class ReleaseInfo:
18
18
  body: str
19
19
 
20
20
  def __init__(
21
- self, version: str = "", published_at: str = "", body: str = ""
21
+ self,
22
+ version: str = "",
23
+ published_at: str = "",
24
+ body: str = "",
22
25
  ) -> None:
23
26
  self.version = version
24
27
  self.published_at = published_at
@@ -34,29 +37,31 @@ class RepoZipUpdator:
34
37
  self.rm_on_error = on_error
35
38
 
36
39
  async def fetch_release_info(self, url: str, latest: bool = True) -> list:
37
- """
38
- 请求版本信息。
40
+ """请求版本信息。
39
41
  返回一个列表,每个元素是一个字典,包含版本号、发布时间、更新内容、commit hash等信息。
40
42
  """
41
43
  try:
42
44
  ssl_context = ssl.create_default_context(
43
- cafile=certifi.where()
45
+ cafile=certifi.where(),
44
46
  ) # 新增:创建基于 certifi 的 SSL 上下文
45
47
  connector = aiohttp.TCPConnector(
46
- ssl=ssl_context
48
+ ssl=ssl_context,
47
49
  ) # 新增:使用 TCPConnector 指定 SSL 上下文
48
- async with aiohttp.ClientSession(
49
- trust_env=True, connector=connector
50
- ) as session:
51
- async with session.get(url) as response:
52
- # 检查 HTTP 状态码
53
- if response.status != 200:
54
- text = await response.text()
55
- logger.error(
56
- f"请求 {url} 失败,状态码: {response.status}, 内容: {text}"
57
- )
58
- raise Exception(f"请求失败,状态码: {response.status}")
59
- result = await response.json()
50
+ async with (
51
+ aiohttp.ClientSession(
52
+ trust_env=True,
53
+ connector=connector,
54
+ ) as session,
55
+ session.get(url) as response,
56
+ ):
57
+ # 检查 HTTP 状态码
58
+ if response.status != 200:
59
+ text = await response.text()
60
+ logger.error(
61
+ f"请求 {url} 失败,状态码: {response.status}, 内容: {text}",
62
+ )
63
+ raise Exception(f"请求失败,状态码: {response.status}")
64
+ result = await response.json()
60
65
  if not result:
61
66
  return []
62
67
  # if latest:
@@ -72,7 +77,7 @@ class RepoZipUpdator:
72
77
  "body": release["body"],
73
78
  "tag_name": release["tag_name"],
74
79
  "zipball_url": release["zipball_url"],
75
- }
80
+ },
76
81
  )
77
82
  except Exception as e:
78
83
  logger.error(f"解析版本信息时发生异常: {e}")
@@ -80,8 +85,7 @@ class RepoZipUpdator:
80
85
  return ret
81
86
 
82
87
  def github_api_release_parser(self, releases: list) -> list:
83
- """
84
- 解析 GitHub API 返回的 releases 信息。
88
+ """解析 GitHub API 返回的 releases 信息。
85
89
  返回一个列表,每个元素是一个字典,包含版本号、发布时间、更新内容、commit hash等信息。
86
90
  """
87
91
  ret = []
@@ -93,22 +97,25 @@ class RepoZipUpdator:
93
97
  "body": release["body"],
94
98
  "tag_name": release["tag_name"],
95
99
  "zipball_url": release["zipball_url"],
96
- }
100
+ },
97
101
  )
98
102
  return ret
99
103
 
100
104
  def unzip(self):
101
- raise NotImplementedError()
105
+ raise NotImplementedError
102
106
 
103
107
  async def update(self):
104
- raise NotImplementedError()
108
+ raise NotImplementedError
105
109
 
106
110
  def compare_version(self, v1: str, v2: str) -> int:
107
111
  """Semver 版本比较"""
108
112
  return VersionComparator.compare_version(v1, v2)
109
113
 
110
114
  async def check_update(
111
- self, url: str, current_version: str, consider_prerelease: bool = True
115
+ self,
116
+ url: str,
117
+ current_version: str,
118
+ consider_prerelease: bool = True,
112
119
  ) -> ReleaseInfo | None:
113
120
  update_data = await self.fetch_release_info(url)
114
121
 
@@ -157,7 +164,7 @@ class RepoZipUpdator:
157
164
  releases = await self.fetch_release_info(url=release_url)
158
165
  except Exception as e:
159
166
  logger.warning(
160
- f"获取 {author}/{repo} 的 GitHub Releases 失败: {e},将尝试下载默认分支"
167
+ f"获取 {author}/{repo} 的 GitHub Releases 失败: {e},将尝试下载默认分支",
161
168
  )
162
169
  releases = []
163
170
  if not releases:
@@ -173,7 +180,7 @@ class RepoZipUpdator:
173
180
  proxy = proxy.rstrip("/")
174
181
  release_url = f"{proxy}/{release_url}"
175
182
  logger.info(
176
- f"检查到设置了镜像站,将使用镜像站下载 {author}/{repo} 仓库源码: {release_url}"
183
+ f"检查到设置了镜像站,将使用镜像站下载 {author}/{repo} 仓库源码: {release_url}",
177
184
  )
178
185
 
179
186
  await download_file(release_url, target_path + ".zip")
@@ -194,13 +201,10 @@ class RepoZipUpdator:
194
201
  repo = match.group(2)
195
202
  branch = match.group(4)
196
203
  return author, repo, branch
197
- else:
198
- raise ValueError("无效的 GitHub URL")
204
+ raise ValueError("无效的 GitHub URL")
199
205
 
200
206
  def unzip_file(self, zip_path: str, target_dir: str):
201
- """
202
- 解压缩文件, 并将压缩包内**第一个**文件夹内的文件移动到 target_dir
203
- """
207
+ """解压缩文件, 并将压缩包内**第一个**文件夹内的文件移动到 target_dir"""
204
208
  os.makedirs(target_dir, exist_ok=True)
205
209
  update_dir = ""
206
210
  with zipfile.ZipFile(zip_path, "r") as z:
@@ -213,20 +217,19 @@ class RepoZipUpdator:
213
217
  if os.path.isdir(os.path.join(target_dir, update_dir, f)):
214
218
  if os.path.exists(os.path.join(target_dir, f)):
215
219
  shutil.rmtree(os.path.join(target_dir, f), onerror=on_error)
216
- else:
217
- if os.path.exists(os.path.join(target_dir, f)):
218
- os.remove(os.path.join(target_dir, f))
220
+ elif os.path.exists(os.path.join(target_dir, f)):
221
+ os.remove(os.path.join(target_dir, f))
219
222
  shutil.move(os.path.join(target_dir, update_dir, f), target_dir)
220
223
 
221
224
  try:
222
225
  logger.debug(
223
- f"删除临时更新文件: {zip_path} 和 {os.path.join(target_dir, update_dir)}"
226
+ f"删除临时更新文件: {zip_path} 和 {os.path.join(target_dir, update_dir)}",
224
227
  )
225
228
  shutil.rmtree(os.path.join(target_dir, update_dir), onerror=on_error)
226
229
  os.remove(zip_path)
227
230
  except BaseException:
228
231
  logger.warning(
229
- f"删除更新文件失败,可以手动删除 {zip_path} 和 {os.path.join(target_dir, update_dir)}"
232
+ f"删除更新文件失败,可以手动删除 {zip_path} 和 {os.path.join(target_dir, update_dir)}",
230
233
  )
231
234
 
232
235
  def format_name(self, name: str) -> str:
@@ -1,31 +1,31 @@
1
1
  from .auth import AuthRoute
2
- from .plugin import PluginRoute
3
- from .config import ConfigRoute
4
- from .update import UpdateRoute
5
- from .stat import StatRoute
6
- from .log import LogRoute
7
- from .static_file import StaticFileRoute
8
2
  from .chat import ChatRoute
9
- from .tools import ToolsRoute
3
+ from .config import ConfigRoute
10
4
  from .conversation import ConversationRoute
11
5
  from .file import FileRoute
12
- from .session_management import SessionManagementRoute
13
- from .persona import PersonaRoute
14
6
  from .knowledge_base import KnowledgeBaseRoute
7
+ from .log import LogRoute
8
+ from .persona import PersonaRoute
9
+ from .plugin import PluginRoute
10
+ from .session_management import SessionManagementRoute
11
+ from .stat import StatRoute
12
+ from .static_file import StaticFileRoute
13
+ from .tools import ToolsRoute
14
+ from .update import UpdateRoute
15
15
 
16
16
  __all__ = [
17
17
  "AuthRoute",
18
- "PluginRoute",
19
- "ConfigRoute",
20
- "UpdateRoute",
21
- "StatRoute",
22
- "LogRoute",
23
- "StaticFileRoute",
24
18
  "ChatRoute",
25
- "ToolsRoute",
19
+ "ConfigRoute",
26
20
  "ConversationRoute",
27
21
  "FileRoute",
28
- "SessionManagementRoute",
29
- "PersonaRoute",
30
22
  "KnowledgeBaseRoute",
23
+ "LogRoute",
24
+ "PersonaRoute",
25
+ "PluginRoute",
26
+ "SessionManagementRoute",
27
+ "StatRoute",
28
+ "StaticFileRoute",
29
+ "ToolsRoute",
30
+ "UpdateRoute",
31
31
  ]
@@ -1,10 +1,13 @@
1
- import jwt
2
- import datetime
3
1
  import asyncio
4
- from .route import Route, Response, RouteContext
2
+ import datetime
3
+
4
+ import jwt
5
5
  from quart import request
6
- from astrbot.core import DEMO_MODE
6
+
7
7
  from astrbot import logger
8
+ from astrbot.core import DEMO_MODE
9
+
10
+ from .route import Response, Route, RouteContext
8
11
 
9
12
 
10
13
  class AuthRoute(Route):
@@ -37,13 +40,12 @@ class AuthRoute(Route):
37
40
  "token": self.generate_jwt(username),
38
41
  "username": username,
39
42
  "change_pwd_hint": change_pwd_hint,
40
- }
43
+ },
41
44
  )
42
45
  .__dict__
43
46
  )
44
- else:
45
- await asyncio.sleep(3)
46
- return Response().error("用户名或密码错误").__dict__
47
+ await asyncio.sleep(3)
48
+ return Response().error("用户名或密码错误").__dict__
47
49
 
48
50
  async def edit_account(self):
49
51
  if DEMO_MODE: