agent-Specialization/utils/conversation_manager/path_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

94 lines
3.4 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 PathMixin:
"""ConversationManager path mixin 能力 mixin。"""
def _load_host_workspaces(self) -> List[Dict[str, str]]:
"""读取宿主机工作区配置,用于把对话归入 workspace 子目录。"""
try:
path = Path(HOST_WORKSPACES_FILE).expanduser().resolve()
if not path.exists():
return []
raw = json.loads(path.read_text(encoding="utf-8"))
workspaces = raw.get("workspaces") if isinstance(raw, dict) else None
if not isinstance(workspaces, list):
return []
result: List[Dict[str, str]] = []
for item in workspaces:
if not isinstance(item, dict):
continue
workspace_id = str(item.get("workspace_id") or "").strip()
workspace_path = self._normalize_path(item.get("path"))
if workspace_id and workspace_path:
result.append({"workspace_id": workspace_id, "path": str(workspace_path)})
return result
except Exception as exc:
print(f"⚠️ 读取宿主机工作区配置失败: {exc}")
return []
@staticmethod
def _path_is_relative_to(path: Path, parent: Path) -> bool:
try:
path.relative_to(parent)
return True
except ValueError:
return False
def _resolve_workspace_id(self, project_path: Optional[Path]) -> Optional[str]:
"""按 host_workspaces.json 用最长路径前缀匹配 workspace_id。"""
if not project_path:
return None
matches = []
for item in self.host_workspaces:
workspace_path = self._normalize_path(item.get("path"))
if not workspace_path:
continue
if project_path == workspace_path or self._path_is_relative_to(project_path, workspace_path):
matches.append((len(str(workspace_path)), item.get("workspace_id")))
if not matches:
# 未匹配任何工作区时兜底使用 "default"web 模式 / 空配置均适用)
return "default"
matches.sort(reverse=True)
return str(matches[0][1])
def _conversation_dir_for_workspace(self, workspace_id: Optional[str]) -> Path:
return self.conversations_root / workspace_id if workspace_id else self.conversations_root