agent-Specialization/utils/conversation_manager/stats_mixin.py
JOJO f661dc6456 refactor(server,modules,utils,frontend): split oversized modules into packages and fix regressions
- Split server/chat.py, server/status.py, server/tasks.py into sub-packages
- Split utils/context_manager.py, utils/conversation_manager.py into mixin packages
- Split modules/file_manager.py, persistent_terminal.py, terminal_ops.py, mcp_client_manager.py into packages
- Split core/main_terminal_parts/context.py into base + mixins
- Split static/src/app/methods/{ui,message,upload,taskPolling,conversation} into sub-modules
- Fix flattened method exports, dynamic import paths, missing cross-module imports
- Add missing permission/network/execution mode constants and _PERMISSION_MODE_LABEL
- Update AGENTS.md, CLAUDE.md and project memories to reflect new structure
2026-06-20 15:54:48 +08:00

124 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# utils/conversation_manager.py - 对话持久化管理器集成Token统计
import json
import os
import time
import tempfile
import threading
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Any
from dataclasses import dataclass
try:
from config import DATA_DIR, HOST_WORKSPACES_FILE
except ImportError:
import sys
from pathlib import Path
project_root = Path(__file__).resolve().parents[1]
if str(project_root) not in sys.path:
sys.path.insert(0, str(project_root))
from config import DATA_DIR, HOST_WORKSPACES_FILE
@dataclass
class ConversationMetadata:
"""对话元数据"""
id: str
title: str
created_at: str
updated_at: str
project_path: Optional[str]
project_relative_path: Optional[str]
thinking_mode: bool
total_messages: int
total_tools: int
run_mode: str = "fast"
model_key: Optional[str] = None
has_images: bool = False
has_videos: bool = False
status: str = "active" # active, archived, error
class StatsMixin:
"""ConversationManager stats mixin 能力 mixin。"""
def get_statistics(self) -> Dict:
"""
获取对话统计信息
Returns:
Dict: 统计信息
"""
try:
index = self._load_index()
# 统计必须基于全量对话避免启动阶段“仅预热最近20条索引”导致总数偏小。
# 这里按需触发一次全量重建;后续统计可复用已保存的完整索引。
try:
total_files = len(self._iter_conversation_files(sort_by_mtime=False))
except Exception:
total_files = 0
if total_files and len(index) < total_files:
rebuilt_full = self._rebuild_index_from_files(max_count=None)
if rebuilt_full:
self._save_index(rebuilt_full)
index = rebuilt_full
total_conversations = len(index)
total_messages = sum(meta.get("total_messages", 0) for meta in index.values())
total_tools = sum(meta.get("total_tools", 0) for meta in index.values())
# 按状态分类
status_count = {}
for metadata in index.values():
status = metadata.get("status", "active")
status_count[status] = status_count.get(status, 0) + 1
# 按思考模式分类
thinking_mode_count = {
"thinking": sum(1 for meta in index.values() if meta.get("thinking_mode")),
"fast": sum(1 for meta in index.values() if not meta.get("thinking_mode"))
}
# 新增Token统计汇总
total_input_tokens = 0
total_output_tokens = 0
token_stats_count = 0
total_user_messages = 0
for conv_id in index.keys():
conversation_data = self.load_conversation(conv_id)
if not conversation_data:
continue
validated = self._validate_token_statistics(conversation_data)
token_stats = validated.get("token_statistics", {}) or {}
total_input_tokens += token_stats.get("total_input_tokens", 0)
total_output_tokens += token_stats.get("total_output_tokens", 0)
token_stats_count += 1
messages = conversation_data.get("messages") or []
total_user_messages += sum(1 for msg in messages if msg.get("role") == "user")
return {
"total_conversations": total_conversations,
"total_messages": total_messages,
"total_user_messages": total_user_messages,
"total_tools": total_tools,
"status_distribution": status_count,
"thinking_mode_distribution": thinking_mode_count,
"token_statistics": {
"total_input_tokens": total_input_tokens,
"total_output_tokens": total_output_tokens,
"total_tokens": total_input_tokens + total_output_tokens,
"conversations_with_stats": token_stats_count
}
}
except Exception as e:
print(f"⌘ 获取统计信息失败: {e}")
return {}
def get_current_conversation_id(self) -> Optional[str]:
"""获取当前对话ID"""
return self.current_conversation_id
def set_current_conversation_id(self, conversation_id: str):
"""设置当前对话ID"""
self.current_conversation_id = conversation_id