File size: 4,854 Bytes
3f31583
 
 
 
 
 
2f25a40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3f31583
 
2f25a40
 
 
 
3f31583
 
2f25a40
 
3f31583
 
 
2f25a40
 
3f31583
2f25a40
3f31583
 
 
2f25a40
 
 
 
 
 
 
3f31583
 
 
 
 
2f25a40
3f31583
2f25a40
3f31583
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2f25a40
 
 
3f31583
 
2f25a40
 
 
3f31583
 
 
 
2f25a40
 
3f31583
 
2f25a40
 
 
 
 
 
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
109
110
111
112
113
114
115
116
117
"""Parse all sources β€” research papers, textbooks, and web tutorials β€” into chunks.jsonl.

Source priority:
  1. data/papers/*.pdf     β†’ source_type="paper"    (arxiv IDs as source_id)
  2. data/books/*.pdf      β†’ source_type from CANONICAL_TEXT_SOURCES
  3. data/web/*.txt        β†’ source_type from CANONICAL_TEXT_SOURCES

Output: data/chunks.jsonl  (one chunk per line, ready for embedding)

Usage:
    uv run python scripts/parse_corpus.py
"""
from __future__ import annotations

import json
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

from rich.console import Console
from rich.table import Table

from researchpath.corpus import CANONICAL_RL_PAPERS, CANONICAL_TEXT_SOURCES
from researchpath.parsing import chunk_to_dict, parse_pdf, parse_text

console = Console()
ROOT = Path(__file__).resolve().parents[1]
PAPERS_DIR = ROOT / "data" / "papers"
BOOKS_DIR = ROOT / "data" / "books"
WEB_DIR = ROOT / "data" / "web"
CHUNKS_PATH = ROOT / "data" / "chunks.jsonl"

# Build lookup maps
_PAPER_BY_ID = {p.arxiv_id: p for p in CANONICAL_RL_PAPERS}
_TEXT_BY_FILE = {s.filename: s for s in CANONICAL_TEXT_SOURCES}


def main() -> int:
    table = Table(title="Parse summary")
    table.add_column("Source ID", style="bold")
    table.add_column("Type")
    table.add_column("Tag / Title")
    table.add_column("Chunks", justify="right")
    table.add_column("Chars", justify="right")

    total_chunks = 0
    total_chars = 0

    with open(CHUNKS_PATH, "w", encoding="utf-8") as out:

        # ── 1. Research papers ────────────────────────────────────────────────
        pdfs = sorted(PAPERS_DIR.glob("*.pdf")) if PAPERS_DIR.exists() else []
        if not pdfs:
            console.print("[yellow]No PDFs in data/papers/ β€” skipping paper corpus.[/yellow]")
        for pdf in pdfs:
            paper_meta = _PAPER_BY_ID.get(pdf.stem)
            tag = paper_meta.tag if paper_meta else "?"
            n = chars = 0
            for chunk in parse_pdf(pdf, source_type="paper"):
                out.write(json.dumps(chunk_to_dict(chunk), ensure_ascii=False) + "\n")
                n += 1
                chars += chunk.n_chars
            table.add_row(pdf.stem, "paper", tag, str(n), f"{chars:,}")
            total_chunks += n
            total_chars += chars

        # ── 2. Book / course PDFs ─────────────────────────────────────────────
        book_pdfs = sorted(BOOKS_DIR.glob("*.pdf")) if BOOKS_DIR.exists() else []
        for pdf in book_pdfs:
            src = _TEXT_BY_FILE.get(pdf.name)
            if src is None:
                console.print(f"[yellow]  Skipping unknown book PDF: {pdf.name}[/yellow]")
                continue
            n = chars = 0
            for chunk in parse_pdf(pdf, source_id=src.source_id, source_type=src.source_type):
                out.write(json.dumps(chunk_to_dict(chunk), ensure_ascii=False) + "\n")
                n += 1
                chars += chunk.n_chars
            short_title = src.title[:40] + ("…" if len(src.title) > 40 else "")
            table.add_row(src.source_id, src.source_type, short_title, str(n), f"{chars:,}")
            total_chunks += n
            total_chars += chars

        # ── 3. Web / tutorial text files ──────────────────────────────────────
        web_txts = sorted(WEB_DIR.glob("*.txt")) if WEB_DIR.exists() else []
        for txt in web_txts:
            src = _TEXT_BY_FILE.get(txt.name)
            if src is None:
                console.print(f"[yellow]  Skipping unknown web file: {txt.name}[/yellow]")
                continue
            content = txt.read_text(encoding="utf-8")
            n = chars = 0
            for chunk in parse_text(content, source_id=src.source_id, source_type=src.source_type):
                out.write(json.dumps(chunk_to_dict(chunk), ensure_ascii=False) + "\n")
                n += 1
                chars += chunk.n_chars
            short_title = src.title[:40] + ("…" if len(src.title) > 40 else "")
            table.add_row(src.source_id, src.source_type, short_title, str(n), f"{chars:,}")
            total_chunks += n
            total_chars += chars

    if total_chunks == 0:
        console.print("[red]No sources found. Run fetch_corpus.py / fetch_pdfs.py / fetch_web_sources.py first.[/red]")
        return 1

    console.print(table)
    console.print(
        f"\n[bold green]Wrote {total_chunks} chunks ({total_chars:,} chars) "
        f"to {CHUNKS_PATH.relative_to(ROOT)}[/bold green]"
    )
    return 0


if __name__ == "__main__":
    sys.exit(main())