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
@@ -3,7 +3,6 @@ import base64
3
3
  import json
4
4
  import logging
5
5
  import random
6
- from typing import Optional, List
7
6
  from collections.abc import AsyncGenerator
8
7
 
9
8
  from google import genai
@@ -32,7 +31,8 @@ logging.getLogger("google_genai.types").addFilter(SuppressNonTextPartsWarning())
32
31
 
33
32
 
34
33
  @register_provider_adapter(
35
- "googlegenai_chat_completion", "Google Gemini Chat Completion 提供商适配器"
34
+ "googlegenai_chat_completion",
35
+ "Google Gemini Chat Completion 提供商适配器",
36
36
  )
37
37
  class ProviderGoogleGenAI(Provider):
38
38
  CATEGORY_MAPPING = {
@@ -60,11 +60,11 @@ class ProviderGoogleGenAI(Provider):
60
60
  provider_settings,
61
61
  default_persona,
62
62
  )
63
- self.api_keys: List = super().get_keys()
63
+ self.api_keys: list = super().get_keys()
64
64
  self.chosen_api_key: str = self.api_keys[0] if len(self.api_keys) > 0 else ""
65
65
  self.timeout: int = int(provider_config.get("timeout", 180))
66
66
 
67
- self.api_base: Optional[str] = provider_config.get("api_base", None)
67
+ self.api_base: str | None = provider_config.get("api_base", None)
68
68
  if self.api_base and self.api_base.endswith("/"):
69
69
  self.api_base = self.api_base[:-1]
70
70
 
@@ -87,7 +87,8 @@ class ProviderGoogleGenAI(Provider):
87
87
  user_safety_config = self.provider_config.get("gm_safety_settings", {})
88
88
  self.safety_settings = [
89
89
  types.SafetySetting(
90
- category=harm_category, threshold=self.THRESHOLD_MAPPING[threshold_str]
90
+ category=harm_category,
91
+ threshold=self.THRESHOLD_MAPPING[threshold_str],
91
92
  )
92
93
  for config_key, harm_category in self.CATEGORY_MAPPING.items()
93
94
  if (threshold_str := user_safety_config.get(config_key))
@@ -104,27 +105,25 @@ class ProviderGoogleGenAI(Provider):
104
105
  if len(keys) > 0:
105
106
  self.set_key(random.choice(keys))
106
107
  logger.info(
107
- f"检测到 Key 异常({e.message}),正在尝试更换 API Key 重试... 当前 Key: {self.chosen_api_key[:12]}..."
108
+ f"检测到 Key 异常({e.message}),正在尝试更换 API Key 重试... 当前 Key: {self.chosen_api_key[:12]}...",
108
109
  )
109
110
  await asyncio.sleep(1)
110
111
  return True
111
- else:
112
- logger.error(
113
- f"检测到 Key 异常({e.message}),且已没有可用的 Key。 当前 Key: {self.chosen_api_key[:12]}..."
114
- )
115
- raise Exception("达到了 Gemini 速率限制, 请稍后再试...")
116
- else:
117
112
  logger.error(
118
- f"发生了错误(gemini_source)。Provider 配置如下: {self.provider_config}"
113
+ f"检测到 Key 异常({e.message}),且已没有可用的 Key当前 Key: {self.chosen_api_key[:12]}...",
119
114
  )
120
- raise e
115
+ raise Exception("达到了 Gemini 速率限制, 请稍后再试...")
116
+ logger.error(
117
+ f"发生了错误(gemini_source)。Provider 配置如下: {self.provider_config}",
118
+ )
119
+ raise e
121
120
 
122
121
  async def _prepare_query_config(
123
122
  self,
124
123
  payloads: dict,
125
- tools: Optional[ToolSet] = None,
126
- system_instruction: Optional[str] = None,
127
- modalities: Optional[list[str]] = None,
124
+ tools: ToolSet | None = None,
125
+ system_instruction: str | None = None,
126
+ modalities: list[str] | None = None,
128
127
  temperature: float = 0.7,
129
128
  ) -> types.GenerateContentConfig:
130
129
  """准备查询配置"""
@@ -152,7 +151,7 @@ class ProviderGoogleGenAI(Provider):
152
151
  logger.warning("代码执行工具与搜索工具互斥,已忽略搜索工具")
153
152
  if url_context:
154
153
  logger.warning(
155
- "代码执行工具与URL上下文工具互斥,已忽略URL上下文工具"
154
+ "代码执行工具与URL上下文工具互斥,已忽略URL上下文工具",
156
155
  )
157
156
  else:
158
157
  if native_search:
@@ -163,13 +162,13 @@ class ProviderGoogleGenAI(Provider):
163
162
  tool_list.append(types.Tool(url_context=types.UrlContext()))
164
163
  else:
165
164
  logger.warning(
166
- "当前 SDK 版本不支持 URL 上下文工具,已忽略该设置,请升级 google-genai 包"
165
+ "当前 SDK 版本不支持 URL 上下文工具,已忽略该设置,请升级 google-genai 包",
167
166
  )
168
167
 
169
168
  elif "gemini-2.0-lite" in model_name:
170
169
  if native_coderunner or native_search or url_context:
171
170
  logger.warning(
172
- "gemini-2.0-lite 不支持代码执行、搜索工具和URL上下文,将忽略这些设置"
171
+ "gemini-2.0-lite 不支持代码执行、搜索工具和URL上下文,将忽略这些设置",
173
172
  )
174
173
  tool_list = None
175
174
 
@@ -186,7 +185,7 @@ class ProviderGoogleGenAI(Provider):
186
185
  tool_list.append(types.Tool(url_context=types.UrlContext()))
187
186
  else:
188
187
  logger.warning(
189
- "当前 SDK 版本不支持 URL 上下文工具,已忽略该设置,请升级 google-genai 包"
188
+ "当前 SDK 版本不支持 URL 上下文工具,已忽略该设置,请升级 google-genai 包",
190
189
  )
191
190
 
192
191
  if not tool_list:
@@ -196,7 +195,7 @@ class ProviderGoogleGenAI(Provider):
196
195
  logger.warning("已启用原生工具,函数工具将被忽略")
197
196
  elif tools and (func_desc := tools.get_func_desc_google_genai_style()):
198
197
  tool_list = [
199
- types.Tool(function_declarations=func_desc["function_declarations"])
198
+ types.Tool(function_declarations=func_desc["function_declarations"]),
200
199
  ]
201
200
 
202
201
  return types.GenerateContentConfig(
@@ -223,8 +222,9 @@ class ProviderGoogleGenAI(Provider):
223
222
  thinking_budget=min(
224
223
  int(
225
224
  self.provider_config.get("gm_thinking_config", {}).get(
226
- "budget", 0
227
- )
225
+ "budget",
226
+ 0,
227
+ ),
228
228
  ),
229
229
  24576,
230
230
  ),
@@ -234,7 +234,7 @@ class ProviderGoogleGenAI(Provider):
234
234
  else None
235
235
  ),
