opal-optimizer 0.3.5__tar.gz → 0.3.6__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.6}/CHANGELOG.md +10 -0
  2. {opal_optimizer-0.3.5/opal_optimizer.egg-info → opal_optimizer-0.3.6}/PKG-INFO +34 -22
  3. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/README.md +30 -21
  4. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/docs/GUIDE.md +3 -1
  5. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/_version.py +1 -1
  6. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/mailbox.py +7 -1
  7. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/cli.py +11 -1
  8. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/estimate.py +15 -5
  9. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/report_privacy.py +1 -1
  10. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/wrapper.py +1 -0
  11. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6/opal_optimizer.egg-info}/PKG-INFO +34 -22
  12. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/pyproject.toml +5 -0
  13. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/skills/train-with-agent/SKILL.md +11 -4
  14. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/LICENSE +0 -0
  15. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/MANIFEST.in +0 -0
  16. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/README.md +0 -0
  17. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/cifar10_resnet.py +0 -0
  18. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/diffusion_mnist.py +0 -0
  19. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/goldfish_swahili/README.md +0 -0
  20. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/gpt_small.py +0 -0
  21. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/mnist_mlp.py +0 -0
  22. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/modded_nanogpt/README.md +0 -0
  23. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/modded_nanogpt/opal.patch +0 -0
  24. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/overfit/README.md +0 -0
  25. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/overfit/records.py +0 -0
  26. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/overfit/resnet.py +0 -0
  27. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/overfit/splits.py +0 -0
  28. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/overfit/train.py +0 -0
  29. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/raev2_b/README.md +0 -0
  30. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/sit_b2/README.md +0 -0
  31. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/examples/sit_b2/opal.patch +0 -0
  32. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/__init__.py +0 -0
  33. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/agent.py +0 -0
  34. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/__init__.py +0 -0
  35. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/_codex_client.py +0 -0
  36. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/_privacy.py +0 -0
  37. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/base.py +0 -0
  38. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/claude.py +0 -0
  39. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/codex.py +0 -0
  40. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/backends/unavailable.py +0 -0
  41. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/context.py +0 -0
  42. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/defaults.json +0 -0
  43. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/defaults.py +0 -0
  44. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/hf.py +0 -0
  45. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/__init__.py +0 -0
  46. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/aggressive.py +0 -0
  47. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/audit.py +0 -0
  48. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/boundary.py +0 -0
  49. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/checkpoint.py +0 -0
  50. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/ddp.py +0 -0
  51. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/integrity.py +0 -0
  52. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/nested.py +0 -0
  53. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/neutral_analysis.py +0 -0
  54. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/precision.py +0 -0
  55. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/recovery.py +0 -0
  56. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/schedulers.py +0 -0
  57. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/setup.py +0 -0
  58. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/skip.py +0 -0
  59. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/taskfacts.py +0 -0
  60. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/misc/waiting.py +0 -0
  61. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/programs.py +0 -0
  62. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/prompts/example_updater.py +0 -0
  63. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/prompts/neutral.md +0 -0
  64. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/report.py +0 -0
  65. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/report_page.py +0 -0
  66. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/results.py +0 -0
  67. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/schedule.py +0 -0
  68. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal/summaries.py +0 -0
  69. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal_optimizer.egg-info/SOURCES.txt +0 -0
  70. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal_optimizer.egg-info/dependency_links.txt +0 -0
  71. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal_optimizer.egg-info/entry_points.txt +0 -0
  72. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal_optimizer.egg-info/requires.txt +0 -0
  73. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/opal_optimizer.egg-info/top_level.txt +0 -0
  74. {opal_optimizer-0.3.5 → opal_optimizer-0.3.6}/setup.cfg +0 -0
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.6 (2026-10-22)
4
+
5
+ - 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.
6
+ - `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.
7
+ - 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.
8
+ - `opal estimate` no longer offers a decision count that still waits more than 4x, and says when training ends before the first answer.
9
+ - The report records the training waiting time for user scripts.
10
+ - The privacy filter keeps numbers such as `/1000`.
11
+ - The PyPI page links to the repository, the guide and the blog post, and README links work outside GitHub.
12
+
3
13
  ## 0.3.5 (2026-10-21)
4
14
 
5
15
  - 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.6
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
 
@@ -144,7 +156,7 @@ Each number is one run, and each agent started from the task's own recipe:
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
 
150
162
  Our run records and final weights are on Hugging Face: [snow-leopard/opal-results](https://huggingface.co/datasets/snow-leopard/opal-results).
@@ -178,17 +190,17 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
178
190
 
179
191
  | Option | Default | Meaning |
180
192
  |---|---|---|
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). |
193
+ | `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
194
  | `every` | 50 | Steps between checks: losses are read, a non-finite loss is caught, the analysis program runs. |
