Skip to content

Commit b7eb4d6

Browse files
JuliaEdomclaude
andcommitted
feat(autotune): S7 cycle orchestrator + verifier seam
run_cycle.py drives dataset → train → eval → markdown report from one TOML config, so a cycle stops being four hand-typed commands whose flags drifted between runs. Two flags mattered enough to pin in tests: `--fewshot-top-k 0` (the tuning replaces few-shot; anything else is a train/inference mismatch) and unique `--report-suffix` per target (two runs sharing one write the same file and erase each other). The report refuses to quote raw EA alone. The v10 base run has 47 transport holes, so its raw EA reads 37.0% against 48.4% on the 153 questions it really answered; the markdown carries answered/exceptions next to every number and, for two targets, a paired comparison over the questions both answered. That paired block reproduces the hand-rolled scoring exactly — tuned 55.6% vs base 48.4%, +7.2 pp, fixed 24 / broke 13, McNemar exact p = 0.099 — which is an independent second implementation agreeing, not a copy. `train` is honest about not being local: mode="remote" prints the runbook pointer instead of pretending a Colab/Kaggle session can be started here. nl_sql.autotune.verifiers is the answer → pass/fail seam the track needs for the S6 levers (execution-filter of gold, teacher normalisation). It mirrors the eval runner deliberately: candidate SQL through the AST guards, gold unguarded, comparison through safe_compare_pred so a candidate that never executed cannot be blessed by an empty gold (the qid-518 class, pinned by a mutation-checked test). Config is TOML, not the YAML the plan sketched: tomllib is stdlib, so this adds no dependency and no type stubs, and comments still survive. Also un-breaks CI: colab_serve.ipynb landed in efcf46e with an unused import and an unguarded open(), so `ruff check`/`ruff format --check` have been red at HEAD since. Fixed in place, plus a note that the vLLM path in that cell is dead and should not be retried. Gate §7.3 green: ruff, format, mypy strict, pytest 550 passed / 14 skipped cov 91.05%, hygiene, no_raw_compare. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent efcf46e commit b7eb4d6

7 files changed

Lines changed: 1117 additions & 24 deletions

File tree

‎configs/autotune/cycle.toml‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Autotune cycle config — plan_autotune S7.
2+
#
3+
# .venv/Scripts/python.exe scripts/autotune/run_cycle.py --dry-run
4+
# .venv/Scripts/python.exe scripts/autotune/run_cycle.py --only eval,report
5+
#
6+
# TOML rather than YAML: tomllib is stdlib (no new dependency, no type stubs)
7+
# and still allows the comments that make this file readable.
8+
9+
name = "v10"
10+
11+
[dataset]
12+
# Rebuilds data/autotune/{train,val}.jsonl from data/bird_train.parquet.
13+
# Off by default: the v10 adapter was trained on the dataset already on disk,
14+
# and rebuilding it silently invalidates that provenance.
15+
enabled = false
16+
17+
[train]
18+
# local — run train_qlora.py on this machine (needs CUDA; bnb-4bit is CUDA-only)
19+
# remote — the real path of this track: Kaggle/Colab notebook, driven by hand
20+
# skip — reuse an adapter that already exists
21+
mode = "remote"
22+
adapter = ".tmp/v10_adapter"
23+
runbook = "plan_autotune.md → 'RUNBOOK добивки 47 вопросов'"
24+
# Used only when mode = "local".
25+
args = ["--epochs", "1", "--val-rows", "64"]
26+
27+
[eval]
28+
config = "E"
29+
n = 200
30+
seed = 0
31+
provider = "local_vllm"
32+
# The tuning replaces few-shot; keep it at 0 so train and inference agree.
33+
fewshot_top_k = 0
34+
explain_provider = "mistral"
35+
reports_root = "eval/reports"
36+
37+
[[eval.targets]]
38+
label = "tuned"
39+
model = "sqltuned"
40+
suffix = "student-tuned"
41+
42+
[[eval.targets]]
43+
label = "base"
44+
# The training base, 4-bit — NOT Qwen/Qwen2.5-Coder-7B-Instruct.
45+
model = "unsloth/qwen2.5-coder-7b-instruct-bnb-4bit"
46+
suffix = "student-base"
47+
48+
[report]
49+
out = ".tmp/autotune_cycle_report.md"
50+
51+
# Reference points the verdict is read against (EA %, n=200, config E, seed 0).
52+
[report.anchors]
53+
"codestral (product)" = 61.5
54+
"mimo-v2.5-free" = 68.5
55+
"Opus 4.8 max" = 79.5

