Merge afc branch into dev, prioritizing afc changes and migrating database async modifications from dev

This commit is contained in:
Windpicker-owo
2025-09-27 23:37:40 +08:00
138 changed files with 12183 additions and 5968 deletions

View File

@@ -14,6 +14,7 @@ Chat Frequency Analyzer
- MIN_CHATS_FOR_PEAK: 在一个窗口内需要多少次聊天才能被认为是高峰时段。
- MIN_GAP_BETWEEN_PEAKS_HOURS: 两个独立高峰时段之间的最小间隔(小时)。
"""
import time as time_module
from datetime import datetime, timedelta, time
from typing import List, Tuple, Optional
@@ -72,12 +73,14 @@ class ChatFrequencyAnalyzer:
current_window_end = datetimes[i]
# 合并重叠或相邻的高峰时段
if peak_windows and current_window_start - peak_windows[-1][1] < timedelta(hours=MIN_GAP_BETWEEN_PEAKS_HOURS):
if peak_windows and current_window_start - peak_windows[-1][1] < timedelta(
hours=MIN_GAP_BETWEEN_PEAKS_HOURS
):
# 扩展上一个窗口的结束时间
peak_windows[-1] = (peak_windows[-1][0], current_window_end)
else:
peak_windows.append((current_window_start, current_window_end))
return peak_windows
def get_peak_chat_times(self, chat_id: str) -> List[Tuple[time, time]]:
@@ -100,7 +103,7 @@ class ChatFrequencyAnalyzer:
return []
peak_datetime_windows = self._find_peak_windows(timestamps)
# 将 datetime 窗口转换为 time 窗口,并进行归一化处理
peak_time_windows = []
for start_dt, end_dt in peak_datetime_windows:
@@ -110,7 +113,7 @@ class ChatFrequencyAnalyzer:
# 更新缓存
self._analysis_cache[chat_id] = (time_module.time(), peak_time_windows)
return peak_time_windows
def is_in_peak_time(self, chat_id: str, now: Optional[datetime] = None) -> bool:
@@ -126,7 +129,7 @@ class ChatFrequencyAnalyzer:
"""
if now is None:
now = datetime.now()
now_time = now.time()
peak_times = self.get_peak_chat_times(chat_id)
@@ -137,7 +140,7 @@ class ChatFrequencyAnalyzer:
else: # 跨天
if now_time >= start_time or now_time <= end_time:
return True
return False

View File

@@ -56,7 +56,7 @@ class ChatFrequencyTracker:
now = time.time()
if chat_id not in self._timestamps:
self._timestamps[chat_id] = []
self._timestamps[chat_id].append(now)
logger.debug(f"为 chat_id '{chat_id}' 记录了新的聊天时间: {now}")
self._save_timestamps()

View File

@@ -14,15 +14,16 @@ Frequency-Based Proactive Trigger
- TRIGGER_CHECK_INTERVAL_SECONDS: 触发器检查的周期(秒)。
- COOLDOWN_HOURS: 在同一个高峰时段内触发一次后的冷却时间(小时)。
"""
import asyncio
import time
from datetime import datetime
from typing import Dict, Optional
from src.common.logger import get_logger
from src.chat.chat_loop.proactive.events import ProactiveTriggerEvent
from src.chat.heart_flow.heartflow import heartflow
from src.chat.chat_loop.sleep_manager.sleep_manager import SleepManager
# AFC manager has been moved to chatter plugin
# TODO: 需要重新实现主动思考和睡眠管理功能
from .analyzer import chat_frequency_analyzer
logger = get_logger("FrequencyBasedTrigger")
@@ -39,8 +40,8 @@ class FrequencyBasedTrigger:
一个周期性任务,根据聊天频率分析结果来触发主动思考。
"""
def __init__(self, sleep_manager: SleepManager):
self._sleep_manager = sleep_manager
def __init__(self):
# TODO: 需要重新实现睡眠管理器
self._task: Optional[asyncio.Task] = None
# 记录上次为用户触发的时间,用于冷却控制
# 格式: { "chat_id": timestamp }
@@ -53,19 +54,21 @@ class FrequencyBasedTrigger:
await asyncio.sleep(TRIGGER_CHECK_INTERVAL_SECONDS)
logger.debug("开始执行频率触发器检查...")
# 1. 检查角色是否清醒
if self._sleep_manager.is_sleeping():
logger.debug("角色正在睡眠,跳过本次频率触发检查。")
continue
# 1. TODO: 检查角色是否清醒 - 需要重新实现睡眠状态检查
# 暂时跳过睡眠检查
# if self._sleep_manager.is_sleeping():
# logger.debug("角色正在睡眠,跳过本次频率触发检查。")
# continue
# 2. 获取所有已知的聊天ID
# 注意】这里我们假设所有 subheartflow 的 ID 就是 chat_id
all_chat_ids = list(heartflow.subheartflows.keys())
# 注意AFC管理器已移至chatter插件此功能暂时禁用
# all_chat_ids = list(afc_manager.affinity_flow_chatters.keys())
all_chat_ids = [] # 暂时禁用此功能
if not all_chat_ids:
continue
now = datetime.now()
for chat_id in all_chat_ids:
# 3. 检查是否处于冷却时间内
last_triggered_time = self._last_triggered.get(chat_id, 0)
@@ -74,29 +77,11 @@ class FrequencyBasedTrigger:
# 4. 检查当前是否是该用户的高峰聊天时间
if chat_frequency_analyzer.is_in_peak_time(chat_id, now):
sub_heartflow = await heartflow.get_or_create_subheartflow(chat_id)
if not sub_heartflow:
logger.warning(f"无法为 {chat_id} 获取或创建 sub_heartflow。")
continue
# 5. 检查用户当前是否已有活跃的思考或回复任务
cycle_detail = sub_heartflow.heart_fc_instance.context.current_cycle_detail
if cycle_detail and not cycle_detail.end_time:
logger.debug(f"用户 {chat_id} 的聊天循环正忙(仍在周期 {cycle_detail.cycle_id} 中),本次不触发。")
continue
logger.info(f"检测到用户 {chat_id} 处于聊天高峰期,且聊天循环空闲,准备触发主动思考。")
# 6. 直接调用 proactive_thinker
event = ProactiveTriggerEvent(
source="frequency_analyzer",
reason="User is in a high-frequency chat period."
)
await sub_heartflow.heart_fc_instance.proactive_thinker.think(event)
# 7. 更新触发时间,进入冷却
self._last_triggered[chat_id] = time.time()
# 5. 检查用户当前是否已有活跃的处理任务
# 注意AFC管理器已移至chatter插件此功能暂时禁用
# chatter = afc_manager.get_or_create_chatter(chat_id)
logger.info(f"检测到用户 {chat_id} 处于聊天高峰期但AFC功能已移至chatter插件")
continue
except asyncio.CancelledError:
logger.info("频率触发器任务被取消。")