File size: 2,033 Bytes
b76f199
 
 
 
 
 
 
 
 
 
 
 
 
732b14f
 
 
 
b76f199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
732b14f
b76f199
 
 
 
 
732b14f
 
 
b76f199
 
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
"""Runtime hierarchical RAG: re-index user-edited section text without a full PDF re-ingest."""

from __future__ import annotations

import logging

from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.ext.asyncio import AsyncSession

from app.api import rate_limit
from app.db.database import get_db
from app.db.models import Report
from app.models.schemas import RuntimeRagSyncBody, RuntimeRagSyncResponse
from app.services.runtime_rag_index import (
    runtime_section_vector_doc_id,
    upsert_runtime_section_index_async,
)

logger = logging.getLogger(__name__)
router = APIRouter()


@router.post(
    "/reports/{report_id}/rag/runtime/sync",
    response_model=RuntimeRagSyncResponse,
    summary="Re-index edited section text for hierarchical RAG",
)
async def sync_runtime_section_rag(
    report_id: str,
    body: RuntimeRagSyncBody,
    request: Request,
    db: AsyncSession = Depends(get_db),
    _rl: None = Depends(rate_limit.check_read),
) -> RuntimeRagSyncResponse:
    """Embed the given text under a virtual document so retrieval sees it immediately.

    Chunks use the same document → section → paragraph hierarchy as file ingest.
    """
    tenant_id: str = request.state.tenant_id
    report = await db.get(Report, report_id)
    if report is None or report.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="Report not found")

    vid = runtime_section_vector_doc_id(report_id, body.section_code)
    title = (body.section_title or "").strip() or body.section_code
    n = await upsert_runtime_section_index_async(
        tenant_id=tenant_id,
        virtual_doc_id=vid,
        section_title=title,
        body_text=body.text,
    )
    from app.retrieval.semantic_cache import invalidate_semantic_cache_for_tenant

    await invalidate_semantic_cache_for_tenant(tenant_id)
    logger.info("runtime RAG sync report=%s section=%s chunks=%d", report_id, body.section_code, n)
    return RuntimeRagSyncResponse(virtual_doc_id=vid, chunks_indexed=n)