183
195
  | `blocking` | `True` | `False` or `"auto"` to keep training while the agent thinks. |
184
196
  | `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). |
197
+ | `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
198
  | `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). |
199
+ | `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
200
  | `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
201
 
190
202
  Gradient accumulation, GradScaler, checkpoints and resume, DDP, GPU machines without internet, privacy and cost are
191
- covered in the [guide](docs/GUIDE.md).
203
+ covered in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md).
192
204
 
193
205
  ### Experimental Codex backend
194
206
 
@@ -207,7 +219,7 @@ Code CLI against a local mock.
207
219
 
208
220
  ## Status and limitations
209
221
 
210
- v0.3.5, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
222
+ v0.3.6, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
211
223
  Multi-node, FSDP and DeepSpeed are not supported. Accelerate works if you wrap after `accelerator.prepare`, without
212
224
  fp16 GradScaler or Accelerate's gradient accumulation; the HF Trainer works through `opal.hf.OpalTrainer`. Both are
213
225
  tested on CPU. Lightning should work through `opt.observe(loss)` but we have not tried it. The agent can make a run
@@ -227,6 +239,6 @@ worse: it cannot restart or roll back training, it can only fix its programs at
227
239
 
228
240
  ## License
229
241
 
230
- MIT, see [LICENSE](LICENSE).
242
+ MIT, see [LICENSE](https://github.com/xingjian-bai/opal/blob/main/LICENSE).
231
243
 
232
244
  *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
 
@@ -119,7 +128,7 @@ Each number is one run, and each agent started from the task's own recipe:
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
 
125
134
  Our run records and final weights are on Hugging Face: [snow-leopard/opal-results](https://huggingface.co/datasets/snow-leopard/opal-results).
@@ -153,17 +162,17 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
153
162
 
154
163
  | Option | Default | Meaning |
155
164
  |---|---|---|
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). |
165
+ | `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
166
  | `every` | 50 | Steps between checks: losses are read, a non-finite loss is caught, the analysis program runs. |
158
167
  | `blocking` | `True` | `False` or `"auto"` to keep training while the agent thinks. |
