This commit is contained in:
SengokuCola
2025-07-15 17:05:53 +08:00
29 changed files with 480 additions and 376 deletions

View File

@@ -1,6 +1,7 @@
from src.plugin_system.apis import send_api
async def send_loading(chat_id: str, content: str):
await send_api.custom_to_stream(
message_type="loading",
@@ -9,7 +10,8 @@ async def send_loading(chat_id: str, content: str):
storage_message=False,
show_log=True,
)
async def send_unloading(chat_id: str):
await send_api.custom_to_stream(
message_type="loading",
@@ -18,4 +20,3 @@ async def send_unloading(chat_id: str):
storage_message=False,
show_log=True,
)

View File

@@ -125,7 +125,7 @@ class ChatMood:
)
self.last_change_time = 0
# 发送初始情绪状态到ws端
asyncio.create_task(self.send_emotion_update(self.mood_values))
@@ -231,10 +231,10 @@ class ChatMood:
if numerical_mood_response:
_old_mood_values = self.mood_values.copy()
self.mood_values = numerical_mood_response
# 发送情绪更新到ws端
await self.send_emotion_update(self.mood_values)
logger.info(f"[{self.chat_id}] 情绪变化: {_old_mood_values} -> {self.mood_values}")
self.last_change_time = message_time
@@ -308,10 +308,10 @@ class ChatMood:
if numerical_mood_response:
_old_mood_values = self.mood_values.copy()
self.mood_values = numerical_mood_response
# 发送情绪更新到ws端
await self.send_emotion_update(self.mood_values)
logger.info(f"[{self.chat_id}] 情绪回归: {_old_mood_values} -> {self.mood_values}")
self.regression_count += 1
@@ -322,9 +322,9 @@ class ChatMood:
"joy": mood_values.get("joy", 5),
"anger": mood_values.get("anger", 1),
"sorrow": mood_values.get("sorrow", 1),
"fear": mood_values.get("fear", 1)
"fear": mood_values.get("fear", 1),
}
await send_api.custom_to_stream(
message_type="emotion",
content=emotion_data,
@@ -332,7 +332,7 @@ class ChatMood:
storage_message=False,
show_log=True,
)
logger.info(f"[{self.chat_id}] 发送情绪更新: {emotion_data}")
@@ -345,27 +345,27 @@ class MoodRegressionTask(AsyncTask):
async def run(self):
self.run_count += 1
logger.info(f"[回归任务] 第{self.run_count}次检查,当前管理{len(self.mood_manager.mood_list)}个聊天的情绪状态")
now = time.time()
regression_executed = 0
for mood in self.mood_manager.mood_list:
chat_info = f"chat {mood.chat_id}"
if mood.last_change_time == 0:
logger.debug(f"[回归任务] {chat_info} 尚未有情绪变化,跳过回归")
continue
time_since_last_change = now - mood.last_change_time
# 检查是否有极端情绪需要快速回归
high_emotions = {k: v for k, v in mood.mood_values.items() if v >= 8}
has_extreme_emotion = len(high_emotions) > 0
# 回归条件1. 正常时间间隔(120s) 或 2. 有极端情绪且距上次变化>=30s
should_regress = False
regress_reason = ""
if time_since_last_change > 120:
should_regress = True
regress_reason = f"常规回归(距上次变化{int(time_since_last_change)}秒)"
@@ -373,24 +373,28 @@ class MoodRegressionTask(AsyncTask):
should_regress = True
high_emotion_str = ", ".join([f"{k}={v}" for k, v in high_emotions.items()])
regress_reason = f"极端情绪快速回归({high_emotion_str}, 距上次变化{int(time_since_last_change)}秒)"
if should_regress:
if mood.regression_count >= 3:
logger.debug(f"[回归任务] {chat_info} 已达到最大回归次数(3次),停止回归")
continue
logger.info(f"[回归任务] {chat_info} 开始情绪回归 ({regress_reason},第{mood.regression_count + 1}次回归)")
logger.info(
f"[回归任务] {chat_info} 开始情绪回归 ({regress_reason},第{mood.regression_count + 1}次回归)"
)
await mood.regress_mood()
regression_executed += 1
else:
if has_extreme_emotion:
remaining_time = 5 - time_since_last_change
high_emotion_str = ", ".join([f"{k}={v}" for k, v in high_emotions.items()])
logger.debug(f"[回归任务] {chat_info} 存在极端情绪({high_emotion_str}),距离快速回归还需等待{int(remaining_time)}")
logger.debug(
f"[回归任务] {chat_info} 存在极端情绪({high_emotion_str}),距离快速回归还需等待{int(remaining_time)}"
)
else:
remaining_time = 120 - time_since_last_change
logger.debug(f"[回归任务] {chat_info} 距离回归还需等待{int(remaining_time)}")
if regression_executed > 0:
logger.info(f"[回归任务] 本次执行了{regression_executed}个聊天的情绪回归")
else:
@@ -409,11 +413,11 @@ class MoodManager:
return
logger.info("启动情绪管理任务...")
# 启动情绪回归任务
regression_task = MoodRegressionTask(self)
await async_task_manager.add_task(regression_task)
self.task_started = True
logger.info("情绪管理任务已启动(情绪回归)")
@@ -435,7 +439,7 @@ class MoodManager:
# 发送重置后的情绪状态到ws端
asyncio.create_task(mood.send_emotion_update(mood.mood_values))
return
# 如果没有找到现有的mood创建新的
new_mood = ChatMood(chat_id)
self.mood_list.append(new_mood)

View File

