File size: 2,958 Bytes
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
"""Compute a prerequisite reading path for a target RL paper.

Usage:
    uv run python scripts/plan.py --target 1710.02298                   # plan for Rainbow, no prior knowledge
    uv run python scripts/plan.py --target 1710.02298 --known 1312.5602  # already know DQN
    uv run python scripts/plan.py --target 1710.02298 --known DQN,A3C   # use tags or arxiv IDs
    uv run python scripts/plan.py --list                                # list all canonical papers
"""
from __future__ import annotations

import argparse
import sys
from pathlib import Path

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

from rich.console import Console
from rich.panel import Panel

from researchpath.corpus import CANONICAL_RL_PAPERS
from researchpath.planning import plan_reading_path

console = Console()
_TAG_TO_ID = {p.tag.lower(): p.arxiv_id for p in CANONICAL_RL_PAPERS}
_ID_TO_PAPER = {p.arxiv_id: p for p in CANONICAL_RL_PAPERS}


def _resolve(token: str) -> str:
    """Accept either an arxiv ID or a paper tag (case-insensitive)."""
    token = token.strip()
    if token in _ID_TO_PAPER:
        return token
    if token.lower() in _TAG_TO_ID:
        return _TAG_TO_ID[token.lower()]
    raise SystemExit(f"Unknown paper: {token!r}. Use --list to see options.")


def _list_papers() -> None:
    console.print("[bold cyan]Canonical RL papers:[/bold cyan]\n")
    for p in CANONICAL_RL_PAPERS:
        console.print(f"  [{p.arxiv_id}]  [bold]{p.tag:20s}[/bold]  ({p.year})  {p.title}")


def main() -> int:
    parser = argparse.ArgumentParser(description="Plan a prerequisite reading path.")
    parser.add_argument("--target", help="arxiv ID or tag of the paper you want to read.")
    parser.add_argument(
        "--known",
        default="",
        help="Comma-separated arxiv IDs or tags you already understand (default: none).",
    )
    parser.add_argument("--list", action="store_true", help="List all canonical papers.")
    args = parser.parse_args()

    if args.list:
        _list_papers()
        return 0

    if not args.target:
        parser.error("--target is required (or pass --list).")

    target_id = _resolve(args.target)
    known_ids = {_resolve(t) for t in args.known.split(",") if t.strip()}

    plan = plan_reading_path(target_id, known_ids=known_ids)
    target = _ID_TO_PAPER[target_id]

    header = f"Reading path for [{target.arxiv_id}] {target.tag}{target.title}"
    if known_ids:
        known_tags = ", ".join(sorted(_ID_TO_PAPER[k].tag for k in known_ids))
        header += f"\nAssuming you already know: {known_tags}"

    console.print(Panel(header, border_style="cyan"))
    console.print()
    console.print(plan.render_text())
    console.print()
    console.print(
        f"[dim]{len(plan.steps)} step(s). Static prerequisite graph; "
        f"add LLM-extracted concept dependencies in a future iteration.[/dim]"
    )
    return 0


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