File size: 6,845 Bytes
1a8377a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1742ac7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1a8377a
 
1742ac7
1a8377a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1742ac7
1a8377a
 
1742ac7
 
 
1a8377a
1742ac7
 
 
1a8377a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1742ac7
 
1a8377a
 
 
 
 
 
 
1742ac7
1a8377a
 
 
 
 
 
 
 
 
 
 
1742ac7
1a8377a
 
 
 
 
1742ac7
1a8377a
 
 
1742ac7
 
 
1a8377a
 
 
1742ac7
 
1a8377a
 
 
 
 
1742ac7
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
"""Phase 6 pre-flight on free CPU, second pass: how do we get a working lm-eval without moving the image?

The first pass (12:13Z) answered a question nobody had asked and killed two assumptions at once:

* `pip install --user --no-deps lm-eval==0.4.13` **cannot be imported at all** -- `lm_eval/api/metrics.py:11`
  does `import sacrebleu` at module scope, so `--tasks list` exits 1 with `ModuleNotFoundError`. The
  `--no-deps` flag was E-045/12's answer to "a resolving install might drop a different transformers into
  ~/.local"; it bought that safety at the price of a harness that does not start. Review-found, CPU-proved.
* The rest of this file's own first draft then died on `%`-formatting a template that contains `%s` specs.

So the real question is which install keeps the image's `transformers 5.0.0` and `torch` *and* gets the
metric libraries. lm-eval declares `transformers>=4.1`, which 5.0.0 already satisfies -- pip leaves a
satisfied requirement alone -- so a resolving install may be safe here. This measures it rather than
arguing: it snapshots the versions and file locations of the packages Phase 6 depends on, installs with
deps, snapshots again, and only then parses `--tasks list` and dumps the nine task configs.

**No eval data is touched and none can be:** installing, listing task ids and reading YAML configs never
calls `datasets.load_dataset`; that happens only when a task is requested against a model, which is
Phase 6's own step (§3.3).
"""
import json
import os
import re
import subprocess
import sys

os.chdir("/kaggle/working")
PIN = "0.4.13"
TASKS = ["arc_challenge", "arc_easy", "hellaswag", "mmlu", "piqa", "truthfulqa_mc1",
         "truthfulqa_mc2", "winogrande", "gsm8k"]
PRIMARY_METRIC = {"arc_challenge": "acc,none", "arc_easy": "acc,none", "hellaswag": "acc,none",
                  "mmlu": "acc,none", "piqa": "acc,none", "truthfulqa_mc1": "acc,none",
                  "truthfulqa_mc2": "acc,none", "winogrande": "acc,none",
                  "gsm8k": "exact_match,flexible-extract"}
WATCH = ["transformers", "torch", "numpy", "pandas", "datasets", "evaluate", "accelerate", "scipy",
         "sympy", "sacrebleu", "lm_eval"]

SNAPSHOT = """
import importlib.metadata as md, json, os, sys
out = {}
for name in %s:
    try:
        mod = __import__(name)
        where = getattr(mod, "__file__", None) or "?"
    except Exception:
        where = None
    try:
        ver = md.version(name.replace("_", "-"))
    except Exception:
        ver = getattr(__import__(name), "__version__", None) if where else None
    out[name] = {"version": str(ver) if ver else None,
                 "file": where, "user_dir": bool(where and "/.local/" in where)}
print("SNAPSHOT " + json.dumps(out, default=str))
""" % (WATCH,)


def sh(argv, label, timeout=2400):
    print("=== " + label, flush=True)
    p = subprocess.run(argv, capture_output=True, text=True, timeout=timeout)
    out = (p.stdout or "") + (p.stderr or "")
    print(label, "rc", p.returncode, "chars", len(out), flush=True)
    return p.returncode, out


def snapshot(label):
    open("snap.py", "w").write(SNAPSHOT)
    rc, out = sh([sys.executable, "snap.py"], "snapshot_" + label, 600)
    line = next((l for l in out.splitlines() if l.startswith("SNAPSHOT ")), "")
    d = json.loads(line[len("SNAPSHOT "):]) if line else {}
    for k, v in sorted(d.items()):
        print("   %-12s %-10s %s" % (k, v["version"], ("<USER>" if v["user_dir"] else v["file"])),
              flush=True)
    return d