159
168
  | `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). |
169
+ | `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
170
  | `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). |
171
+ | `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
172
  | `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
173
 
165
174
  Gradient accumulation, GradScaler, checkpoints and resume, DDP, GPU machines without internet, privacy and cost are
166
- covered in the [guide](docs/GUIDE.md).
175
+ covered in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md).
167
176
 
168
177
  ### Experimental Codex backend
169
178
 
@@ -182,7 +191,7 @@ Code CLI against a local mock.
182
191
 
183
192
  ## Status and limitations
184
193
 
185
- v0.3.5, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
194
+ v0.3.6, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
186
195
  Multi-node, FSDP and DeepSpeed are not supported. Accelerate works if you wrap after `accelerator.prepare`, without
187
196
  fp16 GradScaler or Accelerate's gradient accumulation; the HF Trainer works through `opal.hf.OpalTrainer`. Both are
188
197
  tested on CPU. Lightning should work through `opt.observe(loss)` but we have not tried it. The agent can make a run
@@ -202,6 +211,6 @@ worse: it cannot restart or roll back training, it can only fix its programs at
202
211
 
203
212
  ## License
204
213
 
205
- MIT, see [LICENSE](LICENSE).
214
+ MIT, see [LICENSE](https://github.com/xingjian-bai/opal/blob/main/LICENSE).
206
215
 
207
216
  *The name:* OPAL stands for OPtimizer with an Agent in the Loop.
@@ -328,7 +328,7 @@ Private locations and recorded identities are omitted from messages, code and pl
328
328
  calls.
329
329
 
330
330
  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
331
+ 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
332
  `max_cost_usd`. A change can have any number of these tags:
333
333
 
334
334
  - `common practice`: standard recipes, such as Muon, warmup, cosine or linear decay, clipping and AdamW betas.
@@ -485,6 +485,8 @@ On the GPU machine, `opal doctor --backend mailbox:/scratch/me/opal-box` checks
485
485
  whether a broker serves it. That machine needs no Claude Code. Set the model and effort on the broker with
486
486
  `opal broker ... --model claude-opus-5-5 --effort high`, the defaults, rather than in the run's `backend_options`.
487
487
  The broker's machine must stay awake for the whole run. On a Mac, use `caffeinate -i opal broker ...`.
488
+ The broker's ssh runs with `BatchMode=yes`, so it cannot ask for a password or a second factor. Log in with a key,
489
+ or open a connection it can share before you start it: `ssh -fNM -o ControlPath=~/.ssh/opal-%r@%h-%p me@gpu-host`.
488
490
  The run waits while no broker answers, as described under "When the agent is unavailable".
489
491
  Mailbox runs do not make code-change summaries during training.
490
492
  Run `opal report <run> --summarize` where Claude Code is logged in to add them.
@@ -1,3 +1,3 @@
1
1
  """The package, build metadata and backend clients share this version."""
2
2
 
3
- __version__ = "0.3.5"
3
+ __version__ = "0.3.6"
@@ -490,7 +490,13 @@ class _Folder:
490
490
  def _sh(self, cmd, data=None):
491
491
  # without data, give ssh an empty stdin: it would otherwise read (and forward) the broker's own stdin
492
492
  io = dict(input=data) if data is not None else dict(stdin=subprocess.DEVNULL)
493
- return subprocess.run(self.base + [cmd], capture_output=True, timeout=120, **io)
493
+ p = subprocess.run(self.base + [cmd], capture_output=True, timeout=120, **io)
494
+ if p.returncode == 255 and b"Permission denied" in p.stderr: # BatchMode: ssh cannot ask for a password
495
+ p.stderr = p.stderr.strip() + (
496
+ " (the broker's ssh cannot ask for a password: use an ssh key, or open a connection it can share "
497
+ f"first: ssh -fNM -o ControlPath=~/.ssh/opal-%r@%h-%p {self.ssh})"
498
+ ).encode()
499
+ return p
494
500
 
495
501
  def read(self, name):
496
502
  if not self.ssh:
@@ -148,6 +148,7 @@ def doctor_mailbox(spec):
148
148
 
149
149
 
150
150
  def doctor(a):
151
+ import os
151
152
  import platform
152
153
  import shutil
153
154
  import subprocess
@@ -163,6 +164,13 @@ def doctor(a):
163
164
  f"torch {torch.__version__}; CUDA available: {torch.cuda.is_available()}"
164
165
  + (f" ({torch.cuda.device_count()} x {torch.cuda.get_device_name(0)})" if torch.cuda.is_available() else "")
165
166
  )
167
+ if not torch.cuda.is_available() and (shutil.which("nvidia-smi") or os.path.exists("/dev/nvidiactl")):
168
+ built = f"built for CUDA {torch.version.cuda}" if torch.version.cuda else "a CPU-only build"
169
+ print(
170
+ f"this machine has an NVIDIA GPU, but torch {torch.__version__} ({built}) cannot use it: for GPU training, "
171
+ "install the torch build for your driver first (https://pytorch.org/get-started/locally/), or check "
172
+ "CUDA_VISIBLE_DEVICES"
173
+ )
166
174
  for name in ("codex" if a.backend == "codex" else "claude",):
167
175
  exe = shutil.which(name)
168
176
  ver = ""
@@ -209,7 +217,9 @@ def doctor(a):
209
217
  reply = (out.get("text") or "").strip()
210
218
  ok = out.get("returncode") == 0 and "READY" in reply
211
219
  print(
212
- f"live call: {'ok' if ok else 'failed'} ({out.get('seconds', 0):.0f} s; reply {reply[:80]!r}; error {out.get('error')})"
220
+ f"live call: {'ok' if ok else 'failed'} ({out.get('seconds', 0):.0f} s"
221
+ + (f", ${out['cost_usd']:.2f}" if isinstance(out.get("cost_usd"), (int, float)) else "")
222
+ + f"; reply {reply[:80]!r}; error {out.get('error')})"
213
223
  )
214
224
  if a.backend == "codex":
215
225
  print(f"Codex thread: {out.get('session')}; provider rollout: {out.get('rollout')}")
@@ -102,16 +102,26 @@ def lines(total_steps, sec_per_step, decisions, blocking=True, max_lag=50, model
102
102
  f"decisions not set: {decisions} scheduled instead of {capped_from}, so that the expected waiting stays "
103
103
  f"within {MAX_WAIT_FACTOR:g}x the run's own time (set decisions= to choose)"
104
104
  )
105
- if over and (decisions <= 1 or not explicit):
105
+ fewer = capped_decisions(decisions, run, blocking, sec_per_step, max_lag) if over else decisions
106
+ # a run shorter than one typical answer ends before the answer arrives when training does not wait for it
107
+ no_wait = (
108
+ "blocking=False lets training go on while the agent thinks"
109
+ if run >= FIRST_SECONDS[1]
110
+ else "blocking=False would not help: training would end before the first answer arrives"
111
+ )
112
+ if over and (
113
+ decisions <= 1 or not explicit or waiting(fewer, blocking, sec_per_step, max_lag) > MAX_WAIT_FACTOR * run
114
+ ):
115
+ n = decisions if not explicit and decisions > 1 else 1
106
116
  out.append(
107
- f"even {'one decision' if decisions <= 1 else f'{decisions} decisions'} wait{'s' * (decisions <= 1)} more "
108
- f"than {MAX_WAIT_FACTOR:g}x the run's own time; blocking=False lets training go on while the agent thinks"
117
+ f"even {'one decision' if n <= 1 else f'{n} decisions'} wait{'s' * (n <= 1)} more "
118
+ f"than {MAX_WAIT_FACTOR:g}x the run's own time; {no_wait}"
109
119
  )
110
120
  elif over:
111
121
  out.append(
112
122
  f"the expected waiting is more than {MAX_WAIT_FACTOR:g}x the run's own time: ask fewer times "
113
- f"(decisions={capped_decisions(decisions, run, blocking, sec_per_step, max_lag)} keeps it within "
114
- f"{MAX_WAIT_FACTOR:g}x) or let training go on while the agent thinks (blocking=False)"
123
+ f"(decisions={fewer} keeps it within {MAX_WAIT_FACTOR:g}x) or let training go on while the agent thinks "
124
+ f"(blocking=False)"
115
125
  )
116
126
  if (model, effort) != (MODEL, EFFORT):
117
127
  out.append(
@@ -125,7 +125,7 @@ class Privacy:
125
125
  def _path(self, match):
126
126
  path = match[0]
127
127
  # Avoid eating arithmetic such as `1 / 2`, or regex syntax such as /\w+/.
128
- if path in ("/", "//"):
128
+ if path in ("/", "//") or not re.search(r"[A-Za-z]", path): # also `(u-1)/1000`: no letters, no location
129
129
  return path
130
130
  for root in sorted(self.roots, key=len, reverse=True):
131
131
  if path == root:
@@ -1647,6 +1647,7 @@ class AgentOptimizer(
1647
1647
  decisions=self.worker.k if self.worker else 0,
1648
1648
  cost_usd=self.worker.recorded_cost if self.worker else 0,
1649
1649
  unknown_cost_decisions=sorted(self.worker.unknown_cost_decisions) if self.worker else [],
1650
+ agent_wait_seconds=round(self.agent_wait_seconds, 1), # the report's "training waiting time"
1650
1651
  **(dict(pending_decision=self.worker.k) if settling else {}),
1651
1652
  **(dict(usage=self.worker.usage) if self.worker and self.worker.usage is not None else {}),
1652
1653
  **(dict(eval_integrity=checked) if checked else {}),
@@ -1,9 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opal-optimizer
3
- Version: 0.3.5
3
+ Version: 0.3.6
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
 
@@ -144,7 +156,7 @@ Each number is one run, and each agent started from the task's own recipe:
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
 
150
162
  Our run records and final weights are on Hugging Face: [snow-leopard/opal-results](https://huggingface.co/datasets/snow-leopard/opal-results).
@@ -178,17 +190,17 @@ Our run records and final weights are on Hugging Face: [snow-leopard/opal-result
178
190
 
179
191
  | Option | Default | Meaning |
180
192
  |---|---|---|
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). |
193
+ | `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
194
  | `every` | 50 | Steps between checks: losses are read, a non-finite loss is caught, the analysis program runs. |
