"""Document and strategy API routes. Endpoints: POST /documents Upload a .docx file POST /documents/{id}/process Run chunking strategies (stub until Phase 2) DELETE /documents/{id} Remove document + vectors GET /strategies List available chunking strategies """ from fastapi import APIRouter, File, UploadFile from src.core.exceptions import DocumentProcessingError from src.core.models import PaginatedResponse, StrategyName from src.documents.models import ( DeleteResponse, DocumentResponse, DocumentListResponse, ProcessRequest, ProcessResponse, StrategiesResponse, StrategyInfo, ) from src.documents import service router = APIRouter() @router.get("/documents", response_model=DocumentListResponse) async def list_documents(offset: int = 0, limit: int = 50): """List all uploaded documents with pagination.""" result = service.list_documents(offset=offset, limit=limit) return DocumentListResponse( items=[DocumentResponse(**doc) for doc in result["items"]], total=result["total"], offset=result["offset"], limit=result["limit"], ) @router.post("/documents", response_model=DocumentResponse, status_code=201) async def upload_document(file: UploadFile = File(...)): """Upload a .docx file. Parses it, stores the document tree in SQLite.""" if not file.filename: raise DocumentProcessingError("No filename provided") if not file.filename.lower().endswith((".docx", ".doc")): raise DocumentProcessingError("Only .docx and .doc files are supported") content = await file.read() if not content: raise DocumentProcessingError("Empty file") doc = service.upload_document(file.filename, content) return DocumentResponse( id=doc["id"], filename=doc["filename"], paragraph_count=doc.get("paragraph_count", 0), chunk_counts=doc.get("chunk_counts", {}), created_at=doc["created_at"], ) @router.post("/documents/{doc_id}/process", response_model=ProcessResponse) async def process_document(doc_id: str, request: ProcessRequest): """Run selected chunking strategies on an uploaded document. Currently returns stub results until Phase 2 implements the strategies. """ return service.process_document(doc_id, request) @router.delete("/documents/{doc_id}", response_model=DeleteResponse) async def delete_document(doc_id: str): """Delete a document and all its chunk vectors from Qdrant.""" deleted = service.delete_document(doc_id) return DeleteResponse(deleted=deleted, document_id=doc_id) @router.get("/strategies", response_model=StrategiesResponse) async def list_strategies(): """List available chunking strategies with descriptions.""" strategies = service.list_strategies() return StrategiesResponse( strategies=[StrategyInfo(**s) for s in strategies] )