AstrBot 4.5.1__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.
- 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 +47 -52
- 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.2.dist-info}/METADATA +2 -1
- astrbot-4.5.2.dist-info/RECORD +261 -0
- astrbot-4.5.1.dist-info/RECORD +0 -260
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/WHEEL +0 -0
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/entry_points.txt +0 -0
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/licenses/LICENSE +0 -0
astrbot/dashboard/routes/chat.py
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import
|
|
1
|
+
import asyncio
|
|
2
2
|
import json
|
|
3
3
|
import os
|
|
4
|
-
import
|
|
4
|
+
import uuid
|
|
5
5
|
from contextlib import asynccontextmanager
|
|
6
|
-
|
|
7
|
-
from
|
|
8
|
-
from quart import
|
|
9
|
-
|
|
6
|
+
|
|
7
|
+
from quart import Response as QuartResponse
|
|
8
|
+
from quart import g, make_response, request
|
|
9
|
+
|
|
10
10
|
from astrbot.core import logger
|
|
11
11
|
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
|
|
12
|
-
from astrbot.core.
|
|
12
|
+
from astrbot.core.db import BaseDatabase
|
|
13
13
|
from astrbot.core.platform.astr_message_event import MessageSession
|
|
14
|
+
from astrbot.core.platform.sources.webchat.webchat_queue_mgr import webchat_queue_mgr
|
|
15
|
+
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
16
|
+
|
|
17
|
+
from .route import Response, Route, RouteContext
|
|
14
18
|
|
|
15
19
|
|
|
16
20
|
@asynccontextmanager
|
|
@@ -70,10 +74,9 @@ class ChatRoute(Route):
|
|
|
70
74
|
|
|
71
75
|
if filename_ext == ".wav":
|
|
72
76
|
return QuartResponse(f.read(), mimetype="audio/wav")
|
|
73
|
-
|
|
77
|
+
if filename_ext[1:] in self.supported_imgs:
|
|
74
78
|
return QuartResponse(f.read(), mimetype="image/jpeg")
|
|
75
|
-
|
|
76
|
-
return QuartResponse(f.read())
|
|
79
|
+
return QuartResponse(f.read())
|
|
77
80
|
|
|
78
81
|
except (FileNotFoundError, OSError):
|
|
79
82
|
return Response().error("File access error").__dict__
|
|
@@ -96,7 +99,7 @@ class ChatRoute(Route):
|
|
|
96
99
|
return Response().error("Missing key: file").__dict__
|
|
97
100
|
|
|
98
101
|
file = post_data["file"]
|
|
99
|
-
filename = f"{
|
|
102
|
+
filename = f"{uuid.uuid4()!s}"
|
|
100
103
|
# 通过文件格式判断文件类型
|
|
101
104
|
if file.content_type.startswith("audio"):
|
|
102
105
|
filename += ".wav"
|
|
@@ -131,10 +134,10 @@ class ChatRoute(Route):
|
|
|
131
134
|
if not conversation_id:
|
|
132
135
|
return Response().error("conversation_id is empty").__dict__
|
|
133
136
|
|
|
134
|
-
#
|
|
137
|
+
# 追加用户消息
|
|
135
138
|
webchat_conv_id = await self._get_webchat_conv_id_from_conv_id(conversation_id)
|
|
136
139
|
|
|
137
|
-
#
|
|
140
|
+
# 获取会话特定的队列
|
|
138
141
|
back_queue = webchat_queue_mgr.get_or_create_back_queue(webchat_conv_id)
|
|
139
142
|
|
|
140
143
|
new_his = {"type": "user", "message": message}
|
|
@@ -179,7 +182,7 @@ class ChatRoute(Route):
|
|
|
179
182
|
except Exception as e:
|
|
180
183
|
if not client_disconnected:
|
|
181
184
|
logger.debug(
|
|
182
|
-
f"[WebChat] 用户 {username} 断开聊天长连接。 {e}"
|
|
185
|
+
f"[WebChat] 用户 {username} 断开聊天长连接。 {e}",
|
|
183
186
|
)
|
|
184
187
|
client_disconnected = True
|
|
185
188
|
|
|
@@ -197,7 +200,7 @@ class ChatRoute(Route):
|
|
|
197
200
|
or not streaming
|
|
198
201
|
or type == "break"
|
|
199
202
|
):
|
|
200
|
-
#
|
|
203
|
+
# 追加机器人消息
|
|
201
204
|
new_his = {"type": "bot", "message": result_text}
|
|
202
205
|
await self.platform_history_mgr.insert(
|
|
203
206
|
platform_id="webchat",
|
|
@@ -209,7 +212,7 @@ class ChatRoute(Route):
|
|
|
209
212
|
except BaseException as e:
|
|
210
213
|
logger.exception(f"WebChat stream unexpected error: {e}", exc_info=True)
|
|
211
214
|
|
|
212
|
-
#
|
|
215
|
+
# 将消息放入会话特定的队列
|
|
213
216
|
chat_queue = webchat_queue_mgr.get_or_create_queue(webchat_conv_id)
|
|
214
217
|
await chat_queue.put(
|
|
215
218
|
(
|
|
@@ -222,7 +225,7 @@ class ChatRoute(Route):
|
|
|
222
225
|
"selected_provider": selected_provider,
|
|
223
226
|
"selected_model": selected_model,
|
|
224
227
|
},
|
|
225
|
-
)
|
|
228
|
+
),
|
|
226
229
|
)
|
|
227
230
|
|
|
228
231
|
response = await make_response(
|
|
@@ -243,7 +246,8 @@ class ChatRoute(Route):
|
|
|
243
246
|
NOTE: 关于这里为什么要单独做一个 WebChat 的 Conversation ID 出来,这个是为了向前兼容。
|
|
244
247
|
"""
|
|
245
248
|
conversation = await self.conv_mgr.get_conversation(
|
|
246
|
-
unified_msg_origin="webchat",
|
|
249
|
+
unified_msg_origin="webchat",
|
|
250
|
+
conversation_id=conversation_id,
|
|
247
251
|
)
|
|
248
252
|
if not conversation:
|
|
249
253
|
raise ValueError(f"Conversation with ID {conversation_id} not found.")
|
|
@@ -267,7 +271,9 @@ class ChatRoute(Route):
|
|
|
267
271
|
conversation_id=conversation_id,
|
|
268
272
|
)
|
|
269
273
|
await self.platform_history_mgr.delete(
|
|
270
|
-
platform_id="webchat",
|
|
274
|
+
platform_id="webchat",
|
|
275
|
+
user_id=webchat_conv_id,
|
|
276
|
+
offset_sec=99999999,
|
|
271
277
|
)
|
|
272
278
|
return Response().ok().__dict__
|
|
273
279
|
|
|
@@ -314,7 +320,10 @@ class ChatRoute(Route):
|
|
|
314
320
|
|
|
315
321
|
# Get platform message history
|
|
316
322
|
history_ls = await self.platform_history_mgr.get(
|
|
317
|
-
platform_id="webchat",
|
|
323
|
+
platform_id="webchat",
|
|
324
|
+
user_id=webchat_conv_id,
|
|
325
|
+
page=1,
|
|
326
|
+
page_size=1000,
|
|
318
327
|
)
|
|
319
328
|
|
|
320
329
|
history_res = [history.model_dump() for history in history_ls]
|
|
@@ -325,7 +334,7 @@ class ChatRoute(Route):
|
|
|
325
334
|
data={
|
|
326
335
|
"history": history_res,
|
|
327
336
|
"is_running": self.running_convs.get(webchat_conv_id, False),
|
|
328
|
-
}
|
|
337
|
+
},
|
|
329
338
|
)
|
|
330
339
|
.__dict__
|
|
331
340
|
)
|
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
import os
|
|
1
|
+
import asyncio
|
|
3
2
|
import inspect
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import os
|
|
4
|
+
import traceback
|
|
5
|
+
|
|
6
6
|
from quart import request
|
|
7
|
+
|
|
8
|
+
from astrbot.core import file_token_service, logger
|
|
9
|
+
from astrbot.core.config.astrbot_config import AstrBotConfig
|
|
7
10
|
from astrbot.core.config.default import (
|
|
8
|
-
DEFAULT_CONFIG,
|
|
9
11
|
CONFIG_METADATA_2,
|
|
10
|
-
DEFAULT_VALUE_MAP,
|
|
11
12
|
CONFIG_METADATA_3,
|
|
12
13
|
CONFIG_METADATA_3_SYSTEM,
|
|
14
|
+
DEFAULT_CONFIG,
|
|
15
|
+
DEFAULT_VALUE_MAP,
|
|
13
16
|
)
|
|
14
|
-
from astrbot.core.utils.astrbot_path import get_astrbot_path
|
|
15
|
-
from astrbot.core.config.astrbot_config import AstrBotConfig
|
|
16
17
|
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
|
|
17
|
-
from astrbot.core.platform.register import
|
|
18
|
-
from astrbot.core.provider.register import provider_registry
|
|
19
|
-
from astrbot.core.star.star import star_registry
|
|
20
|
-
from astrbot.core import logger, file_token_service
|
|
18
|
+
from astrbot.core.platform.register import platform_cls_map, platform_registry
|
|
21
19
|
from astrbot.core.provider import Provider
|
|
20
|
+
from astrbot.core.provider.entities import ProviderType
|
|
22
21
|
from astrbot.core.provider.provider import RerankProvider
|
|
23
|
-
import
|
|
22
|
+
from astrbot.core.provider.register import provider_registry
|
|
23
|
+
from astrbot.core.star.star import star_registry
|
|
24
|
+
from astrbot.core.utils.astrbot_path import get_astrbot_path
|
|
25
|
+
|
|
26
|
+
from .route import Response, Route, RouteContext
|
|
24
27
|
|
|
25
28
|
|
|
26
29
|
def try_cast(value: str, type_: str):
|
|
@@ -33,9 +36,7 @@ def try_cast(value: str, type_: str):
|
|
|
33
36
|
type_ == "float"
|
|
34
37
|
and isinstance(value, str)
|
|
35
38
|
and value.replace(".", "", 1).isdigit()
|
|
36
|
-
):
|
|
37
|
-
return float(value)
|
|
38
|
-
elif type_ == "float" and isinstance(value, int):
|
|
39
|
+
) or (type_ == "float" and isinstance(value, int)):
|
|
39
40
|
return float(value)
|
|
40
41
|
elif type_ == "float":
|
|
41
42
|
try:
|
|
@@ -61,7 +62,7 @@ def validate_config(data, schema: dict, is_core: bool) -> tuple[list[str], dict]
|
|
|
61
62
|
continue
|
|
62
63
|
if meta["type"] == "list" and not isinstance(value, list):
|
|
63
64
|
errors.append(
|
|
64
|
-
f"错误的类型 {path}{key}: 期望是 list, 得到了 {type(value).__name__}"
|
|
65
|
+
f"错误的类型 {path}{key}: 期望是 list, 得到了 {type(value).__name__}",
|
|
65
66
|
)
|
|
66
67
|
elif (
|
|
67
68
|
meta["type"] == "list"
|
|
@@ -80,31 +81,31 @@ def validate_config(data, schema: dict, is_core: bool) -> tuple[list[str], dict]
|
|
|
80
81
|
casted = try_cast(value, "int")
|
|
81
82
|
if casted is None:
|
|
82
83
|
errors.append(
|
|
83
|
-
f"错误的类型 {path}{key}: 期望是 int, 得到了 {type(value).__name__}"
|
|
84
|
+
f"错误的类型 {path}{key}: 期望是 int, 得到了 {type(value).__name__}",
|
|
84
85
|
)
|
|
85
86
|
data[key] = casted
|
|
86
87
|
elif meta["type"] == "float" and not isinstance(value, float):
|
|
87
88
|
casted = try_cast(value, "float")
|
|
88
89
|
if casted is None:
|
|
89
90
|
errors.append(
|
|
90
|
-
f"错误的类型 {path}{key}: 期望是 float, 得到了 {type(value).__name__}"
|
|
91
|
+
f"错误的类型 {path}{key}: 期望是 float, 得到了 {type(value).__name__}",
|
|
91
92
|
)
|
|
92
93
|
data[key] = casted
|
|
93
94
|
elif meta["type"] == "bool" and not isinstance(value, bool):
|
|
94
95
|
errors.append(
|
|
95
|
-
f"错误的类型 {path}{key}: 期望是 bool, 得到了 {type(value).__name__}"
|
|
96
|
+
f"错误的类型 {path}{key}: 期望是 bool, 得到了 {type(value).__name__}",
|
|
96
97
|
)
|
|
97
98
|
elif meta["type"] in ["string", "text"] and not isinstance(value, str):
|
|
98
99
|
errors.append(
|
|
99
|
-
f"错误的类型 {path}{key}: 期望是 string, 得到了 {type(value).__name__}"
|
|
100
|
+
f"错误的类型 {path}{key}: 期望是 string, 得到了 {type(value).__name__}",
|
|
100
101
|
)
|
|
101
102
|
elif meta["type"] == "list" and not isinstance(value, list):
|
|
102
103
|
errors.append(
|
|
103
|
-
f"错误的类型 {path}{key}: 期望是 list, 得到了 {type(value).__name__}"
|
|
104
|
+
f"错误的类型 {path}{key}: 期望是 list, 得到了 {type(value).__name__}",
|
|
104
105
|
)
|
|
105
106
|
elif meta["type"] == "object" and not isinstance(value, dict):
|
|
106
107
|
errors.append(
|
|
107
|
-
f"错误的类型 {path}{key}: 期望是 dict, 得到了 {type(value).__name__}"
|
|
108
|
+
f"错误的类型 {path}{key}: 期望是 dict, 得到了 {type(value).__name__}",
|
|
108
109
|
)
|
|
109
110
|
|
|
110
111
|
if is_core:
|
|
@@ -127,7 +128,9 @@ def save_config(post_config: dict, config: AstrBotConfig, is_core: bool = False)
|
|
|
127
128
|
try:
|
|
128
129
|
if is_core:
|
|
129
130
|
errors, post_config = validate_config(
|
|
130
|
-
post_config,
|
|
131
|
+
post_config,
|
|
132
|
+
CONFIG_METADATA_2,
|
|
133
|
+
is_core,
|
|
131
134
|
)
|
|
132
135
|
else:
|
|
133
136
|
errors, post_config = validate_config(post_config, config.schema, is_core)
|
|
@@ -143,7 +146,9 @@ def save_config(post_config: dict, config: AstrBotConfig, is_core: bool = False)
|
|
|
143
146
|
|
|
144
147
|
class ConfigRoute(Route):
|
|
145
148
|
def __init__(
|
|
146
|
-
self,
|
|
149
|
+
self,
|
|
150
|
+
context: RouteContext,
|
|
151
|
+
core_lifecycle: AstrBotCoreLifecycle,
|
|
147
152
|
) -> None:
|
|
148
153
|
super().__init__(context)
|
|
149
154
|
self.core_lifecycle = core_lifecycle
|
|
@@ -199,7 +204,7 @@ class ConfigRoute(Route):
|
|
|
199
204
|
return Response().ok(message="更新成功").__dict__
|
|
200
205
|
except Exception as e:
|
|
201
206
|
logger.error(traceback.format_exc())
|
|
202
|
-
return Response().error(f"更新路由表失败: {
|
|
207
|
+
return Response().error(f"更新路由表失败: {e!s}").__dict__
|
|
203
208
|
|
|
204
209
|
async def update_ucr(self):
|
|
205
210
|
"""更新 UMOP 配置路由表"""
|
|
@@ -218,7 +223,7 @@ class ConfigRoute(Route):
|
|
|
218
223
|
return Response().ok(message="更新成功").__dict__
|
|
219
224
|
except Exception as e:
|
|
220
225
|
logger.error(traceback.format_exc())
|
|
221
|
-
return Response().error(f"更新路由表失败: {
|
|
226
|
+
return Response().error(f"更新路由表失败: {e!s}").__dict__
|
|
222
227
|
|
|
223
228
|
async def delete_ucr(self):
|
|
224
229
|
"""删除 UMOP 配置路由表中的一项"""
|
|
@@ -238,7 +243,7 @@ class ConfigRoute(Route):
|
|
|
238
243
|
return Response().ok(message="删除成功").__dict__
|
|
239
244
|
except Exception as e:
|
|
240
245
|
logger.error(traceback.format_exc())
|
|
241
|
-
return Response().error(f"删除路由表项失败: {
|
|
246
|
+
return Response().error(f"删除路由表项失败: {e!s}").__dict__
|
|
242
247
|
|
|
243
248
|
async def get_default_config(self):
|
|
244
249
|
"""获取默认配置文件"""
|
|
@@ -305,13 +310,12 @@ class ConfigRoute(Route):
|
|
|
305
310
|
success = self.acm.delete_conf(conf_id)
|
|
306
311
|
if success:
|
|
307
312
|
return Response().ok(message="删除成功").__dict__
|
|
308
|
-
|
|
309
|
-
return Response().error("删除失败").__dict__
|
|
313
|
+
return Response().error("删除失败").__dict__
|
|
310
314
|
except ValueError as e:
|
|
311
315
|
return Response().error(str(e)).__dict__
|
|
312
316
|
except Exception as e:
|
|
313
317
|
logger.error(traceback.format_exc())
|
|
314
|
-
return Response().error(f"删除配置文件失败: {
|
|
318
|
+
return Response().error(f"删除配置文件失败: {e!s}").__dict__
|
|
315
319
|
|
|
316
320
|
async def update_abconf(self):
|
|
317
321
|
"""更新指定 AstrBot 配置文件信息"""
|
|
@@ -329,13 +333,12 @@ class ConfigRoute(Route):
|
|
|
329
333
|
success = self.acm.update_conf_info(conf_id, name=name)
|
|
330
334
|
if success:
|
|
331
335
|
return Response().ok(message="更新成功").__dict__
|
|
332
|
-
|
|
333
|
-
return Response().error("更新失败").__dict__
|
|
336
|
+
return Response().error("更新失败").__dict__
|
|
334
337
|
except ValueError as e:
|
|
335
338
|
return Response().error(str(e)).__dict__
|
|
336
339
|
except Exception as e:
|
|
337
340
|
logger.error(traceback.format_exc())
|
|
338
|
-
return Response().error(f"更新配置文件失败: {
|
|
341
|
+
return Response().error(f"更新配置文件失败: {e!s}").__dict__
|
|
339
342
|
|
|
340
343
|
async def _test_single_provider(self, provider):
|
|
341
344
|
"""辅助函数:测试单个 provider 的可用性"""
|
|
@@ -352,17 +355,18 @@ class ConfigRoute(Route):
|
|
|
352
355
|
"error": None,
|
|
353
356
|
}
|
|
354
357
|
logger.debug(
|
|
355
|
-
f"Attempting to check provider: {status_info['name']} (ID: {status_info['id']}, Type: {status_info['type']}, Model: {status_info['model']})"
|
|
358
|
+
f"Attempting to check provider: {status_info['name']} (ID: {status_info['id']}, Type: {status_info['type']}, Model: {status_info['model']})",
|
|
356
359
|
)
|
|
357
360
|
|
|
358
361
|
if provider_capability_type == ProviderType.CHAT_COMPLETION:
|
|
359
362
|
try:
|
|
360
363
|
logger.debug(f"Sending 'Ping' to provider: {status_info['name']}")
|
|
361
364
|
response = await asyncio.wait_for(
|
|
362
|
-
provider.text_chat(prompt="REPLY `PONG` ONLY"),
|
|
365
|
+
provider.text_chat(prompt="REPLY `PONG` ONLY"),
|
|
366
|
+
timeout=45.0,
|
|
363
367
|
)
|
|
364
368
|
logger.debug(
|
|
365
|
-
f"Received response from {status_info['name']}: {response}"
|
|
369
|
+
f"Received response from {status_info['name']}: {response}",
|
|
366
370
|
)
|
|
367
371
|
if response is not None:
|
|
368
372
|
status_info["status"] = "available"
|
|
@@ -386,14 +390,14 @@ class ConfigRoute(Route):
|
|
|
386
390
|
except Exception as _:
|
|
387
391
|
pass
|
|
388
392
|
logger.info(
|
|
389
|
-
f"Provider {status_info['name']} (ID: {status_info['id']}) is available. Response snippet: '{response_text_snippet}'"
|
|
393
|
+
f"Provider {status_info['name']} (ID: {status_info['id']}) is available. Response snippet: '{response_text_snippet}'",
|
|
390
394
|
)
|
|
391
395
|
else:
|
|
392
396
|
status_info["error"] = (
|
|
393
397
|
"Test call returned None, but expected an LLMResponse object."
|
|
394
398
|
)
|
|
395
399
|
logger.warning(
|
|
396
|
-
f"Provider {status_info['name']} (ID: {status_info['id']}) test call returned None."
|
|
400
|
+
f"Provider {status_info['name']} (ID: {status_info['id']}) test call returned None.",
|
|
397
401
|
)
|
|
398
402
|
|
|
399
403
|
except asyncio.TimeoutError:
|
|
@@ -401,16 +405,16 @@ class ConfigRoute(Route):
|
|
|
401
405
|
"Connection timed out after 45 seconds during test call."
|
|
402
406
|
)
|
|
403
407
|
logger.warning(
|
|
404
|
-
f"Provider {status_info['name']} (ID: {status_info['id']}) timed out."
|
|
408
|
+
f"Provider {status_info['name']} (ID: {status_info['id']}) timed out.",
|
|
405
409
|
)
|
|
406
410
|
except Exception as e:
|
|
407
411
|
error_message = str(e)
|
|
408
412
|
status_info["error"] = error_message
|
|
409
413
|
logger.warning(
|
|
410
|
-
f"Provider {status_info['name']} (ID: {status_info['id']}) is unavailable. Error: {error_message}"
|
|
414
|
+
f"Provider {status_info['name']} (ID: {status_info['id']}) is unavailable. Error: {error_message}",
|
|
411
415
|
)
|
|
412
416
|
logger.debug(
|
|
413
|
-
f"Traceback for {status_info['name']}:\n{traceback.format_exc()}"
|
|
417
|
+
f"Traceback for {status_info['name']}:\n{traceback.format_exc()}",
|
|
414
418
|
)
|
|
415
419
|
|
|
416
420
|
elif provider_capability_type == ProviderType.EMBEDDING:
|
|
@@ -432,7 +436,7 @@ class ConfigRoute(Route):
|
|
|
432
436
|
exc_info=True,
|
|
433
437
|
)
|
|
434
438
|
status_info["status"] = "unavailable"
|
|
435
|
-
status_info["error"] = f"Embedding test failed: {
|
|
439
|
+
status_info["error"] = f"Embedding test failed: {e!s}"
|
|
436
440
|
|
|
437
441
|
elif provider_capability_type == ProviderType.TEXT_TO_SPEECH:
|
|
438
442
|
try:
|
|
@@ -447,17 +451,20 @@ class ConfigRoute(Route):
|
|
|
447
451
|
)
|
|
448
452
|
except Exception as e:
|
|
449
453
|
logger.error(
|
|
450
|
-
f"Error testing TTS provider {provider_name}: {e}",
|
|
454
|
+
f"Error testing TTS provider {provider_name}: {e}",
|
|
455
|
+
exc_info=True,
|
|
451
456
|
)
|
|
452
457
|
status_info["status"] = "unavailable"
|
|
453
|
-
status_info["error"] = f"TTS test failed: {
|
|
458
|
+
status_info["error"] = f"TTS test failed: {e!s}"
|
|
454
459
|
elif provider_capability_type == ProviderType.SPEECH_TO_TEXT:
|
|
455
460
|
try:
|
|
456
461
|
logger.debug(
|
|
457
|
-
f"Sending health check audio to provider: {status_info['name']}"
|
|
462
|
+
f"Sending health check audio to provider: {status_info['name']}",
|
|
458
463
|
)
|
|
459
464
|
sample_audio_path = os.path.join(
|
|
460
|
-
get_astrbot_path(),
|
|
465
|
+
get_astrbot_path(),
|
|
466
|
+
"samples",
|
|
467
|
+
"stt_health_check.wav",
|
|
461
468
|
)
|
|
462
469
|
if not os.path.exists(sample_audio_path):
|
|
463
470
|
status_info["status"] = "unavailable"
|
|
@@ -465,7 +472,7 @@ class ConfigRoute(Route):
|
|
|
465
472
|
"STT test failed: sample audio file not found."
|
|
466
473
|
)
|
|
467
474
|
logger.warning(
|
|
468
|
-
f"STT test for {status_info['name']} failed: sample audio file not found at {sample_audio_path}"
|
|
475
|
+
f"STT test for {status_info['name']} failed: sample audio file not found at {sample_audio_path}",
|
|
469
476
|
)
|
|
470
477
|
else:
|
|
471
478
|
text_result = await provider.get_text(sample_audio_path)
|
|
@@ -477,7 +484,7 @@ class ConfigRoute(Route):
|
|
|
477
484
|
else text_result
|
|
478
485
|
)
|
|
479
486
|
logger.info(
|
|
480
|
-
f"Provider {status_info['name']} (ID: {status_info['id']}) is available. Response snippet: '{snippet}'"
|
|
487
|
+
f"Provider {status_info['name']} (ID: {status_info['id']}) is available. Response snippet: '{snippet}'",
|
|
481
488
|
)
|
|
482
489
|
else:
|
|
483
490
|
status_info["status"] = "unavailable"
|
|
@@ -485,14 +492,15 @@ class ConfigRoute(Route):
|
|
|
485
492
|
f"STT test failed: unexpected result type {type(text_result)}"
|
|
486
493
|
)
|
|
487
494
|
logger.warning(
|
|
488
|
-
f"STT test for {status_info['name']} failed: unexpected result type {type(text_result)}"
|
|
495
|
+
f"STT test for {status_info['name']} failed: unexpected result type {type(text_result)}",
|
|
489
496
|
)
|
|
490
497
|
except Exception as e:
|
|
491
498
|
logger.error(
|
|
492
|
-
f"Error testing STT provider {provider_name}: {e}",
|
|
499
|
+
f"Error testing STT provider {provider_name}: {e}",
|
|
500
|
+
exc_info=True,
|
|
493
501
|
)
|
|
494
502
|
status_info["status"] = "unavailable"
|
|
495
|
-
status_info["error"] = f"STT test failed: {
|
|
503
|
+
status_info["error"] = f"STT test failed: {e!s}"
|
|
496
504
|
elif provider_capability_type == ProviderType.RERANK:
|
|
497
505
|
try:
|
|
498
506
|
assert isinstance(provider, RerankProvider)
|
|
@@ -504,11 +512,11 @@ class ConfigRoute(Route):
|
|
|
504
512
|
exc_info=True,
|
|
505
513
|
)
|
|
506
514
|
status_info["status"] = "unavailable"
|
|
507
|
-
status_info["error"] = f"Rerank test failed: {
|
|
515
|
+
status_info["error"] = f"Rerank test failed: {e!s}"
|
|
508
516
|
|
|
509
517
|
else:
|
|
510
518
|
logger.debug(
|
|
511
|
-
f"Provider {provider_name} is not a Chat Completion or Embedding provider. Marking as available without test. Meta: {meta}"
|
|
519
|
+
f"Provider {provider_name} is not a Chat Completion or Embedding provider. Marking as available without test. Meta: {meta}",
|
|
512
520
|
)
|
|
513
521
|
status_info["status"] = "available"
|
|
514
522
|
status_info["error"] = (
|
|
@@ -518,7 +526,10 @@ class ConfigRoute(Route):
|
|
|
518
526
|
return status_info
|
|
519
527
|
|
|
520
528
|
def _error_response(
|
|
521
|
-
self,
|
|
529
|
+
self,
|
|
530
|
+
message: str,
|
|
531
|
+
status_code: int = 500,
|
|
532
|
+
log_fn=logger.error,
|
|
522
533
|
):
|
|
523
534
|
log_fn(message)
|
|
524
535
|
# 记录更详细的traceback信息,但只在是严重错误时
|
|
@@ -531,7 +542,9 @@ class ConfigRoute(Route):
|
|
|
531
542
|
provider_id = request.args.get("id")
|
|
532
543
|
if not provider_id:
|
|
533
544
|
return self._error_response(
|
|
534
|
-
"Missing provider_id parameter",
|
|
545
|
+
"Missing provider_id parameter",
|
|
546
|
+
400,
|
|
547
|
+
logger.warning,
|
|
535
548
|
)
|
|
536
549
|
|
|
537
550
|
logger.info(f"API call: /config/provider/check_one id={provider_id}")
|
|
@@ -541,7 +554,7 @@ class ConfigRoute(Route):
|
|
|
541
554
|
|
|
542
555
|
if not target:
|
|
543
556
|
logger.warning(
|
|
544
|
-
f"Provider with id '{provider_id}' not found in provider_manager."
|
|
557
|
+
f"Provider with id '{provider_id}' not found in provider_manager.",
|
|
545
558
|
)
|
|
546
559
|
return (
|
|
547
560
|
Response()
|
|
@@ -554,7 +567,8 @@ class ConfigRoute(Route):
|
|
|
554
567
|
|
|
555
568
|
except Exception as e:
|
|
556
569
|
return self._error_response(
|
|
557
|
-
f"Critical error checking provider {provider_id}: {e}",
|
|
570
|
+
f"Critical error checking provider {provider_id}: {e}",
|
|
571
|
+
500,
|
|
558
572
|
)
|
|
559
573
|
|
|
560
574
|
async def get_configs(self):
|
|
@@ -646,13 +660,13 @@ class ConfigRoute(Route):
|
|
|
646
660
|
dim = len(vec)
|
|
647
661
|
|
|
648
662
|
logger.info(
|
|
649
|
-
f"检测到 {provider_config.get('id', 'unknown')} 的嵌入向量维度为 {dim}"
|
|
663
|
+
f"检测到 {provider_config.get('id', 'unknown')} 的嵌入向量维度为 {dim}",
|
|
650
664
|
)
|
|
651
665
|
|
|
652
666
|
return Response().ok({"embedding_dimensions": dim}).__dict__
|
|
653
667
|
except Exception as e:
|
|
654
668
|
logger.error(traceback.format_exc())
|
|
655
|
-
return Response().error(f"获取嵌入维度失败: {
|
|
669
|
+
return Response().error(f"获取嵌入维度失败: {e!s}").__dict__
|
|
656
670
|
|
|
657
671
|
async def get_platform_list(self):
|
|
658
672
|
"""获取所有平台的列表"""
|
|
@@ -693,7 +707,7 @@ class ConfigRoute(Route):
|
|
|
693
707
|
try:
|
|
694
708
|
save_config(self.config, self.config, is_core=True)
|
|
695
709
|
await self.core_lifecycle.platform_manager.load_platform(
|
|
696
|
-
new_platform_config
|
|
710
|
+
new_platform_config,
|
|
697
711
|
)
|
|
698
712
|
except Exception as e:
|
|
699
713
|
return Response().error(str(e)).__dict__
|
|
@@ -705,7 +719,7 @@ class ConfigRoute(Route):
|
|
|
705
719
|
try:
|
|
706
720
|
save_config(self.config, self.config, is_core=True)
|
|
707
721
|
await self.core_lifecycle.provider_manager.load_provider(
|
|
708
|
-
new_provider_config
|
|
722
|
+
new_provider_config,
|
|
709
723
|
)
|
|
710
724
|
except Exception as e:
|
|
711
725
|
return Response().error(str(e)).__dict__
|
|
@@ -802,9 +816,9 @@ class ConfigRoute(Route):
|
|
|
802
816
|
if cache_key in self._logo_token_cache:
|
|
803
817
|
cached_token = self._logo_token_cache[cache_key]
|
|
804
818
|
# 确保platform_default_tmpl[platform.name]存在且为字典
|
|
805
|
-
if platform.name not in platform_default_tmpl
|
|
806
|
-
platform_default_tmpl[platform.name]
|
|
807
|
-
|
|
819
|
+
if platform.name not in platform_default_tmpl or not isinstance(
|
|
820
|
+
platform_default_tmpl[platform.name], dict
|
|
821
|
+
):
|
|
808
822
|
platform_default_tmpl[platform.name] = {}
|
|
809
823
|
platform_default_tmpl[platform.name]["logo_token"] = cached_token
|
|
810
824
|
logger.debug(f"Using cached logo token for platform {platform.name}")
|
|
@@ -826,13 +840,14 @@ class ConfigRoute(Route):
|
|
|
826
840
|
# 检查文件是否存在并注册令牌
|
|
827
841
|
if os.path.exists(logo_file_path):
|
|
828
842
|
logo_token = await file_token_service.register_file(
|
|
829
|
-
logo_file_path,
|
|
843
|
+
logo_file_path,
|
|
844
|
+
timeout=3600,
|
|
830
845
|
)
|
|
831
846
|
|
|
832
847
|
# 确保platform_default_tmpl[platform.name]存在且为字典
|
|
833
|
-
if platform.name not in platform_default_tmpl
|
|
834
|
-
platform_default_tmpl[platform.name]
|
|
835
|
-
|
|
848
|
+
if platform.name not in platform_default_tmpl or not isinstance(
|
|
849
|
+
platform_default_tmpl[platform.name], dict
|
|
850
|
+
):
|
|
836
851
|
platform_default_tmpl[platform.name] = {}
|
|
837
852
|
|
|
838
853
|
platform_default_tmpl[platform.name]["logo_token"] = logo_token
|
|
@@ -843,18 +858,18 @@ class ConfigRoute(Route):
|
|
|
843
858
|
logger.debug(f"Logo token registered for platform {platform.name}")
|
|
844
859
|
else:
|
|
845
860
|
logger.warning(
|
|
846
|
-
f"Platform {platform.name} logo file not found: {logo_file_path}"
|
|
861
|
+
f"Platform {platform.name} logo file not found: {logo_file_path}",
|
|
847
862
|
)
|
|
848
863
|
|
|
849
864
|
except (ImportError, AttributeError) as e:
|
|
850
865
|
logger.warning(
|
|
851
|
-
f"Failed to import required modules for platform {platform.name}: {e}"
|
|
866
|
+
f"Failed to import required modules for platform {platform.name}: {e}",
|
|
852
867
|
)
|
|
853
868
|
except OSError as e:
|
|
854
869
|
logger.warning(f"File system error for platform {platform.name} logo: {e}")
|
|
855
870
|
except Exception as e:
|
|
856
871
|
logger.warning(
|
|
857
|
-
f"Unexpected error registering logo for platform {platform.name}: {e}"
|
|
872
|
+
f"Unexpected error registering logo for platform {platform.name}: {e}",
|
|
858
873
|
)
|
|
859
874
|
|
|
860
875
|
async def _get_astrbot_config(self):
|
|
@@ -873,7 +888,7 @@ class ConfigRoute(Route):
|
|
|
873
888
|
# 收集logo注册任务
|
|
874
889
|
if platform.logo_path:
|
|
875
890
|
logo_registration_tasks.append(
|
|
876
|
-
self._register_platform_logo(platform, platform_default_tmpl)
|
|
891
|
+
self._register_platform_logo(platform, platform_default_tmpl),
|
|
877
892
|
)
|
|
878
893
|
|
|
879
894
|
# 并行执行logo注册
|
|
@@ -905,13 +920,15 @@ class ConfigRoute(Route):
|
|
|
905
920
|
"description": f"{plugin_name} 配置",
|
|
906
921
|
"type": "object",
|
|
907
922
|
"items": plugin_md.config.schema, # 初始化时通过 __setattr__ 存入了 schema
|
|
908
|
-
}
|
|
923
|
+
},
|
|
909
924
|
}
|
|
910
925
|
break
|
|
911
926
|
|
|
912
927
|
return ret
|
|
913
928
|
|
|
914
|
-
async def _save_astrbot_configs(
|
|
929
|
+
async def _save_astrbot_configs(
|
|
930
|
+
self, post_configs: dict, conf_id: str | None = None
|
|
931
|
+
):
|
|
915
932
|
try:
|
|
916
933
|
if conf_id not in self.acm.confs:
|
|
917
934
|
raise ValueError(f"配置文件 {conf_id} 不存在")
|