File size: 3,480 Bytes
702222d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
from __future__ import annotations

import logging
import os

from .manual import discover_metadata_txt, ingest_manuals, resolve_local_mp4
from .metadata import parse_metadata_txt
from .pdf_manual import ingest_pdf_manual
from .pipeline import run_etl


logger = logging.getLogger(__name__)


def batch_build_index(
    *,
    data_root: str,
    runs_root: str = "runs",
    skip_existing_runs: bool = True,
    skip_vlm_if_no_ollama: bool = False,
    include_pdf: bool = False,
    pdf_path: str | None = None,
    vehicle_metadata: dict | None = None,
    pdf_pages_per_chunk: int = 2,
    **etl_kwargs,
) -> dict:
    """
    For each metadata .txt under data_root:
    1) Always upsert manual procedure doc (via full manual scan at end, or per-file ETL metadata)
    2) If local .mp4 exists, run video ETL into runs/<safe_id>/
    """
    txt_paths = discover_metadata_txt(data_root)
    os.makedirs(runs_root, exist_ok=True)

    processed_videos = 0
    skipped_videos = 0
    missing_mp4 = 0
    errors: list[str] = []

    for meta_path in txt_paths:
        mp4 = resolve_local_mp4(meta_path)
        if not mp4:
            missing_mp4 += 1
            continue

        meta = parse_metadata_txt(meta_path)
        safe_id = _safe_run_id(meta_path)
        out_dir = os.path.join(runs_root, safe_id)

        if skip_existing_runs and os.path.isfile(os.path.join(out_dir, "summary.json")):
            skipped_videos += 1
            continue

        try:
            run_etl(
                video_path=mp4,
                metadata_path=meta_path,
                out_dir=out_dir,
                **etl_kwargs,
            )
            processed_videos += 1
        except Exception as e:
            msg = f"{meta_path}: {e}"
            logger.exception(msg)
            errors.append(msg)

    manual_summary = ingest_manuals(
        root_dir=data_root,
        embed_model=etl_kwargs.get("embed_model", "text-embedding-v3"),
        embed_backend=etl_kwargs.get("embed_backend", "cloud"),
        ollama_host=etl_kwargs.get("ollama_host"),
        qdrant_path=etl_kwargs.get("qdrant_path", ".fixsense_qdrant"),
        collection=etl_kwargs.get("collection", "fixsense_rag"),
        use_chroma=etl_kwargs.get("use_chroma", False),
    )

    pdf_summary = None
    if include_pdf and pdf_path:
        pdf_summary = ingest_pdf_manual(
            pdf_path=pdf_path,
            vehicle_metadata=vehicle_metadata,
            pages_per_chunk=pdf_pages_per_chunk,
            embed_model=etl_kwargs.get("embed_model", "text-embedding-v3"),
            embed_backend=etl_kwargs.get("embed_backend", "cloud"),
            ollama_host=etl_kwargs.get("ollama_host"),
            qdrant_path=etl_kwargs.get("qdrant_path", ".fixsense_qdrant"),
            collection=etl_kwargs.get("collection", "fixsense_rag"),
            use_chroma=etl_kwargs.get("use_chroma", False),
        )

    return {
        "data_root": data_root,
        "metadata_files": len(txt_paths),
        "processed_videos": processed_videos,
        "skipped_videos": skipped_videos,
        "missing_mp4": missing_mp4,
        "manual_ingest": manual_summary,
        "pdf_ingest": pdf_summary,
        "errors": errors,
        "skip_vlm_if_no_ollama": skip_vlm_if_no_ollama,
    }


def _safe_run_id(metadata_path: str) -> str:
    base = os.path.splitext(os.path.basename(metadata_path))[0]
    safe = "".join(c if c.isalnum() or c in "-_" else "_" for c in base)
    return safe[:120]