File size: 2,627 Bytes
b76f199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e561e67
 
 
 
 
 
 
 
b76f199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
732b14f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
80
81
82
83
84
85
86
"""Re-index user-edited section text into the vector store for immediate hierarchical RAG."""

from __future__ import annotations

import logging

from langchain_core.documents import Document

from app.config import settings
from app.ingest.hierarchy import build_hierarchical_documents
from app.ingest.pipeline import stamp_chunks_for_index
from app.vectorstore.factory import get_vectorstore

logger = logging.getLogger(__name__)


def runtime_section_vector_doc_id(report_id: str, section_code: str) -> str:
    """Stable virtual ``doc_id`` for a report section overlay (document + section + paragraph tiers)."""
    return f"runtime-{report_id}-{section_code}"


def upsert_runtime_section_index(
    *,
    tenant_id: str,
    virtual_doc_id: str,
    section_title: str,
    body_text: str,
) -> int:
    """Remove prior chunks for ``virtual_doc_id`` and insert fresh hierarchical rows.

    Returns:
        Number of chunks written (0 if ``body_text`` is empty after stripping).
    """
    vs = get_vectorstore()
    vs.delete_document(virtual_doc_id)
    text = (body_text or "").strip()
    if not text:
        logger.info("runtime RAG: cleared index for doc=%s", virtual_doc_id)
        return 0

    from app.services.document_sanitiser import sanitise_text_for_rag_sync

    text = sanitise_text_for_rag_sync(text, tenant_id=tenant_id)
    if not text:
        logger.info("runtime RAG: empty after sanitisation doc=%s", virtual_doc_id)
        return 0

    label = (section_title or "").strip() or virtual_doc_id
    raw = [Document(page_content=text, metadata={"source": label})]
    hier = build_hierarchical_documents(
        raw,
        doc_id=virtual_doc_id,
        filename=label,
        chunk_size=settings.chunk_size,
        chunk_overlap=settings.chunk_overlap,
    )
    stamped = stamp_chunks_for_index(hier, doc_id=virtual_doc_id, tenant_id=tenant_id)
    vs.add_documents(stamped)
    logger.info(
        "runtime RAG: indexed %d chunks for doc=%s tenant=%s",
        len(stamped),
        virtual_doc_id,
        tenant_id,
    )
    return len(stamped)


async def upsert_runtime_section_index_async(
    *,
    tenant_id: str,
    virtual_doc_id: str,
    section_title: str,
    body_text: str,
) -> int:
    """Non-blocking wrapper for :func:`upsert_runtime_section_index`."""
    from app.async_executor import run_sync_in_executor

    return await run_sync_in_executor(
        lambda: upsert_runtime_section_index(
            tenant_id=tenant_id,
            virtual_doc_id=virtual_doc_id,
            section_title=section_title,
            body_text=body_text,
        )
    )