feat(documents): add text pdf ingestion with heading reconstruction
Why: - Support Text PDFs in the same DocumentTree/markdown contract as DOCX. Changes: - PyMuPDF parser, text-layer gate, shared heading heuristics, upload dispatch for .pdf. Impact: - Scanned/image PDFs are rejected at upload; requires pymupdf installed. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
"""Document and strategy API routes.
|
||||
|
||||
Endpoints:
|
||||
POST /documents Upload a .docx file
|
||||
POST /documents/{id}/process Run chunking strategies (stub until Phase 2)
|
||||
POST /documents Upload a .docx / .doc / .pdf file
|
||||
POST /documents/{id}/process Run chunking strategies
|
||||
DELETE /documents/{id} Remove document + vectors
|
||||
GET /strategies List available chunking strategies
|
||||
"""
|
||||
@@ -10,7 +10,6 @@ Endpoints:
|
||||
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,
|
||||
@@ -20,6 +19,7 @@ from src.documents.models import (
|
||||
StrategiesResponse,
|
||||
StrategyInfo,
|
||||
)
|
||||
from src.documents.parser import SUPPORTED_SUFFIXES
|
||||
from src.documents import service
|
||||
|
||||
router = APIRouter()
|
||||
@@ -39,11 +39,15 @@ async def list_documents(offset: int = 0, limit: int = 50):
|
||||
|
||||
@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."""
|
||||
"""Upload a document. 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")
|
||||
|
||||
lower = file.filename.lower()
|
||||
if not any(lower.endswith(s) for s in SUPPORTED_SUFFIXES):
|
||||
raise DocumentProcessingError(
|
||||
f"Only {', '.join(SUPPORTED_SUFFIXES)} files are supported"
|
||||
)
|
||||
|
||||
content = await file.read()
|
||||
if not content:
|
||||
@@ -61,10 +65,7 @@ async def upload_document(file: UploadFile = File(...)):
|
||||
|
||||
@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.
|
||||
"""
|
||||
"""Run selected chunking strategies on an uploaded document."""
|
||||
return service.process_document(doc_id, request)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user