Spaces:
Sleeping
Sleeping
| """ | |
| Session registry β maps session_id β Session (agent + conversation history). | |
| In the initial implementation this is a module-level dict (in-process). | |
| Replace with Redis or another distributed store for horizontal scaling. | |
| Each session holds: | |
| agent : GUIDEAgent instance (stateful CMA) | |
| history : ordered list of {role, content} turn dicts | |
| created_at : ISO-8601 UTC timestamp string | |
| """ | |
| from __future__ import annotations | |
| import uuid | |
| from dataclasses import dataclass, field | |
| from datetime import datetime, timezone | |
| from typing import TYPE_CHECKING | |
| from fastapi import HTTPException | |
| if TYPE_CHECKING: | |
| from src.agent.agent import GUIDEAgent | |
| # --------------------------------------------------------------------------- | |
| # Session container | |
| # --------------------------------------------------------------------------- | |
| class Session: | |
| agent: "GUIDEAgent" | |
| history: list[dict] = field(default_factory=list) | |
| # Privacy audit trail β append-only log of every event where data either | |
| # left the process (outbound to Anthropic) or was processed locally. | |
| audit: list[dict] = field(default_factory=list) | |
| created_at: str = field( | |
| default_factory=lambda: datetime.now(timezone.utc).isoformat() | |
| ) | |
| _sessions: dict[str, Session] = {} | |
| # --------------------------------------------------------------------------- | |
| # Registry operations | |
| # --------------------------------------------------------------------------- | |
| def create_session() -> str: | |
| """ | |
| Create a new GUIDEAgent session, register it, and return the session_id. | |
| The session_id is a random UUID-4 string. | |
| """ | |
| from src.agent.agent import GUIDEAgent # local import avoids circular deps | |
| session_id = str(uuid.uuid4()) | |
| _sessions[session_id] = Session(agent=GUIDEAgent(session_id)) | |
| return session_id | |
| def get_session(session_id: str) -> Session: | |
| """ | |
| Return the Session for *session_id*. | |
| Raises HTTP 404 (not KeyError) so callers in route handlers can let the | |
| exception propagate directly to FastAPI without extra wrapping. | |
| """ | |
| session = _sessions.get(session_id) | |
| if session is None: | |
| raise HTTPException( | |
| status_code=404, | |
| detail=f"Session '{session_id}' not found. " | |
| "Create one with POST /api/session/create.", | |
| ) | |
| return session | |
| def append_history(session_id: str, role: str, content: str) -> None: | |
| """Append a turn to the session's conversation history.""" | |
| _sessions[session_id].history.append({"role": role, "content": content}) | |
| def append_audit(session_id: str, entry: dict) -> None: | |
| """Append an event to the session's privacy audit trail.""" | |
| _sessions[session_id].audit.append(entry) | |
| def delete_session(session_id: str) -> None: | |
| """Remove *session_id* from the registry (idempotent).""" | |
| _sessions.pop(session_id, None) | |
| def session_count() -> int: | |
| """Return the number of active sessions (used by the health endpoint).""" | |
| return len(_sessions) | |