Merge branch 'dev' of https://github.com/MoFox-Studio/MoFox_Bot into dev
This commit is contained in:
@@ -14,6 +14,9 @@ from src.config.config import global_config
|
||||
|
||||
logger = get_logger("plan_executor")
|
||||
|
||||
# 全局背景任务集合
|
||||
_background_tasks = set()
|
||||
|
||||
|
||||
class ChatterPlanExecutor:
|
||||
"""
|
||||
@@ -89,7 +92,9 @@ class ChatterPlanExecutor:
|
||||
|
||||
# 将其他动作放入后台任务执行,避免阻塞主流程
|
||||
if other_actions:
|
||||
asyncio.create_task(self._execute_other_actions(other_actions, plan))
|
||||
task = asyncio.create_task(self._execute_other_actions(other_actions, plan))
|
||||
_background_tasks.add(task)
|
||||
task.add_done_callback(_background_tasks.discard)
|
||||
logger.info(f"已将 {len(other_actions)} 个其他动作放入后台任务执行。")
|
||||
# 注意:后台任务的结果不会立即计入本次返回的统计数据
|
||||
|
||||
|
||||
@@ -254,15 +254,7 @@ class ChatterPlanFilter:
|
||||
plan
|
||||
)
|
||||
|
||||
actions_before_now = await get_actions_by_timestamp_with_chat(
|
||||
chat_id=plan.chat_id,
|
||||
timestamp_start=time.time() - 3600,
|
||||
timestamp_end=time.time(),
|
||||
limit=5,
|
||||
)
|
||||
|
||||
actions_before_now_block = build_readable_actions(actions=actions_before_now)
|
||||
actions_before_now_block = f"你刚刚选择并执行过的action是:\n{actions_before_now_block}"
|
||||
actions_before_now_block = ""
|
||||
|
||||
self.last_obs_time_mark = time.time()
|
||||
|
||||
@@ -285,6 +277,7 @@ class ChatterPlanFilter:
|
||||
动作描述:不进行回复,等待合适的回复时机
|
||||
- 当你刚刚发送了消息,没有人回复时,选择no_reply
|
||||
- 当你一次发送了太多消息,为了避免打扰聊天节奏,选择no_reply
|
||||
- 在认为对方话没有讲完的时候选择这个
|
||||
{{
|
||||
"action": "no_reply",
|
||||
"reason":"不回复的原因"
|
||||
|
||||
@@ -168,6 +168,9 @@ class ChatterActionPlanner:
|
||||
action_modifier = ActionModifier(self.action_manager, self.chat_id)
|
||||
await action_modifier.modify_actions()
|
||||
|
||||
# 在生成初始计划前,刷新缓存消息到未读列表
|
||||
await self._flush_cached_messages_to_unread(context)
|
||||
|
||||
initial_plan = await self.generator.generate(chat_mode)
|
||||
|
||||
# 确保Plan中包含所有当前可用的动作
|
||||
@@ -258,6 +261,9 @@ class ChatterActionPlanner:
|
||||
# 重新运行主规划流程,这次将正确使用Focus模式
|
||||
return await self._enhanced_plan_flow(context)
|
||||
try:
|
||||
# Normal模式开始时,刷新缓存消息到未读列表
|
||||
await self._flush_cached_messages_to_unread(context)
|
||||
|
||||
unread_messages = context.get_unread_messages() if context else []
|
||||
|
||||
if not unread_messages:
|
||||
@@ -459,6 +465,45 @@ class ChatterActionPlanner:
|
||||
except Exception as e:
|
||||
logger.warning(f"同步chat_mode到ChatStream失败: {e}")
|
||||
|
||||
async def _flush_cached_messages_to_unread(self, context: "StreamContext | None") -> list:
|
||||
"""在planner开始时将缓存消息刷新到未读消息列表
|
||||
|
||||
此方法在动作修改器执行后、生成初始计划前调用,确保计划阶段能看到所有积累的消息。
|
||||
|
||||
Args:
|
||||
context: 流上下文
|
||||
|
||||
Returns:
|
||||
list: 刷新的消息列表
|
||||
"""
|
||||
if not context:
|
||||
return []
|
||||
|
||||
try:
|
||||
from src.chat.message_manager.message_manager import message_manager
|
||||
|
||||
stream_id = context.stream_id
|
||||
|
||||
if message_manager.is_running and message_manager.has_cached_messages(stream_id):
|
||||
# 获取缓存消息
|
||||
cached_messages = message_manager.flush_cached_messages(stream_id)
|
||||
|
||||
if cached_messages:
|
||||
# 直接添加到上下文的未读消息列表
|
||||
for message in cached_messages:
|
||||
context.unread_messages.append(message)
|
||||
logger.info(f"Planner开始前刷新缓存消息到未读列表: stream={stream_id}, 数量={len(cached_messages)}")
|
||||
return cached_messages
|
||||
|
||||
return []
|
||||
|
||||
except ImportError:
|
||||
logger.debug("MessageManager不可用,跳过缓存刷新")
|
||||
return []
|
||||
except Exception as e:
|
||||
logger.warning(f"Planner刷新缓存消息失败: error={e}")
|
||||
return []
|
||||
|
||||
def _update_stats_from_execution_result(self, execution_result: dict[str, Any]):
|
||||
"""根据执行结果更新规划器统计"""
|
||||
if not execution_result:
|
||||
|
||||
@@ -11,6 +11,9 @@ from src.plugin_system import BasePlugin, ComponentInfo, register_plugin
|
||||
from src.plugin_system.base.component_types import PermissionNodeField
|
||||
from src.plugin_system.base.config_types import ConfigField
|
||||
|
||||
# 全局背景任务集合
|
||||
_background_tasks = set()
|
||||
|
||||
from .actions.read_feed_action import ReadFeedAction
|
||||
from .actions.send_feed_action import SendFeedAction
|
||||
from .commands.send_feed_command import SendFeedCommand
|
||||
@@ -117,8 +120,14 @@ class MaiZoneRefactoredPlugin(BasePlugin):
|
||||
logger.info("MaiZone重构版插件服务已注册。")
|
||||
|
||||
# --- 启动后台任务 ---
|
||||
asyncio.create_task(scheduler_service.start())
|
||||
asyncio.create_task(monitor_service.start())
|
||||
task1 = asyncio.create_task(scheduler_service.start())
|
||||
_background_tasks.add(task1)
|
||||
task1.add_done_callback(_background_tasks.discard)
|
||||
|
||||
task2 = asyncio.create_task(monitor_service.start())
|
||||
_background_tasks.add(task2)
|
||||
task2.add_done_callback(_background_tasks.discard)
|
||||
|
||||
logger.info("MaiZone后台监控和定时任务已启动。")
|
||||
|
||||
def get_plugin_components(self) -> list[tuple[ComponentInfo, type]]:
|
||||
|
||||
@@ -7,6 +7,7 @@ import base64
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
import aiofiles
|
||||
import aiohttp
|
||||
|
||||
from src.common.logger import get_logger
|
||||
@@ -86,8 +87,8 @@ class ImageService:
|
||||
if b64_json:
|
||||
image_bytes = base64.b64decode(b64_json)
|
||||
file_path = Path(image_dir) / f"image_{i + 1}.png"
|
||||
with open(file_path, "wb") as f:
|
||||
f.write(image_bytes)
|
||||
async with aiofiles.open(file_path, "wb") as f:
|
||||
await f.write(image_bytes)
|
||||
logger.info(f"成功保存AI图片到: {file_path}")
|
||||
return True
|
||||
else:
|
||||
|
||||
@@ -12,6 +12,7 @@ from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import aiofiles
|
||||
import aiohttp
|
||||
import bs4
|
||||
import json5
|
||||
@@ -397,8 +398,8 @@ class QZoneService:
|
||||
}
|
||||
# 成功获取后,异步写入本地文件作为备份
|
||||
try:
|
||||
with open(cookie_file_path, "wb") as f:
|
||||
f.write(orjson.dumps(parsed_cookies))
|
||||
async with aiofiles.open(cookie_file_path, "wb") as f:
|
||||
await f.write(orjson.dumps(parsed_cookies))
|
||||
logger.info(f"通过Napcat服务成功更新Cookie,并已保存至: {cookie_file_path}")
|
||||
except Exception as e:
|
||||
logger.warning(f"保存Cookie到文件时出错: {e}")
|
||||
@@ -413,8 +414,9 @@ class QZoneService:
|
||||
logger.info("尝试从本地Cookie文件加载...")
|
||||
if cookie_file_path.exists():
|
||||
try:
|
||||
with open(cookie_file_path, "rb") as f:
|
||||
cookies = orjson.loads(f.read())
|
||||
async with aiofiles.open(cookie_file_path, "rb") as f:
|
||||
content = await f.read()
|
||||
cookies = orjson.loads(content)
|
||||
logger.info(f"成功从本地文件加载Cookie: {cookie_file_path}")
|
||||
return cookies
|
||||
except Exception as e:
|
||||
|
||||
@@ -13,6 +13,8 @@ logger = get_logger("stt_whisper_plugin")
|
||||
# 全局变量来缓存模型,避免重复加载
|
||||
_whisper_model = None
|
||||
_is_loading = False
|
||||
_model_ready_event = asyncio.Event()
|
||||
_background_tasks = set() # 背景任务集合
|
||||
|
||||
class LocalASRTool(BaseTool):
|
||||
"""
|
||||
@@ -29,7 +31,7 @@ class LocalASRTool(BaseTool):
|
||||
"""
|
||||
一个类方法,用于在插件加载时触发一次模型加载。
|
||||
"""
|
||||
global _whisper_model, _is_loading
|
||||
global _whisper_model, _is_loading, _model_ready_event
|
||||
if _whisper_model is None and not _is_loading:
|
||||
_is_loading = True
|
||||
try:
|
||||
@@ -47,6 +49,7 @@ class LocalASRTool(BaseTool):
|
||||
_whisper_model = None
|
||||
finally:
|
||||
_is_loading = False
|
||||
_model_ready_event.set() # 通知等待的任务
|
||||
|
||||
async def execute(self, function_args: dict) -> str:
|
||||
audio_path = function_args.get("audio_path")
|
||||
@@ -55,9 +58,9 @@ class LocalASRTool(BaseTool):
|
||||
return "错误:缺少 audio_path 参数。"
|
||||
|
||||
global _whisper_model
|
||||
# 增强的等待逻辑:只要模型还没准备好,就一直等待后台加载任务完成
|
||||
while _is_loading:
|
||||
await asyncio.sleep(0.2)
|
||||
# 使用 Event 等待模型加载完成
|
||||
if _is_loading:
|
||||
await _model_ready_event.wait()
|
||||
|
||||
if _whisper_model is None:
|
||||
return "Whisper 模型加载失败,无法识别语音。"
|
||||
@@ -90,7 +93,9 @@ class STTWhisperPlugin(BasePlugin):
|
||||
from src.config.config import global_config
|
||||
if global_config.voice.asr_provider == "local":
|
||||
# 使用 create_task 在后台开始加载,不阻塞主流程
|
||||
asyncio.create_task(LocalASRTool.load_model_once(self.config or {}))
|
||||
task = asyncio.create_task(LocalASRTool.load_model_once(self.config or {}))
|
||||
_background_tasks.add(task)
|
||||
task.add_done_callback(_background_tasks.discard)
|
||||
except Exception as e:
|
||||
logger.error(f"触发 Whisper 模型预加载时出错: {e}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user