File size: 2,040 Bytes
9009a09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
"""One-off migration: repair PDF font-map damage in the existing RAG index.

The chunks were extracted before `controlai_rag.textfix` existed, so the damage
is baked into `data/rag_index/chunks.json`. The corruption is deterministic, so
the chunks can be repaired in place -- no re-extraction of the source PDFs, and
chunk ids stay stable, which keeps the dense index aligned.

Rebuilds BM25 here; run `python -m controlai_rag.retriever --build` afterwards
to regenerate the embeddings from the repaired text.
"""

from __future__ import annotations

import json
import pickle
import shutil
import sys
from pathlib import Path

PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path:
    sys.path.insert(0, str(PROJECT_ROOT))

from controlai_rag.index import INDEX_DIR, tokenize_corpus
from controlai_rag.textfix import looks_damaged, repair


def main() -> int:
    chunks_path = INDEX_DIR / "chunks.json"
    chunks = json.loads(chunks_path.read_text(encoding="utf-8"))

    backup = chunks_path.with_suffix(".json.pre-textfix")
    if not backup.exists():
        shutil.copy2(chunks_path, backup)
        print(f"Backed up original to {backup.name}")

    damaged = repaired = 0
    for chunk in chunks:
        text = chunk.get("text", "")
        if not looks_damaged(text):
            continue
        damaged += 1
        fixed = repair(text)
        if fixed != text:
            chunk["text"] = fixed
            repaired += 1

    print(f"{damaged} damaged chunks found, {repaired} repaired, {len(chunks)} total")
    chunks_path.write_text(json.dumps(chunks, ensure_ascii=False, indent=2), encoding="utf-8")

    from rank_bm25 import BM25Okapi

    print("Rebuilding BM25 over the repaired text ...")
    bm25 = BM25Okapi([tokenize_corpus(c["text"]) for c in chunks])
    (INDEX_DIR / "bm25.pkl").write_bytes(pickle.dumps(bm25))
    print("Done. Now run: python -m controlai_rag.retriever --build")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())