feat(jobs): add background job support for benchmarks and processing

Why:
- Long benchmark and process runs block HTTP clients; task #20 required async execution.

Changes:
- SQLite jobs table + GET /jobs and GET /jobs/{id}
- POST /benchmarks?background=true and POST /documents/{id}/process?background=true return 202 + job_id
- FastAPI BackgroundTasks execute work in-process; sync paths unchanged

Impact:
- Jobs are lost on server restart (Option A, no external queue)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-22 15:32:33 +03:30
parent 47e4846270
commit 3f9677f6aa
11 changed files with 473 additions and 40 deletions

View File

@@ -2,12 +2,13 @@
Endpoints:
POST /documents Upload a .docx / .doc / .pdf file
POST /documents/{id}/process Run chunking strategies
POST /documents/{id}/process Run chunking strategies; ?background=true enqueues a job
DELETE /documents/{id} Remove document + vectors
GET /strategies List available chunking strategies
"""
from fastapi import APIRouter, File, UploadFile
from fastapi import APIRouter, BackgroundTasks, File, Query, UploadFile
from fastapi.responses import JSONResponse
from src.core.exceptions import DocumentProcessingError
from src.documents.models import (
@@ -21,6 +22,9 @@ from src.documents.models import (
)
from src.documents.parser import SUPPORTED_SUFFIXES
from src.documents import service
from src.jobs import service as jobs_service
from src.jobs.models import JobCreatedResponse
from src.storage import sqlite as db
router = APIRouter()
@@ -63,9 +67,32 @@ 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."""
@router.post("/documents/{doc_id}/process")
async def process_document(
doc_id: str,
request: ProcessRequest,
background_tasks: BackgroundTasks,
background: bool = Query(
False, description="Run in background; returns job_id (HTTP 202)"
),
):
"""Run selected chunking strategies on an uploaded document.
Pass ``background=true`` to enqueue and poll ``GET /jobs/{job_id}``.
"""
if background:
if db.get_document(doc_id) is None:
raise DocumentProcessingError(f"Document not found: {doc_id}")
job = jobs_service.enqueue_process(doc_id, request)
background_tasks.add_task(jobs_service.run_process_job, job["id"])
body = JobCreatedResponse(
job_id=job["id"],
job_type=job["job_type"],
status=job["status"],
poll_url=f"/jobs/{job['id']}",
)
return JSONResponse(status_code=202, content=body.model_dump())
return service.process_document(doc_id, request)