mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 03:55:55 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45a10b4ac7 | ||
|
|
b5d33ef629 | ||
|
|
d3629916bf | ||
|
|
c5cb26d295 | ||
|
|
4b2785c5eb | ||
|
|
7ed190e6d2 | ||
|
|
eac041cdd2 | ||
|
|
05527cfc01 | ||
|
|
61e2af4a14 | ||
|
|
79804b6ecd | ||
|
|
76434b2f4e | ||
|
|
ec8bd4922e | ||
|
|
4ffa773fac | ||
|
|
ea8b7bc8aa | ||
|
|
39ce5646f6 | ||
|
|
5092a82739 | ||
|
|
3bba0b6d9a | ||
|
|
7a19dd503d | ||
|
|
9e6a01fefd | ||
|
|
933471b4d9 | ||
|
|
f81808d239 | ||
|
|
96832b6f7d | ||
|
|
e2eb0a84b0 | ||
|
|
c8eb2e3376 | ||
|
|
21fe5822f9 | ||
|
|
d49cc9a7a3 | ||
|
|
910d0bfae1 |
@@ -37,7 +37,10 @@ class JSONConfigFile(file_model.ConfigFile):
|
||||
self.template_data = json.load(f)
|
||||
|
||||
with open(self.config_file_name, "r", encoding="utf-8") as f:
|
||||
cfg = json.load(f)
|
||||
try:
|
||||
cfg = json.load(f)
|
||||
except json.JSONDecodeError as e:
|
||||
raise Exception(f"配置文件 {self.config_file_name} 语法错误: {e}")
|
||||
|
||||
if completion:
|
||||
|
||||
|
||||
29
pkg/config/migrations/m008_ad_fixwin_config_migrate.py
Normal file
29
pkg/config/migrations/m008_ad_fixwin_config_migrate.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .. import migration
|
||||
|
||||
|
||||
@migration.migration_class("ad-fixwin-cfg-migration", 8)
|
||||
class AdFixwinConfigMigration(migration.Migration):
|
||||
"""迁移"""
|
||||
|
||||
async def need_migrate(self) -> bool:
|
||||
"""判断当前环境是否需要运行此迁移"""
|
||||
return isinstance(
|
||||
self.ap.pipeline_cfg.data["rate-limit"]["fixwin"]["default"],
|
||||
int
|
||||
)
|
||||
|
||||
async def run(self):
|
||||
"""执行迁移"""
|
||||
|
||||
for session_name in self.ap.pipeline_cfg.data["rate-limit"]["fixwin"]:
|
||||
|
||||
temp_dict = {
|
||||
"window-size": 60,
|
||||
"limit": self.ap.pipeline_cfg.data["rate-limit"]["fixwin"][session_name]
|
||||
}
|
||||
|
||||
self.ap.pipeline_cfg.data["rate-limit"]["fixwin"][session_name] = temp_dict
|
||||
|
||||
await self.ap.pipeline_cfg.dump_config()
|
||||
24
pkg/config/migrations/m009_msg_truncator_cfg.py
Normal file
24
pkg/config/migrations/m009_msg_truncator_cfg.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .. import migration
|
||||
|
||||
|
||||
@migration.migration_class("msg-truncator-cfg-migration", 9)
|
||||
class MsgTruncatorConfigMigration(migration.Migration):
|
||||
"""迁移"""
|
||||
|
||||
async def need_migrate(self) -> bool:
|
||||
"""判断当前环境是否需要运行此迁移"""
|
||||
return 'msg-truncate' not in self.ap.pipeline_cfg.data
|
||||
|
||||
async def run(self):
|
||||
"""执行迁移"""
|
||||
|
||||
self.ap.pipeline_cfg.data['msg-truncate'] = {
|
||||
'method': 'round',
|
||||
'round': {
|
||||
'max-round': 10
|
||||
}
|
||||
}
|
||||
|
||||
await self.ap.pipeline_cfg.dump_config()
|
||||
@@ -15,7 +15,7 @@ from ..command import cmdmgr
|
||||
from ..plugin import manager as plugin_mgr
|
||||
from ..pipeline import pool
|
||||
from ..pipeline import controller, stagemgr
|
||||
from ..utils import version as version_mgr, proxy as proxy_mgr
|
||||
from ..utils import version as version_mgr, proxy as proxy_mgr, announce as announce_mgr
|
||||
|
||||
|
||||
class Application:
|
||||
@@ -69,6 +69,8 @@ class Application:
|
||||
|
||||
ver_mgr: version_mgr.VersionManager = None
|
||||
|
||||
ann_mgr: announce_mgr.AnnouncementManager = None
|
||||
|
||||
proxy_mgr: proxy_mgr.ProxyManager = None
|
||||
|
||||
logger: logging.Logger = None
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import traceback
|
||||
|
||||
from . import app
|
||||
from ..audit import identifier
|
||||
from . import stage
|
||||
|
||||
# 引入启动阶段实现以便注册
|
||||
from .stages import load_config, setup_logger, build_app, migrate
|
||||
from .stages import load_config, setup_logger, build_app, migrate, show_notes
|
||||
|
||||
|
||||
stage_order = [
|
||||
"LoadConfigStage",
|
||||
"MigrationStage",
|
||||
"SetupLoggerStage",
|
||||
"BuildAppStage"
|
||||
"BuildAppStage",
|
||||
"ShowNotesStage"
|
||||
]
|
||||
|
||||
|
||||
@@ -27,6 +30,7 @@ async def make_app() -> app.Application:
|
||||
for stage_name in stage_order:
|
||||
stage_cls = stage.preregistered_stages[stage_name]
|
||||
stage_inst = stage_cls()
|
||||
|
||||
await stage_inst.run(ap)
|
||||
|
||||
await ap.initialize()
|
||||
@@ -35,5 +39,8 @@ async def make_app() -> app.Application:
|
||||
|
||||
|
||||
async def main():
|
||||
app_inst = await make_app()
|
||||
await app_inst.run()
|
||||
try:
|
||||
app_inst = await make_app()
|
||||
await app_inst.run()
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
|
||||
@@ -8,16 +8,3 @@ from ...config.impls import pymodule
|
||||
|
||||
load_python_module_config = config_mgr.load_python_module_config
|
||||
load_json_config = config_mgr.load_json_config
|
||||
|
||||
|
||||
async def override_config_manager(cfg_mgr: config_mgr.ConfigManager) -> list[str]:
|
||||
override_json = json.load(open("override.json", "r", encoding="utf-8"))
|
||||
overrided = []
|
||||
|
||||
config = cfg_mgr.data
|
||||
for key in override_json:
|
||||
if key in config:
|
||||
config[key] = override_json[key]
|
||||
overrided.append(key)
|
||||
|
||||
return overrided
|
||||
|
||||
@@ -73,6 +73,9 @@ class Query(pydantic.BaseModel):
|
||||
resp_message_chain: typing.Optional[list[mirai.MessageChain]] = None
|
||||
"""回复消息链,从resp_messages包装而得"""
|
||||
|
||||
# ======= 内部保留 =======
|
||||
current_stage: "pkg.pipeline.stagemgr.StageInstContainer" = None
|
||||
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
|
||||
44
pkg/core/note.py
Normal file
44
pkg/core/note.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import abc
|
||||
import typing
|
||||
|
||||
from . import app
|
||||
|
||||
preregistered_notes: list[typing.Type[LaunchNote]] = []
|
||||
|
||||
def note_class(name: str, number: int):
|
||||
"""注册一个启动信息
|
||||
"""
|
||||
def decorator(cls: typing.Type[LaunchNote]) -> typing.Type[LaunchNote]:
|
||||
cls.name = name
|
||||
cls.number = number
|
||||
preregistered_notes.append(cls)
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
class LaunchNote(abc.ABC):
|
||||
"""启动信息
|
||||
"""
|
||||
name: str
|
||||
|
||||
number: int
|
||||
|
||||
ap: app.Application
|
||||
|
||||
def __init__(self, ap: app.Application):
|
||||
self.ap = ap
|
||||
|
||||
@abc.abstractmethod
|
||||
async def need_show(self) -> bool:
|
||||
"""判断当前环境是否需要显示此启动信息
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
async def yield_note(self) -> typing.AsyncGenerator[typing.Tuple[str, int], None]:
|
||||
"""生成启动信息
|
||||
"""
|
||||
pass
|
||||
0
pkg/core/notes/__init__.py
Normal file
0
pkg/core/notes/__init__.py
Normal file
20
pkg/core/notes/n001_classic_msgs.py
Normal file
20
pkg/core/notes/n001_classic_msgs.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import typing
|
||||
|
||||
from .. import note, app
|
||||
|
||||
|
||||
@note.note_class("ClassicNotes", 1)
|
||||
class ClassicNotes(note.LaunchNote):
|
||||
"""经典启动信息
|
||||
"""
|
||||
|
||||
async def need_show(self) -> bool:
|
||||
return True
|
||||
|
||||
async def yield_note(self) -> typing.AsyncGenerator[typing.Tuple[str, int], None]:
|
||||
|
||||
yield await self.ap.ann_mgr.show_announcements()
|
||||
|
||||
yield await self.ap.ver_mgr.show_version_update()
|
||||
21
pkg/core/notes/n002_selection_mode_on_windows.py
Normal file
21
pkg/core/notes/n002_selection_mode_on_windows.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import typing
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from .. import note, app
|
||||
|
||||
|
||||
@note.note_class("SelectionModeOnWindows", 2)
|
||||
class SelectionModeOnWindows(note.LaunchNote):
|
||||
"""Windows 上的选择模式提示信息
|
||||
"""
|
||||
|
||||
async def need_show(self) -> bool:
|
||||
return os.name == 'nt'
|
||||
|
||||
async def yield_note(self) -> typing.AsyncGenerator[typing.Tuple[str, int], None]:
|
||||
|
||||
yield """您正在使用 Windows 系统,若窗口左上角显示处于”选择“模式,程序将被暂停运行,此时请右键窗口中空白区域退出选择模式。""", logging.INFO
|
||||
@@ -53,12 +53,10 @@ class BuildAppStage(stage.BootingStage):
|
||||
|
||||
# 发送公告
|
||||
ann_mgr = announce.AnnouncementManager(ap)
|
||||
await ann_mgr.show_announcements()
|
||||
ap.ann_mgr = ann_mgr
|
||||
|
||||
ap.query_pool = pool.QueryPool()
|
||||
|
||||
await ap.ver_mgr.show_version_update()
|
||||
|
||||
plugin_mgr_inst = plugin_mgr.PluginManager(ap)
|
||||
await plugin_mgr_inst.initialize()
|
||||
ap.plugin_mgr = plugin_mgr_inst
|
||||
|
||||
@@ -5,7 +5,7 @@ import importlib
|
||||
from .. import stage, app
|
||||
from ...config import migration
|
||||
from ...config.migrations import m001_sensitive_word_migration, m002_openai_config_migration, m003_anthropic_requester_cfg_completion, m004_moonshot_cfg_completion
|
||||
from ...config.migrations import m005_deepseek_cfg_completion, m006_vision_config, m007_qcg_center_url
|
||||
from ...config.migrations import m005_deepseek_cfg_completion, m006_vision_config, m007_qcg_center_url, m008_ad_fixwin_config_migrate, m009_msg_truncator_cfg
|
||||
|
||||
|
||||
@stage.stage_class("MigrationStage")
|
||||
|
||||
28
pkg/core/stages/show_notes.py
Normal file
28
pkg/core/stages/show_notes.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .. import stage, app, note
|
||||
from ..notes import n001_classic_msgs, n002_selection_mode_on_windows
|
||||
|
||||
|
||||
@stage.stage_class("ShowNotesStage")
|
||||
class ShowNotesStage(stage.BootingStage):
|
||||
"""显示启动信息阶段
|
||||
"""
|
||||
|
||||
async def run(self, ap: app.Application):
|
||||
|
||||
# 排序
|
||||
note.preregistered_notes.sort(key=lambda x: x.number)
|
||||
|
||||
for note_cls in note.preregistered_notes:
|
||||
try:
|
||||
note_inst = note_cls(ap)
|
||||
if await note_inst.need_show():
|
||||
async for ret in note_inst.yield_note():
|
||||
if not ret:
|
||||
continue
|
||||
msg, level = ret
|
||||
if msg:
|
||||
ap.logger.log(level, msg)
|
||||
except Exception as e:
|
||||
continue
|
||||
@@ -4,6 +4,8 @@ import asyncio
|
||||
import typing
|
||||
import traceback
|
||||
|
||||
import mirai
|
||||
|
||||
from ..core import app, entities
|
||||
from . import entities as pipeline_entities
|
||||
from ..plugin import events
|
||||
@@ -68,6 +70,17 @@ class Controller:
|
||||
"""检查输出
|
||||
"""
|
||||
if result.user_notice:
|
||||
# 处理str类型
|
||||
|
||||
if isinstance(result.user_notice, str):
|
||||
result.user_notice = mirai.MessageChain(
|
||||
mirai.Plain(result.user_notice)
|
||||
)
|
||||
elif isinstance(result.user_notice, list):
|
||||
result.user_notice = mirai.MessageChain(
|
||||
*result.user_notice
|
||||
)
|
||||
|
||||
await self.ap.platform_mgr.send(
|
||||
query.message_event,
|
||||
result.user_notice,
|
||||
@@ -109,6 +122,8 @@ class Controller:
|
||||
|
||||
while i < len(self.ap.stage_mgr.stage_containers):
|
||||
stage_container = self.ap.stage_mgr.stage_containers[i]
|
||||
|
||||
query.current_stage = stage_container # 标记到 Query 对象里
|
||||
|
||||
result = stage_container.inst.process(query, stage_container.inst_name)
|
||||
|
||||
@@ -149,7 +164,7 @@ class Controller:
|
||||
try:
|
||||
await self._execute_from_stage(0, query)
|
||||
except Exception as e:
|
||||
self.ap.logger.error(f"处理请求时出错 query_id={query.query_id}: {e}")
|
||||
self.ap.logger.error(f"处理请求时出错 query_id={query.query_id} stage={query.current_stage.inst_name} : {e}")
|
||||
self.ap.logger.debug(f"Traceback: {traceback.format_exc()}")
|
||||
# traceback.print_exc()
|
||||
finally:
|
||||
|
||||
0
pkg/pipeline/msgtrun/__init__.py
Normal file
0
pkg/pipeline/msgtrun/__init__.py
Normal file
35
pkg/pipeline/msgtrun/msgtrun.py
Normal file
35
pkg/pipeline/msgtrun/msgtrun.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .. import stage, entities, stagemgr
|
||||
from ...core import entities as core_entities
|
||||
from . import truncator
|
||||
from .truncators import round
|
||||
|
||||
|
||||
@stage.stage_class("ConversationMessageTruncator")
|
||||
class ConversationMessageTruncator(stage.PipelineStage):
|
||||
"""会话消息截断器
|
||||
|
||||
用于截断会话消息链,以适应平台消息长度限制。
|
||||
"""
|
||||
trun: truncator.Truncator
|
||||
|
||||
async def initialize(self):
|
||||
use_method = self.ap.pipeline_cfg.data['msg-truncate']['method']
|
||||
|
||||
for trun in truncator.preregistered_truncators:
|
||||
if trun.name == use_method:
|
||||
self.trun = trun(self.ap)
|
||||
break
|
||||
else:
|
||||
raise ValueError(f"未知的截断器: {use_method}")
|
||||
|
||||
async def process(self, query: core_entities.Query, stage_inst_name: str) -> entities.StageProcessResult:
|
||||
"""处理
|
||||
"""
|
||||
query = await self.trun.truncate(query)
|
||||
|
||||
return entities.StageProcessResult(
|
||||
result_type=entities.ResultType.CONTINUE,
|
||||
new_query=query
|
||||
)
|
||||
56
pkg/pipeline/msgtrun/truncator.py
Normal file
56
pkg/pipeline/msgtrun/truncator.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import typing
|
||||
import abc
|
||||
|
||||
from ...core import entities as core_entities, app
|
||||
|
||||
|
||||
preregistered_truncators: list[typing.Type[Truncator]] = []
|
||||
|
||||
|
||||
def truncator_class(
|
||||
name: str
|
||||
) -> typing.Callable[[typing.Type[Truncator]], typing.Type[Truncator]]:
|
||||
"""截断器类装饰器
|
||||
|
||||
Args:
|
||||
name (str): 截断器名称
|
||||
|
||||
Returns:
|
||||
typing.Callable[[typing.Type[Truncator]], typing.Type[Truncator]]: 装饰器
|
||||
"""
|
||||
def decorator(cls: typing.Type[Truncator]) -> typing.Type[Truncator]:
|
||||
assert issubclass(cls, Truncator)
|
||||
|
||||
cls.name = name
|
||||
|
||||
preregistered_truncators.append(cls)
|
||||
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
class Truncator(abc.ABC):
|
||||
"""消息截断器基类
|
||||
"""
|
||||
|
||||
name: str
|
||||
|
||||
ap: app.Application
|
||||
|
||||
def __init__(self, ap: app.Application):
|
||||
self.ap = ap
|
||||
|
||||
async def initialize(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
async def truncate(self, query: core_entities.Query) -> core_entities.Query:
|
||||
"""截断
|
||||
|
||||
一般只需要操作query.messages,也可以扩展操作query.prompt, query.user_message。
|
||||
请勿操作其他字段。
|
||||
"""
|
||||
pass
|
||||
0
pkg/pipeline/msgtrun/truncators/__init__.py
Normal file
0
pkg/pipeline/msgtrun/truncators/__init__.py
Normal file
32
pkg/pipeline/msgtrun/truncators/round.py
Normal file
32
pkg/pipeline/msgtrun/truncators/round.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .. import truncator
|
||||
from ....core import entities as core_entities
|
||||
|
||||
|
||||
@truncator.truncator_class("round")
|
||||
class RoundTruncator(truncator.Truncator):
|
||||
"""前文回合数阶段器
|
||||
"""
|
||||
|
||||
async def truncate(self, query: core_entities.Query) -> core_entities.Query:
|
||||
"""截断
|
||||
"""
|
||||
max_round = self.ap.pipeline_cfg.data['msg-truncate']['round']['max-round']
|
||||
|
||||
temp_messages = []
|
||||
|
||||
current_round = 0
|
||||
|
||||
# 从后往前遍历
|
||||
for msg in query.messages[::-1]:
|
||||
if current_round < max_round:
|
||||
temp_messages.append(msg)
|
||||
if msg.role == 'user':
|
||||
current_round += 1
|
||||
else:
|
||||
break
|
||||
|
||||
query.messages = temp_messages[::-1]
|
||||
|
||||
return query
|
||||
@@ -62,9 +62,8 @@ class ChatMessageHandler(handler.MessageHandler):
|
||||
)
|
||||
|
||||
if event_ctx.event.alter is not None:
|
||||
query.message_chain = mirai.MessageChain([
|
||||
mirai.Plain(event_ctx.event.alter)
|
||||
])
|
||||
# if isinstance(event_ctx.event, str): # 现在暂时不考虑多模态alter
|
||||
query.user_message.content = event_ctx.event.alter
|
||||
|
||||
text_length = 0
|
||||
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
# 固定窗口算法
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import time
|
||||
|
||||
from .. import algo
|
||||
|
||||
|
||||
# 固定窗口算法
|
||||
class SessionContainer:
|
||||
|
||||
wait_lock: asyncio.Lock
|
||||
|
||||
records: dict[int, int]
|
||||
"""访问记录,key为每分钟的起始时间戳,value为访问次数"""
|
||||
"""访问记录,key为每窗口长度的起始时间戳,value为访问次数"""
|
||||
|
||||
def __init__(self):
|
||||
self.wait_lock = asyncio.Lock()
|
||||
@@ -47,30 +44,34 @@ class FixedWindowAlgo(algo.ReteLimitAlgo):
|
||||
|
||||
# 等待锁
|
||||
async with container.wait_lock:
|
||||
|
||||
# 获取窗口大小和限制
|
||||
window_size = self.ap.pipeline_cfg.data['rate-limit']['fixwin']['default']['window-size']
|
||||
limitation = self.ap.pipeline_cfg.data['rate-limit']['fixwin']['default']['limit']
|
||||
|
||||
if session_name in self.ap.pipeline_cfg.data['rate-limit']['fixwin']:
|
||||
window_size = self.ap.pipeline_cfg.data['rate-limit']['fixwin'][session_name]['window-size']
|
||||
limitation = self.ap.pipeline_cfg.data['rate-limit']['fixwin'][session_name]['limit']
|
||||
|
||||
# 获取当前时间戳
|
||||
now = int(time.time())
|
||||
|
||||
# 获取当前分钟的起始时间戳
|
||||
now = now - now % 60
|
||||
# 获取当前窗口的起始时间戳
|
||||
now = now - now % window_size
|
||||
|
||||
# 获取当前分钟的访问次数
|
||||
# 获取当前窗口的访问次数
|
||||
count = container.records.get(now, 0)
|
||||
|
||||
limitation = self.ap.pipeline_cfg.data['rate-limit']['fixwin']['default']
|
||||
|
||||
if session_name in self.ap.pipeline_cfg.data['rate-limit']['fixwin']:
|
||||
limitation = self.ap.pipeline_cfg.data['rate-limit']['fixwin'][session_name]
|
||||
|
||||
# 如果访问次数超过了限制
|
||||
if count >= limitation:
|
||||
if self.ap.pipeline_cfg.data['rate-limit']['strategy'] == 'drop':
|
||||
return False
|
||||
elif self.ap.pipeline_cfg.data['rate-limit']['strategy'] == 'wait':
|
||||
# 等待下一分钟
|
||||
await asyncio.sleep(60 - time.time() % 60)
|
||||
# 等待下一窗口
|
||||
await asyncio.sleep(window_size - time.time() % window_size)
|
||||
|
||||
now = int(time.time())
|
||||
now = now - now % 60
|
||||
now = now - now % window_size
|
||||
|
||||
if now not in container.records:
|
||||
container.records = {}
|
||||
|
||||
@@ -44,8 +44,8 @@ class GroupRespondRuleCheckStage(stage.PipelineStage):
|
||||
|
||||
use_rule = rules['default']
|
||||
|
||||
if str(query.launcher_id) in use_rule:
|
||||
use_rule = use_rule[str(query.launcher_id)]
|
||||
if str(query.launcher_id) in rules:
|
||||
use_rule = rules[str(query.launcher_id)]
|
||||
|
||||
for rule_matcher in self.rule_matchers: # 任意一个匹配就放行
|
||||
res = await rule_matcher.match(str(query.message_chain), query.message_chain, use_rule, query)
|
||||
|
||||
@@ -20,11 +20,14 @@ class PrefixRule(rule_model.GroupRespondRule):
|
||||
for prefix in prefixes:
|
||||
if message_text.startswith(prefix):
|
||||
|
||||
# 查找第一个plain元素
|
||||
for me in message_chain:
|
||||
if isinstance(me, mirai.Plain):
|
||||
me.text = me.text[len(prefix):]
|
||||
|
||||
return entities.RuleJudgeResult(
|
||||
matching=True,
|
||||
replacement=mirai.MessageChain([
|
||||
mirai.Plain(message_text[len(prefix):])
|
||||
]),
|
||||
replacement=message_chain,
|
||||
)
|
||||
|
||||
return entities.RuleJudgeResult(
|
||||
|
||||
@@ -13,6 +13,7 @@ from .respback import respback
|
||||
from .wrapper import wrapper
|
||||
from .preproc import preproc
|
||||
from .ratelimit import ratelimit
|
||||
from .msgtrun import msgtrun
|
||||
|
||||
|
||||
# 请求处理阶段顺序
|
||||
@@ -21,6 +22,7 @@ stage_order = [
|
||||
"BanSessionCheckStage", # 封禁会话检查
|
||||
"PreContentFilterStage", # 内容过滤前置阶段
|
||||
"PreProcessor", # 预处理器
|
||||
"ConversationMessageTruncator", # 会话消息截断器
|
||||
"RequireRateLimitOccupancy", # 请求速率限制占用
|
||||
"MessageProcessor", # 处理器
|
||||
"ReleaseRateLimitOccupancy", # 释放速率限制占用
|
||||
|
||||
@@ -71,7 +71,7 @@ class ResponseWrapper(stage.PipelineStage):
|
||||
|
||||
reply_text = ''
|
||||
|
||||
if result.content is not None: # 有内容
|
||||
if result.content: # 有内容
|
||||
reply_text = str(result.get_content_mirai_message_chain())
|
||||
|
||||
# ============= 触发插件事件 ===============
|
||||
|
||||
@@ -198,7 +198,6 @@ class OfficialMessageConverter(adapter_model.MessageConverter):
|
||||
bot_account_id: int = 0,
|
||||
) -> mirai.MessageChain:
|
||||
yiri_msg_list = []
|
||||
|
||||
# 存id
|
||||
|
||||
yiri_msg_list.append(
|
||||
@@ -218,7 +217,7 @@ class OfficialMessageConverter(adapter_model.MessageConverter):
|
||||
yiri_msg_list.append(mirai.At(target=mention.id))
|
||||
|
||||
for attachment in message.attachments:
|
||||
if attachment.content_type == "image":
|
||||
if attachment.content_type.startswith("image"):
|
||||
yiri_msg_list.append(mirai.Image(url=attachment.url))
|
||||
else:
|
||||
logging.warning(
|
||||
|
||||
@@ -102,9 +102,16 @@ class OpenAIChatCompletions(api.LLMAPIRequester):
|
||||
messages: typing.List[llm_entities.Message],
|
||||
funcs: typing.List[tools_entities.LLMFunction] = None,
|
||||
) -> llm_entities.Message:
|
||||
req_messages = [ # req_messages 仅用于类内,外部同步由 query.messages 进行
|
||||
m.dict(exclude_none=True) for m in messages
|
||||
]
|
||||
req_messages = [] # req_messages 仅用于类内,外部同步由 query.messages 进行
|
||||
for m in messages:
|
||||
msg_dict = m.dict(exclude_none=True)
|
||||
content = msg_dict.get("content")
|
||||
if isinstance(content, list):
|
||||
# 检查 content 列表中是否每个部分都是文本
|
||||
if all(isinstance(part, dict) and part.get("type") == "text" for part in content):
|
||||
# 将所有文本部分合并为一个字符串
|
||||
msg_dict["content"] = "\n".join(part["text"] for part in content)
|
||||
req_messages.append(msg_dict)
|
||||
|
||||
try:
|
||||
return await self._closure(req_messages, model, funcs)
|
||||
|
||||
@@ -4,6 +4,7 @@ import json
|
||||
import typing
|
||||
import os
|
||||
import base64
|
||||
import logging
|
||||
|
||||
import pydantic
|
||||
import requests
|
||||
@@ -107,17 +108,20 @@ class AnnouncementManager:
|
||||
|
||||
async def show_announcements(
|
||||
self
|
||||
):
|
||||
) -> typing.Tuple[str, int]:
|
||||
"""显示公告"""
|
||||
try:
|
||||
announcements = await self.fetch_new()
|
||||
ann_text = ""
|
||||
for ann in announcements:
|
||||
self.ap.logger.info(f'[公告] {ann.time}: {ann.content}')
|
||||
ann_text += f"[公告] {ann.time}: {ann.content}\n"
|
||||
|
||||
if announcements:
|
||||
|
||||
await self.ap.ctr_mgr.main.post_announcement_showed(
|
||||
ids=[item.id for item in announcements]
|
||||
)
|
||||
|
||||
return ann_text, logging.INFO
|
||||
except Exception as e:
|
||||
self.ap.logger.warning(f'获取公告时出错: {e}')
|
||||
return f'获取公告时出错: {e}', logging.WARNING
|
||||
|
||||
@@ -1 +1 @@
|
||||
semantic_version = "v3.2.1"
|
||||
semantic_version = "v3.2.4"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import typing
|
||||
import logging
|
||||
import time
|
||||
|
||||
import requests
|
||||
@@ -213,11 +215,11 @@ class VersionManager:
|
||||
|
||||
async def show_version_update(
|
||||
self
|
||||
):
|
||||
) -> typing.Tuple[str, int]:
|
||||
try:
|
||||
|
||||
if await self.ap.ver_mgr.is_new_version_available():
|
||||
self.ap.logger.info("有新版本可用,请使用 !update 命令更新")
|
||||
return "有新版本可用,请使用管理员账号发送 !update 命令更新", logging.INFO
|
||||
|
||||
except Exception as e:
|
||||
self.ap.logger.warning(f"检查版本更新时出错: {e}")
|
||||
return f"检查版本更新时出错: {e}", logging.WARNING
|
||||
|
||||
@@ -29,7 +29,16 @@
|
||||
"strategy": "drop",
|
||||
"algo": "fixwin",
|
||||
"fixwin": {
|
||||
"default": 60
|
||||
"default": {
|
||||
"window-size": 60,
|
||||
"limit": 60
|
||||
}
|
||||
}
|
||||
},
|
||||
"msg-truncate": {
|
||||
"method": "round",
|
||||
"round": {
|
||||
"max-round": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user