"""多会话持久化管理模块。 每个会话对应一个独立的 JSON 文件存储在 ./sessions/ 目录下。 """ import json import os import uuid from datetime import datetime, timezone from pathlib import Path from typing import Optional from dotenv import load_dotenv load_dotenv() SESSIONS_DIR = Path(os.getenv("SESSIONS_DIR", "./sessions")) def _ensure_dir(): SESSIONS_DIR.mkdir(parents=True, exist_ok=True) def _session_path(session_id: str) -> Path: return SESSIONS_DIR / f"{session_id}.json" def generate_session_id() -> str: return uuid.uuid4().hex[:12] def create_session(name: str = "", agent_state: Optional[dict] = None) -> dict: """创建新会话,返回会话元数据。""" _ensure_dir() sid = generate_session_id() now = _now_iso() data = { "session_id": sid, "session_name": name or f"新建报表 {now[:10]}", "created_at": now, "updated_at": now, "agent_state": agent_state or {}, } with open(_session_path(sid), "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) return data def load_session(session_id: str) -> Optional[dict]: """按 ID 加载会话数据。未找到则返回 None。""" _ensure_dir() fp = _session_path(session_id) if not fp.exists(): return None with open(fp, "r", encoding="utf-8") as f: return json.load(f) def save_session(session_id: str, agent_state: dict, session_name: str = ""): """将会话状态保存(更新)至磁盘。""" _ensure_dir() fp = _session_path(session_id) data = {} if fp.exists(): with open(fp, "r", encoding="utf-8") as f: data = json.load(f) data["session_id"] = session_id if session_name: data["session_name"] = session_name if not data.get("session_name"): data["session_name"] = f"报表 {data.get('created_at', _now_iso())[:10]}" data["updated_at"] = _now_iso() if not data.get("created_at"): data["created_at"] = data["updated_at"] data["agent_state"] = agent_state with open(fp, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) def list_all_sessions() -> list[dict]: """列出所有历史会话(仅摘要,不含完整 agent_state)。""" _ensure_dir() sessions = [] for fp in sorted(SESSIONS_DIR.glob("*.json"), key=os.path.getmtime, reverse=True): try: with open(fp, "r", encoding="utf-8") as f: data = json.load(f) sessions.append({ "session_id": data.get("session_id", fp.stem), "session_name": data.get("session_name", fp.stem), "created_at": data.get("created_at", ""), "updated_at": data.get("updated_at", ""), }) except (json.JSONDecodeError, KeyError): continue return sessions def delete_session(session_id: str) -> bool: """按 ID 删除会话文件。""" _ensure_dir() fp = _session_path(session_id) if fp.exists(): fp.unlink() return True return False def _now_iso() -> str: return datetime.now(timezone.utc).isoformat()