236
236
  automatic_function_calling=types.AutomaticFunctionCallingConfig(
237
- disable=True
237
+ disable=True,
238
238
  ),
239
239
  )
240
240
 
@@ -268,7 +268,7 @@ class ProviderGoogleGenAI(Provider):
268
268
  [
269
269
  self.provider_config.get("gm_native_coderunner", False),
270
270
  self.provider_config.get("gm_native_search", False),
271
- ]
271
+ ],
272
272
  )
273
273
  for message in payloads["messages"]:
274
274
  role, content = message["role"], message.get("content")
@@ -304,7 +304,7 @@ class ProviderGoogleGenAI(Provider):
304
304
  logger.warning("assistant 角色的消息内容为空,已添加空格占位")
305
305
  if native_tool_enabled and "tool_calls" in message:
306
306
  logger.warning(
307
- "检测到启用Gemini原生工具,且上下文中存在函数调用,建议使用 /reset 重置上下文"
307
+ "检测到启用Gemini原生工具,且上下文中存在函数调用,建议使用 /reset 重置上下文",
308
308
  )
309
309
  parts = [types.Part.from_text(text=" ")]
310
310
  append_or_extend(gemini_contents, parts, types.ModelContent)
@@ -317,7 +317,7 @@ class ProviderGoogleGenAI(Provider):
317
317
  "name": message["tool_call_id"],
318
318
  "content": message["content"],
319
319
  },
320
- )
320
+ ),
321
321
  ]
322
322
  append_or_extend(gemini_contents, parts, types.UserContent)
323
323
 
@@ -328,7 +328,8 @@ class ProviderGoogleGenAI(Provider):
328
328
 
329
329
  @staticmethod
