opal-optimizer 0.3.5__tar.gz → 0.3.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/CHANGELOG.md +18 -0
  2. {opal_optimizer-0.3.5/opal_optimizer.egg-info → opal_optimizer-0.3.7}/PKG-INFO +48 -28
  3. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/README.md +44 -27
  4. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/docs/GUIDE.md +8 -13
  5. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/README.md +26 -13
  6. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/cifar10_resnet.py +2 -3
  7. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/diffusion_mnist.py +2 -3
  8. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/goldfish_swahili/README.md +8 -12
  9. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/gpt_small.py +2 -3
  10. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/mnist_mlp.py +2 -3
  11. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/modded_nanogpt/README.md +1 -1
  12. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/overfit/train.py +1 -1
  13. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/raev2_b/README.md +14 -22
  14. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/sit_b2/README.md +1 -1
  15. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/_version.py +1 -1
  16. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/mailbox.py +7 -1
  17. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/cli.py +11 -1
  18. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/estimate.py +15 -5
  19. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/report_privacy.py +1 -1
  20. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/wrapper.py +5 -0
  21. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7/opal_optimizer.egg-info}/PKG-INFO +48 -28
  22. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/pyproject.toml +5 -0
  23. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/skills/train-with-agent/SKILL.md +11 -4
  24. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/LICENSE +0 -0
  25. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/MANIFEST.in +0 -0
  26. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/modded_nanogpt/opal.patch +0 -0
  27. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/overfit/README.md +0 -0
  28. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/overfit/records.py +0 -0
  29. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/overfit/resnet.py +0 -0
  30. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/overfit/splits.py +0 -0
  31. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/examples/sit_b2/opal.patch +0 -0
  32. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/__init__.py +0 -0
  33. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/agent.py +0 -0
  34. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/__init__.py +0 -0
  35. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/_codex_client.py +0 -0
  36. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/_privacy.py +0 -0
  37. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/base.py +0 -0
  38. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/claude.py +0 -0
  39. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/codex.py +0 -0
  40. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/backends/unavailable.py +0 -0
  41. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/context.py +0 -0
  42. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/defaults.json +0 -0
  43. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/defaults.py +0 -0
  44. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/hf.py +0 -0
  45. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/__init__.py +0 -0
  46. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/aggressive.py +0 -0
  47. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/audit.py +0 -0
  48. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/boundary.py +0 -0
  49. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/checkpoint.py +0 -0
  50. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/ddp.py +0 -0
  51. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/integrity.py +0 -0
  52. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/nested.py +0 -0
  53. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/neutral_analysis.py +0 -0
  54. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/precision.py +0 -0
  55. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/recovery.py +0 -0
  56. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/schedulers.py +0 -0
  57. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/setup.py +0 -0
  58. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/skip.py +0 -0
  59. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/taskfacts.py +0 -0
  60. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/misc/waiting.py +0 -0
  61. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/programs.py +0 -0
  62. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/prompts/example_updater.py +0 -0
  63. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/prompts/neutral.md +0 -0
  64. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/report.py +0 -0
  65. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/report_page.py +0 -0
  66. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/results.py +0 -0
  67. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/schedule.py +0 -0
  68. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal/summaries.py +0 -0
  69. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal_optimizer.egg-info/SOURCES.txt +0 -0
  70. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal_optimizer.egg-info/dependency_links.txt +0 -0
  71. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal_optimizer.egg-info/entry_points.txt +0 -0
  72. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal_optimizer.egg-info/requires.txt +0 -0
  73. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/opal_optimizer.egg-info/top_level.txt +0 -0
  74. {opal_optimizer-0.3.5 → opal_optimizer-0.3.7}/setup.cfg +0 -0
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.7 (2026-10-22)
4
+
5
+ - Documentation and examples only, apart from one printed notice; the optimizer math, the agent prompt and the agent's tools are unchanged.
6
+ - The README and the examples page report each task's newest OPAL run (RAEv2-B 3.19, Goldfish 77.25, LightningDiT-B/1 2.31) and no longer list older-system numbers.
7
+ - The examples page has a section on training without waiting for the agent (`blocking=False`), with the RAEv2-B official, blocking and non-blocking runs.
8
+ - The README says why long runs suit OPAL, with measured waiting times.
9
+ - `blocking="auto"` is no longer documented or offered by the example scripts. Using it prints that it is experimental and untested on real training runs.
10
+
11
+ ## 0.3.6 (2026-10-22)
12
+
13
+ - Fixes from a from-scratch test of the install and the agent prompt; the optimizer math, the agent prompt and the agent's tools are unchanged.
14
+ - `opal doctor` says when the machine has an NVIDIA GPU that the installed torch cannot use; the README explains how to install the torch build for the driver first.
15
+ - The ssh mailbox broker explains a "Permission denied" from a host that asks for a password, and the guide and skill show how to share one connection.
16
+ - `opal estimate` no longer offers a decision count that still waits more than 4x, and says when training ends before the first answer.
17
+ - The report records the training waiting time for user scripts.
18
+ - The privacy filter keeps numbers such as `/1000`.
19
+ - The PyPI page links to the repository, the guide and the blog post, and README links work outside GitHub.
20
+
3
21
  ## 0.3.5 (2026-10-21)
4
22
 
5
23
  - Reject uneven DDP inputs on every rank through the optimizer's Joinable interface instead of hanging (#2).
@@ -1,9 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opal-optimizer
3
- Version: 0.3.5
3
+ Version: 0.3.7
4
4
  Summary: A coding agent inside your training run, rewriting the optimizer while it trains
5
5
  Author: Xingjian Bai, Jia-Hao Liang, Jayden Teoh, Vincent Sitzmann
6
6
  License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/xingjian-bai/opal
8
+ Project-URL: Documentation, https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md
9
+ Project-URL: Blog post, https://xingjianbai.com/reactive-optimizers/
7
10
  Requires-Python: >=3.10
8
11
  Description-Content-Type: text/markdown
9
12
  License-File: LICENSE
@@ -30,9 +33,9 @@ Dynamic: license-file
30
33
 
31
34
  <p align="center">
32
35
  <a href="https://xingjianbai.com/reactive-optimizers/">Blog post</a> ·
33
- <a href="docs/GUIDE.md">Guide</a> ·
36
+ <a href="https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md">Guide</a> ·
34
37
  <a href="#start-with-your-coding-agent">Start with your agent</a> ·
35
- <a href="examples/README.md">Examples</a>
38
+ <a href="https://github.com/xingjian-bai/opal/blob/main/examples/README.md">Examples</a>
36
39
  </p>
37
40
 