‎scripts/autotune/colab/colab_serve.ipynb‎

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,8 @@
11
{
2-
"nbformat": 4,
3-
"nbformat_minor": 5,
4-
"metadata": {
5-
"colab": {
6-
"provenance": []
7-
},
8-
"kernelspec": {
9-
"display_name": "Python 3",
10-
"name": "python3"
11-
},
12-
"language_info": {
13-
"name": "python"
14-
},
15-
"accelerator": "GPU"
16-
},
172
"cells": [
183
{
194
"cell_type": "markdown",
5+
"id": "7fb27b941602401d91542211134fc71a",
206
"metadata": {},
217
"source": [
228
"# NL_SQL autotune — Colab SERVE (tuned student behind a public URL)\n",
@@ -38,6 +24,7 @@
3824
{
3925
"cell_type": "code",
4026
"execution_count": null,
27+
"id": "acae54e37e7d407bbb7b55eff062a284",
4128
"metadata": {},
4229
"outputs": [],
4330
"source": [
@@ -47,12 +34,13 @@
4734
"DRIVE_DIR = \"/content/drive/MyDrive/nlsql_autotune\" # kaggle.json persists here\n",
4835
"ADAPTER_LOCAL = \"/content/nlsql_adapter\"\n",
4936
"SERVE_HOURS = 8.0\n",
50-
"PORT = 8000\n"
37+
"PORT = 8000"
5138
]
5239
},
5340
{
5441
"cell_type": "code",
5542
"execution_count": null,
43+
"id": "9a63283cbaf04dbcab1f6479b197f3a8",
5644
"metadata": {},
5745
"outputs": [],
5846
"source": [
@@ -78,12 +66,13 @@
7866
" up = files.upload()\n",
7967
" cred.write_bytes(next(iter(up.values())))\n",
8068
" drive_cred.write_bytes(cred.read_bytes())\n",
81-
"cred.chmod(0o600)\n"
69+
"cred.chmod(0o600)"
8270
]
8371
},
8472
{
8573
"cell_type": "code",
8674
"execution_count": null,
75+
"id": "8dd0d8092fe74a7c96281538738b07e2",
8776
"metadata": {},
8877
"outputs": [],
8978
"source": [
@@ -106,25 +95,36 @@
10695
"adapter_dir = str(hits[0].parent)\n",
10796
"if not Path(adapter_dir, \"adapter_model.safetensors\").exists():\n",
10897
" raise RuntimeError(f\"adapter_model.safetensors missing next to {hits[0]}\")\n",
109-
"print(f\"adapter_dir resolved: {adapter_dir}\", flush=True)\n"
98+
"print(f\"adapter_dir resolved: {adapter_dir}\", flush=True)"
11099
]
111100
},
112101
{
113102
"cell_type": "code",
114103
"execution_count": null,
104+
"id": "72eea5119410473aa328ad9291626812",
115105
"metadata": {},
116106
"outputs": [],
117107
"source": [
118108
"import json\n",
119-
"import os\n",
120109
"import re\n",
121110
"import subprocess\n",
122111
"import sys\n",
123112
"import time\n",
124113
"import urllib.request\n",
125114
"from pathlib import Path\n",
126115
"\n",
127-
"subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"vllm>=0.6.3\"], check=True)\n",
116+
"# DEAD PATH (2026-07-21): vLLM does not run on the Colab image at all — cu13\n",
117+
"# wheel linkage, an aimv2 collision on 0.9.2, and triton PassManager failures\n",
118+
"# on SM 7.5 LoRA kernels. Serving now goes through the transformers+peft+bnb\n",
119+
"# shim instead (plan_autotune.md). Kept for the record; do not retry.\n",
120+
"# Colab ships torch 2.11+cu128; the latest vLLM PyPI wheel is built for CUDA 13\n",
121+
"# and dies on `libcudart.so.13`. Pin vLLM to the cu128 build via uv's torch-backend\n",
122+
"# selector so it matches the preinstalled torch instead of the driver's CUDA 13.\n",
123+
"subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"uv\"], check=True)\n",
124+
"subprocess.run(\n",
125+
" [\"uv\", \"pip\", \"install\", \"-q\", \"--system\", \"--torch-backend=cu128\", \"vllm\"],\n",
126+
" check=True,\n",
127+
")\n",
128128
"cf = \"/content/cloudflared\"\n",
129129
"if not Path(cf).exists():\n",
130130
" urllib.request.urlretrieve(\n",
@@ -154,7 +154,7 @@
154154
" f\"sqltuned={adapter_dir}\",\n",
155155
"]\n",
156156
"print(\" \".join(vllm_cmd), flush=True)\n",
157-
"vllm_log = open(\"/content/vllm.log\", \"w\")\n",
157+
"vllm_log = open(\"/content/vllm.log\", \"w\") # noqa: SIM115 - handle is the child's stdout, must outlive this cell\n",
158158
"vllm_proc = subprocess.Popen(vllm_cmd, stdout=vllm_log, stderr=subprocess.STDOUT)\n",
159159
"\n",
160160
"print(\"waiting for vllm (первый старт качает ~5.5GB весов)...\", flush=True)\n",
@@ -226,8 +226,23 @@
226226
"while time.time() < deadline and vllm_proc.poll() is None:\n",
227227
" time.sleep(300)\n",
228228
" print(f\"alive, url={url}, {int((deadline - time.time()) / 60)} min left\", flush=True)\n",
229-
"print(\"serve window over\")\n"
229+
"print(\"serve window over\")"
230230
]
231231
}
232-
]
233-
}
232+
],
233+
"metadata": {
234+
"accelerator": "GPU",
235+
"colab": {
236+
"provenance": []
237+
},
238+
"kernelspec": {
239+
"display_name": "Python 3",
240+
"name": "python3"
241+
},
242+
"language_info": {
243+
"name": "python"
244+
}
245+
},
246+
"nbformat": 4,
247+
"nbformat_minor": 5
248+
}

0 commit comments

Comments
 (0)