183
195
  | `blocking` | `True` | `False` or `"auto"` to keep training while the agent thinks. |
184
196
  | `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). |
197
+ | `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
198
  | `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). |
199
+ | `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
200
  | `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
201
 
190
202
  Gradient accumulation, GradScaler, checkpoints and resume, DDP, GPU machines without internet, privacy and cost are
191
- covered in the [guide](docs/GUIDE.md).
203
+ covered in the [guide](https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md).
192
204
 
193
205
  ### Experimental Codex backend
194
206
 
@@ -207,7 +219,7 @@ Code CLI against a local mock.
207
219
 
208
220
  ## Status and limitations
209
221
 
210
- v0.3.5, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
222
+ v0.3.6, under active development. Tested on one GPU and on DDP within one node (NCCL, eager and `torch.compile`).
211
223
  Multi-node, FSDP and DeepSpeed are not supported. Accelerate works if you wrap after `accelerator.prepare`, without
212
224
  fp16 GradScaler or Accelerate's gradient accumulation; the HF Trainer works through `opal.hf.OpalTrainer`. Both are
213
225
  tested on CPU. Lightning should work through `opt.observe(loss)` but we have not tried it. The agent can make a run
@@ -227,6 +239,6 @@ worse: it cannot restart or roll back training, it can only fix its programs at
227
239
 
228
240
  ## License
229
241
 
230
- MIT, see [LICENSE](LICENSE).
242
+ MIT, see [LICENSE](https://github.com/xingjian-bai/opal/blob/main/LICENSE).
231
243
 
232
244
  *The name:* OPAL stands for OPtimizer with an Agent in the Loop.
@@ -17,6 +17,11 @@ dependencies = [
17
17
  "numpy>=1.26.4", # torch's NumPy bridge
18
18
  ]
19
19
 
20
+ [project.urls]
21
+ Homepage = "https://github.com/xingjian-bai/opal"
22
+ Documentation = "https://github.com/xingjian-bai/opal/blob/main/docs/GUIDE.md"
23
+ "Blog post" = "https://xingjianbai.com/reactive-optimizers/"
24
+
20
25
  [project.optional-dependencies]
21
26
  examples = [
22
27
  "torchvision>=0.20; python_version < '3.13'",
@@ -18,11 +18,14 @@ Keep test data and labels out of the training process, evaluate the final checkp
18
18
  1. **Find the loop.** Locate the training script and the place where the optimizer and LR scheduler are built and
19
19
  where `optimizer.step()` is called. Note: number of optimizer steps in the run (`total_steps`), data-parallel setup
20
20
  (single GPU, DDP, Accelerate, HF Trainer, Lightning, FSDP/DeepSpeed), gradient clipping in the loop, GradScaler,
21
- gradient accumulation, EMA, checkpoint save/load.
21
+ gradient accumulation, EMA, checkpoint save/load, and whether parameter groups are added during training (for
22
+ example layers unfrozen later).
22
23
  2. **Check it is supported.** FSDP, ZeRO/DeepSpeed, tensor or pipeline parallelism and JAX are not supported: stop
23
24
  and tell the user. With an HF Trainer, use `opal.hf.OpalTrainer` (docs/GUIDE.md, "Hugging Face Trainer").
24
25
  With Accelerate, wrap after `accelerator.prepare` (GUIDE, "Accelerate"). Lightning is untested: say so before
25
- continuing.
26
+ continuing. All parameter groups must exist before `wrap`: for layers unfrozen later, create their group up front
27
+ with `requires_grad=False` and set it to True when they are unfrozen. With DDP, every rank must run the same number
28
+ of steps; uneven inputs under `Join` stop with an error.
26
29
  3. **Write the goal sentence with the user**: what the run trains and what it is judged by at the end (for example
27
30
  "ResNet-18 on CIFAR-10, judged by test accuracy after 30 epochs"). Ask; do not invent the metric.
28
31
  4. **Wrap.** Right after the optimizer and scheduler are created:
@@ -46,7 +49,10 @@ Keep test data and labels out of the training process, evaluate the final checkp
46
49
  machine has no agent, use `backend="mailbox:<folder on the GPU machine>"`, check it there with
47
50
  `opal doctor --backend mailbox:<folder>`, and give the user the broker command:
48
51
  `opal broker <folder> --ssh <user@gpu-host> --backend claude --model claude-opus-5-5 --effort high`. With a
49
- mailbox the model and effort are set on the broker, not in `backend_options`. The broker's machine must stay awake
52
+ mailbox the model and effort are set on the broker, not in `backend_options`. Over ssh, the GPU host needs
53
+ `python3` (3.6 or newer) on its non-interactive PATH; the doctor checks it. The broker's ssh cannot answer a
54
+ password prompt: if the host asks for one, tell the user to log in with a key or to open a shared connection
55
+ first (`ssh -fNM -o ControlPath=~/.ssh/opal-%r@%h-%p <user@gpu-host>`). The broker's machine must stay awake
50
56
  for the whole run (on a Mac: `caffeinate -i opal broker ...`); while no broker answers, the run waits.
51
57
  7. **Smoke test before the real run**: run about 100 updates twice with a reduced `total_steps`, once unwrapped and
52
58
  once wrapped with `backend="keep"` (no agent, no cost), and check that the losses match step for step. Time the
@@ -55,7 +61,8 @@ Keep test data and labels out of the training process, evaluate the final checkp
55
61
  run's `total_steps` and the measured seconds per update (add `--decisions K`, `--every E`, `--no-blocking` or
56
62
  `--mailbox` as the wrap call will have them). Show the user its lines as they are: the run's time without the
57
63
  agent, the expected waiting for the agent (a range, and as a multiple of the run's own time) and the expected
58
- cost. If `decisions` is not set, OPAL lowers it so that the waiting stays within 4x the run's own time; say so.
64
+ cost. If `decisions` is not set, OPAL lowers it so that the waiting stays within 4x the run's own time, but keeps
65
+ at least one decision, even when one already waits longer (short runs); say so.
59
66
  Ask for `max_cost_usd`, and give the launch command only after the user confirms. Then switch to the real
60
67
  backend, with `max_cost_usd`. The agent defaults to claude-opus-5-5 at effort high, the blog's
61
68
  setting; set `backend_options=dict(model=..., effort=...)` only if the user asks (the run then warns that the blog's
File without changes
File without changes