330
330
  def _process_content_parts(
331
- candidate: types.Candidate, llm_response: LLMResponse
331
+ candidate: types.Candidate,
332
+ llm_response: LLMResponse,
332
333
  ) -> MessageChain:
333
334
  """处理内容部分并构建消息链"""
334
335
  if not candidate.content:
@@ -381,7 +382,7 @@ class ProviderGoogleGenAI(Provider):
381
382
  llm_response.tools_call_args.append(part.function_call.args)
382
383
  # gemini 返回的 function_call.id 可能为 None
383
384
  llm_response.tools_call_ids.append(
384
- part.function_call.id or part.function_call.name
385
+ part.function_call.id or part.function_call.name,
385
386
  )
386
387
  elif (
387
388
  part.inline_data
@@ -406,11 +407,15 @@ class ProviderGoogleGenAI(Provider):
406
407
  conversation = self._prepare_conversation(payloads)
407
408
  temperature = payloads.get("temperature", 0.7)
408
409
 
409
- result: Optional[types.GenerateContentResponse] = None
410
+ result: types.GenerateContentResponse | None = None
410
411
  while True:
411
412
  try:
412
413
  config = await self._prepare_query_config(
413
- payloads, tools, system_instruction, modalities, temperature
414
+ payloads,
415
+ tools,
416
+ system_instruction,
417
+ modalities,
418
+ temperature,
414
419
  )
415
420
  result = await self.client.models.generate_content(
416
421
  model=self.get_model(),
@@ -427,7 +432,7 @@ class ProviderGoogleGenAI(Provider):
427
432
  raise Exception("温度参数已超过最大值2,仍然发生recitation")
428
433
  temperature += 0.2
429
434
  logger.warning(
430
- f"发生了recitation,正在提高温度至{temperature:.1f}重试..."
435
+ f"发生了recitation,正在提高温度至{temperature:.1f}重试...",
431
436
  )
432
437
  continue
433
438
 
@@ -438,7 +443,7 @@ class ProviderGoogleGenAI(Provider):
438
443
  e.message = ""
439
444
  if "Developer instruction is not enabled" in e.message:
440
445
  logger.warning(
441
- f"{self.get_model()} 不支持 system prompt,已自动去除(影响人格设置)"
446
+ f"{self.get_model()} 不支持 system prompt,已自动去除(影响人格设置)",
442
447
  )
443
448
  system_instruction = None
444
449
  elif "Function calling is not enabled" in e.message:
@@ -451,7 +456,7 @@ class ProviderGoogleGenAI(Provider):
451
456
  or "only supports text output" in e.message
452
457
  ):
453
458
  logger.warning(
454
- f"{self.get_model()} 不支持多模态输出,降级为文本模态"
459
+ f"{self.get_model()} 不支持多模态输出,降级为文本模态",
455
460
  )
456
461
  modalities = ["Text"]
457
462
  else:
@@ -461,12 +466,15 @@ class ProviderGoogleGenAI(Provider):
461
466
  llm_response = LLMResponse("assistant")
462
467
  llm_response.raw_completion = result
463
468
  llm_response.result_chain = self._process_content_parts(
464
- result.candidates[0], llm_response
469
+ result.candidates[0],
470
+ llm_response,
465
471
  )
466
472
  return llm_response
467
473
 
468
474
  async def _query_stream(
469
- self, payloads: dict, tools: ToolSet | None
475
+ self,
476
+ payloads: dict,
477
+ tools: ToolSet | None,
470
478
  ) -> AsyncGenerator[LLMResponse, None]:
471
479
  """流式请求 Gemini API"""
472
480
  system_instruction = next(
@@ -480,7 +488,9 @@ class ProviderGoogleGenAI(Provider):
480
488
  while True:
481
489
  try:
482
490
  config = await self._prepare_query_config(
483
- payloads, tools, system_instruction
491
+ payloads,
492
+ tools,
493
+ system_instruction,
484
494
  )
485
495
  result = await self.client.models.generate_content_stream(
486
496
  model=self.get_model(),
@@ -493,7 +503,7 @@ class ProviderGoogleGenAI(Provider):
493
503
  e.message = ""
494
504
  if "Developer instruction is not enabled" in e.message:
495
505
  logger.warning(
496
- f"{self.get_model()} 不支持 system prompt,已自动去除(影响人格设置)"
506
+ f"{self.get_model()} 不支持 system prompt,已自动去除(影响人格设置)",
497
507
  )
498
508
  system_instruction = None
499
509
  elif "Function calling is not enabled" in e.message:
@@ -523,7 +533,8 @@ class ProviderGoogleGenAI(Provider):
523
533
  llm_response = LLMResponse("assistant", is_chunk=False)
524
534
  llm_response.raw_completion = chunk
525
535
  llm_response.result_chain = self._process_content_parts(
526
- chunk.candidates[0], llm_response
536
+ chunk.candidates[0],
537
+ llm_response,
527
538
  )
528
539
  yield llm_response
529
540
  return
@@ -539,7 +550,8 @@ class ProviderGoogleGenAI(Provider):
539
550
  final_response = LLMResponse("assistant", is_chunk=False)
540
551
  final_response.raw_completion = chunk
541
552
  final_response.result_chain = self._process_content_parts(
542
- chunk.candidates[0], final_response
553
+ chunk.candidates[0],
554
+ final_response,
543
555
  )
544
556
  break
545
557
 
@@ -550,7 +562,7 @@ class ProviderGoogleGenAI(Provider):
550
562
  # Set the complete accumulated text in the final response
551
563
  if accumulated_text:
552
564
  final_response.result_chain = MessageChain(
553
- chain=[Comp.Plain(accumulated_text)]
565
+ chain=[Comp.Plain(accumulated_text)],
554
566
  )
555
567
  elif not final_response.result_chain:
556
568
  # If no text was accumulated and no final response was set, provide empty space
@@ -560,7 +572,7 @@ class ProviderGoogleGenAI(Provider):
560
572
 
561
573
  async def text_chat(
562
574
  self,
563
- prompt: str,
575
+ prompt=None,
564
576
  session_id=None,
565
577
  image_urls=None,
566
578
  func_tool=None,
@@ -572,8 +584,12 @@ class ProviderGoogleGenAI(Provider):
572
584
  ) -> LLMResponse:
573
585
  if contexts is None:
574
586
  contexts = []
575
- new_record = await self.assemble_context(prompt, image_urls)
576
- context_query = [*contexts, new_record]
587
+ new_record = None
588
+ if prompt is not None:
589
+ new_record = await self.assemble_context(prompt, image_urls)
590
+ context_query = self._ensure_message_to_dicts(contexts)
591
+ if new_record:
592
+ context_query.append(new_record)
577
593
  if system_prompt:
578
594
  context_query.insert(0, {"role": "system", "content": system_prompt})
579
595
 
@@ -609,7 +625,7 @@ class ProviderGoogleGenAI(Provider):
609
625
 
610
626
  async def text_chat_stream(
611
627
  self,
612
- prompt,
628
+ prompt=None,
613
629
  session_id=None,
614
630
  image_urls=None,
615
631
  func_tool=None,
@@ -621,8 +637,12 @@ class ProviderGoogleGenAI(Provider):
621
637
  ) -> AsyncGenerator[LLMResponse, None]:
622
638
  if contexts is None:
623
639
  contexts = []
624
- new_record = await self.assemble_context(prompt, image_urls)
625
- context_query = [*contexts, new_record]
640
+ new_record = None
641
+ if prompt is not None:
642
+ new_record = await self.assemble_context(prompt, image_urls)
643
+ context_query = self._ensure_message_to_dicts(contexts)
644
+ if new_record:
645
+ context_query.append(new_record)
626
646
  if system_prompt:
627
647
  context_query.insert(0, {"role": "system", "content": system_prompt})
628
648
 
@@ -680,9 +700,7 @@ class ProviderGoogleGenAI(Provider):
680
700
  self._init_client()
681
701
 
682
702
  async def assemble_context(self, text: str, image_urls: list[str] | None = None):
683
- """
684
- 组装上下文。
685
- """
703
+ """组装上下文。"""
686
704
  if image_urls:
687
705
  user_content = {
688
706
  "role": "user",
@@ -704,22 +722,18 @@ class ProviderGoogleGenAI(Provider):
704
722
  {
705
723
  "type": "image_url",
706
724
  "image_url": {"url": image_data},
707
- }
725
+ },
708
726
  )
709
727
  return user_content
710
- else:
711
- return {"role": "user", "content": text}
728
+ return {"role": "user", "content": text}
712
729
 
713
730
  async def encode_image_bs64(self, image_url: str) -> str:
714
- """
715
- 将图片转换为 base64
716
- """
731
+ """将图片转换为 base64"""
717
732
  if image_url.startswith("base64://"):
718
733
  return image_url.replace("base64://", "data:image/jpeg;base64,")
719
734
  with open(image_url, "rb") as f:
720
735
  image_bs64 = base64.b64encode(f.read()).decode("utf-8")
721
736
  return "data:image/jpeg;base64," + image_bs64
722
- return ""
723
737
 
724
738
  async def terminate(self):
725
739
  logger.info("Google GenAI 适配器已终止。")
@@ -13,7 +13,9 @@ from ..register import register_provider_adapter
13
13
 
14
14
 
15
15
  @register_provider_adapter(
16
- "gemini_tts", "Gemini TTS API", provider_type=ProviderType.TEXT_TO_SPEECH
16
+ "gemini_tts",
17
+ "Gemini TTS API",
18
+ provider_type=ProviderType.TEXT_TO_SPEECH,
17
19
  )
18
20
  class ProviderGeminiTTSAPI(TTSProvider):
19
21
  def __init__(
@@ -28,13 +30,13 @@ class ProviderGeminiTTSAPI(TTSProvider):
28
30
  http_options = types.HttpOptions(timeout=timeout * 1000)
29
31
 
30
32
  if api_base:
31
- if api_base.endswith("/"):
32
- api_base = api_base[:-1]
33
+ api_base = api_base.removesuffix("/")
33
34
  http_options.base_url = api_base
34
35
 
35
36
  self.client = genai.Client(api_key=api_key, http_options=http_options).aio
36
37
  self.model: str = provider_config.get(
37
- "gemini_tts_model", "gemini-2.5-flash-preview-tts"
38
+ "gemini_tts_model",
39
+ "gemini-2.5-flash-preview-tts",
38
40
  )
39
41
  self.prefix: str | None = provider_config.get(
40
42
  "gemini_tts_prefix",
@@ -54,8 +56,8 @@ class ProviderGeminiTTSAPI(TTSProvider):
54
56
  voice_config=types.VoiceConfig(
55
57
  prebuilt_voice_config=types.PrebuiltVoiceConfig(
56
58
  voice_name=self.voice_name,
57
- )
58
- )
59
+ ),
60
+ ),
59
61
  ),
60
62
  ),
