Spaces:
Sleeping
Sleeping
| from typing import Any, Dict, List | |
| from fastapi import APIRouter | |
| from pydantic import BaseModel | |
| from app.agents.brain_agent import BrainAgent | |
| from app.rag.chromadb_client import ChromaDBClient | |
| from app.schemas.session import Intention, Session | |
| from app.services.graph_state import GraphStateManager | |
| from app.services.project_service import ProjectService | |
| from app.services.student_memory import StudentMemoryService | |
| from app.websockets.handlers import get_db, get_graph_manager, _journal | |
| router = APIRouter(prefix="/session", tags=["session"]) | |
| _brain = BrainAgent() | |
| _memory = StudentMemoryService() | |
| _project_service = ProjectService() | |
| # In-progress ingestion status | |
| _ingest_status: Dict[str, str] = {} # project_id -> "indexing" | "extracting" | "ready" | "error" | |
| class CreateSessionRequest(BaseModel): | |
| topic: str | |
| intention: Intention | |
| class CreateSessionResponse(BaseModel): | |
| project_id: str | |
| topic: str | |
| intention: Intention | |
| def create_session(req: CreateSessionRequest): | |
| project = _project_service.create(req.topic or "Study Project", str(req.intention.value)) | |
| _ingest_status[project.project_id] = "indexing" | |
| return CreateSessionResponse( | |
| project_id=project.project_id, topic=req.topic, intention=req.intention | |
| ) | |
| def ingest_status(project_id: str): | |
| return {"status": _ingest_status.get(project_id, "unknown")} | |
| def set_ingest_status(project_id: str, status: str) -> None: | |
| _ingest_status[project_id] = status | |
| class CommitRequest(BaseModel): | |
| project_id: str | |
| topic: str | |
| intention: str | |
| nodes: List[Dict[str, Any]] | |
| content_files: List[str] = [] | |
| document_id: str = "" | |
| file_ids: List[str] = [] | |
| async def commit_session(req: CommitRequest): | |
| """Kept for compatibility -> the frontend no longer calls this directly. | |
| Session History is now committed automatically as part of Push | |
| (COMMIT_PROJECT, see handlers.py's commit_project_snapshot() call), | |
| which calls the same commit_project_snapshot() this route delegates to. | |
| """ | |
| from app.services.project_commit import commit_project_snapshot | |
| return await commit_project_snapshot( | |
| req.project_id, req.topic, req.intention, req.nodes, | |
| req.content_files, req.document_id, req.file_ids, | |
| ) | |
| def get_trajectory(document_id: str): | |
| """Persistent learning trajectory for a paper (for the Evaluation window).""" | |
| from app.services.memory_service import MemoryService | |
| return {"trajectory": MemoryService().read_trajectory(document_id)} | |
| class ClearRequest(BaseModel): | |
| project_id: str | |
| document_id: str = "" | |
| file_ids: List[str] = [] | |
| def clear_session(req: ClearRequest): | |
| """Wipe everything tied to this session/document -> persistent and in-memory. | |
| A session is exactly one input document set, so clearing means starting over | |
| with nothing: no curriculum tree, no lessons, no chunks, no uploaded files. | |
| Cognee's cross-session student-memory profile is intentionally NOT touched -> | |
| it's meant to persist and grow across documents, not reset with them. | |
| """ | |
| import shutil | |
| from pathlib import Path | |
| from app.rag.evidence_ingestion import EvidenceIngestionService | |
| from app.services.annotation_service import get_annotation_service | |
| from app.services.memory_service import MemoryService | |
| from app.services.project_files import project_upload_dir | |
| _journal.clear_session(req.project_id) | |
| mgr = get_graph_manager() | |
| mgr.clear_session(req.project_id) | |
| EvidenceIngestionService().reset_project(req.project_id) | |
| _ingest_status.pop(req.project_id, None) | |
| base = Path.home() / ".studybuddy" | |
| session_file = base / "sessions" / f"{req.project_id}.json" | |
| if session_file.exists(): | |
| session_file.unlink() | |
| document_id = req.document_id | |
| if document_id: | |
| for p in ( | |
| base / "sessions" / f"doc_{document_id}.json", | |
| base / "graphs" / f"doc_{document_id}.json", | |
| ): | |
| if p.exists(): | |
| p.unlink() | |
| get_annotation_service().delete_for_document(document_id) | |
| MemoryService().flush_cluster(document_id) | |
| # document_id is a combined hash of the whole file set, not a single PDF's cache | |
| # filename -> the per-file PDF cache is keyed by file_ids instead. | |
| for file_id in req.file_ids: | |
| p = base / "pdfs" / f"{file_id}.pdf" | |
| if p.exists(): | |
| p.unlink() | |
| # This session's own upload folder -> never a shared directory. | |
| shutil.rmtree(project_upload_dir(req.project_id), ignore_errors=True) | |
| return {"status": "cleared"} | |