def parse_ids(text):
    return set(re.findall(r"^\s*(?:-\s*)?([a-z][a-z0-9_]{2,})\s*$", text, re.M))


before = snapshot("before")

rc, out = sh([sys.executable, "-m", "pip", "install", "--user", "--quiet",
              "lm-eval==" + PIN], "pip_install_with_deps", 2400)
print("   ", out.strip()[-400:], flush=True)
after = snapshot("after")
moved = {k: (before.get(k, {}).get("version"), v["version"])
         for k, v in after.items() if before.get(k, {}).get("version") != v["version"]
         and k != "lm_eval"}
print("PIP_MOVED", json.dumps(moved, default=str), flush=True)
assert after["transformers"]["version"] == "5.0.0", "transformers moved off 5.0.0"
assert after["transformers"]["file"] == before["transformers"]["file"], "transformers moved location"

rc, listed = sh([sys.executable, "-m", "lm_eval", "--tasks", "list"], "tasks_list", 1200)
ids = parse_ids(listed)
print("PARSED_IDS", len(ids), "all nine present:", all(t in ids for t in TASKS), flush=True)
if not ids:
    print("RAW_TAIL", repr(listed[-1500:]), flush=True)

open("taskconfig.py", "w").write("""
import json
from lm_eval.tasks import TaskManager
tm = TaskManager()
try:
    out = {"index_type": str(type(next(iter(tm.task_index.values()))))}
except Exception as e:
    out = {"index_type": "empty or unreachable: " + type(e).__name__}
recs = {}
for t in %s:
    r = {"registered": t in tm.task_index}
    try:
        item = tm.task_index[t]
        cfg = item if isinstance(item, dict) else getattr(item, "config", {})
        r["keys"] = sorted(cfg)[:14]
        r["num_fewshot"] = cfg.get("num_fewshot")
        r["metric_list"] = [(m.get("metric_uri") if isinstance(m, dict) else m)
                            for m in (cfg.get("metric_list") or [])]
        r["dataset_path"] = cfg.get("dataset_path")
        r["dataset_name"] = cfg.get("dataset_name")
        r["test_split"] = cfg.get("test_split")
        r["validation_split"] = cfg.get("validation_split")
        r["fewshot_split"] = cfg.get("fewshot_split")
    except Exception as e:
        r["error"] = "%%s: %%s" %% (type(e).__name__, str(e)[:220])
    recs[t] = r
print("TASKCONFIG " + json.dumps({"meta": out, "tasks": recs}, default=str))
""" % (TASKS,))
rc, tc = sh([sys.executable, "taskconfig.py"], "task_configs", 1200)
line = next((l for l in tc.splitlines() if l.startswith("TASKCONFIG ")), "")
info = json.loads(line[len("TASKCONFIG "):]) if line else {"meta": {}, "tasks": {}}
cfgs = info.get("tasks", {})
print("TASKCONFIG_META", json.dumps(info.get("meta"), default=str), flush=True)
for t in TASKS:
    c = cfgs.get(t, {})
    m = PRIMARY_METRIC[t]
    print("TASK %-16s fewshot=%-6s test=%-10s val=%-8s metric %-34s declared=%s" % (
        t, c.get("num_fewshot"), c.get("test_split"), c.get("validation_split"), m,
        m in (c.get("metric_list") or [])), flush=True)

print("HARNESS_PROBE_JSON", json.dumps({
    "pip_moved": moved, "tasks_listed": len(ids), "all_present": all(t in ids for t in TASKS),
    "primary_declared": {t: PRIMARY_METRIC[t] in (cfgs.get(t) or {}).get("metric_list", [])
                         for t in TASKS}, "configs": cfgs}, default=str)[:6000], flush=True)
ok = bool(ids) and all(t in ids for t in TASKS) and bool(cfgs)
print("VERDICT P6_HARNESS_PROBE", "OK" if ok else "PROBLEM", flush=True)
raise SystemExit(0 if ok else 6)