Spaces:
Sleeping
Sleeping
File size: 4,788 Bytes
2e818da | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | 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
@router.post("/create", response_model=CreateSessionResponse)
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
)
@router.get("/ingest-status/{project_id}")
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] = []
@router.post("/commit")
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,
)
@router.get("/trajectory/{document_id}")
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] = []
@router.post("/clear")
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"}
|