diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index bbe86a2d4..523bdd848 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -2,7 +2,7 @@ 如需修改配置,请在 `data/cmd_config.json` 中修改或者在管理面板中可视化修改。 """ -VERSION = "3.4.25" +VERSION = "3.4.26" DB_PATH = "data/data_v3.db" # 默认配置 @@ -28,7 +28,10 @@ DEFAULT_CONFIG = { "segmented_reply": { "enable": False, "only_llm_result": True, + "interval_method": "random", "interval": "1.5,3.5", + "log_base": 2.6, + "words_count_threshold": 150, "regex": ".*?[。?!~…]+|.+$" }, "no_permission_reply": True, @@ -242,10 +245,26 @@ CONFIG_METADATA_2 = { "description": "仅对 LLM 结果分段", "type": "bool", }, + "interval_method": { + "description": "间隔时间计算方法", + "type": "string", + "options": ["random", "log"], + "hint": "分段回复的间隔时间计算方法。random 为随机时间,log 为根据消息长度计算,$y=log_{log\_base}(x)$,x为字数,y的单位为秒。", + }, "interval": { "description": "随机间隔时间(秒)", "type": "string", - "hint": "每一段回复的间隔时间,格式为 `最小时间,最大时间`。如 `0.75,2.5`", + "hint": "`random` 方法用。每一段回复的间隔时间,格式为 `最小时间,最大时间`。如 `0.75,2.5`", + }, + "log_base": { + "description": "对数函数底数", + "type": "float", + "hint": "`log` 方法用。对数函数的底数。默认为 2.6", + }, + "words_count_threshold": { + "description": "字数阈值", + "type": "int", + "hint": "超过这个字数的消息不会被分段回复。默认为 150", }, "regex": { "description": "正则表达式", diff --git a/astrbot/core/pipeline/respond/stage.py b/astrbot/core/pipeline/respond/stage.py index 330417d29..16423ef6c 100644 --- a/astrbot/core/pipeline/respond/stage.py +++ b/astrbot/core/pipeline/respond/stage.py @@ -1,11 +1,13 @@ import random import asyncio +import math from typing import Union, AsyncGenerator from ..stage import register_stage, Stage from ..context import PipelineContext from astrbot.core.platform.astr_message_event import AstrMessageEvent from astrbot.core.message.message_event_result import MessageChain from astrbot.core import logger +from astrbot.core.message.message_event_result import BaseMessageComponent, Plain from astrbot.core.star.star_handler import star_handlers_registry, EventType @register_stage @@ -16,6 +18,9 @@ class RespondStage(Stage): # 分段回复 self.enable_seg: bool = ctx.astrbot_config['platform_settings']['segmented_reply']['enable'] self.only_llm_result = ctx.astrbot_config['platform_settings']['segmented_reply']['only_llm_result'] + + self.interval_method = ctx.astrbot_config['platform_settings']['segmented_reply']['interval_method'] + self.log_base = float(ctx.astrbot_config['platform_settings']['segmented_reply']['log_base']) interval_str: str = ctx.astrbot_config['platform_settings']['segmented_reply']['interval'] interval_str_ls = interval_str.replace(" ", "").split(",") try: @@ -24,7 +29,27 @@ class RespondStage(Stage): logger.error(f'解析分段回复的间隔时间失败。{e}') self.interval = [1.5, 3.5] logger.info(f"分段回复间隔时间:{self.interval}") - + + async def _word_cnt(self, text: str) -> int: + '''分段回复 统计字数''' + if all(ord(c) < 128 for c in text): + word_count = len(text.split()) + else: + word_count = len([c for c in text if c.isalnum()]) + return word_count + + async def _calc_comp_interval(self, comp: BaseMessageComponent) -> float: + '''分段回复 计算间隔时间''' + if self.interval_method == 'log': + if isinstance(comp, Plain): + wc = await self._word_cnt(comp.text) + i = math.log(wc + 1, self.log_base) + return random.uniform(i, i + 0.5) + else: + return random.uniform(1, 1.75) + else: + # random + return random.uniform(self.interval[0], self.interval[1]) async def process(self, event: AstrMessageEvent) -> Union[None, AsyncGenerator[None, None]]: result = event.get_result() @@ -37,8 +62,9 @@ class RespondStage(Stage): if self.enable_seg and ((self.only_llm_result and result.is_llm_result()) or not self.only_llm_result): # 分段回复 for comp in result.chain: + i = await self._calc_comp_interval(comp) + await asyncio.sleep(i) await event.send(MessageChain([comp])) - await asyncio.sleep(random.uniform(self.interval[0], self.interval[1])) else: await event.send(result) await event._post_send() diff --git a/astrbot/core/pipeline/result_decorate/stage.py b/astrbot/core/pipeline/result_decorate/stage.py index f5a32178a..365344493 100644 --- a/astrbot/core/pipeline/result_decorate/stage.py +++ b/astrbot/core/pipeline/result_decorate/stage.py @@ -27,6 +27,7 @@ class ResultDecorateStage(Stage): self.t2i_word_threshold = 150 # 分段回复 + self.words_count_threshold = int(ctx.astrbot_config['platform_settings']['segmented_reply']['words_count_threshold']) self.enable_segmented_reply = ctx.astrbot_config['platform_settings']['segmented_reply']['enable'] self.only_llm_result = ctx.astrbot_config['platform_settings']['segmented_reply']['only_llm_result'] self.regex = ctx.astrbot_config['platform_settings']['segmented_reply']['regex'] @@ -77,6 +78,10 @@ class ResultDecorateStage(Stage): new_chain = [] for comp in result.chain: if isinstance(comp, Plain): + if len(comp.text) > self.words_count_threshold: + # 不分段回复 + new_chain.append(comp) + continue split_response = re.findall(self.regex, comp.text) if not split_response: new_chain.append(comp)