from __future__ import annotations import json import os import uuid from datetime import datetime, timezone from pathlib import Path from typing import Literal _DEFAULT_BASE = Path(__file__).parent.parent / "sessions" _SEED: dict = { "current_step": 0, "route": {}, "selected_ids": [], "indicator_defs": {}, "bind_results": {}, "artifacts": {}, "trace": [], } def new_session( input_mode: Literal["message", "chips"], question: str = "", base_dir: Path | None = None, ) -> str: base = Path(base_dir) if base_dir else _DEFAULT_BASE session_id = str(uuid.uuid4()) session_dir = base / session_id session_dir.mkdir(parents=True, exist_ok=True) state = { "session_id": session_id, "created_at": datetime.now(timezone.utc).isoformat(), "input_mode": input_mode, "question": question, **_SEED, } _write_json(session_dir / "state.json", state) return session_id def write_state( session_id: str, patch: dict, base_dir: Path | None = None, ) -> None: base = Path(base_dir) if base_dir else _DEFAULT_BASE state_path = base / session_id / "state.json" state = json.loads(state_path.read_text()) for key, value in patch.items(): if key == "bind_results" and isinstance(value, dict): state.setdefault("bind_results", {}).update(value) else: state[key] = value _write_json(state_path, state) def load_state(session_id: str, base_dir: Path | None = None) -> dict: base = Path(base_dir) if base_dir else _DEFAULT_BASE return json.loads((base / session_id / "state.json").read_text()) def _write_json(path: Path, data: dict) -> None: tmp = path.with_suffix(".json.tmp") tmp.write_text(json.dumps(data, indent=2, ensure_ascii=False)) os.replace(tmp, path)