@@ -107,7 +107,6 @@ class S4UStreamGenerator:
model_name: str,
**kwargs,
) -> AsyncGenerator[str, None]:
buffer = ""
delimiters = ",。!?,.!?\n\r" # For final trimming
punctuation_buffer = ""

View File

@@ -43,23 +43,24 @@ logger = get_logger("watching")
class WatchingState(Enum):
"""视线状态枚举"""
WANDERING = "wandering" # 随意看
DANMU = "danmu" # 看弹幕
LENS = "lens" # 看镜头
DANMU = "danmu" # 看弹幕
LENS = "lens" # 看镜头
class ChatWatching:
def __init__(self, chat_id: str):
self.chat_id: str = chat_id
self.current_state: WatchingState = WatchingState.LENS # 默认看镜头
self.last_sent_state: Optional[WatchingState] = None # 上次发送的状态
self.state_needs_update: bool = True # 是否需要更新状态
self.last_sent_state: Optional[WatchingState] = None # 上次发送的状态
self.state_needs_update: bool = True # 是否需要更新状态
# 状态切换相关
self.is_replying: bool = False # 是否正在生成回复
self.reply_finished_time: Optional[float] = None # 回复完成时间
self.danmu_viewing_duration: float = 1.0 # 看弹幕持续时间(秒)
self.is_replying: bool = False # 是否正在生成回复
self.reply_finished_time: Optional[float] = None # 回复完成时间
self.danmu_viewing_duration: float = 1.0 # 看弹幕持续时间(秒)
logger.info(f"[{self.chat_id}] 视线管理器初始化,默认状态: {self.current_state.value}")
async def _change_state(self, new_state: WatchingState, reason: str = ""):
@@ -69,7 +70,7 @@ class ChatWatching:
self.current_state = new_state
self.state_needs_update = True
logger.info(f"[{self.chat_id}] 视线状态切换: {old_state.value}{new_state.value} ({reason})")
# 立即发送视线状态更新
await self._send_watching_update()
else:
@@ -86,7 +87,7 @@ class ChatWatching:
"""开始生成回复时调用"""
self.is_replying = True
self.reply_finished_time = None
if look_at_lens:
await self._change_state(WatchingState.LENS, "开始生成回复-看镜头")
else:
@@ -96,35 +97,29 @@ class ChatWatching:
"""生成回复完毕时调用"""
self.is_replying = False
self.reply_finished_time = time.time()
# 先看弹幕1秒
await self._change_state(WatchingState.DANMU, "回复完毕-看弹幕")
logger.info(f"[{self.chat_id}] 回复完毕,将看弹幕{self.danmu_viewing_duration}秒后转为看镜头")
# 设置定时器1秒后自动切换到看镜头
asyncio.create_task(self._auto_switch_to_lens())
async def _auto_switch_to_lens(self):
"""自动切换到看镜头(延迟执行)"""
await asyncio.sleep(self.danmu_viewing_duration)
# 检查是否仍需要切换(可能状态已经被其他事件改变)
if (self.reply_finished_time is not None and
self.current_state == WatchingState.DANMU and
not self.is_replying):
if self.reply_finished_time is not None and self.current_state == WatchingState.DANMU and not self.is_replying:
await self._change_state(WatchingState.LENS, "看弹幕时间结束")
self.reply_finished_time = None # 重置完成时间
async def _send_watching_update(self):
"""立即发送视线状态更新"""
await send_api.custom_to_stream(
message_type="watching",
content=self.current_state.value,
stream_id=self.chat_id,
storage_message=False
message_type="watching", content=self.current_state.value, stream_id=self.chat_id, storage_message=False
)
logger.info(f"[{self.chat_id}] 发送视线状态更新: {self.current_state.value}")
self.last_sent_state = self.current_state
self.state_needs_update = False
@@ -139,11 +134,10 @@ class ChatWatching:
"current_state": self.current_state.value,
"is_replying": self.is_replying,
"reply_finished_time": self.reply_finished_time,
"state_needs_update": self.state_needs_update
"state_needs_update": self.state_needs_update,
}
class WatchingManager:
def __init__(self):
self.watching_list: list[ChatWatching] = []
@@ -156,7 +150,7 @@ class WatchingManager:
return
logger.info("启动视线管理系统...")
self.task_started = True
logger.info("视线管理系统已启动(状态变化时立即发送)")
@@ -169,10 +163,10 @@ class WatchingManager:
new_watching = ChatWatching(chat_id)
self.watching_list.append(new_watching)
logger.info(f"为chat {chat_id}创建新的视线管理器")
# 发送初始状态
asyncio.create_task(new_watching._send_watching_update())
return new_watching
def reset_watching_by_chat_id(self, chat_id: str):
@@ -185,27 +179,24 @@ class WatchingManager:
watching.is_replying = False
watching.reply_finished_time = None
logger.info(f"[{chat_id}] 视线状态已重置为默认状态")
# 发送重置后的状态
asyncio.create_task(watching._send_watching_update())
return
# 如果没有找到现有的watching创建新的
new_watching = ChatWatching(chat_id)
self.watching_list.append(new_watching)
logger.info(f"为chat {chat_id}创建并重置视线管理器")
# 发送初始状态
asyncio.create_task(new_watching._send_watching_update())
def get_all_watching_info(self) -> dict:
"""获取所有聊天的视线状态信息(用于调试)"""
return {
watching.chat_id: watching.get_state_info()
for watching in self.watching_list
}
return {watching.chat_id: watching.get_state_info() for watching in self.watching_list}
# 全局视线管理器实例
watching_manager = WatchingManager()
"""全局视线管理器"""
"""全局视线管理器"""