fix:ai哥神秘修复无法模式切换

This commit is contained in:
SengokuCola
2025-06-15 01:27:54 +08:00
parent 4b7dae41e5
commit 327580dbec
6 changed files with 197 additions and 75 deletions

View File

@@ -320,11 +320,11 @@ async def clear_temp_emoji() -> None:
logger.info("[清理] 完成")
async def clean_unused_emojis(emoji_dir: str, emoji_objects: List["MaiEmoji"]) -> None:
async def clean_unused_emojis(emoji_dir: str, emoji_objects: List["MaiEmoji"],removed_count:int) -> int:
"""清理指定目录中未被 emoji_objects 追踪的表情包文件"""
if not os.path.exists(emoji_dir):
logger.warning(f"[清理] 目标目录不存在,跳过清理: {emoji_dir}")
return
return removed_count
try:
# 获取内存中所有有效表情包的完整路径集合
@@ -352,6 +352,8 @@ async def clean_unused_emojis(emoji_dir: str, emoji_objects: List["MaiEmoji"]) -
logger.info(f"[清理] 在目录 {emoji_dir} 中清理了 {cleaned_count} 个破损表情包。")
else:
logger.info(f"[清理] 目录 {emoji_dir} 中没有需要清理的。")
return removed_count + cleaned_count
except Exception as e:
logger.error(f"[错误] 清理未使用表情包文件时出错 ({emoji_dir}): {str(e)}")
@@ -564,7 +566,7 @@ class EmojiManager:
self.emoji_objects = [e for e in self.emoji_objects if e not in objects_to_remove]
# 清理 EMOJI_REGISTED_DIR 目录中未被追踪的文件
await clean_unused_emojis(EMOJI_REGISTED_DIR, self.emoji_objects)
removed_count = await clean_unused_emojis(EMOJI_REGISTED_DIR, self.emoji_objects,removed_count)
# 输出清理结果
if removed_count > 0:

View File

