Spaces:
Sleeping
Sleeping
File size: 2,727 Bytes
569bceb | 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 | """Canonical paragraph rollout: scan indexed chunks, optionally apply replacement to .docx."""
import logging
from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.rate_limit import check_generate, check_read
from app.db.database import get_db
from app.models.schemas import (
CanonicalApplyRequest,
CanonicalApplyResponse,
CanonicalRolloutRequest,
CanonicalRolloutResponse,
)
from app.services.canonical_rollout import (
apply_canonical_replacement,
scan_canonical_rollout,
)
logger = logging.getLogger(__name__)
router = APIRouter()
@router.post(
"/content/canonical-scan",
response_model=CanonicalRolloutResponse,
summary="Find chunks semantically similar to a canonical paragraph",
)
async def canonical_scan(
request: Request,
body: CanonicalRolloutRequest,
db: AsyncSession = Depends(get_db),
_: None = Depends(check_read),
) -> CanonicalRolloutResponse:
"""Discover candidate chunks for upgrading to an improved standard paragraph.
Uses the same embedding search as the rest of the RAG stack, plus token Jaccard
versus the canonical text to reduce false positives. Proposed text is either
the canonical paragraph or an LLM merge (``adapt_with_llm``) when an API key is set.
Review ``compatibility`` (high / review / low) before any automatic apply policy.
"""
tenant_id: str = request.state.tenant_id
try:
return await scan_canonical_rollout(db, tenant_id, body)
except Exception as exc: # noqa: BLE001
logger.exception("canonical-scan failed: %s", exc)
raise HTTPException(status_code=500, detail="Canonical scan failed") from exc
@router.post(
"/content/canonical-apply",
response_model=CanonicalApplyResponse,
summary="Replace one chunk in a .docx and re-index (destructive layout)",
)
async def canonical_apply(
request: Request,
body: CanonicalApplyRequest,
db: AsyncSession = Depends(get_db),
_: None = Depends(check_generate),
) -> CanonicalApplyResponse:
"""Apply a replacement string for one indexed chunk.
Rebuilds the Word file as plain paragraphs (tables/complex layout are not preserved),
deletes old vector rows for the document, and re-runs ingestion.
**Requires** ``confirm_destructive_docx: true``.
"""
tenant_id: str = request.state.tenant_id
try:
return await apply_canonical_replacement(db, tenant_id, body)
except HTTPException:
raise
except Exception as exc: # noqa: BLE001
logger.exception("canonical-apply failed: %s", exc)
raise HTTPException(status_code=500, detail="Canonical apply failed") from exc
|