AstrBot 3.5.6__py3-none-any.whl → 4.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- astrbot/api/__init__.py +16 -4
- astrbot/api/all.py +2 -1
- astrbot/api/event/__init__.py +5 -6
- astrbot/api/event/filter/__init__.py +37 -34
- astrbot/api/platform/__init__.py +7 -8
- astrbot/api/provider/__init__.py +8 -7
- astrbot/api/star/__init__.py +3 -4
- astrbot/api/util/__init__.py +2 -2
- astrbot/cli/__init__.py +1 -0
- astrbot/cli/__main__.py +18 -197
- astrbot/cli/commands/__init__.py +6 -0
- astrbot/cli/commands/cmd_conf.py +209 -0
- astrbot/cli/commands/cmd_init.py +56 -0
- astrbot/cli/commands/cmd_plug.py +245 -0
- astrbot/cli/commands/cmd_run.py +62 -0
- astrbot/cli/utils/__init__.py +18 -0
- astrbot/cli/utils/basic.py +76 -0
- astrbot/cli/utils/plugin.py +246 -0
- astrbot/cli/utils/version_comparator.py +90 -0
- astrbot/core/__init__.py +17 -19
- astrbot/core/agent/agent.py +14 -0
- astrbot/core/agent/handoff.py +38 -0
- astrbot/core/agent/hooks.py +30 -0
- astrbot/core/agent/mcp_client.py +385 -0
- astrbot/core/agent/message.py +175 -0
- astrbot/core/agent/response.py +14 -0
- astrbot/core/agent/run_context.py +22 -0
- astrbot/core/agent/runners/__init__.py +3 -0
- astrbot/core/agent/runners/base.py +65 -0
- astrbot/core/agent/runners/coze/coze_agent_runner.py +367 -0
- astrbot/core/agent/runners/coze/coze_api_client.py +324 -0
- astrbot/core/agent/runners/dashscope/dashscope_agent_runner.py +403 -0
- astrbot/core/agent/runners/dify/dify_agent_runner.py +336 -0
- astrbot/core/agent/runners/dify/dify_api_client.py +195 -0
- astrbot/core/agent/runners/tool_loop_agent_runner.py +400 -0
- astrbot/core/agent/tool.py +285 -0
- astrbot/core/agent/tool_executor.py +17 -0
- astrbot/core/astr_agent_context.py +19 -0
- astrbot/core/astr_agent_hooks.py +36 -0
- astrbot/core/astr_agent_run_util.py +80 -0
- astrbot/core/astr_agent_tool_exec.py +246 -0
- astrbot/core/astrbot_config_mgr.py +275 -0
- astrbot/core/config/__init__.py +2 -2
- astrbot/core/config/astrbot_config.py +60 -20
- astrbot/core/config/default.py +1972 -453
- astrbot/core/config/i18n_utils.py +110 -0
- astrbot/core/conversation_mgr.py +285 -75
- astrbot/core/core_lifecycle.py +167 -62
- astrbot/core/db/__init__.py +305 -102
- astrbot/core/db/migration/helper.py +69 -0
- astrbot/core/db/migration/migra_3_to_4.py +357 -0
- astrbot/core/db/migration/migra_45_to_46.py +44 -0
- astrbot/core/db/migration/migra_webchat_session.py +131 -0
- astrbot/core/db/migration/shared_preferences_v3.py +48 -0
- astrbot/core/db/migration/sqlite_v3.py +497 -0
- astrbot/core/db/po.py +259 -55
- astrbot/core/db/sqlite.py +773 -528
- astrbot/core/db/vec_db/base.py +73 -0
- astrbot/core/db/vec_db/faiss_impl/__init__.py +3 -0
- astrbot/core/db/vec_db/faiss_impl/document_storage.py +392 -0
- astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +93 -0
- astrbot/core/db/vec_db/faiss_impl/sqlite_init.sql +17 -0
- astrbot/core/db/vec_db/faiss_impl/vec_db.py +204 -0
- astrbot/core/event_bus.py +26 -22
- astrbot/core/exceptions.py +9 -0
- astrbot/core/file_token_service.py +98 -0
- astrbot/core/initial_loader.py +19 -10
- astrbot/core/knowledge_base/chunking/__init__.py +9 -0
- astrbot/core/knowledge_base/chunking/base.py +25 -0
- astrbot/core/knowledge_base/chunking/fixed_size.py +59 -0
- astrbot/core/knowledge_base/chunking/recursive.py +161 -0
- astrbot/core/knowledge_base/kb_db_sqlite.py +301 -0
- astrbot/core/knowledge_base/kb_helper.py +642 -0
- astrbot/core/knowledge_base/kb_mgr.py +330 -0
- astrbot/core/knowledge_base/models.py +120 -0
- astrbot/core/knowledge_base/parsers/__init__.py +13 -0
- astrbot/core/knowledge_base/parsers/base.py +51 -0
- astrbot/core/knowledge_base/parsers/markitdown_parser.py +26 -0
- astrbot/core/knowledge_base/parsers/pdf_parser.py +101 -0
- astrbot/core/knowledge_base/parsers/text_parser.py +42 -0
- astrbot/core/knowledge_base/parsers/url_parser.py +103 -0
- astrbot/core/knowledge_base/parsers/util.py +13 -0
- astrbot/core/knowledge_base/prompts.py +65 -0
- astrbot/core/knowledge_base/retrieval/__init__.py +14 -0
- astrbot/core/knowledge_base/retrieval/hit_stopwords.txt +767 -0
- astrbot/core/knowledge_base/retrieval/manager.py +276 -0
- astrbot/core/knowledge_base/retrieval/rank_fusion.py +142 -0
- astrbot/core/knowledge_base/retrieval/sparse_retriever.py +136 -0
- astrbot/core/log.py +21 -15
- astrbot/core/message/components.py +413 -287
- astrbot/core/message/message_event_result.py +35 -24
- astrbot/core/persona_mgr.py +192 -0
- astrbot/core/pipeline/__init__.py +14 -14
- 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 +13 -14
- astrbot/core/pipeline/content_safety_check/strategies/keywords.py +2 -1
- astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
- astrbot/core/pipeline/context.py +7 -1
- astrbot/core/pipeline/context_utils.py +107 -0
- astrbot/core/pipeline/preprocess_stage/stage.py +63 -36
- astrbot/core/pipeline/process_stage/method/agent_request.py +48 -0
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py +464 -0
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/third_party.py +202 -0
- astrbot/core/pipeline/process_stage/method/star_request.py +26 -32
- astrbot/core/pipeline/process_stage/stage.py +21 -15
- astrbot/core/pipeline/process_stage/utils.py +125 -0
- astrbot/core/pipeline/rate_limit_check/stage.py +34 -36
- astrbot/core/pipeline/respond/stage.py +142 -101
- astrbot/core/pipeline/result_decorate/stage.py +124 -57
- astrbot/core/pipeline/scheduler.py +21 -16
- astrbot/core/pipeline/session_status_check/stage.py +37 -0
- astrbot/core/pipeline/stage.py +11 -76
- astrbot/core/pipeline/waking_check/stage.py +69 -33
- astrbot/core/pipeline/whitelist_check/stage.py +10 -7
- astrbot/core/platform/__init__.py +6 -6
- astrbot/core/platform/astr_message_event.py +107 -129
- astrbot/core/platform/astrbot_message.py +32 -12
- astrbot/core/platform/manager.py +62 -18
- astrbot/core/platform/message_session.py +30 -0
- astrbot/core/platform/platform.py +16 -24
- astrbot/core/platform/platform_metadata.py +9 -4
- astrbot/core/platform/register.py +12 -7
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +136 -60
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +126 -46
- astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +63 -31
- astrbot/core/platform/sources/dingtalk/dingtalk_event.py +30 -26
- astrbot/core/platform/sources/discord/client.py +129 -0
- astrbot/core/platform/sources/discord/components.py +139 -0
- astrbot/core/platform/sources/discord/discord_platform_adapter.py +473 -0
- astrbot/core/platform/sources/discord/discord_platform_event.py +313 -0
- astrbot/core/platform/sources/lark/lark_adapter.py +27 -18
- astrbot/core/platform/sources/lark/lark_event.py +39 -13
- astrbot/core/platform/sources/misskey/misskey_adapter.py +770 -0
- astrbot/core/platform/sources/misskey/misskey_api.py +964 -0
- astrbot/core/platform/sources/misskey/misskey_event.py +163 -0
- astrbot/core/platform/sources/misskey/misskey_utils.py +550 -0
- astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +149 -33
- 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 +14 -8
- astrbot/core/platform/sources/satori/satori_adapter.py +792 -0
- astrbot/core/platform/sources/satori/satori_event.py +432 -0
- astrbot/core/platform/sources/slack/client.py +164 -0
- astrbot/core/platform/sources/slack/slack_adapter.py +416 -0
- astrbot/core/platform/sources/slack/slack_event.py +253 -0
- astrbot/core/platform/sources/telegram/tg_adapter.py +100 -43
- astrbot/core/platform/sources/telegram/tg_event.py +136 -36
- astrbot/core/platform/sources/webchat/webchat_adapter.py +72 -22
- astrbot/core/platform/sources/webchat/webchat_event.py +46 -22
- astrbot/core/platform/sources/webchat/webchat_queue_mgr.py +35 -0
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +926 -0
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +178 -0
- astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +159 -0
- astrbot/core/platform/sources/wecom/wecom_adapter.py +169 -27
- astrbot/core/platform/sources/wecom/wecom_event.py +162 -77
- astrbot/core/platform/sources/wecom/wecom_kf.py +279 -0
- astrbot/core/platform/sources/wecom/wecom_kf_message.py +196 -0
- astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +297 -0
- astrbot/core/platform/sources/wecom_ai_bot/__init__.py +15 -0
- astrbot/core/platform/sources/wecom_ai_bot/ierror.py +19 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +472 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +417 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +152 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +153 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +168 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +209 -0
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +306 -0
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +186 -0
- astrbot/core/platform_message_history_mgr.py +49 -0
- astrbot/core/provider/__init__.py +2 -3
- astrbot/core/provider/entites.py +8 -8
- astrbot/core/provider/entities.py +154 -98
- astrbot/core/provider/func_tool_manager.py +446 -458
- astrbot/core/provider/manager.py +345 -207
- astrbot/core/provider/provider.py +188 -73
- astrbot/core/provider/register.py +9 -7
- astrbot/core/provider/sources/anthropic_source.py +295 -115
- astrbot/core/provider/sources/azure_tts_source.py +224 -0
- astrbot/core/provider/sources/bailian_rerank_source.py +236 -0
- astrbot/core/provider/sources/dashscope_tts.py +138 -14
- astrbot/core/provider/sources/edge_tts_source.py +24 -19
- astrbot/core/provider/sources/fishaudio_tts_api_source.py +58 -13
- astrbot/core/provider/sources/gemini_embedding_source.py +61 -0
- astrbot/core/provider/sources/gemini_source.py +310 -132
- astrbot/core/provider/sources/gemini_tts_source.py +81 -0
- astrbot/core/provider/sources/groq_source.py +15 -0
- astrbot/core/provider/sources/gsv_selfhosted_source.py +151 -0
- astrbot/core/provider/sources/gsvi_tts_source.py +14 -7
- astrbot/core/provider/sources/minimax_tts_api_source.py +159 -0
- astrbot/core/provider/sources/openai_embedding_source.py +40 -0
- astrbot/core/provider/sources/openai_source.py +241 -145
- astrbot/core/provider/sources/openai_tts_api_source.py +18 -7
- astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
- astrbot/core/provider/sources/vllm_rerank_source.py +71 -0
- astrbot/core/provider/sources/volcengine_tts.py +115 -0
- astrbot/core/provider/sources/whisper_api_source.py +18 -13
- astrbot/core/provider/sources/whisper_selfhosted_source.py +19 -12
- astrbot/core/provider/sources/xinference_rerank_source.py +116 -0
- astrbot/core/provider/sources/xinference_stt_provider.py +197 -0
- astrbot/core/provider/sources/zhipu_source.py +6 -73
- astrbot/core/star/__init__.py +43 -11
- astrbot/core/star/config.py +17 -18
- astrbot/core/star/context.py +362 -138
- astrbot/core/star/filter/__init__.py +4 -3
- astrbot/core/star/filter/command.py +111 -35
- astrbot/core/star/filter/command_group.py +46 -34
- 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 +45 -12
- astrbot/core/star/filter/regex.py +4 -2
- astrbot/core/star/register/__init__.py +19 -15
- astrbot/core/star/register/star.py +41 -13
- astrbot/core/star/register/star_handler.py +236 -86
- astrbot/core/star/session_llm_manager.py +280 -0
- astrbot/core/star/session_plugin_manager.py +170 -0
- astrbot/core/star/star.py +36 -43
- astrbot/core/star/star_handler.py +47 -85
- astrbot/core/star/star_manager.py +442 -260
- astrbot/core/star/star_tools.py +167 -45
- astrbot/core/star/updator.py +17 -20
- astrbot/core/umop_config_router.py +106 -0
- astrbot/core/updator.py +38 -13
- astrbot/core/utils/astrbot_path.py +39 -0
- astrbot/core/utils/command_parser.py +1 -1
- astrbot/core/utils/io.py +119 -60
- astrbot/core/utils/log_pipe.py +1 -1
- astrbot/core/utils/metrics.py +11 -10
- astrbot/core/utils/migra_helper.py +73 -0
- astrbot/core/utils/path_util.py +63 -62
- astrbot/core/utils/pip_installer.py +37 -15
- astrbot/core/utils/session_lock.py +29 -0
- astrbot/core/utils/session_waiter.py +19 -20
- astrbot/core/utils/shared_preferences.py +174 -34
- astrbot/core/utils/t2i/__init__.py +4 -1
- astrbot/core/utils/t2i/local_strategy.py +386 -238
- astrbot/core/utils/t2i/network_strategy.py +109 -49
- astrbot/core/utils/t2i/renderer.py +29 -14
- astrbot/core/utils/t2i/template/astrbot_powershell.html +184 -0
- astrbot/core/utils/t2i/template_manager.py +111 -0
- astrbot/core/utils/tencent_record_helper.py +115 -1
- astrbot/core/utils/version_comparator.py +10 -13
- astrbot/core/zip_updator.py +112 -65
- astrbot/dashboard/routes/__init__.py +20 -13
- astrbot/dashboard/routes/auth.py +20 -9
- astrbot/dashboard/routes/chat.py +297 -141
- astrbot/dashboard/routes/config.py +652 -55
- astrbot/dashboard/routes/conversation.py +107 -37
- astrbot/dashboard/routes/file.py +26 -0
- astrbot/dashboard/routes/knowledge_base.py +1244 -0
- astrbot/dashboard/routes/log.py +27 -2
- astrbot/dashboard/routes/persona.py +202 -0
- astrbot/dashboard/routes/plugin.py +197 -139
- astrbot/dashboard/routes/route.py +27 -7
- astrbot/dashboard/routes/session_management.py +354 -0
- astrbot/dashboard/routes/stat.py +85 -18
- astrbot/dashboard/routes/static_file.py +5 -2
- astrbot/dashboard/routes/t2i.py +233 -0
- astrbot/dashboard/routes/tools.py +184 -120
- astrbot/dashboard/routes/update.py +59 -36
- astrbot/dashboard/server.py +96 -36
- astrbot/dashboard/utils.py +165 -0
- astrbot-4.7.0.dist-info/METADATA +294 -0
- astrbot-4.7.0.dist-info/RECORD +274 -0
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/WHEEL +1 -1
- astrbot/core/db/plugin/sqlite_impl.py +0 -112
- astrbot/core/db/sqlite_init.sql +0 -50
- astrbot/core/pipeline/platform_compatibility/stage.py +0 -56
- astrbot/core/pipeline/process_stage/method/llm_request.py +0 -606
- astrbot/core/platform/sources/gewechat/client.py +0 -806
- astrbot/core/platform/sources/gewechat/downloader.py +0 -55
- astrbot/core/platform/sources/gewechat/gewechat_event.py +0 -255
- astrbot/core/platform/sources/gewechat/gewechat_platform_adapter.py +0 -103
- astrbot/core/platform/sources/gewechat/xml_data_parser.py +0 -110
- astrbot/core/provider/sources/dashscope_source.py +0 -203
- astrbot/core/provider/sources/dify_source.py +0 -281
- astrbot/core/provider/sources/llmtuner_source.py +0 -132
- astrbot/core/rag/embedding/openai_source.py +0 -20
- astrbot/core/rag/knowledge_db_mgr.py +0 -94
- astrbot/core/rag/store/__init__.py +0 -9
- astrbot/core/rag/store/chroma_db.py +0 -42
- astrbot/core/utils/dify_api_client.py +0 -152
- astrbot-3.5.6.dist-info/METADATA +0 -249
- astrbot-3.5.6.dist-info/RECORD +0 -158
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/entry_points.txt +0 -0
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,58 +1,38 @@
|
|
|
1
|
-
import random
|
|
2
1
|
import asyncio
|
|
3
2
|
import math
|
|
4
|
-
import
|
|
3
|
+
import random
|
|
4
|
+
from collections.abc import AsyncGenerator
|
|
5
|
+
|
|
5
6
|
import astrbot.core.message.components as Comp
|
|
6
|
-
from typing import Union, AsyncGenerator
|
|
7
|
-
from ..stage import register_stage, Stage
|
|
8
|
-
from ..context import PipelineContext
|
|
9
|
-
from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
|
10
|
-
from astrbot.core.message.message_event_result import MessageChain, ResultContentType
|
|
11
7
|
from astrbot.core import logger
|
|
12
|
-
from astrbot.core.message.
|
|
13
|
-
from astrbot.core.
|
|
14
|
-
from astrbot.core.
|
|
8
|
+
from astrbot.core.message.components import BaseMessageComponent, ComponentType
|
|
9
|
+
from astrbot.core.message.message_event_result import MessageChain, ResultContentType
|
|
10
|
+
from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
|
11
|
+
from astrbot.core.star.star_handler import EventType
|
|
15
12
|
from astrbot.core.utils.path_util import path_Mapping
|
|
16
13
|
|
|
14
|
+
from ..context import PipelineContext, call_event_hook
|
|
15
|
+
from ..stage import Stage, register_stage
|
|
16
|
+
|
|
17
17
|
|
|
18
18
|
@register_stage
|
|
19
19
|
class RespondStage(Stage):
|
|
20
20
|
# 组件类型到其非空判断函数的映射
|
|
21
21
|
_component_validators = {
|
|
22
22
|
Comp.Plain: lambda comp: bool(
|
|
23
|
-
comp.text and comp.text.strip()
|
|
23
|
+
comp.text and comp.text.strip(),
|
|
24
24
|
), # 纯文本消息需要strip
|
|
25
25
|
Comp.Face: lambda comp: comp.id is not None, # QQ表情
|
|
26
26
|
Comp.Record: lambda comp: bool(comp.file), # 语音
|
|
27
27
|
Comp.Video: lambda comp: bool(comp.file), # 视频
|
|
28
28
|
Comp.At: lambda comp: bool(comp.qq) or bool(comp.name), # @
|
|
29
|
-
Comp.AtAll: lambda comp: True, # @所有人
|
|
30
|
-
Comp.RPS: lambda comp: True, # 不知道是啥(未完成)
|
|
31
|
-
Comp.Dice: lambda comp: True, # 骰子(未完成)
|
|
32
|
-
Comp.Shake: lambda comp: True, # 摇一摇(未完成)
|
|
33
|
-
Comp.Anonymous: lambda comp: True, # 匿名(未完成)
|
|
34
|
-
Comp.Share: lambda comp: bool(comp.url) and bool(comp.title), # 分享
|
|
35
|
-
Comp.Contact: lambda comp: True, # 联系人(未完成)
|
|
36
|
-
Comp.Location: lambda comp: bool(comp.lat and comp.lon), # 位置
|
|
37
|
-
Comp.Music: lambda comp: bool(comp._type)
|
|
38
|
-
and bool(comp.url)
|
|
39
|
-
and bool(comp.audio), # 音乐
|
|
40
29
|
Comp.Image: lambda comp: bool(comp.file), # 图片
|
|
41
30
|
Comp.Reply: lambda comp: bool(comp.id) and comp.sender_id is not None, # 回复
|
|
42
|
-
Comp.RedBag: lambda comp: bool(comp.title), # 红包
|
|
43
31
|
Comp.Poke: lambda comp: comp.id != 0 and comp.qq != 0, # 戳一戳
|
|
44
|
-
Comp.
|
|
45
|
-
Comp.Node: lambda comp: bool(comp.name)
|
|
46
|
-
and comp.uin != 0
|
|
47
|
-
and bool(comp.content), # 一个转发节点
|
|
32
|
+
Comp.Node: lambda comp: bool(comp.content), # 转发节点
|
|
48
33
|
Comp.Nodes: lambda comp: bool(comp.nodes), # 多个转发节点
|
|
49
|
-
Comp.
|
|
50
|
-
Comp.
|
|
51
|
-
Comp.CardImage: lambda comp: bool(comp.file), # 卡片图片
|
|
52
|
-
Comp.TTS: lambda comp: bool(comp.text and comp.text.strip()), # 语音合成
|
|
53
|
-
Comp.Unknown: lambda comp: bool(comp.text and comp.text.strip()), # 未知消息
|
|
54
|
-
Comp.File: lambda comp: bool(comp.file), # 文件
|
|
55
|
-
Comp.WechatEmoji: lambda comp: bool(comp.md5), # 微信表情
|
|
34
|
+
Comp.File: lambda comp: bool(comp.file_ or comp.url),
|
|
35
|
+
Comp.WechatEmoji: lambda comp: comp.md5 is not None, # 微信表情
|
|
56
36
|
}
|
|
57
37
|
|
|
58
38
|
async def initialize(self, ctx: PipelineContext):
|
|
@@ -79,7 +59,7 @@ class RespondStage(Stage):
|
|
|
79
59
|
"segmented_reply"
|
|
80
60
|
]["interval_method"]
|
|
81
61
|
self.log_base = float(
|
|
82
|
-
ctx.astrbot_config["platform_settings"]["segmented_reply"]["log_base"]
|
|
62
|
+
ctx.astrbot_config["platform_settings"]["segmented_reply"]["log_base"],
|
|
83
63
|
)
|
|
84
64
|
interval_str: str = ctx.astrbot_config["platform_settings"]["segmented_reply"][
|
|
85
65
|
"interval"
|
|
@@ -107,17 +87,16 @@ class RespondStage(Stage):
|
|
|
107
87
|
wc = await self._word_cnt(comp.text)
|
|
108
88
|
i = math.log(wc + 1, self.log_base)
|
|
109
89
|
return random.uniform(i, i + 0.5)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
# random
|
|
114
|
-
return random.uniform(self.interval[0], self.interval[1])
|
|
90
|
+
return random.uniform(1, 1.75)
|
|
91
|
+
# random
|
|
92
|
+
return random.uniform(self.interval[0], self.interval[1])
|
|
115
93
|
|
|
116
94
|
async def _is_empty_message_chain(self, chain: list[BaseMessageComponent]):
|
|
117
95
|
"""检查消息链是否为空
|
|
118
96
|
|
|
119
97
|
Args:
|
|
120
98
|
chain (list[BaseMessageComponent]): 包含消息对象的列表
|
|
99
|
+
|
|
121
100
|
"""
|
|
122
101
|
if not chain:
|
|
123
102
|
return True
|
|
@@ -129,32 +108,77 @@ class RespondStage(Stage):
|
|
|
129
108
|
if comp_type in self._component_validators:
|
|
130
109
|
if self._component_validators[comp_type](comp):
|
|
131
110
|
return False
|
|
132
|
-
else:
|
|
133
|
-
logger.info(f"空内容检查: 无法识别的组件类型: {comp_type.__name__}")
|
|
134
111
|
|
|
135
112
|
# 如果所有组件都为空
|
|
136
113
|
return True
|
|
137
114
|
|
|
115
|
+
def is_seg_reply_required(self, event: AstrMessageEvent) -> bool:
|
|
116
|
+
"""检查是否需要分段回复"""
|
|
117
|
+
if not self.enable_seg:
|
|
118
|
+
return False
|
|
119
|
+
|
|
120
|
+
if self.only_llm_result and not event.get_result().is_llm_result():
|
|
121
|
+
return False
|
|
122
|
+
|
|
123
|
+
if event.get_platform_name() in [
|
|
124
|
+
"qq_official",
|
|
125
|
+
"weixin_official_account",
|
|
126
|
+
"dingtalk",
|
|
127
|
+
]:
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
return True
|
|
131
|
+
|
|
132
|
+
def _extract_comp(
|
|
133
|
+
self,
|
|
134
|
+
raw_chain: list[BaseMessageComponent],
|
|
135
|
+
extract_types: set[ComponentType],
|
|
136
|
+
modify_raw_chain: bool = True,
|
|
137
|
+
):
|
|
138
|
+
extracted = []
|
|
139
|
+
if modify_raw_chain:
|
|
140
|
+
remaining = []
|
|
141
|
+
for comp in raw_chain:
|
|
142
|
+
if comp.type in extract_types:
|
|
143
|
+
extracted.append(comp)
|
|
144
|
+
else:
|
|
145
|
+
remaining.append(comp)
|
|
146
|
+
raw_chain[:] = remaining
|
|
147
|
+
else:
|
|
148
|
+
extracted = [comp for comp in raw_chain if comp.type in extract_types]
|
|
149
|
+
|
|
150
|
+
return extracted
|
|
151
|
+
|
|
138
152
|
async def process(
|
|
139
|
-
self,
|
|
140
|
-
|
|
153
|
+
self,
|
|
154
|
+
event: AstrMessageEvent,
|
|
155
|
+
) -> None | AsyncGenerator[None, None]:
|
|
141
156
|
result = event.get_result()
|
|
142
157
|
if result is None:
|
|
143
158
|
return
|
|
144
159
|
if result.result_content_type == ResultContentType.STREAMING_FINISH:
|
|
145
160
|
return
|
|
146
161
|
|
|
162
|
+
logger.info(
|
|
163
|
+
f"Prepare to send - {event.get_sender_name()}/{event.get_sender_id()}: {event._outline_chain(result.chain)}",
|
|
164
|
+
)
|
|
165
|
+
|
|
147
166
|
if result.result_content_type == ResultContentType.STREAMING_RESULT:
|
|
167
|
+
if result.async_stream is None:
|
|
168
|
+
logger.warning("async_stream 为空,跳过发送。")
|
|
169
|
+
return
|
|
148
170
|
# 流式结果直接交付平台适配器处理
|
|
149
|
-
|
|
150
|
-
"
|
|
171
|
+
realtime_segmenting = (
|
|
172
|
+
self.config.get("provider_settings", {}).get(
|
|
173
|
+
"unsupported_streaming_strategy",
|
|
174
|
+
"realtime_segmenting",
|
|
175
|
+
)
|
|
176
|
+
== "realtime_segmenting"
|
|
151
177
|
)
|
|
152
|
-
logger.info(f"应用流式输出({event.
|
|
153
|
-
await event.
|
|
154
|
-
await event.send_streaming(result.async_stream, use_fallback)
|
|
155
|
-
await event._post_send()
|
|
178
|
+
logger.info(f"应用流式输出({event.get_platform_id()})")
|
|
179
|
+
await event.send_streaming(result.async_stream, realtime_segmenting)
|
|
156
180
|
return
|
|
157
|
-
|
|
181
|
+
if len(result.chain) > 0:
|
|
158
182
|
# 检查路径映射
|
|
159
183
|
if mappings := self.platform_settings.get("path_mapping", []):
|
|
160
184
|
for idx, component in enumerate(result.chain):
|
|
@@ -163,71 +187,88 @@ class RespondStage(Stage):
|
|
|
163
187
|
component.file = path_Mapping(mappings, component.file)
|
|
164
188
|
event.get_result().chain[idx] = component
|
|
165
189
|
|
|
166
|
-
await event._pre_send()
|
|
167
|
-
|
|
168
190
|
# 检查消息链是否为空
|
|
169
191
|
try:
|
|
170
192
|
if await self._is_empty_message_chain(result.chain):
|
|
171
193
|
logger.info("消息为空,跳过发送阶段")
|
|
172
|
-
event.clear_result()
|
|
173
|
-
event.stop_event()
|
|
174
194
|
return
|
|
175
195
|
except Exception as e:
|
|
176
196
|
logger.warning(f"空内容检查异常: {e}")
|
|
177
197
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
198
|
+
# 将 Plain 为空的消息段移除
|
|
199
|
+
result.chain = [
|
|
200
|
+
comp
|
|
201
|
+
for comp in result.chain
|
|
202
|
+
if not (
|
|
203
|
+
isinstance(comp, Comp.Plain)
|
|
204
|
+
and (not comp.text or not comp.text.strip())
|
|
205
|
+
)
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
# 发送消息链
|
|
209
|
+
# Record 需要强制单独发送
|
|
210
|
+
need_separately = {ComponentType.Record}
|
|
211
|
+
if self.is_seg_reply_required(event):
|
|
212
|
+
header_comps = self._extract_comp(
|
|
213
|
+
result.chain,
|
|
214
|
+
{ComponentType.Reply, ComponentType.At},
|
|
215
|
+
modify_raw_chain=True,
|
|
216
|
+
)
|
|
217
|
+
if not result.chain or len(result.chain) == 0:
|
|
218
|
+
# may fix #2670
|
|
219
|
+
logger.warning(
|
|
220
|
+
f"实际消息链为空, 跳过发送阶段。header_chain: {header_comps}, actual_chain: {result.chain}",
|
|
221
|
+
)
|
|
222
|
+
return
|
|
196
223
|
for comp in result.chain:
|
|
197
224
|
i = await self._calc_comp_interval(comp)
|
|
198
225
|
await asyncio.sleep(i)
|
|
199
226
|
try:
|
|
200
|
-
|
|
227
|
+
if comp.type in need_separately:
|
|
228
|
+
await event.send(MessageChain([comp]))
|
|
229
|
+
else:
|
|
230
|
+
await event.send(MessageChain([*header_comps, comp]))
|
|
231
|
+
header_comps.clear()
|
|
201
232
|
except Exception as e:
|
|
202
|
-
logger.error(
|
|
203
|
-
|
|
233
|
+
logger.error(
|
|
234
|
+
f"发送消息链失败: chain = {MessageChain([comp])}, error = {e}",
|
|
235
|
+
exc_info=True,
|
|
236
|
+
)
|
|
204
237
|
else:
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
for handler in handlers:
|
|
219
|
-
try:
|
|
220
|
-
logger.debug(
|
|
221
|
-
f"hook(on_after_message_sent) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}"
|
|
238
|
+
if all(
|
|
239
|
+
comp.type in {ComponentType.Reply, ComponentType.At}
|
|
240
|
+
for comp in result.chain
|
|
241
|
+
):
|
|
242
|
+
# may fix #2670
|
|
243
|
+
logger.warning(
|
|
244
|
+
f"消息链全为 Reply 和 At 消息段, 跳过发送阶段。chain: {result.chain}",
|
|
245
|
+
)
|
|
246
|
+
return
|
|
247
|
+
sep_comps = self._extract_comp(
|
|
248
|
+
result.chain,
|
|
249
|
+
need_separately,
|
|
250
|
+
modify_raw_chain=True,
|
|
222
251
|
)
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
252
|
+
for comp in sep_comps:
|
|
253
|
+
chain = MessageChain([comp])
|
|
254
|
+
try:
|
|
255
|
+
await event.send(chain)
|
|
256
|
+
except Exception as e:
|
|
257
|
+
logger.error(
|
|
258
|
+
f"发送消息链失败: chain = {chain}, error = {e}",
|
|
259
|
+
exc_info=True,
|
|
260
|
+
)
|
|
261
|
+
chain = MessageChain(result.chain)
|
|
262
|
+
if result.chain and len(result.chain) > 0:
|
|
263
|
+
try:
|
|
264
|
+
await event.send(chain)
|
|
265
|
+
except Exception as e:
|
|
266
|
+
logger.error(
|
|
267
|
+
f"发送消息链失败: chain = {chain}, error = {e}",
|
|
268
|
+
exc_info=True,
|
|
269
|
+
)
|
|
226
270
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
f"{star_map[handler.handler_module_path].name} - {handler.handler_name} 终止了事件传播。"
|
|
230
|
-
)
|
|
231
|
-
return
|
|
271
|
+
if await call_event_hook(event, EventType.OnAfterMessageSentEvent):
|
|
272
|
+
return
|
|
232
273
|
|
|
233
274
|
event.clear_result()
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import time
|
|
2
1
|
import re
|
|
2
|
+
import time
|
|
3
3
|
import traceback
|
|
4
|
-
from
|
|
5
|
-
|
|
6
|
-
from
|
|
7
|
-
from astrbot.core.
|
|
4
|
+
from collections.abc import AsyncGenerator
|
|
5
|
+
|
|
6
|
+
from astrbot.core import file_token_service, html_renderer, logger
|
|
7
|
+
from astrbot.core.message.components import At, File, Image, Node, Plain, Record, Reply
|
|
8
8
|
from astrbot.core.message.message_event_result import ResultContentType
|
|
9
|
+
from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
|
9
10
|
from astrbot.core.platform.message_type import MessageType
|
|
10
|
-
from astrbot.core import
|
|
11
|
-
from astrbot.core.message.components import Plain, Image, At, Reply, Record, File, Node
|
|
12
|
-
from astrbot.core import html_renderer
|
|
13
|
-
from astrbot.core.star.star_handler import star_handlers_registry, EventType
|
|
11
|
+
from astrbot.core.star.session_llm_manager import SessionServiceManager
|
|
14
12
|
from astrbot.core.star.star import star_map
|
|
13
|
+
from astrbot.core.star.star_handler import EventType, star_handlers_registry
|
|
14
|
+
|
|
15
|
+
from ..context import PipelineContext
|
|
16
|
+
from ..stage import Stage, register_stage, registered_stages
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
@register_stage
|
|
@@ -28,12 +30,12 @@ class ResultDecorateStage(Stage):
|
|
|
28
30
|
self.t2i_word_threshold = ctx.astrbot_config["t2i_word_threshold"]
|
|
29
31
|
try:
|
|
30
32
|
self.t2i_word_threshold = int(self.t2i_word_threshold)
|
|
31
|
-
|
|
32
|
-
self.t2i_word_threshold = 50
|
|
33
|
+
self.t2i_word_threshold = max(self.t2i_word_threshold, 50)
|
|
33
34
|
except BaseException:
|
|
34
35
|
self.t2i_word_threshold = 150
|
|
35
36
|
self.t2i_strategy = ctx.astrbot_config["t2i_strategy"]
|
|
36
37
|
self.t2i_use_network = self.t2i_strategy == "remote"
|
|
38
|
+
self.t2i_active_template = ctx.astrbot_config["t2i_active_template"]
|
|
37
39
|
|
|
38
40
|
self.forward_threshold = ctx.astrbot_config["platform_settings"][
|
|
39
41
|
"forward_threshold"
|
|
@@ -43,7 +45,7 @@ class ResultDecorateStage(Stage):
|
|
|
43
45
|
self.words_count_threshold = int(
|
|
44
46
|
ctx.astrbot_config["platform_settings"]["segmented_reply"][
|
|
45
47
|
"words_count_threshold"
|
|
46
|
-
]
|
|
48
|
+
],
|
|
47
49
|
)
|
|
48
50
|
self.enable_segmented_reply = ctx.astrbot_config["platform_settings"][
|
|
49
51
|
"segmented_reply"
|
|
@@ -62,13 +64,15 @@ class ResultDecorateStage(Stage):
|
|
|
62
64
|
]
|
|
63
65
|
self.content_safe_check_stage = None
|
|
64
66
|
if self.content_safe_check_reply:
|
|
65
|
-
for
|
|
66
|
-
if
|
|
67
|
-
self.content_safe_check_stage =
|
|
67
|
+
for stage_cls in registered_stages:
|
|
68
|
+
if stage_cls.__name__ == "ContentSafetyCheckStage":
|
|
69
|
+
self.content_safe_check_stage = stage_cls()
|
|
70
|
+
await self.content_safe_check_stage.initialize(ctx)
|
|
68
71
|
|
|
69
72
|
async def process(
|
|
70
|
-
self,
|
|
71
|
-
|
|
73
|
+
self,
|
|
74
|
+
event: AstrMessageEvent,
|
|
75
|
+
) -> None | AsyncGenerator[None, None]:
|
|
72
76
|
result = event.get_result()
|
|
73
77
|
if result is None or not result.chain:
|
|
74
78
|
return
|
|
@@ -90,34 +94,36 @@ class ResultDecorateStage(Stage):
|
|
|
90
94
|
if isinstance(comp, Plain):
|
|
91
95
|
text += comp.text
|
|
92
96
|
async for _ in self.content_safe_check_stage.process(
|
|
93
|
-
event,
|
|
97
|
+
event,
|
|
98
|
+
check_text=text,
|
|
94
99
|
):
|
|
95
100
|
yield
|
|
96
101
|
|
|
97
102
|
# 发送消息前事件钩子
|
|
98
103
|
handlers = star_handlers_registry.get_handlers_by_event_type(
|
|
99
|
-
EventType.OnDecoratingResultEvent,
|
|
104
|
+
EventType.OnDecoratingResultEvent,
|
|
105
|
+
plugins_name=event.plugins_name,
|
|
100
106
|
)
|
|
101
107
|
for handler in handlers:
|
|
102
108
|
try:
|
|
103
109
|
logger.debug(
|
|
104
|
-
f"hook(on_decorating_result) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}"
|
|
110
|
+
f"hook(on_decorating_result) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}",
|
|
105
111
|
)
|
|
106
112
|
if is_stream:
|
|
107
113
|
logger.warning(
|
|
108
|
-
"启用流式输出时,依赖发送消息前事件钩子的插件可能无法正常工作"
|
|
114
|
+
"启用流式输出时,依赖发送消息前事件钩子的插件可能无法正常工作",
|
|
109
115
|
)
|
|
110
116
|
await handler.handler(event)
|
|
111
117
|
if event.get_result() is None or not event.get_result().chain:
|
|
112
118
|
logger.debug(
|
|
113
|
-
f"hook(on_decorating_result) -> {star_map[handler.handler_module_path].name} - {handler.handler_name} 将消息结果清空。"
|
|
119
|
+
f"hook(on_decorating_result) -> {star_map[handler.handler_module_path].name} - {handler.handler_name} 将消息结果清空。",
|
|
114
120
|
)
|
|
115
121
|
except BaseException:
|
|
116
122
|
logger.error(traceback.format_exc())
|
|
117
123
|
|
|
118
124
|
if event.is_stopped():
|
|
119
125
|
logger.info(
|
|
120
|
-
f"{star_map[handler.handler_module_path].name} - {handler.handler_name} 终止了事件传播。"
|
|
126
|
+
f"{star_map[handler.handler_module_path].name} - {handler.handler_name} 终止了事件传播。",
|
|
121
127
|
)
|
|
122
128
|
return
|
|
123
129
|
|
|
@@ -140,7 +146,11 @@ class ResultDecorateStage(Stage):
|
|
|
140
146
|
break
|
|
141
147
|
|
|
142
148
|
# 分段回复
|
|
143
|
-
if self.enable_segmented_reply
|
|
149
|
+
if self.enable_segmented_reply and event.get_platform_name() not in [
|
|
150
|
+
"qq_official",
|
|
151
|
+
"weixin_official_account",
|
|
152
|
+
"dingtalk",
|
|
153
|
+
]:
|
|
144
154
|
if (
|
|
145
155
|
self.only_llm_result and result.is_llm_result()
|
|
146
156
|
) or not self.only_llm_result:
|
|
@@ -151,9 +161,21 @@ class ResultDecorateStage(Stage):
|
|
|
151
161
|
# 不分段回复
|
|
152
162
|
new_chain.append(comp)
|
|
153
163
|
continue
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
164
|
+
try:
|
|
165
|
+
split_response = re.findall(
|
|
166
|
+
self.regex,
|
|
167
|
+
comp.text,
|
|
168
|
+
re.DOTALL | re.MULTILINE,
|
|
169
|
+
)
|
|
170
|
+
except re.error:
|
|
171
|
+
logger.error(
|
|
172
|
+
f"分段回复正则表达式错误,使用默认分段方式: {traceback.format_exc()}",
|
|
173
|
+
)
|
|
174
|
+
split_response = re.findall(
|
|
175
|
+
r".*?[。?!~…]+|.+$",
|
|
176
|
+
comp.text,
|
|
177
|
+
re.DOTALL | re.MULTILINE,
|
|
178
|
+
)
|
|
157
179
|
if not split_response:
|
|
158
180
|
new_chain.append(comp)
|
|
159
181
|
continue
|
|
@@ -168,68 +190,110 @@ class ResultDecorateStage(Stage):
|
|
|
168
190
|
result.chain = new_chain
|
|
169
191
|
|
|
170
192
|
# TTS
|
|
193
|
+
tts_provider = self.ctx.plugin_manager.context.get_using_tts_provider(
|
|
194
|
+
event.unified_msg_origin,
|
|
195
|
+
)
|
|
196
|
+
|
|
171
197
|
if (
|
|
172
198
|
self.ctx.astrbot_config["provider_tts_settings"]["enable"]
|
|
173
199
|
and result.is_llm_result()
|
|
200
|
+
and SessionServiceManager.should_process_tts_request(event)
|
|
174
201
|
):
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
202
|
+
if not tts_provider:
|
|
203
|
+
logger.warning(
|
|
204
|
+
f"会话 {event.unified_msg_origin} 未配置文本转语音模型。",
|
|
205
|
+
)
|
|
206
|
+
else:
|
|
207
|
+
new_chain = []
|
|
208
|
+
for comp in result.chain:
|
|
209
|
+
if isinstance(comp, Plain) and len(comp.text) > 1:
|
|
210
|
+
try:
|
|
211
|
+
logger.info(f"TTS 请求: {comp.text}")
|
|
212
|
+
audio_path = await tts_provider.get_audio(comp.text)
|
|
213
|
+
logger.info(f"TTS 结果: {audio_path}")
|
|
214
|
+
if not audio_path:
|
|
215
|
+
logger.error(
|
|
216
|
+
f"由于 TTS 音频文件未找到,消息段转语音失败: {comp.text}",
|
|
217
|
+
)
|
|
218
|
+
new_chain.append(comp)
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
use_file_service = self.ctx.astrbot_config[
|
|
222
|
+
"provider_tts_settings"
|
|
223
|
+
]["use_file_service"]
|
|
224
|
+
callback_api_base = self.ctx.astrbot_config[
|
|
225
|
+
"callback_api_base"
|
|
226
|
+
]
|
|
227
|
+
dual_output = self.ctx.astrbot_config[
|
|
228
|
+
"provider_tts_settings"
|
|
229
|
+
]["dual_output"]
|
|
230
|
+
|
|
231
|
+
url = None
|
|
232
|
+
if use_file_service and callback_api_base:
|
|
233
|
+
token = await file_token_service.register_file(
|
|
234
|
+
audio_path,
|
|
235
|
+
)
|
|
236
|
+
url = f"{callback_api_base}/api/file/{token}"
|
|
237
|
+
logger.debug(f"已注册:{url}")
|
|
238
|
+
|
|
184
239
|
new_chain.append(
|
|
185
|
-
Record(
|
|
240
|
+
Record(
|
|
241
|
+
file=url or audio_path,
|
|
242
|
+
url=url or audio_path,
|
|
243
|
+
),
|
|
186
244
|
)
|
|
187
|
-
if
|
|
245
|
+
if dual_output:
|
|
188
246
|
new_chain.append(comp)
|
|
189
|
-
|
|
190
|
-
logger.error(
|
|
191
|
-
|
|
192
|
-
)
|
|
247
|
+
except Exception:
|
|
248
|
+
logger.error(traceback.format_exc())
|
|
249
|
+
logger.error("TTS 失败,使用文本发送。")
|
|
193
250
|
new_chain.append(comp)
|
|
194
|
-
|
|
195
|
-
logger.error(traceback.format_exc())
|
|
196
|
-
logger.error("TTS 失败,使用文本发送。")
|
|
251
|
+
else:
|
|
197
252
|
new_chain.append(comp)
|
|
198
|
-
|
|
199
|
-
new_chain.append(comp)
|
|
200
|
-
result.chain = new_chain
|
|
253
|
+
result.chain = new_chain
|
|
201
254
|
|
|
202
255
|
# 文本转图片
|
|
203
256
|
elif (
|
|
204
257
|
result.use_t2i_ is None and self.ctx.astrbot_config["t2i"]
|
|
205
258
|
) or result.use_t2i_:
|
|
206
|
-
|
|
259
|
+
parts = []
|
|
207
260
|
for comp in result.chain:
|
|
208
261
|
if isinstance(comp, Plain):
|
|
209
|
-
|
|
262
|
+
parts.append("\n\n" + comp.text)
|
|
210
263
|
else:
|
|
211
264
|
break
|
|
265
|
+
plain_str = "".join(parts)
|
|
212
266
|
if plain_str and len(plain_str) > self.t2i_word_threshold:
|
|
213
267
|
render_start = time.time()
|
|
214
268
|
try:
|
|
215
269
|
url = await html_renderer.render_t2i(
|
|
216
|
-
plain_str,
|
|
270
|
+
plain_str,
|
|
271
|
+
return_url=True,
|
|
272
|
+
use_network=self.t2i_use_network,
|
|
273
|
+
template_name=self.t2i_active_template,
|
|
217
274
|
)
|
|
218
275
|
except BaseException:
|
|
219
276
|
logger.error("文本转图片失败,使用文本发送。")
|
|
220
277
|
return
|
|
221
278
|
if time.time() - render_start > 3:
|
|
222
279
|
logger.warning(
|
|
223
|
-
"文本转图片耗时超过了 3 秒,如果觉得很慢可以使用 /t2i 关闭文本转图片模式。"
|
|
280
|
+
"文本转图片耗时超过了 3 秒,如果觉得很慢可以使用 /t2i 关闭文本转图片模式。",
|
|
224
281
|
)
|
|
225
282
|
if url:
|
|
226
283
|
if url.startswith("http"):
|
|
227
284
|
result.chain = [Image.fromURL(url)]
|
|
285
|
+
elif (
|
|
286
|
+
self.ctx.astrbot_config["t2i_use_file_service"]
|
|
287
|
+
and self.ctx.astrbot_config["callback_api_base"]
|
|
288
|
+
):
|
|
289
|
+
token = await file_token_service.register_file(url)
|
|
290
|
+
url = f"{self.ctx.astrbot_config['callback_api_base']}/api/file/{token}"
|
|
291
|
+
logger.debug(f"已注册:{url}")
|
|
292
|
+
result.chain = [Image.fromURL(url)]
|
|
228
293
|
else:
|
|
229
294
|
result.chain = [Image.fromFileSystem(url)]
|
|
230
295
|
|
|
231
296
|
# 触发转发消息
|
|
232
|
-
has_forwarded = False
|
|
233
297
|
if event.get_platform_name() == "aiocqhttp":
|
|
234
298
|
word_cnt = 0
|
|
235
299
|
for comp in result.chain:
|
|
@@ -237,19 +301,22 @@ class ResultDecorateStage(Stage):
|
|
|
237
301
|
word_cnt += len(comp.text)
|
|
238
302
|
if word_cnt > self.forward_threshold:
|
|
239
303
|
node = Node(
|
|
240
|
-
uin=event.get_self_id(),
|
|
304
|
+
uin=event.get_self_id(),
|
|
305
|
+
name="AstrBot",
|
|
306
|
+
content=[*result.chain],
|
|
241
307
|
)
|
|
242
308
|
result.chain = [node]
|
|
243
|
-
has_forwarded = True
|
|
244
309
|
|
|
245
|
-
|
|
310
|
+
has_plain = any(isinstance(item, Plain) for item in result.chain)
|
|
311
|
+
if has_plain:
|
|
246
312
|
# at 回复
|
|
247
313
|
if (
|
|
248
314
|
self.reply_with_mention
|
|
249
315
|
and event.get_message_type() != MessageType.FRIEND_MESSAGE
|
|
250
316
|
):
|
|
251
317
|
result.chain.insert(
|
|
252
|
-
0,
|
|
318
|
+
0,
|
|
319
|
+
At(qq=event.get_sender_id(), name=event.get_sender_name()),
|
|
253
320
|
)
|
|
254
321
|
if len(result.chain) > 1 and isinstance(result.chain[1], Plain):
|
|
255
322
|
result.chain[1].text = "\n" + result.chain[1].text
|