File size: 6,361 Bytes
81f30c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env bash
# Cadena completa y reanudable del experimento principal (reemplaza run_eval_v1 -> run_full_index -> run_morning):
#   1. eval-v1       60 problemas × 5 configs, tabla de muestra de Mathlib
#   2. eval-v1-full  `full` y `no_critic` con el índice completo (278 160, ANN IVF_SQ); `no_rag` y las líneas
#                    base no dependen del índice y se copian de eval-v1
#   3. cierre        informes, §6.3 del informe final, resumen en data/eval/results/chain_summary.md, métricas QR con el índice ANN
# Cada etapa se reanuda sola hasta 4 veces (caída del proceso, red) y se corta limpio si la key no tiene crédito;
# volver a lanzar el mismo comando continúa donde quedó.
# Uso: setsid nohup bash scripts/run_chain.sh > data/eval/results/chain.log 2>&1 < /dev/null &
set -u
cd "$(dirname "$0")/.."
export PATH="$HOME/.elan/bin:$PATH" PYTHONUNBUFFERED=1
F() { grep --line-buffered -v "Warning\|Loading\|it/s\|prompt name\|_fork_exec\|WARN lance"; }
step() { echo "=== $1 $(date '+%F %H:%M') mem_avail=$(free -m | awk '/Mem:/{print $7}')MB ==="; }
PROBLEMS=data/eval/problems/eval_v1.jsonl
CONFIGS=full,no_rag,no_critic,baseline_a,baseline_b

key_left() {
  uv run python - <<'PY' 2>/dev/null || echo 0
import httpx
key = [l.split("=", 1)[1].strip() for l in open(".env") if l.startswith("OPENROUTER_API_KEY=")][0]
d = httpx.get("https://openrouter.ai/api/v1/auth/key", headers={"Authorization": "Bearer " + key}, timeout=30).json()["data"]
print(round(d.get("limit_remaining") or 0, 3))
PY
}

pending() {  # pares (config, problema) sin resultado válido en data/eval/runs/$1
  uv run python - "$1" "$PROBLEMS" "$CONFIGS" <<'PY'
import json, sys
from pathlib import Path
run, problems, configs = sys.argv[1:4]
probs = [json.loads(l)["problem_id"] for l in open(problems, encoding="utf-8") if l.strip()]
p = Path("data/eval/runs") / run / "results.jsonl"
rows = [json.loads(l) for l in p.read_text(encoding="utf-8").splitlines() if l.strip()] if p.exists() else []
done = {(r["config"], r["problem_id"]) for r in rows if r["status"] != "error"}
print(sum((c, q) not in done for c in configs.split(",") for q in probs))
PY
}

run_until_done() {  # $1 run-id
  for i in 1 2 3 4; do
    n=$(pending "$1"); [ "$n" = "0" ] && return 0
    left=$(key_left)
    awk "BEGIN{exit !($left > 0.02)}" || { echo "[chain] sin crédito en la key (US\$$left): $n pares pendientes en $1"; return 1; }
    step "$1 intento $i: $n pares pendientes, key US\$$left"
    uv run python -m ragsolver.orchestrator.run --problems "$PROBLEMS" --configs "$CONFIGS" --run-id "$1" --workers 6 2>&1 | F
  done
  [ "$(pending "$1")" = "0" ]
}

# 1. eval-v1 -------------------------------------------------------------------------------------------
run_until_done eval-v1; ok1=$?
step "REPORT eval-v1"
uv run python scripts/eval_report.py --run-id eval-v1 --problems "$PROBLEMS" 2>&1 | F

# 2. eval-v1-full ----------------------------------------------------------------------------------------
ok2=1
if [ $ok1 = 0 ]; then
  step "eval-v1-full: índice ANN y copia de configs independientes del índice"
  uv run python - <<'PY' 2>&1 | F