38
41
  Most networks are trained with an optimizer that is fixed before training starts. OPAL (OPtimizer with an Agent in
@@ -44,22 +47,22 @@ program, which measures whatever the agent wants to look at.
44
47
  The agent can change anything that can be written as optimizer code: the update rule of each parameter group, the
45
48
  learning rates and their schedules, momentum and β₂, preconditioning, clipping, guards against loss spikes. It is
46
49
  asked not to touch your model, data, loss, batch size, number of steps or evaluation. Its programs run inside your
47
- training process, so this is a rule, not a sandbox (see [Safety](docs/GUIDE.md#safety)). Until the agent changes
50
+ training process, so this is a rule, not a sandbox (see [Safety](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#safety)). Until the agent changes
48
51
  something, the wrapped run is bit-for-bit the same as the plain one.
49
52
 
50
53
  ## Start with your coding agent
51
54
 
52
- Clone this repository, open your own project in a coding agent (Claude Code, Codex, Cursor or similar) and paste this
53
- prompt. The agent installs OPAL, wires it into your training script, asks you what it cannot know, and checks that
54
- nothing changed before anything costs money.
55
+ Open your own project in a coding agent (Claude Code, Codex, Cursor or similar) and paste this prompt. The agent
56
+ installs OPAL, wires it into your training script, asks you what it cannot know, and checks that nothing changed
57
+ before anything costs money.
55
58
 
56
59
  ```text
57
60
  I want to train my model with OPAL, a reactive optimizer: a coding agent that rewrites the optimizer while the
58
- model trains. OPAL is cloned at <PATH TO OPAL>. Please set it up for this project:
61
+ model trains. Its docs are at https://github.com/xingjian-bai/opal. Please set it up for this project:
59
62
 
60
- 1. Read <PATH TO OPAL>/README.md and <PATH TO OPAL>/skills/train-with-agent/SKILL.md, and follow the skill. Do not
61
- edit any file inside <PATH TO OPAL>; configure everything through opal.wrap arguments.
62
- 2. Install OPAL into the Python environment this project trains with (pip install -e <PATH TO OPAL>), and
63
+ 1. Read the README there and skills/train-with-agent/SKILL.md, and follow the skill. Do not edit OPAL's own files;
64
+ configure everything through opal.wrap arguments.
65
+ 2. Install OPAL into the Python environment this project trains with (pip install opal-optimizer), and
63
66
  run `opal doctor` (free, offline) to check that torch and Claude Code are ready on the machine that will run
64
67
  training; run `opal doctor --live` (one tiny paid agent call) only once I approve an agent run. If Claude Code is
65
68
  missing or not logged in there, tell me what to run; do not log in for me.
@@ -71,8 +74,9 @@ model trains. OPAL is cloned at <PATH TO OPAL>. Please set it up for this projec
71
74
  must match step for step. Show me both. If I only asked for a dry run, stop here.
72
75
  7. Before the real run, time an update in the smoke test and run `opal estimate --total-steps N --sec-per-step S`;
73
76
  show me what it prints (how long training will wait for the agent, also as a multiple of the run's own time, and
74
- the cost). Ask me for a cost cap (max_cost_usd) and how many times the agent should decide, and wait for my
75
- go-ahead; then give me the exact command to launch it, and how to follow it (opal show, opal report).
77
+ the cost). Ask me for a cost cap (max_cost_usd); leave decisions unset unless I ask, so that the waiting stays
78
+ within 4x the run's own time. Wait for my go-ahead; then give me the exact command to launch it, and how to
79
+ follow it (opal show, opal report).
76
80
  ```
77
81
 
78
82
  ## Start by hand
@@ -84,6 +88,11 @@ opal doctor # torch and Claude Code ready?
84
88
 
85
89
  The package is called `opal-optimizer` on PyPI, because `opal` there is an unrelated web framework. Both install as `import opal`, so do not install both.
86
90
 
91
+ On a GPU machine, install OPAL into the environment you already train with, or install the PyTorch build for your
92
+ driver first ([pytorch.org](https://pytorch.org/get-started/locally/)). In an empty environment, `pip install
93
+ opal-optimizer` pulls the newest torch from PyPI, which may need a newer NVIDIA driver; `opal doctor` then shows
94
+ `CUDA available: False`.
95
+
87
96
  ```python
88
97
  import torch, opal
89
98
 
@@ -115,8 +124,11 @@ opal show opal_runs/<folder> # one line per decision
115
124
  opal report opal_runs/<folder> # one HTML page: loss, learning rates, rationales, code diffs
116
125
  ```
117
126
 
127
+ Each wrapped run prints a `profiler_start` / `profiler_stop` pair on stderr when a program starts: OPAL audits one
128
+ update of each new program. It is harmless.
129
+
118
130
  The guide shows how to check the keep run against your plain run byte for byte, and how to use the Hugging Face
119
- Trainer: [Try it without an agent](docs/GUIDE.md#try-it-without-an-agent).
131
+ Trainer: [Try it without an agent](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#try-it-without-an-agent).
120
132
 
121
133
  ## Prerequisites
122
134
 
@@ -128,7 +140,7 @@ Trainer: [Try it without an agent](docs/GUIDE.md#try-it-without-an-agent).
128
140
  The short summaries of code changes in the report are not counted (`summarize=False` turns them off). A dry run
129
141
  with `backend="keep"` needs neither and makes no model calls.
130
142
  - **No internet or no login on the GPU machine?** Run the agent on your laptop or a login node and connect it to the
131
- training job through a shared folder (see [the mailbox broker](docs/GUIDE.md#gpu-machines-without-an-agent)).
143
+ training job through a mailbox folder, over ssh or a shared filesystem (see [the mailbox broker](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#gpu-machines-without-an-agent)).
132
144
 
133
145
  ## Results
134
146
 
@@ -137,16 +149,24 @@ Each number is one run, and each agent started from the task's own recipe:
137
149
  | Example | Measure | Official recipe | Reactive optimizer |
138
150
  |---|---|---|---|
139
151
  | SiT-B/2, ImageNet 256, 400K updates | FID-50K ↓ | 35.7 (our evaluator) | 23.6 |
140
- | RAEv2-B, ImageNet 256, 20 epochs | FID-50K ↓ | 3.39 | 3.10 (prompt asked for efficiency), 3.46 (neutral prompt); rerun on OPAL 0.3: 3.19 |
141
- | Goldfish 125M GPT, Swahili | FLORES-200 bits ↓ | 79.37 | 77.60; rerun on OPAL 0.3: 77.25 |
152
+ | RAEv2-B, ImageNet 256, 20 epochs | FID-50K ↓ | 3.39 | 3.19 |
153
+ | Goldfish 125M GPT, Swahili | FLORES-200 bits ↓ | 79.37 | 77.25 |
142
154
  | modded-nanogpt record #36 | validation loss ↓ | 3.2781 | 3.2749 |
143
- | LightningDiT-B/1, ImageNet 256, 160 epochs | FID-50K ↓ (with guidance) | 3.22 | rerun on OPAL 0.3: 2.31 |
155
+ | LightningDiT-B/1, ImageNet 256, 160 epochs | FID-50K ↓ (with guidance) | 3.22 | 2.31 |
144
156
 
145
157
  It does not always help: a run of modded-nanogpt on OPAL 0.2.0 ended at 3.2878, worse than record #36. Your
146
158
  numbers may also differ from ours, because the agent can choose differently from one run to the next. The
147
- [examples page](examples/README.md) has every number with its version, hardware, time and cost, the small CPU and
159
+ [examples page](https://github.com/xingjian-bai/opal/blob/main/examples/README.md) has every number with its version, hardware, time and cost, the small CPU and
148
160
  one-GPU scripts, and how to rerun each setting.
149
161
 
162
+ **Long runs suit it best.** An agent decision takes about the same time in any run, about 40 s on average in ours,
163
+ so the waiting adds up to a roughly fixed cost. In our runs of several hours it came to 0.49 h of 3.75 h on RAEv2-B,
164
+ 0.67 h of 5.65 h on Goldfish and 1.05 h of 11.2 h on SiT-B/2. In short runs it dominates: our one-GPU scripts, which
165
+ set 50 decisions, took 27 to 31 min with the agent against 1 to 3 min without, and with `blocking=False` they ended
166
+ before the first answer arrived. The small scripts are a quick way to try OPAL. If you have the compute, use it on runs
167
+ of a few hours or more, where `blocking=False` also
168
+ [kept the result](https://github.com/xingjian-bai/opal/blob/main/examples/README.md#training-without-waiting-for-the-agent).
169
+
150
170
  Our run records and final weights are on Hugging Face: [snow-leopard/opal-results](https://huggingface.co/datasets/snow-leopard/opal-results).
151
171
 
152
172
  ## How it works
@@ -167,8 +187,8 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
167
187
  steps. If it raises an error, the run goes back to the previous program. A failure at its first step is undone
168
188
  exactly when a copy of the parameters and optimizer state fits in `snapshot_limit_gb` (default 2); a later failure
169
189
  is not undone, and the agent is told. Every version is kept in `programs/`.
170
- - **Training waits for the agent by default.** With `blocking=False` training keeps going while the agent thinks, and
171
- `"auto"` waits only when an edit would land too late. Both are faster, and noisier.
190
+ - **Training waits for the agent by default.** With `blocking=False` training keeps going while the agent thinks. That is
191
+ faster, and noisier.
172
192
  - **What the agent sees.** Your goal sentence, the names and shapes of the parameters, the training losses, its own
173
193
  analysis reports, and read-only copies of your training script and model code (`share_code=False` keeps them out).
174
194
  Values you log are only recorded, unless you pass `show_agent=True`. With `validation=N` it also sees the
@@ -178,17 +198,17 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
178
198
 
179
199
  | Option | Default | Meaning |
180
200
  |---|---|---|
181
- | `decisions` | see right | How many times the agent is asked. Each time it may also keep everything as it is. If you do not set it, a run that waits for the agent asks at most as many times as keep the waiting under 4× the run's own training time (`opal estimate` shows the number), and at most 50. If you set it, that cap is off. A fifth of the decisions fall in the first 5% of the run; `schedule="uniform"` spaces them evenly. More in the [guide](docs/GUIDE.md#cost-and-limits). |
201
+ | `decisions` | see right | How many times the agent is asked. Each time it may also keep everything as it is. If you do not set it, a run that waits for the agent asks at most as many times as keep the waiting under 4× the run's own training time (`opal estimate` shows the number), and at most 50, but at least once, even when one decision already waits longer than that. If you set it, that cap is off. A fifth of the decisions fall in the first 5% of the run; `schedule="uniform"` spaces them evenly. More in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#cost-and-limits). |
182
202
  | `every` | 50 | Steps between checks: losses are read, a non-finite loss is caught, the analysis program runs. |
183
- | `blocking` | `True` | `False` or `"auto"` to keep training while the agent thinks. |
203
+ | `blocking` | `True` | `False` to keep training while the agent thinks. |
184
204
  | `max_cost_usd` | none | Stop asking the agent once it has spent this much. |
185
- | `backend_options` | `{}` | `model=` (default `claude-opus-5-5`), `effort=` (default `high`) and `timeout=`. A failed agent turn is retried with growing pauses; an agent that is unavailable (login, usage limit, outage) is waited for, with no deadline unless you set `unavailable_timeout=`. Details in the [guide](docs/GUIDE.md#cost-and-limits). |
205
+ | `backend_options` | `{}` | `model=` (default `claude-opus-5-5`), `effort=` (default `high`) and `timeout=`. A failed agent turn is retried with growing pauses; an agent that is unavailable (login, usage limit, outage) is waited for, with no deadline unless you set `unavailable_timeout=`. Details in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#cost-and-limits). |
186
206
  | `prompt` | `"neutral"` | The agent's instructions: the facts of the run and the protocol, with no recommended values. `"aggressive"` adds a review of every group's hyperparameters at each decision. You can also pass the path of your own template. |
187
- | `validation` | none | `N`: your script evaluates a held-out development set about every N updates, at times it does not announce, and reports it with `opt.report_dev(loss=..., accuracy=...)`. The agent sees each result. See the [guide](docs/GUIDE.md#what-it-can-and-cannot-change). |
207
+ | `validation` | none | `N`: your script evaluates a held-out development set about every N updates, at times it does not announce, and reports it with `opt.report_dev(loss=..., accuracy=...)`. The agent sees each result. See the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#what-it-can-and-cannot-change). |
188
208
  | `eval_integrity` | on with `validation` | Flags a run whose program moves the parameters somewhere else for an evaluation and back afterwards. The agent is not told. |
189
209
 
190
210
  Gradient accumulation, GradScaler, checkpoints and resume, DDP, GPU machines without internet, privacy and cost are
191
- covered in the [guide](docs/GUIDE.md).
211
+ covered in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md).
192
212
 
193
213
  ### Experimental Codex backend
194
214
 
@@ -207,7 +227,7 @@ Code CLI against a local mock.
207
227
 
208
228
  ## Status and limitations
209
229
 
210
- v0.3.5, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
230
+ v0.3.7, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
211
231
  Multi-node, FSDP and DeepSpeed are not supported. Accelerate works if you wrap after `accelerator.prepare`, without
212
232
  fp16 GradScaler or Accelerate's gradient accumulation; the HF Trainer works through `opal.hf.OpalTrainer`. Both are
213
233
  tested on CPU. Lightning should work through `opt.observe(loss)` but we have not tried it. The agent can make a run
@@ -227,6 +247,6 @@ worse: it cannot restart or roll back training, it can only fix its programs at
227
247
 
228
248
  ## License
229
249
 
230
- MIT, see [LICENSE](LICENSE).
250
+ MIT, see [LICENSE](https://github.com/xingjian-bai/opal/blob/main/LICENSE).
231
251
 
232
252
  *The name:* OPAL stands for OPtimizer with an Agent in the Loop.
@@ -5,9 +5,9 @@
5
5
 
6
6
  <p align="center">
7
7
  <a href="https://xingjianbai.com/reactive-optimizers/">Blog post</a> ·
8
- <a href="docs/GUIDE.md">Guide</a> ·
8
+ <a href="https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md">Guide</a> ·
9
9
  <a href="#start-with-your-coding-agent">Start with your agent</a> ·
10
- <a href="examples/README.md">Examples</a>
10
+ <a href="https://github.com/xingjian-bai/opal/blob/main/examples/README.md">Examples</a>
11
11
  </p>
12
12
 
13
13
  Most networks are trained with an optimizer that is fixed before training starts. OPAL (OPtimizer with an Agent in
@@ -19,22 +19,22 @@ program, which measures whatever the agent wants to look at.
19
19
  The agent can change anything that can be written as optimizer code: the update rule of each parameter group, the
20
20
  learning rates and their schedules, momentum and β₂, preconditioning, clipping, guards against loss spikes. It is
21
21
  asked not to touch your model, data, loss, batch size, number of steps or evaluation. Its programs run inside your
22
- training process, so this is a rule, not a sandbox (see [Safety](docs/GUIDE.md#safety)). Until the agent changes
22
+ training process, so this is a rule, not a sandbox (see [Safety](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#safety)). Until the agent changes
23
23
  something, the wrapped run is bit-for-bit the same as the plain one.
24
24
 
25
25
  ## Start with your coding agent
26
26
 
27
- Clone this repository, open your own project in a coding agent (Claude Code, Codex, Cursor or similar) and paste this
28
- prompt. The agent installs OPAL, wires it into your training script, asks you what it cannot know, and checks that
29
- nothing changed before anything costs money.
27
+ Open your own project in a coding agent (Claude Code, Codex, Cursor or similar) and paste this prompt. The agent
28
+ installs OPAL, wires it into your training script, asks you what it cannot know, and checks that nothing changed
29
+ before anything costs money.
30
30
 
31
31
  ```text
32
32
  I want to train my model with OPAL, a reactive optimizer: a coding agent that rewrites the optimizer while the
33
- model trains. OPAL is cloned at <PATH TO OPAL>. Please set it up for this project:
33
+ model trains. Its docs are at https://github.com/xingjian-bai/opal. Please set it up for this project:
34
34
 
35
- 1. Read <PATH TO OPAL>/README.md and <PATH TO OPAL>/skills/train-with-agent/SKILL.md, and follow the skill. Do not
36
- edit any file inside <PATH TO OPAL>; configure everything through opal.wrap arguments.
37
- 2. Install OPAL into the Python environment this project trains with (pip install -e <PATH TO OPAL>), and
35
+ 1. Read the README there and skills/train-with-agent/SKILL.md, and follow the skill. Do not edit OPAL's own files;
36
+ configure everything through opal.wrap arguments.
37
+ 2. Install OPAL into the Python environment this project trains with (pip install opal-optimizer), and
38
38
  run `opal doctor` (free, offline) to check that torch and Claude Code are ready on the machine that will run
39
39
  training; run `opal doctor --live` (one tiny paid agent call) only once I approve an agent run. If Claude Code is
40
40
  missing or not logged in there, tell me what to run; do not log in for me.
@@ -46,8 +46,9 @@ model trains. OPAL is cloned at <PATH TO OPAL>. Please set it up for this projec
46
46
  must match step for step. Show me both. If I only asked for a dry run, stop here.
47
47
  7. Before the real run, time an update in the smoke test and run `opal estimate --total-steps N --sec-per-step S`;
48
48
  show me what it prints (how long training will wait for the agent, also as a multiple of the run's own time, and
49
- the cost). Ask me for a cost cap (max_cost_usd) and how many times the agent should decide, and wait for my
50
- go-ahead; then give me the exact command to launch it, and how to follow it (opal show, opal report).
49
+ the cost). Ask me for a cost cap (max_cost_usd); leave decisions unset unless I ask, so that the waiting stays
50
+ within 4x the run's own time. Wait for my go-ahead; then give me the exact command to launch it, and how to
51
+ follow it (opal show, opal report).
51
52
  ```
52
53
 
53
54
  ## Start by hand
@@ -59,6 +60,11 @@ opal doctor # torch and Claude Code ready?
59
60
 
60
61
  The package is called `opal-optimizer` on PyPI, because `opal` there is an unrelated web framework. Both install as `import opal`, so do not install both.
61
62
 
63
+ On a GPU machine, install OPAL into the environment you already train with, or install the PyTorch build for your
64
+ driver first ([pytorch.org](https://pytorch.org/get-started/locally/)). In an empty environment, `pip install
65
+ opal-optimizer` pulls the newest torch from PyPI, which may need a newer NVIDIA driver; `opal doctor` then shows
66
+ `CUDA available: False`.
67
+
62
68
  ```python
63
69
  import torch, opal
64
70
 
@@ -90,8 +96,11 @@ opal show opal_runs/<folder> # one line per decision
90
96
  opal report opal_runs/<folder> # one HTML page: loss, learning rates, rationales, code diffs
91
97
  ```
92
98
 
99
+ Each wrapped run prints a `profiler_start` / `profiler_stop` pair on stderr when a program starts: OPAL audits one
100
+ update of each new program. It is harmless.
101
+
93
102
  The guide shows how to check the keep run against your plain run byte for byte, and how to use the Hugging Face
94
- Trainer: [Try it without an agent](docs/GUIDE.md#try-it-without-an-agent).
103
+ Trainer: [Try it without an agent](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#try-it-without-an-agent).
95
104
 
96
105
  ## Prerequisites
97
106
 
@@ -103,7 +112,7 @@ Trainer: [Try it without an agent](docs/GUIDE.md#try-it-without-an-agent).
103
112
  The short summaries of code changes in the report are not counted (`summarize=False` turns them off). A dry run
104
113
  with `backend="keep"` needs neither and makes no model calls.
105
114
  - **No internet or no login on the GPU machine?** Run the agent on your laptop or a login node and connect it to the
106
- training job through a shared folder (see [the mailbox broker](docs/GUIDE.md#gpu-machines-without-an-agent)).
115
+ training job through a mailbox folder, over ssh or a shared filesystem (see [the mailbox broker](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#gpu-machines-without-an-agent)).
107
116
 
108
117
  ## Results
109
118
 
@@ -112,16 +121,24 @@ Each number is one run, and each agent started from the task's own recipe:
112
121
  | Example | Measure | Official recipe | Reactive optimizer |
113
122
  |---|---|---|---|
114
123
  | SiT-B/2, ImageNet 256, 400K updates | FID-50K ↓ | 35.7 (our evaluator) | 23.6 |
115
- | RAEv2-B, ImageNet 256, 20 epochs | FID-50K ↓ | 3.39 | 3.10 (prompt asked for efficiency), 3.46 (neutral prompt); rerun on OPAL 0.3: 3.19 |
116
- | Goldfish 125M GPT, Swahili | FLORES-200 bits ↓ | 79.37 | 77.60; rerun on OPAL 0.3: 77.25 |
124
+ | RAEv2-B, ImageNet 256, 20 epochs | FID-50K ↓ | 3.39 | 3.19 |
125
+ | Goldfish 125M GPT, Swahili | FLORES-200 bits ↓ | 79.37 | 77.25 |
117
126
  | modded-nanogpt record #36 | validation loss ↓ | 3.2781 | 3.2749 |
118
- | LightningDiT-B/1, ImageNet 256, 160 epochs | FID-50K ↓ (with guidance) | 3.22 | rerun on OPAL 0.3: 2.31 |
127
+ | LightningDiT-B/1, ImageNet 256, 160 epochs | FID-50K ↓ (with guidance) | 3.22 | 2.31 |
119
128
 
120
129
  It does not always help: a run of modded-nanogpt on OPAL 0.2.0 ended at 3.2878, worse than record #36. Your
121
130
  numbers may also differ from ours, because the agent can choose differently from one run to the next. The
122
- [examples page](examples/README.md) has every number with its version, hardware, time and cost, the small CPU and
131
+ [examples page](https://github.com/xingjian-bai/opal/blob/main/examples/README.md) has every number with its version, hardware, time and cost, the small CPU and
123
132
  one-GPU scripts, and how to rerun each setting.
124
133
 
134
+ **Long runs suit it best.** An agent decision takes about the same time in any run, about 40 s on average in ours,
135
+ so the waiting adds up to a roughly fixed cost. In our runs of several hours it came to 0.49 h of 3.75 h on RAEv2-B,
136
+ 0.67 h of 5.65 h on Goldfish and 1.05 h of 11.2 h on SiT-B/2. In short runs it dominates: our one-GPU scripts, which
137
+ set 50 decisions, took 27 to 31 min with the agent against 1 to 3 min without, and with `blocking=False` they ended
138
+ before the first answer arrived. The small scripts are a quick way to try OPAL. If you have the compute, use it on runs
139
+ of a few hours or more, where `blocking=False` also
140
+ [kept the result](https://github.com/xingjian-bai/opal/blob/main/examples/README.md#training-without-waiting-for-the-agent).
141
+
125
142
  Our run records and final weights are on Hugging Face: [snow-leopard/opal-results](https://huggingface.co/datasets/snow-leopard/opal-results).
126
143
 
127
144
  ## How it works
@@ -142,8 +159,8 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
142
159
  steps. If it raises an error, the run goes back to the previous program. A failure at its first step is undone
143
160
  exactly when a copy of the parameters and optimizer state fits in `snapshot_limit_gb` (default 2); a later failure
144
161
  is not undone, and the agent is told. Every version is kept in `programs/`.
145
- - **Training waits for the agent by default.** With `blocking=False` training keeps going while the agent thinks, and
146
- `"auto"` waits only when an edit would land too late. Both are faster, and noisier.
162
+ - **Training waits for the agent by default.** With `blocking=False` training keeps going while the agent thinks. That is
163
+ faster, and noisier.
147
164
  - **What the agent sees.** Your goal sentence, the names and shapes of the parameters, the training losses, its own
148
165
  analysis reports, and read-only copies of your training script and model code (`share_code=False` keeps them out).
149
166
  Values you log are only recorded, unless you pass `show_agent=True`. With `validation=N` it also sees the
@@ -153,17 +170,17 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
153
170
 
154
171
  | Option | Default | Meaning |
155
172
  |---|---|---|
156
- | `decisions` | see right | How many times the agent is asked. Each time it may also keep everything as it is. If you do not set it, a run that waits for the agent asks at most as many times as keep the waiting under 4× the run's own training time (`opal estimate` shows the number), and at most 50. If you set it, that cap is off. A fifth of the decisions fall in the first 5% of the run; `schedule="uniform"` spaces them evenly. More in the [guide](docs/GUIDE.md#cost-and-limits). |
173
+ | `decisions` | see right | How many times the agent is asked. Each time it may also keep everything as it is. If you do not set it, a run that waits for the agent asks at most as many times as keep the waiting under 4× the run's own training time (`opal estimate` shows the number), and at most 50, but at least once, even when one decision already waits longer than that. If you set it, that cap is off. A fifth of the decisions fall in the first 5% of the run; `schedule="uniform"` spaces them evenly. More in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#cost-and-limits). |
157
174
  | `every` | 50 | Steps between checks: losses are read, a non-finite loss is caught, the analysis program runs. |
158
- | `blocking` | `True` | `False` or `"auto"` to keep training while the agent thinks. |
175
+ | `blocking` | `True` | `False` to keep training while the agent thinks. |
159
176
  | `max_cost_usd` | none | Stop asking the agent once it has spent this much. |
160
- | `backend_options` | `{}` | `model=` (default `claude-opus-5-5`), `effort=` (default `high`) and `timeout=`. A failed agent turn is retried with growing pauses; an agent that is unavailable (login, usage limit, outage) is waited for, with no deadline unless you set `unavailable_timeout=`. Details in the [guide](docs/GUIDE.md#cost-and-limits). |
177
+ | `backend_options` | `{}` | `model=` (default `claude-opus-5-5`), `effort=` (default `high`) and `timeout=`. A failed agent turn is retried with growing pauses; an agent that is unavailable (login, usage limit, outage) is waited for, with no deadline unless you set `unavailable_timeout=`. Details in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#cost-and-limits). |
161
178
  | `prompt` | `"neutral"` | The agent's instructions: the facts of the run and the protocol, with no recommended values. `"aggressive"` adds a review of every group's hyperparameters at each decision. You can also pass the path of your own template. |
162
- | `validation` | none | `N`: your script evaluates a held-out development set about every N updates, at times it does not announce, and reports it with `opt.report_dev(loss=..., accuracy=...)`. The agent sees each result. See the [guide](docs/GUIDE.md#what-it-can-and-cannot-change). |
179
+ | `validation` | none | `N`: your script evaluates a held-out development set about every N updates, at times it does not announce, and reports it with `opt.report_dev(loss=..., accuracy=...)`. The agent sees each result. See the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md#what-it-can-and-cannot-change). |
163
180
  | `eval_integrity` | on with `validation` | Flags a run whose program moves the parameters somewhere else for an evaluation and back afterwards. The agent is not told. |
164
181
 
165
182
  Gradient accumulation, GradScaler, checkpoints and resume, DDP, GPU machines without internet, privacy and cost are
166
- covered in the [guide](docs/GUIDE.md).
183
+ covered in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md).
167
184
 
168
185
  ### Experimental Codex backend
169
186
 
@@ -182,7 +199,7 @@ Code CLI against a local mock.
182
199
 
183
200
  ## Status and limitations
184
201
 
185
- v0.3.5, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
202
+ v0.3.7, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
186
203
  Multi-node, FSDP and DeepSpeed are not supported. Accelerate works if you wrap after `accelerator.prepare`, without
187
204
  fp16 GradScaler or Accelerate's gradient accumulation; the HF Trainer works through `opal.hf.OpalTrainer`. Both are
188
205
  tested on CPU. Lightning should work through `opt.observe(loss)` but we have not tried it. The agent can make a run
@@ -202,6 +219,6 @@ worse: it cannot restart or roll back training, it can only fix its programs at
202
219
 
203
220
  ## License
204
221
 
205
- MIT, see [LICENSE](LICENSE).
222
+ MIT, see [LICENSE](https://github.com/xingjian-bai/opal/blob/main/LICENSE).
206
223
 
207
224
  *The name:* OPAL stands for OPtimizer with an Agent in the Loop.
@@ -32,9 +32,8 @@ The picture shows `blocking=False`. With the default `blocking=True`, every rank
32
32
  - A new program takes over at the next update boundary. On one GPU this happens at once. In DDP, rank 0 announces
33
33
  it through an asynchronous broadcast every `sync_every` updates on a separate gloo group. The default is 20.
34
34
  Every rank swaps at the same update.
35
- - With `blocking=True`, all ranks wait at every decision, as in our published runs. With `blocking="auto"`, they wait
36
- at decisions whose answers would arrive late, as described below. With `blocking=False`, they wait only once when
37
- the loss turns non-finite. Waiting uses the gloo group, so a long agent turn does not trip an NCCL timeout.
35
+ - With `blocking=True`, all ranks wait at every decision, as in most of our published runs. With `blocking=False`, they wait
36
+ only once when the loss turns non-finite. Waiting uses the gloo group, so a long agent turn does not trip an NCCL timeout.
38
37
  - A new updater runs on trial for `trial_steps` updates, by default 20. If it raises on any rank, every rank returns
39
38
  to the previous updater. The fallback is the last updater that passed its trial, or the starting one.
40
39
  An updater replaced during its trial does not become the fallback.
@@ -252,8 +251,7 @@ asked so far.
252
251
  | `max_cost_usd` | none | Stop asking once Claude Code reports this spend. Checked before each decision. The Claude backend also caps each decision at the remainder with `--max-budget-usd`, which can overshoot by one model call. `backend_options=dict(max_budget_usd=...)` caps each decision. |
253
252
  | `every` | 50 | Updates between checks. Decisions and the non-finite check happen at checks. |
254
253
  | `backend_options` | `{}` | `model=` defaults to `claude-opus-5-5`, `effort=` to `high`, as in the blog's runs. Another model or effort prints a warning at the start that the blog's results may differ. `timeout=` is seconds per decision, default 3600. `shell_timeout=` is seconds per agent shell command. `max_budget_usd=` caps each decision through Claude Code's `--max-budget-usd`. It can overshoot by one model call. `retries=` retries a failed turn, such as a tool crash, from the programs it started with. Pauses start at `backoff=15` s and double up to `backoff_max=600` s while `timeout` allows. `None`, the default, retries as often as that allows. `0` never retries. Each failure's error goes to the run's log, `decisions/NNN/attempts.jsonl` and the broker's log. The broker flag is `opal broker --retries N`. An unavailable agent has a separate wait, described below. `unavailable_timeout=` sets its deadline. |
255
- | `blocking` | True | Training waits for each decision, as in our published runs. False keeps training. "auto" waits at the first decision and when an answer would arrive more than `max_lag_updates` updates late. |
256
- | `max_lag_updates` | 50 | With `blocking="auto"`, the predicted lag in updates above which training waits. |
254
+ | `blocking` | True | Training waits for each decision, as in most of our published runs. False keeps training. |
257
255
  | `share_code` | True | False keeps the training script and source files out of the agent's folder. |
258
256
  | `schedule` | "front" | When decisions are asked. The learning-rate schedule is `scheduler`. "front" puts a fifth evenly over the first 5% of training and the rest evenly over the remainder. "uniform" asks every total_steps // decisions updates. Segments such as `[(0.05, 20), (1.0, 30)]` give an end fraction and count: here 20 in the first 5%, then 30 over the rest. Update numbers such as `[500, 1000, 5000]` give the exact updates. A list sets the count: omit `decisions` or make it agree, or `wrap` raises. Each decision moves to the next check, every `every` updates. Two on one check merge. None falls in the last `every` updates. The first log line gives the remaining count. |
259
257
  | `show_next_decision` | on with the neutral and aggressive prompts | True gives the next scheduled decision's update in each message. False turns it off. |
@@ -295,15 +293,10 @@ No new decision is asked meanwhile. A failed turn, such as a crash, time limit o
295
293
  instead of the unavailable-agent wait.
296
294
 
297
295
  By default, `blocking=True` waits for the answer at each decision. Its edit takes effect from the update where the
298
- decision was asked, and the agent's thinking adds to wall time. `blocking=False` and `blocking="auto"` allow faster
296
+ decision was asked, and the agent's thinking adds to wall time. `blocking=False` allows faster
299
297
  runs. Without waiting, an answer takes effect answer time / time per update updates after the state the agent saw.
300
298
  No new decision is asked while one is in progress, so fast runs may ask far fewer times.
301
299
 
302
- `blocking="auto"` waits when the answer would arrive more than `max_lag_updates` updates late, by default 50.
303
- It divides the median of the last 5 answer times by the time per update. That update time is the median over the last
304
- 5 check intervals, excluding pauses. It always waits at the first decision.
305
- Each choice, its reason and its lag are in `events.jsonl` and the report.
306
-
307
300
  ## The report
308
301
 
309
302
  Each run keeps `report.html` in its folder. It shows the loss with a tick per decision, your `opt.log` values,
@@ -328,7 +321,7 @@ Private locations and recorded identities are omitted from messages, code and pl
328
321
  calls.
329
322
 
330
323
  Cached descriptions and tags come from Claude Opus at low effort through the Claude Code CLI, with no tools.
331
- One call per code change costs about a cent. It is recorded in `decisions/NNN/summary.json` and excluded from
324
+ One call per code change costs a few cents ($0.02 to $0.06 in our checks). It is recorded in `decisions/NNN/summary.json` and excluded from
332
325
  `max_cost_usd`. A change can have any number of these tags:
333
326
 
334
327
  - `common practice`: standard recipes, such as Muon, warmup, cosine or linear decay, clipping and AdamW betas.
@@ -415,7 +408,7 @@ schedule.
415
408
  The broker keeps waiting. When `close()` returns, the final `report.html` is written.
416
409
  It waits up to 30 s for pending Claude summaries.
417
410
  - `blocking=True` is deterministic given the agent's answers. Replaying a final program with `opal.replay` is
418
- deterministic too. With `blocking=False` or `"auto"`, timing decides when an edit takes effect.
411
+ deterministic too. With `blocking=False`, timing decides when an edit takes effect.
419
412
  Two runs with the same answers can differ.
420
413
  - To turn the agent off mid-run, resume with `backend="keep"` or start a fresh run with `opal.replay(...)`.
421
414
  - For the Hugging Face Trainer, use `opal.hf.OpalTrainer` in place of `Trainer`.
@@ -485,6 +478,8 @@ On the GPU machine, `opal doctor --backend mailbox:/scratch/me/opal-box` checks
485
478
  whether a broker serves it. That machine needs no Claude Code. Set the model and effort on the broker with
486
479
  `opal broker ... --model claude-opus-5-5 --effort high`, the defaults, rather than in the run's `backend_options`.
487
480
  The broker's machine must stay awake for the whole run. On a Mac, use `caffeinate -i opal broker ...`.
481
+ The broker's ssh runs with `BatchMode=yes`, so it cannot ask for a password or a second factor. Log in with a key,
482
+ or open a connection it can share before you start it: `ssh -fNM -o ControlPath=~/.ssh/opal-%r@%h-%p me@gpu-host`.
488
483
  The run waits while no broker answers, as described under "When the agent is unavailable".
489
484
  Mailbox runs do not make code-change summaries during training.
490
485
  Run `opal report <run> --summarize` where Claude Code is logged in to add them.
@@ -6,26 +6,39 @@ number comes from.
6
6
 
7
7
  ## Results by setting
8
8
 
9
- Each number is one run, and each agent started from the task's own recipe. The "Reactive optimizer" column comes
10
- from the harness this package grew out of (for modded-nanogpt, from OPAL 0.1.0). The last column is a rerun on OPAL
11
- 0.3 with the neutral prompt and Opus 5.5 at effort high. In all of these runs, training paused at each decision while
12
- the agent answered, which is OPAL's default.
13
-
14
- | Example | Measure | Official recipe | Reactive optimizer | Made with | On OPAL 0.3 |
15
- |---|---|---|---|---|---|
16
- | [SiT-B/2](sit_b2/), ImageNet 256, 400K updates, 8× H100 | FID-50K ↓ | 35.7 with our evaluator (the SiT paper reports 33.0 with the ADM evaluator; Muon: 30.3) | 23.6 | earlier harness | not rerun |
17
- | [RAEv2-B](raev2_b/), ImageNet 256, 20 epochs, 8× H100 | FID-50K ↓ | 3.39 | 3.10 when asked for efficiency; 3.46 with the neutral prompt | earlier harness | 3.19 |
18
- | [Goldfish 125M GPT](goldfish_swahili/), 1 GB of Swahili, 1× H100 | FLORES-200 bits ↓ | 79.37 | 77.60 | earlier harness | 77.25 |
19
- | [modded-nanogpt record #36](modded_nanogpt/), 162M GPT, 8× H100 | validation loss ↓ | 3.2781 | 3.2749 (a small gain: the track assumes σ = 0.0013 per run) | OPAL 0.1.0 | not rerun |
20
- | LightningDiT-B/1, ImageNet 256, 160 epochs (200K updates), 8× H100 | FID-50K ↓, with guidance / without | 3.22 / 10.12 | – | – | 2.31 / 7.56 |
9
+ Each number is one run, and each agent started from the task's own recipe. Runs on OPAL 0.3 used the neutral prompt
10
+ and Opus 5.5 at effort high. SiT-B/2 comes from the harness this package grew out of. In all of these runs, training
11
+ paused at each decision while the agent answered, which is OPAL's default.
12
+
13
+ | Example | Measure | Official recipe | Reactive optimizer | Made with |
14
+ |---|---|---|---|---|
15
+ | [SiT-B/2](sit_b2/), ImageNet 256, 400K updates, 8× H100 | FID-50K ↓ | 35.7 with our evaluator (the SiT paper reports 33.0 with the ADM evaluator; Muon: 30.3) | 23.6 | earlier harness |
16
+ | [RAEv2-B](raev2_b/), ImageNet 256, 20 epochs, 8× H100 | FID-50K ↓ | 3.39 | 3.19 | OPAL 0.3 |
17
+ | [Goldfish 125M GPT](goldfish_swahili/), 1 GB of Swahili, 1× H100 | FLORES-200 bits ↓ | 79.37 | 77.25 | OPAL 0.3 |
18
+ | [modded-nanogpt record #36](modded_nanogpt/), 162M GPT, 8× H100 | validation loss ↓ | 3.2781 | 3.2749 (a small gain: the track assumes σ = 0.0013 per run) | OPAL 0.1.0 |
19
+ | LightningDiT-B/1, ImageNet 256, 160 epochs (200K updates), 8× H100 | FID-50K ↓, with guidance / without | 3.22 / 10.12 | 2.31 / 7.56 | OPAL 0.3 |
21
20
 
22
21
  It does not always help. On modded-nanogpt, a heavily tuned benchmark, a run on OPAL 0.2.0 with the neutral prompt
23
22
  ended at 3.2878, worse than record #36. Your numbers may also differ from ours, because the agent can make different
24
- choices from one run to the next and hosted models change between versions. Still, most of our reruns on OPAL 0.3
23
+ choices from one run to the next and hosted models change between versions. Still, most of our runs on OPAL 0.3
25
24
  ended ahead of the recipe they started from. The [blog post](https://xingjianbai.com/reactive-optimizers/) has more.
26
25
 
27
26
  LightningDiT-B/1 has no folder here. Both runs used the upstream LightningDiT training code and its 64-epoch config, changed to model B/1, 200,000 updates and `use_qknorm: true` (without query-key normalization the official run diverged near update 194,000). The official recipe trains with AdamW at a constant learning rate of 2e-4. FID-50K uses 50,000 samples against the ADM reference statistics, Euler sampling with 250 steps and timestep shift 0.3; "with guidance" is the upstream setting, a guidance scale of 10 from t = 0.10 on. The LightningDiT paper reports FID-10K for this model, which is not comparable.
28
27
 
28
+ ## Training without waiting for the agent
29
+
30
+ With `blocking=False`, training continues on the current programs while the agent answers, and an edit takes effect
31
+ when its answer arrives. One run on RAEv2-B, OPAL 0.3, 8× H100:
32
+
33
+ | RAEv2-B, 20 epochs | Official recipe | OPAL, training waits (default) | OPAL, `blocking=False` |
34
+ |---|---|---|---|
35
+ | FID-50K ↓ after 25,000 updates | 3.39 | 3.19 | 3.17 |
36
+ | Run time, including evaluations | 3.21 h | 3.75 h, of which 0.49 h waiting for the agent | 3.22 h |
37
+ | Decisions asked / that changed code | – | 50 / 6 | 43 / 3 |
38
+ | Agent cost | – | $13.30 | $9.71 |
39
+
40
+ Without waiting, fewer decisions are asked, because no new decision is asked while one is in progress.
41
+
29
42
  ## Small examples (CPU)
30
43
 
31
44
  `mnist_mlp.py` trains an MLP on MNIST with AdamW and a cosine schedule for 5 epochs. It doubles as the install
@@ -65,9 +65,8 @@ parser.add_argument(
65
65
  nargs="?",
66
66
  const="true",
67
67
  default="true",
68
- choices=["true", "false", "auto"],
69
- help="true (default): training waits for each answer; false: it keeps going; auto: it waits "
70
- "only when an answer would land late",
68
+ choices=["true", "false"],
69
+ help="true (default): training waits for each answer; false: it keeps going",
71
70
  )
72
71
  parser.add_argument("--max_cost_usd", type=float, default=15.0, help="stop asking the agent after this much spend")
73
72
  parser.add_argument(
@@ -52,9 +52,8 @@ parser.add_argument(
52
52
  nargs="?",
53
53
  const="true",
54
54
  default="true",
55
- choices=["true", "false", "auto"],
56
- help="true (default): training waits for each answer; false: it keeps going; auto: it waits "
57
- "only when an answer would land late",
55
+ choices=["true", "false"],
56
+ help="true (default): training waits for each answer; false: it keeps going",
58
57
  ) # opal
59
58
  args = parser.parse_args()
60
59
 
@@ -10,17 +10,13 @@ split at the middle character. This follows upstream's `eval_code/flores_eval_go
10
10
 
11
11
  | FLORES-200 bits ↓ (one run each) | Official recipe | Reactive optimizer |
12
12
  |---|---|---|
13
- | after the last update | 79.37 | **77.60** |
13
+ | after the last update | 79.37 | **77.25** |
14
14
 
15
- In this run the agent ended 1.77 bits below the official recipe.
15
+ In this run the agent ended 2.12 bits below the official recipe.
16
16
 
17
- On OPAL 0.3, the rerun scored 77.25: one run, neutral prompt, 51 decisions of which 11 changed the program,
17
+ The agent run used OPAL 0.3 with the neutral prompt: 51 decisions of which 11 changed the program,
18
18
  $17.41, 5 h 39 min on one H100. This wall time includes evaluations and waiting for the agent.
19
19
 
20
- The table above comes from the earlier training system OPAL grew from in October 2026.
21
- It used Claude Code, Opus 5.5 at effort `high`, with that system's neutral prompt and 100 decisions every 793 updates.
22
- Training paused at each decision.
23
-
24
20
  ## Official code and data
25
21
 
26
22
  - Training uses [tylerachang/goldfish](https://github.com/tylerachang/goldfish) at `d4a518a` with
@@ -73,9 +69,9 @@ Then run it once with `backend="keep"` for a few hundred updates: its losses mus
73
69
  | | Official recipe | Reactive optimizer |
74
70
  |---|---|---|
75
71
  | Hardware | 1× H100 80GB | 1× H100 80GB |
76
- | Reported run time, including agent waits | 4.8 h | 5.75 h, of which 0.77 h waiting for the agent |
77
- | Agent cost | – | $17.79 |
78
- | Decisions answered / that changed code | – | 100 / 4 (decisions 1, 2, 9 and 11) |
79
- | FLORES-200 bits | 79.374 | 77.600 |
72
+ | Reported run time, including agent waits | 4.8 h | 5.65 h, of which 0.67 h waiting for the agent |
73
+ | Agent cost | – | $17.41 |
74
+ | Decisions answered / that changed code | – | 51 / 11 |
75
+ | FLORES-200 bits | 79.374 | 77.250 |
80
76
 
81
- The official 4.8 h sums logged update windows, including evaluations. The agent time includes its 0.77 h wait.
77
+ The official 4.8 h sums logged update windows, including evaluations. The agent time includes its 0.67 h wait.
@@ -70,9 +70,8 @@ parser.add_argument(
70
70
  nargs="?",
71
71
  const="true",
72
72
  default="true",
73
- choices=["true", "false", "auto"],
74
- help="true (default): training waits for each answer; false: it keeps going; auto: it waits "
75
- "only when an answer would land late",
73
+ choices=["true", "false"],
74
+ help="true (default): training waits for each answer; false: it keeps going",
76
75
  )
77
76
  parser.add_argument("--max_cost_usd", type=float, default=15.0, help="stop asking the agent after this much spend")
78
77
  parser.add_argument(
@@ -51,9 +51,8 @@ parser.add_argument(
51
51
  nargs="?",
52
52
  const="true",
53
53
  default="true",
54
- choices=["true", "false", "auto"],
55
- help="true (default): training waits for each answer; false: it keeps going; auto: it waits "
56
- "only when an answer would land late",
54
+ choices=["true", "false"],
55
+ help="true (default): training waits for each answer; false: it keeps going",
57
56
  ) # opal
58
57
  args = parser.parse_args()
59
58
 
@@ -14,7 +14,7 @@ The gap is about 2.5 times that, from one run on each side. Record #36's publish
14
14
  It appears in row 36 of the [track README on master](https://github.com/KellerJordan/modded-nanogpt/blob/master/records/track_3_optimization/README.md).
15
15
  The README at the pinned commit below predates that row and lists only records 1 to 12.
16
16
 
17
- **Not rerun on OPAL 0.3 yet.** The 3.2749 run used OPAL 0.1.0 with that version's default prompt, which this release
17
+ The 3.2749 run used OPAL 0.1.0 with that version's default prompt, which this release
18
18
  no longer ships: Claude Code (Opus 5.5, effort `high`), 100 decisions requested with checks every 10 updates (97 after
19
19
  snapping, all answered), training paused at each decision. A later run on OPAL 0.2.0 with the neutral prompt (50 evenly
20
20
  spaced decisions, 2 GPUs, a goal worded as reaching the target loss) ended at **3.2878, worse than record #36**.