@@ -77,10 +77,21 @@ class SubHeartflow:
if self.normal_chat_instance:
logger.info(f"{self.log_prefix} 离开normal模式")
try:
await self.normal_chat_instance.stop_chat() # 调用 stop_chat
logger.debug(f"{self.log_prefix} 开始调用 stop_chat()")
# 添加超时保护,避免无限等待
await asyncio.wait_for(self.normal_chat_instance.stop_chat(), timeout=10.0)
logger.debug(f"{self.log_prefix} stop_chat() 调用完成")
except asyncio.TimeoutError:
logger.warning(f"{self.log_prefix} 停止 NormalChat 超时,强制清理")
# 超时时强制清理
self.normal_chat_instance = None
except Exception as e:
logger.error(f"{self.log_prefix} 停止 NormalChat 监控任务时出错: {e}")
logger.error(traceback.format_exc())
# 出错时也要清理实例
self.normal_chat_instance = None
finally:
logger.debug(f"{self.log_prefix} _stop_normal_chat 完成")
async def _start_normal_chat(self, rewind=False) -> bool:
"""

View File

@@ -161,58 +161,86 @@ class NormalChat:
"""
while True:
try:
async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()):
await asyncio.sleep(0.5) # 每秒检查一次
# 检查任务是否已被取消
if self._chat_task is None or self._chat_task.cancelled():
logger.info(f"[{self.stream_name}] 兴趣监控任务被取消或置空,退出")
break
# 检查任务是否已被取消 - 移动到try块最开始
if self._chat_task is None or self._chat_task.cancelled():
logger.info(f"[{self.stream_name}] 兴趣监控任务被取消或置空,退出")
break
# 检查是否已停用
if self._disabled:
logger.info(f"[{self.stream_name}] 已停用,退出兴趣监控")
break
items_to_process = list(self.interest_dict.items())
if not items_to_process:
continue
await asyncio.sleep(0.5) # 每0.5秒检查一次
# 再次检查取消状态
if self._chat_task is None or self._chat_task.cancelled() or self._disabled:
logger.info(f"[{self.stream_name}] 检测到停止信号,退出")
break
# 并行处理兴趣消息
async def process_single_message(msg_id, message, interest_value, is_mentioned):
"""处理单个兴趣消息"""
try:
# 处理消息
if time.time() - self.start_time > 300:
self.adjust_reply_frequency(duration=300 / 60)
else:
self.adjust_reply_frequency(duration=(time.time() - self.start_time) / 60)
items_to_process = list(self.interest_dict.items())
if not items_to_process:
continue
# print(self.engaging_persons)
# 使用异步上下文管理器处理消息
try:
async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()):
# 在上下文内部再次检查取消状态
if self._chat_task is None or self._chat_task.cancelled() or self._disabled:
logger.info(f"[{self.stream_name}] 在处理上下文中检测到停止信号,退出")
break
await self.normal_response(
message=message,
is_mentioned=is_mentioned,
interested_rate=interest_value * self.willing_amplifier,
)
except Exception as e:
logger.error(
f"[{self.stream_name}] 处理兴趣消息{msg_id}时出错: {e}\n{traceback.format_exc()}"
)
finally:
self.interest_dict.pop(msg_id, None)
# 并行处理兴趣消息
async def process_single_message(msg_id, message, interest_value, is_mentioned):
"""处理单个兴趣消息"""
try:
# 在处理每个消息前检查停止状态
if self._disabled or (self._chat_task and self._chat_task.cancelled()):
return
# 创建并行任务列表
tasks = []
for msg_id, (message, interest_value, is_mentioned) in items_to_process:
task = process_single_message(msg_id, message, interest_value, is_mentioned)
tasks.append(task)
# 处理消息
if time.time() - self.start_time > 300:
self.adjust_reply_frequency(duration=300 / 60)
else:
self.adjust_reply_frequency(duration=(time.time() - self.start_time) / 60)
# 并行执行所有任务,限制并发数量避免资源过度消耗
if tasks:
# 使用信号量控制并发数最多同时处理5个消息
semaphore = asyncio.Semaphore(5)
await self.normal_response(
message=message,
is_mentioned=is_mentioned,
interested_rate=interest_value * self.willing_amplifier,
)
except Exception as e:
logger.error(
f"[{self.stream_name}] 处理兴趣消息{msg_id}时出错: {e}\n{traceback.format_exc()}"
)
finally:
self.interest_dict.pop(msg_id, None)
async def limited_process(task, sem):
async with sem:
await task
# 创建并行任务列表
tasks = []
for msg_id, (message, interest_value, is_mentioned) in items_to_process:
task = process_single_message(msg_id, message, interest_value, is_mentioned)
tasks.append(task)
# 并行执行所有任务,限制并发数量避免资源过度消耗
if tasks:
# 使用信号量控制并发数最多同时处理5个消息
semaphore = asyncio.Semaphore(5)
async def limited_process(task, sem):
async with sem:
await task
limited_tasks = [limited_process(task, semaphore) for task in tasks]
await asyncio.gather(*limited_tasks, return_exceptions=True)
except asyncio.CancelledError:
logger.info(f"[{self.stream_name}] 处理上下文时任务被取消")
break
except Exception as e:
logger.error(f"[{self.stream_name}] 处理上下文时出错: {e}")
await asyncio.sleep(1)
limited_tasks = [limited_process(task, semaphore) for task in tasks]
await asyncio.gather(*limited_tasks, return_exceptions=True)
except asyncio.CancelledError:
logger.info(f"[{self.stream_name}] 兴趣监控任务被取消")
break

View File

@@ -35,14 +35,21 @@ class PromptContext:
"""创建一个异步的临时提示模板作用域"""
# 保存当前上下文并设置新上下文
if context_id is not None:
async with self._context_lock:
if context_id not in self._context_prompts:
self._context_prompts[context_id] = {}
try:
# 添加超时保护,避免长时间等待锁
async with asyncio.wait_for(self._context_lock.acquire(), timeout=5.0):
if context_id not in self._context_prompts:
self._context_prompts[context_id] = {}
self._context_lock.release()
except asyncio.TimeoutError:
logger.warning(f"获取上下文锁超时context_id: {context_id}")
# 超时时直接进入,不设置上下文
context_id = None
# 保存当前协程的上下文值,不影响其他协程
previous_context = self._current_context
# 设置当前协程的新上下文
token = self._current_context_var.set(context_id)
token = self._current_context_var.set(context_id) if context_id else None
else:
# 如果没有提供新上下文,保持当前上下文不变
previous_context = self._current_context
@@ -51,12 +58,17 @@ class PromptContext:
try:
yield self
finally:
# 恢复之前的上下文
if context_id is not None:
if token:
# 恢复之前的上下文,添加异常保护
if context_id is not None and token is not None:
try:
self._current_context_var.reset(token)
else:
self._current_context = previous_context
except Exception as e:
logger.warning(f"恢复上下文时出错: {e}")
# 如果reset失败尝试直接设置
try:
self._current_context = previous_context
except Exception:
pass # 静默忽略恢复失败
async def get_prompt_async(self, name: str) -> Optional["Prompt"]:
"""异步获取当前作用域中的提示模板"""