61
63
  )
@@ -3,12 +3,14 @@ import os
3
3
  import uuid
4
4
 
5
5
  import aiohttp
6
- from ..provider import TTSProvider
7
- from ..entities import ProviderType
8
- from ..register import register_provider_adapter
6
+
9
7
  from astrbot import logger
10
8
  from astrbot.core.utils.astrbot_path import get_astrbot_data_path
11
9
 
10
+ from ..entities import ProviderType
11
+ from ..provider import TTSProvider
12
+ from ..register import register_provider_adapter
13
+
12
14
 
13
15
  @register_provider_adapter(
14
16
  provider_type_name="gsv_tts_selfhost",
@@ -24,7 +26,7 @@ class ProviderGSVTTS(TTSProvider):
24
26
  super().__init__(provider_config, provider_settings)
25
27
 
26
28
  self.api_base = provider_config.get("api_base", "http://127.0.0.1:9880").rstrip(
27
- "/"
29
+ "/",
28
30
  )
29
31
  self.gpt_weights_path: str = provider_config.get("gpt_weights_path", "")
30
32
  self.sovits_weights_path: str = provider_config.get("sovits_weights_path", "")
@@ -40,7 +42,7 @@ class ProviderGSVTTS(TTSProvider):
40
42
  async def initialize(self):
41
43
  """异步初始化:在 ProviderManager 中被调用"""
42
44
  self._session = aiohttp.ClientSession(
43
- timeout=aiohttp.ClientTimeout(total=self.timeout)
45
+ timeout=aiohttp.ClientTimeout(total=self.timeout),
44
46
  )
45
47
  try:
46
48
  await self._set_model_weights()
@@ -52,12 +54,15 @@ class ProviderGSVTTS(TTSProvider):
52
54
  def get_session(self) -> aiohttp.ClientSession:
53
55
  if not self._session or self._session.closed:
54
56
  raise RuntimeError(
55
- "[GSV TTS] Provider HTTP session is not ready or closed."
57
+ "[GSV TTS] Provider HTTP session is not ready or closed.",
56
58
  )
57
59
  return self._session
58
60
 
59
61
  async def _make_request(
60
- self, endpoint: str, params=None, retries: int = 3
62
+ self,
63
+ endpoint: str,
64
+ params=None,
65
+ retries: int = 3,
61
66
  ) -> bytes | None:
62
67
  """发起请求"""
63
68
  for attempt in range(retries):
@@ -67,13 +72,13 @@ class ProviderGSVTTS(TTSProvider):
67
72
  if response.status != 200:
68
73
  error_text = await response.text()
69
74
  raise Exception(
70
- f"[GSV TTS] Request to {endpoint} failed with status {response.status}: {error_text}"
75
+ f"[GSV TTS] Request to {endpoint} failed with status {response.status}: {error_text}",
71
76
  )
72
77
  return await response.read()
73
78
  except Exception as e:
74
79
  if attempt < retries - 1:
75
80
  logger.warning(
76
- f"[GSV TTS] 请求 {endpoint} 第 {attempt + 1} 次失败:{e},重试中..."
81
+ f"[GSV TTS] 请求 {endpoint} 第 {attempt + 1} 次失败:{e},重试中...",
77
82
  )
78
83
  await asyncio.sleep(1)
79
84
  else:
@@ -98,7 +103,7 @@ class ProviderGSVTTS(TTSProvider):
98
103
  {"weights_path": self.sovits_weights_path},
99
104
  )