import json, lancedb
from pathlib import Path
from ragsolver.rag.index import build_ann_index
t = lancedb.connect("data/index/lancedb").open_table("mathlib_full")
if not any("IVF" in str(i.index_type).upper() for i in t.list_indices()):
    build_ann_index(t); print("índice ANN construido")
src = [json.loads(l) for l in open("data/eval/runs/eval-v1/results.jsonl", encoding="utf-8") if l.strip()]
dst_p = Path("data/eval/runs/eval-v1-full/results.jsonl"); dst_p.parent.mkdir(parents=True, exist_ok=True)
dst = [json.loads(l) for l in dst_p.read_text(encoding="utf-8").splitlines() if l.strip()] if dst_p.exists() else []
have = {(r["config"], r["problem_id"]) for r in dst}
new = [r for r in src if r["config"] in ("no_rag", "baseline_a", "baseline_b") and (r["config"], r["problem_id"]) not in have]
with dst_p.open("a", encoding="utf-8") as f:
    f.writelines(json.dumps(r, ensure_ascii=False) + "\n" for r in new)
print(f"copiadas de eval-v1: {len(new)} (ya había {len(dst)})")
PY
  RAGSOLVER_MATHLIB_TABLE=mathlib_full run_until_done eval-v1-full; ok2=$?
  step "REPORT eval-v1-full"
  RAGSOLVER_MATHLIB_TABLE=mathlib_full uv run python scripts/eval_report.py --run-id eval-v1-full --problems "$PROBLEMS" --out docs/eval_report_full_index.md 2>&1 | F
fi

# 3. cierre -------------------------------------------------------------------------------------------------
step "cierre: §6.3 del informe final y PROGRESS"
uv run python scripts/fill_final_report.py --run-id eval-v1 --full-index-run-id eval-v1-full --problems "$PROBLEMS" 2>&1 | F
uv run python - "$ok1" "$ok2" <<'PY'
import json, sys, time
from pathlib import Path
ok1, ok2 = sys.argv[1:3]
L = [f"\n## Resultado automático de la cadena ({time.strftime('%Y-%m-%d %H:%M')})\n", f"eval-v1 completo: {'sí' if ok1 == '0' else 'NO'} · eval-v1-full completo: {'sí' if ok2 == '0' else 'NO'}\n", "| corrida | config | n | pass@1 | Wilson 95 % | llamadas LLM | tokens |", "|---|---|---|---|---|---|---|"]
for run in ("eval-v1", "eval-v1-full"):
    p = Path("data/eval/runs") / run / "summary.json"
    if p.exists():
        for c, s in json.loads(p.read_text(encoding="utf-8")).items():
            L.append(f"| {run} | {c} | {s['n']} | {s['pass@1']} | {s['pass@1_wilson95']} | {s['llm_calls_mean']} | {s['tokens_mean']} |")
L += ["", "Detalle: `docs/eval_report.md`, `docs/eval_report_full_index.md`, `docs/final_report.md` §6.3. Log: `data/eval/results/chain.log`.", ""]
# docs/PLAN_CORRECCIONES.md Fase 3, tarea 5: el resumen automático ya no se agrega a
# docs/PROGRESS.md (documentación editada a mano; auto-escribirla la desincroniza).
Path("data/eval/results/chain_summary.md").open("a", encoding="utf-8").write("\n".join(L))
print("data/eval/results/chain_summary.md actualizado")
PY
if [ ! -f data/eval/results/fase1_full_ann_mathlibqr_100q.json ]; then
  step "MathlibQR 100q sobre el índice completo con ANN (sin costo de API)"
  uv run python -m ragsolver.rag.evaluate --table mathlib_full --sample 100 --out data/eval/results/fase1_full_ann_mathlibqr_100q.json 2>&1 | F | tail -2
  uv run python scripts/eval_report.py --run-id eval-v1 --problems "$PROBLEMS" 2>&1 | F
fi
echo "CHAIN DONE $(date '+%F %H:%M') eval-v1=$ok1 eval-v1-full=$ok2"