document_agent / backend /app /api /actions.py
Jai-rathore29's picture
Deploy: DocAgent backend (deterministic date-anomaly fix)
f65e025
Raw
History Blame Contribute Delete
3.13 kB
"""On-demand actions and export endpoints.
These let the frontend trigger individual functions (re-extract with a chosen
schema, re-summarize) and download results — independent of the chat agent.
"""
from __future__ import annotations
from fastapi import APIRouter, HTTPException, Query
from fastapi.responses import Response
from app.core.logging import get_logger
from app.schemas.documents import Classification, ExtractionResult
from app.schemas.templates import all_template_names
from app.services import (
anomaly,
classification,
export,
extraction,
storage,
summary,
vectorstore,
)
log = get_logger(__name__)
router = APIRouter(prefix="/api/documents", tags=["actions"])
@router.post("/{doc_id}/extract", response_model=ExtractionResult)
async def re_extract(doc_id: str, schema: str | None = Query(None),
provider: str | None = Query(None)):
detail = storage.get(doc_id)
if not detail:
raise HTTPException(404, "Document not found")
chunks = await vectorstore.query(doc_id, "key fields", k=40)
doc_type = detail.classification.doc_type if detail.classification else None
# `schema` is an OPTIONAL user-chosen framing; "auto" (or unset) keeps the
# extractor fully adaptive.
hint = schema if (schema and schema.lower() != "auto") else None
result = await extraction.extract(detail.markdown or "", doc_type, chunks, provider,
schema_hint=hint)
detail.extraction = result
detail.anomalies = await anomaly.detect(detail.markdown or "", result, doc_type, provider)
storage.save(detail)
return result
@router.post("/{doc_id}/classify", response_model=Classification)
async def re_classify(doc_id: str, provider: str | None = Query(None)):
detail = storage.get(doc_id)
if not detail:
raise HTTPException(404, "Document not found")
c = await classification.classify(detail.markdown or "", provider)
detail.classification = c
storage.save(detail)
return c
@router.post("/{doc_id}/summarize")
async def re_summarize(doc_id: str, provider: str | None = Query(None)):
detail = storage.get(doc_id)
if not detail:
raise HTTPException(404, "Document not found")
s = await summary.summarize(detail.markdown or "", provider)
detail.summary = s
storage.save(detail)
return {"summary": s}
@router.get("/{doc_id}/export")
async def export_document(doc_id: str, format: str = Query("json")):
detail = storage.get(doc_id)
if not detail:
raise HTTPException(404, "Document not found")
fmt = format.lower()
if fmt not in export.EXPORTERS:
raise HTTPException(400, f"Unsupported format. Use one of {list(export.EXPORTERS)}")
fn, media, ext = export.EXPORTERS[fmt]
data = fn(detail)
safe = (detail.filename or doc_id).rsplit(".", 1)[0]
return Response(
content=data, media_type=media,
headers={"Content-Disposition": f'attachment; filename="{safe}.{ext}"'},
)
@router.get("/-/schemas")
async def list_schemas():
return {"schemas": all_template_names()}