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.
- astrbot/api/__init__.py +10 -11
- astrbot/api/event/__init__.py +5 -6
- astrbot/api/event/filter/__init__.py +37 -36
- astrbot/api/platform/__init__.py +7 -8
- astrbot/api/provider/__init__.py +7 -7
- astrbot/api/star/__init__.py +3 -4
- astrbot/api/util/__init__.py +2 -2
- astrbot/cli/__main__.py +5 -5
- astrbot/cli/commands/__init__.py +3 -3
- astrbot/cli/commands/cmd_conf.py +19 -16
- astrbot/cli/commands/cmd_init.py +3 -2
- astrbot/cli/commands/cmd_plug.py +8 -10
- astrbot/cli/commands/cmd_run.py +5 -6
- astrbot/cli/utils/__init__.py +6 -6
- astrbot/cli/utils/basic.py +14 -14
- astrbot/cli/utils/plugin.py +24 -15
- astrbot/cli/utils/version_comparator.py +10 -12
- astrbot/core/__init__.py +8 -6
- astrbot/core/agent/agent.py +3 -2
- astrbot/core/agent/handoff.py +6 -2
- astrbot/core/agent/hooks.py +9 -6
- astrbot/core/agent/mcp_client.py +50 -15
- astrbot/core/agent/message.py +168 -0
- astrbot/core/agent/response.py +2 -1
- astrbot/core/agent/run_context.py +2 -3
- astrbot/core/agent/runners/base.py +10 -13
- astrbot/core/agent/runners/tool_loop_agent_runner.py +52 -51
- astrbot/core/agent/tool.py +60 -41
- astrbot/core/agent/tool_executor.py +9 -3
- astrbot/core/astr_agent_context.py +3 -1
- astrbot/core/astrbot_config_mgr.py +29 -9
- astrbot/core/config/__init__.py +2 -2
- astrbot/core/config/astrbot_config.py +28 -26
- astrbot/core/config/default.py +4 -6
- astrbot/core/conversation_mgr.py +105 -36
- astrbot/core/core_lifecycle.py +68 -54
- astrbot/core/db/__init__.py +33 -18
- astrbot/core/db/migration/helper.py +12 -10
- astrbot/core/db/migration/migra_3_to_4.py +53 -34
- astrbot/core/db/migration/migra_45_to_46.py +1 -1
- astrbot/core/db/migration/shared_preferences_v3.py +2 -1
- astrbot/core/db/migration/sqlite_v3.py +26 -23
- astrbot/core/db/po.py +27 -18
- astrbot/core/db/sqlite.py +74 -45
- astrbot/core/db/vec_db/base.py +10 -14
- astrbot/core/db/vec_db/faiss_impl/document_storage.py +90 -77
- astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +9 -3
- astrbot/core/db/vec_db/faiss_impl/vec_db.py +36 -31
- astrbot/core/event_bus.py +8 -6
- astrbot/core/file_token_service.py +6 -5
- astrbot/core/initial_loader.py +7 -5
- astrbot/core/knowledge_base/chunking/__init__.py +1 -3
- astrbot/core/knowledge_base/chunking/base.py +1 -0
- astrbot/core/knowledge_base/chunking/fixed_size.py +2 -0
- astrbot/core/knowledge_base/chunking/recursive.py +16 -10
- astrbot/core/knowledge_base/kb_db_sqlite.py +50 -48
- astrbot/core/knowledge_base/kb_helper.py +30 -17
- astrbot/core/knowledge_base/kb_mgr.py +6 -7
- astrbot/core/knowledge_base/models.py +10 -4
- astrbot/core/knowledge_base/parsers/__init__.py +3 -5
- astrbot/core/knowledge_base/parsers/base.py +1 -0
- astrbot/core/knowledge_base/parsers/markitdown_parser.py +2 -1
- astrbot/core/knowledge_base/parsers/pdf_parser.py +2 -1
- astrbot/core/knowledge_base/parsers/text_parser.py +1 -0
- astrbot/core/knowledge_base/parsers/util.py +1 -1
- astrbot/core/knowledge_base/retrieval/__init__.py +6 -8
- astrbot/core/knowledge_base/retrieval/manager.py +17 -14
- astrbot/core/knowledge_base/retrieval/rank_fusion.py +7 -3
- astrbot/core/knowledge_base/retrieval/sparse_retriever.py +11 -5
- astrbot/core/log.py +21 -13
- astrbot/core/message/components.py +123 -217
- astrbot/core/message/message_event_result.py +24 -24
- astrbot/core/persona_mgr.py +20 -11
- astrbot/core/pipeline/__init__.py +7 -7
- astrbot/core/pipeline/content_safety_check/stage.py +13 -9
- astrbot/core/pipeline/content_safety_check/strategies/__init__.py +1 -2
- astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +12 -13
- astrbot/core/pipeline/content_safety_check/strategies/keywords.py +1 -0
- astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
- astrbot/core/pipeline/context.py +4 -1
- astrbot/core/pipeline/context_utils.py +77 -7
- astrbot/core/pipeline/preprocess_stage/stage.py +12 -9
- astrbot/core/pipeline/process_stage/method/llm_request.py +125 -72
- astrbot/core/pipeline/process_stage/method/star_request.py +19 -17
- astrbot/core/pipeline/process_stage/stage.py +13 -10
- astrbot/core/pipeline/process_stage/utils.py +6 -5
- astrbot/core/pipeline/rate_limit_check/stage.py +37 -36
- astrbot/core/pipeline/respond/stage.py +23 -20
- astrbot/core/pipeline/result_decorate/stage.py +31 -23
- astrbot/core/pipeline/scheduler.py +12 -8
- astrbot/core/pipeline/session_status_check/stage.py +12 -8
- astrbot/core/pipeline/stage.py +10 -4
- astrbot/core/pipeline/waking_check/stage.py +24 -18
- astrbot/core/pipeline/whitelist_check/stage.py +10 -7
- astrbot/core/platform/__init__.py +6 -6
- astrbot/core/platform/astr_message_event.py +76 -110
- astrbot/core/platform/astrbot_message.py +11 -13
- astrbot/core/platform/manager.py +16 -15
- astrbot/core/platform/message_session.py +5 -3
- astrbot/core/platform/platform.py +16 -24
- astrbot/core/platform/platform_metadata.py +4 -4
- astrbot/core/platform/register.py +8 -8
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +23 -15
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +51 -33
- astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +42 -27
- astrbot/core/platform/sources/dingtalk/dingtalk_event.py +7 -3
- astrbot/core/platform/sources/discord/client.py +9 -6
- astrbot/core/platform/sources/discord/components.py +18 -14
- astrbot/core/platform/sources/discord/discord_platform_adapter.py +45 -30
- astrbot/core/platform/sources/discord/discord_platform_event.py +38 -30
- astrbot/core/platform/sources/lark/lark_adapter.py +23 -17
- astrbot/core/platform/sources/lark/lark_event.py +21 -14
- astrbot/core/platform/sources/misskey/misskey_adapter.py +107 -67
- astrbot/core/platform/sources/misskey/misskey_api.py +153 -129
- astrbot/core/platform/sources/misskey/misskey_event.py +20 -15
- astrbot/core/platform/sources/misskey/misskey_utils.py +74 -62
- astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +63 -44
- astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +41 -26
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +36 -17
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_event.py +3 -1
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +12 -7
- astrbot/core/platform/sources/satori/satori_adapter.py +56 -38
- astrbot/core/platform/sources/satori/satori_event.py +34 -25
- astrbot/core/platform/sources/slack/client.py +11 -9
- astrbot/core/platform/sources/slack/slack_adapter.py +52 -36
- astrbot/core/platform/sources/slack/slack_event.py +34 -24
- astrbot/core/platform/sources/telegram/tg_adapter.py +38 -18
- astrbot/core/platform/sources/telegram/tg_event.py +32 -18
- astrbot/core/platform/sources/webchat/webchat_adapter.py +27 -17
- astrbot/core/platform/sources/webchat/webchat_event.py +14 -10
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +115 -120
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +9 -8
- astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +15 -16
- astrbot/core/platform/sources/wecom/wecom_adapter.py +35 -18
- astrbot/core/platform/sources/wecom/wecom_event.py +55 -48
- astrbot/core/platform/sources/wecom/wecom_kf.py +34 -44
- astrbot/core/platform/sources/wecom/wecom_kf_message.py +26 -10
- astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +18 -10
- astrbot/core/platform/sources/wecom_ai_bot/__init__.py +3 -5
- astrbot/core/platform/sources/wecom_ai_bot/ierror.py +0 -1
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +61 -37
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +67 -28
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +8 -9
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +18 -9
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +14 -12
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +22 -12
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +40 -26
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +47 -45
- astrbot/core/platform_message_history_mgr.py +5 -3
- astrbot/core/provider/__init__.py +2 -3
- astrbot/core/provider/entites.py +8 -8
- astrbot/core/provider/entities.py +61 -75
- astrbot/core/provider/func_tool_manager.py +59 -55
- astrbot/core/provider/manager.py +32 -22
- astrbot/core/provider/provider.py +72 -46
- astrbot/core/provider/register.py +7 -7
- astrbot/core/provider/sources/anthropic_source.py +48 -30
- astrbot/core/provider/sources/azure_tts_source.py +17 -13
- astrbot/core/provider/sources/coze_api_client.py +27 -17
- astrbot/core/provider/sources/coze_source.py +104 -87
- astrbot/core/provider/sources/dashscope_source.py +18 -11
- astrbot/core/provider/sources/dashscope_tts.py +36 -23
- astrbot/core/provider/sources/dify_source.py +25 -20
- astrbot/core/provider/sources/edge_tts_source.py +21 -17
- astrbot/core/provider/sources/fishaudio_tts_api_source.py +22 -14
- astrbot/core/provider/sources/gemini_embedding_source.py +12 -13
- astrbot/core/provider/sources/gemini_source.py +72 -58
- astrbot/core/provider/sources/gemini_tts_source.py +8 -6
- astrbot/core/provider/sources/gsv_selfhosted_source.py +17 -14
- astrbot/core/provider/sources/gsvi_tts_source.py +11 -7
- astrbot/core/provider/sources/minimax_tts_api_source.py +50 -40
- astrbot/core/provider/sources/openai_embedding_source.py +6 -8
- astrbot/core/provider/sources/openai_source.py +77 -69
- astrbot/core/provider/sources/openai_tts_api_source.py +14 -6
- astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
- astrbot/core/provider/sources/vllm_rerank_source.py +10 -4
- astrbot/core/provider/sources/volcengine_tts.py +38 -31
- astrbot/core/provider/sources/whisper_api_source.py +14 -12
- astrbot/core/provider/sources/whisper_selfhosted_source.py +15 -11
- astrbot/core/provider/sources/xinference_rerank_source.py +16 -8
- astrbot/core/provider/sources/xinference_stt_provider.py +35 -25
- astrbot/core/star/__init__.py +16 -11
- astrbot/core/star/config.py +10 -15
- astrbot/core/star/context.py +97 -75
- astrbot/core/star/filter/__init__.py +4 -3
- astrbot/core/star/filter/command.py +30 -28
- astrbot/core/star/filter/command_group.py +27 -24
- astrbot/core/star/filter/custom_filter.py +6 -5
- astrbot/core/star/filter/event_message_type.py +4 -2
- astrbot/core/star/filter/permission.py +4 -2
- astrbot/core/star/filter/platform_adapter_type.py +4 -2
- astrbot/core/star/filter/regex.py +4 -2
- astrbot/core/star/register/__init__.py +19 -19
- astrbot/core/star/register/star.py +6 -2
- astrbot/core/star/register/star_handler.py +96 -73
- astrbot/core/star/session_llm_manager.py +48 -14
- astrbot/core/star/session_plugin_manager.py +29 -15
- astrbot/core/star/star.py +1 -2
- astrbot/core/star/star_handler.py +13 -8
- astrbot/core/star/star_manager.py +151 -59
- astrbot/core/star/star_tools.py +44 -37
- astrbot/core/star/updator.py +10 -10
- astrbot/core/umop_config_router.py +10 -4
- astrbot/core/updator.py +13 -5
- astrbot/core/utils/astrbot_path.py +3 -5
- astrbot/core/utils/dify_api_client.py +33 -15
- astrbot/core/utils/io.py +66 -42
- astrbot/core/utils/log_pipe.py +1 -1
- astrbot/core/utils/metrics.py +7 -7
- astrbot/core/utils/path_util.py +15 -16
- astrbot/core/utils/pip_installer.py +5 -5
- astrbot/core/utils/session_waiter.py +19 -20
- astrbot/core/utils/shared_preferences.py +45 -20
- astrbot/core/utils/t2i/__init__.py +4 -1
- astrbot/core/utils/t2i/network_strategy.py +35 -26
- astrbot/core/utils/t2i/renderer.py +11 -5
- astrbot/core/utils/t2i/template_manager.py +14 -15
- astrbot/core/utils/tencent_record_helper.py +19 -13
- astrbot/core/utils/version_comparator.py +10 -13
- astrbot/core/zip_updator.py +43 -40
- astrbot/dashboard/routes/__init__.py +18 -18
- astrbot/dashboard/routes/auth.py +10 -8
- astrbot/dashboard/routes/chat.py +30 -21
- astrbot/dashboard/routes/config.py +92 -75
- astrbot/dashboard/routes/conversation.py +46 -39
- astrbot/dashboard/routes/file.py +4 -2
- astrbot/dashboard/routes/knowledge_base.py +47 -40
- astrbot/dashboard/routes/log.py +9 -4
- astrbot/dashboard/routes/persona.py +19 -16
- astrbot/dashboard/routes/plugin.py +69 -55
- astrbot/dashboard/routes/route.py +3 -1
- astrbot/dashboard/routes/session_management.py +130 -116
- astrbot/dashboard/routes/stat.py +34 -34
- astrbot/dashboard/routes/t2i.py +15 -12
- astrbot/dashboard/routes/tools.py +56 -53
- astrbot/dashboard/routes/update.py +32 -28
- astrbot/dashboard/server.py +30 -26
- astrbot/dashboard/utils.py +8 -4
- {astrbot-4.5.1.dist-info → astrbot-4.5.3.dist-info}/METADATA +2 -1
- astrbot-4.5.3.dist-info/RECORD +261 -0
- astrbot-4.5.1.dist-info/RECORD +0 -260
- {astrbot-4.5.1.dist-info → astrbot-4.5.3.dist-info}/WHEEL +0 -0
- {astrbot-4.5.1.dist-info → astrbot-4.5.3.dist-info}/entry_points.txt +0 -0
- {astrbot-4.5.1.dist-info → astrbot-4.5.3.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",
|
|
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:
|
|
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:
|
|
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,
|
|
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"
|
|
113
|
+
f"检测到 Key 异常({e.message}),且已没有可用的 Key。 当前 Key: {self.chosen_api_key[:12]}...",
|
|
119
114
|
)
|
|
120
|
-
raise
|
|
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:
|
|
126
|
-
system_instruction:
|
|
127
|
-
modalities:
|
|
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",
|
|
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,
|
|
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:
|
|
410
|
+
result: types.GenerateContentResponse | None = None
|
|
410
411
|
while True:
|
|
411
412
|
try:
|
|
412
413
|
config = await self._prepare_query_config(
|
|
413
|
-
payloads,
|
|
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],
|
|
469
|
+
result.candidates[0],
|
|
470
|
+
llm_response,
|
|
465
471
|
)
|
|
466
472
|
return llm_response
|
|
467
473
|
|
|
468
474
|
async def _query_stream(
|
|
469
|
-
self,
|
|
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,
|
|
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],
|
|
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],
|
|
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
|
|
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 =
|
|
576
|
-
|
|
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 =
|
|
625
|
-
|
|
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
|
-
|
|
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",
|
|
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
|
-
|
|
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",
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
5
|
-
from
|
|
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",
|
|
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
|
-
|
|
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
|