100
105
  logger.info(
101
- f"[GSV TTS] 成功设置 SoVITS 模型路径:{self.sovits_weights_path}"
106
+ f"[GSV TTS] 成功设置 SoVITS 模型路径:{self.sovits_weights_path}",
102
107
  )
103
108
  else:
104
109
  logger.info("[GSV TTS] SoVITS 模型路径未配置,将使用内置 SoVITS 模型")
@@ -127,12 +132,10 @@ class ProviderGSVTTS(TTSProvider):
127
132
  with open(path, "wb") as f:
128
133
  f.write(result)
129
134
  return path
130
- else:
131
- raise Exception(f"[GSV TTS] 合成失败,输入文本:{text},错误信息:{result}")
135
+ raise Exception(f"[GSV TTS] 合成失败,输入文本:{text},错误信息:{result}")
132
136
 
133
137
  def build_synthesis_params(self, text: str) -> dict:
134
- """
135
- 构建语音合成所需的参数字典。
138
+ """构建语音合成所需的参数字典。
136
139
 
137
140
  当前仅包含默认参数 + 文本,未来可在此基础上动态添加如情绪、角色等语义控制字段。
138
141
  """
@@ -1,15 +1,20 @@
1
1
  import os
2
+ import urllib.parse
2
3
  import uuid
4
+
3
5
  import aiohttp
4
- import urllib.parse
5
- from ..provider import TTSProvider
6
+
7
+ from astrbot.core.utils.astrbot_path import get_astrbot_data_path
8
+
6
9
  from ..entities import ProviderType
10
+ from ..provider import TTSProvider
7
11
  from ..register import register_provider_adapter
8
- from astrbot.core.utils.astrbot_path import get_astrbot_data_path
9
12
 
10
13
 
11
14
  @register_provider_adapter(
12
- "gsvi_tts_api", "GSVI TTS API", provider_type=ProviderType.TEXT_TO_SPEECH
15
+ "gsvi_tts_api",
16
+ "GSVI TTS API",
17
+ provider_type=ProviderType.TEXT_TO_SPEECH,
13
18
  )
14
19
  class ProviderGSVITTS(TTSProvider):
15
20
  def __init__(
@@ -19,8 +24,7 @@ class ProviderGSVITTS(TTSProvider):
19
24
  ) -> None:
20
25
  super().__init__(provider_config, provider_settings)
21
26
  self.api_base = provider_config.get("api_base", "http://127.0.0.1:5000")
22
- if self.api_base.endswith("/"):
23
- self.api_base = self.api_base[:-1]
27
+ self.api_base = self.api_base.removesuffix("/")
24
28
  self.character = provider_config.get("character")
25
29
  self.emotion = provider_config.get("emotion")
26
30
 
@@ -49,7 +53,7 @@ class ProviderGSVITTS(TTSProvider):
49
53
  else:
50
54
  error_text = await response.text()
51
55
  raise Exception(
52
- f"GSVI TTS API 请求失败,状态码: {response.status},错误: {error_text}"
56
+ f"GSVI TTS API 请求失败,状态码: {response.status},错误: {error_text}",
53
57
  )
54
58
 
55
59
  return path