Skip to content

feat: one prime monitor, sft online evals on the training run - #4228

Merged
hallerite merged 17 commits into
mainfrom
feat/prime-monitor-unify
Nov 2, 2026
Merged

hallerite merged 17 commits into
mainfrom
feat/prime-monitor-unify

Conversation

@mikasenghaas

@mikasenghaas mikasenghaas commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Summary

Part of the monitors stack: #4228 → #4316 → #4317 → #4244. Base of the stack.

  • One prime monitor. PrimeMonitor and PrimeMonitorConfig replace the train and eval pair. [monitors.prime] and --no-monitors.prime are unchanged.
  • One monitors config. MonitorsConfig (wandb, file, prime) replaces TrainMonitorsConfig and EvalMonitorsConfig. The online-eval process takes the trainer's monitors as they are. The RL trainer gets a monitors.prime field, which it doesn't set up yet.
  • The process that supervises the launch registers the Prime run, and every component attaches to it. This is how a hosted launch works: the platform pre-creates the run, passes RUN_ID to each pod, and sets the final status from the JobSet.
    • Local: the rl or sft launcher.
    • SLURM, single node: the rl or sft launcher inside the job. The submitting process registers nothing.
    • SLURM, multi node: python -m prime_rl.entrypoints.run_owner {rl,sft} <config> srun ... wraps the job's launch step. It fails the run when the step exits non-zero, or when SLURM sends SIGTERM (cancel, time limit).
  • The Prime run gets the whole launch config. An rl launch registers the full RL config (trainer, inference, orchestrator, deployment), not only the orchestrator's. Multi-node SLURM jobs write the full rl.json and sft.json for the run owner. The multi-node SFT trainer runs its own trainer.json, as the RL components already do.
    • The run's id becomes the run identity: PRL_RUN_ID, and with it the W&B run id and the episodes' run.id. The launcher also exports it as RUN_ID, which each component's prime monitor attaches to.
    • A launch that fails marks the run FAILED with the failed component's error, e.g. RuntimeError: Trainer failed with exit code -9. The SDK leaves an attached run's failure to the process that registered it.
    • A component started without a launcher still registers its own run.
  • SFT online evals log to the same run, like RL. The online-eval process uploads its eval metrics and every eval episode to the sft run. Before, it opened one Prime evaluation per eval epoch.
    • The process that registered the run finishes it once every component exited, and fails it when a component fails. Attached components only flush, so no component can finish the run while another still logs to it. On hosted training the platform sets the final status from the JobSet.
    • A paused run no longer gets reported as crashed: the SDK's exit hook is dropped after the flush.
  • Standalone uv run eval still opens one Prime evaluation per source and epoch.
    • Without a Prime API key, it fails at launch with the prime login hint, like rl and sft. The evaluations open later, when each epoch starts.
  • Fewer helpers.
    • The links record is keyed by the monitor's config field (prime, wandb), and run_links takes the MonitorsConfig.
    • The platform record is written through update_record.
    • register takes the config directly and turns the SDK's missing-key error into the prime login hint.
  • The orchestrator's monitor producer is orchestrator, the name every other place uses (orchestrator.log, the W&B labels).

Breaking

  • SFT online evals no longer create Prime evaluations. Their results are on the sft run's Prime page, and an sft run's monitors/prime/run.json no longer lists evaluations.
  • With the prime monitor on, PRL_RUN_ID (and so the W&B run id) of an rl or sft launch is the Prime run's id instead of a random uuid.
  • A custom multi-node SLURM template (slurm.template_path) must wrap its launch srun in python -m prime_rl.entrypoints.run_owner {rl,sft} <config>, as the bundled templates do. Without it, nothing registers the shared run.
  • A multi-node SFT job's sft.json is now the full config. The trainer reads trainer.json; a custom multi-node SFT template must pass {{ config_path }} (the trainer's) to the trainer and {{ run_config_path }} to the run owner.
  • The Prime run's config of an rl launch is the full RL config instead of the orchestrator config.
  • An RL config can no longer turn monitors.wandb or monitors.file on in one component only: RLConfig rejects it ("must match across components"). Set the shared [monitors.<name>] instead.
  • A hosted orchestrator no longer finishes its attached run; the platform marks it COMPLETED from the JobSet, as it already did.
  • The orchestrator's file-monitor producer is orchestrator instead of orch. Its annotation stream moves from monitors/file/traces/annotations/orch to .../annotations/orchestrator, and its metrics.jsonl rows carry "producer": "orchestrator".

Verification

Stack head (#4244 at 450707973), 2026-10-30:

Run Prime Result
Hosted RL, reverse-text, 10 steps (prime train --pr 4244 --image-tag v0.10.1.dev143) u68lwuthm9f7e87fxb2pnq5f COMPLETED by the platform; trainer and orchestrator attached via $RUN_ID
Hosted SFT, fake data, online evals every 5 steps uvmvu17v6bks0kbip83xubnd COMPLETED; online evals at steps 0, 5, 10 in the same run
Local rl, reverse-text, 5 steps g4n7oc8hqhz2pfqfi5hj9nno COMPLETED by the launcher; W&B run has the same id
Local sft, 10 steps, evals every 5 dxz2k1etkdbh3go026y96td4 COMPLETED by the launcher; eval metrics at steps 0, 5, 10
Local eval evaluation exit 0

On hosted runs the platform stores the submitted TOML as the run's config; the full resolved config is registered for local and SLURM launches only.

Run ownership:

  • Local RL, 3 steps: qxnhsl4ngd9qocvlcqzbbquw COMPLETED. The orchestrator only flushed; the launcher finished the run.
  • run_owner with a step that exits 0: sppd7r2l1kdposmws2tarp8g COMPLETED.
  • SIGTERM to run_owner while the step runs: the step received the forwarded TERM and finished a 2 s teardown before the owner exited 143; rfev5w1qbxnxlko6dwd22apt FAILED with RuntimeError: SLURM terminated the job.
  • uv run rl @ examples/basic/reverse-text/rl.toml --max-steps 3 --no-monitors.wandb, local: vtcuhcp16vigkskxu02d763l COMPLETED. Its run_config on the platform has trainer, inference, orchestrator and deployment.
  • Multi-node dry runs: rl.json loads as a multi-node RLConfig and registers trainer, inference, orchestrator, deployment and slurm. sft.json loads as a multi-node SFTConfig, and the script's TRAIN_CONFIG_PATH is trainer.json.
  • Multi-node RL and SFT dry runs (examples/advanced/qwen3-30b-a3b/math.toml, .../sft/h200/base.toml) render scripts that pass bash -n. run_owner loads the configs the scripts point to.
  • run_owner rl <orchestrator.json> bash -s <<'STEP_SH' ... exit 3: the step saw RUN_ID=PRL_RUN_ID=<id>, the wrapper exited 3, and Prime shows run ekc8dcf8agutxcrizts1bkbe FAILED with RuntimeError: The SLURM job step exited with code 3.
  • SIGTERM to a running run_owner: exit 143, and Prime shows run q4g9hv95qoqsl06yeacle2jd FAILED with RuntimeError: SLURM terminated the job.
  • test_slurm_run_owner_fails_the_run_with_its_step: the owner registers the run (not attached), exports its id, and fails the run only when the step fails.

Latest head, local reverse-text:

Run Prime run Status
uv run rl @ examples/basic/reverse-text/rl.toml --max-steps 5 --no-monitors.wandb kbv529xg6xoop15a0yf7ijwq COMPLETED
uv run sft @ examples/basic/reverse-text/sft.toml --max-steps 10 --eval.interval 5 --no-ckpt bwd98te1u76s82qqrloaty8i COMPLETED; trainer and online eval attached via $RUN_ID
uv run eval @ configs/debug/eval/single-turn.toml evaluation banner shows the evaluation link

Earlier head, with episode counts and the failure path:

Run Prime link Local = Prime episodes Arrival → ingested (p50 / max) Status
uv run sft @ examples/basic/reverse-text/sft.toml --max-steps 10 --eval.interval 5 --no-ckpt training run 96 eval (3 epochs × 32) 1.0 s / 1.9 s COMPLETED
uv run rl @ examples/basic/reverse-text/rl.toml --max-steps 5 --no-monitors.wandb training run 707 train + 512 eval 1.0 s / 1.8 s COMPLETED
Failure path: the RL run above, with the trainer killed (SIGKILL) mid-run training run — — FAILED: RuntimeError: Trainer failed with exit code -9
  • SFT run: the trainer and the online-eval process both logged Logging metrics and episodes to Prime run neakqq9sbr4s4o0942oeb2ys (...) (attached via $RUN_ID).
  • RL run: the orchestrator attached through $RUN_ID. An episode read back from Prime Traces carries run.id = fnog09c4s2updmlso4omjoh7, so PRL_RUN_ID is the Prime id.
  • Failure path: components attach to the launcher's run, and the SDK skips a failed finish from an attached run. So the launcher marks the run failed itself, with the component's error.
  • Unit tests: pytest tests/unit --ignore=tests/unit/train tests/unit/train/sft/test_prime_monitor.py: 810 passed, with main merged.
  • Hosted: not run on this head. fix: mark live config fields instead of freezing the rest #4318, which unblocked hosted RL on main, has merged.

🤖 Generated with Claude Code


Note

Medium Risk
Changes run identity, Prime lifecycle ownership, and multi-node SLURM wiring; custom templates or mixed monitor settings can break tracking or fail validation.

Overview
Unifies Prime and monitor configuration so training uses one platform run and one config shape everywhere.

Prime: PrimeTrainMonitor / PrimeEvalMonitor and separate train/eval monitor config types become PrimeMonitor and shared MonitorsConfig. The rl/sft launcher (or multi-node run_owner) registers the platform run via launch_run, sets PRL_RUN_ID / RUN_ID / W&B run id, and finishes or fails the run when the launch ends; worker processes attach and only flush. RL registration sends the full RL config, not orchestrator-only. SFT online evals now upload metrics and episodes to that same training run instead of opening per-epoch Prime evaluations; standalone uv run eval still opens one evaluation per env/epoch.

Launch / SLURM: Bundled multi-node templates wrap the job step in python -m prime_rl.entrypoints.run_owner. Multi-node SFT writes full sft.json for the owner and trainer.json for torchrun. run_links takes MonitorsConfig; link records use prime / wandb keys.

Other: RL validates monitors.wandb and monitors.file are on or off consistently on trainer and orchestrator. Orchestrator file-monitor producer is orchestrator (annotation path rename). Docs/skills updated accordingly.

Reviewed by Cursor Bugbot for commit 7e0c55f. Bugbot is set up for automated code reviews on this repo. Configure here.

One PrimeMonitor and one PrimeMonitorConfig replace the train and eval
pair. A training run's processes log to one Prime training run: the rl
orchestrator or sft trainer registers it, and an sft online-eval
process attaches to the trainer's run instead of opening one Prime
evaluation per epoch. PRL_RUN_FINISHER names the process that finishes
a shared run; the sft launcher sets it to the online-eval process,
which drains its final evals after the trainer exits. The other
processes flush and drop the SDK's crash hook, which also stops a
paused run from being reported as crashed. A standalone eval still
opens one evaluation per source and epoch.

Monitors share more of the base class: one MonitorContext replaces
per-monitor init arguments, every hook but log_metrics defaults to a
no-op, and the fan-out helpers share one error path. Episodes are
logged once, on arrival; the unused effective-cohort log calls are
gone, since batch membership already travels as annotations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread src/prime_rl/monitors/prime.py Resolved
Comment thread src/prime_rl/monitors/prime.py Resolved
mikasenghaas and others added 5 commits October 24, 2026 14:23
Drop the monitor base refactor (MonitorContext, the subset removal, the
fan-out helper, the config class renames) from this change: the files
it touched go back to main. PrimeMonitor keeps main's monitor API and
the layout of the old train monitor, with the standalone-eval methods
folded in; the configs keep their class names, with one
PrimeMonitorConfig in place of the train and eval pair.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The launcher registers the run before it starts any component and makes
the run's id the run identity: $PRL_RUN_ID (and with it the W&B run id)
and $RUN_ID, which every component's prime monitor attaches to - the
same shape as a hosted launch, whose platform pre-creates the run.

The sft online-eval process no longer polls run.json for the trainer's
run, and the record drops its attempt stamp. An attached run leaves its
failure to the process that registered it, so a launch that fails marks
the run failed. Components started without a launcher still register
their own run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ProcessGroup.wait chains the component's error to its SystemExit, so
the launcher marks the run failed with that error instead of
"SystemExit: 1". The exit code stays 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

# Conflicts:
#	packages/prime-rl-configs/src/prime_rl/configs/rl.py
mikasenghaas and others added 2 commits October 28, 2026 23:35
With one prime monitor config, TrainMonitorsConfig and
EvalMonitorsConfig were the same type. The online-eval process now
takes the trainer's MonitorsConfig as is. The RL trainer gains a
monitors.prime field that it does not set up yet.

Also export RUN_ID in the multi-node SFT script, which renders
PRL_RUN_ID explicitly instead of inheriting the submitter environment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Key the links record by the monitor's config field ("prime",
  "wandb"); run_links takes the MonitorsConfig.
- Write the platform record through update_record instead of four
  wrappers.
- Read $PRIME_RUNS_MODE inline; register turns the SDK's missing-key
  error into the login hint.
- Fold registration() into register().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mikasenghaas and others added 2 commits October 29, 2026 00:18
The producer stamps each metrics row and names the orchestrator's
annotation stream, so it now matches the component name used
everywhere else (orchestrator.log, PRL_RUN_FINISHER, W&B labels).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	packages/prime-rl-configs/src/prime_rl/configs/orchestrator.py

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread src/prime_rl/monitors/prime.py Resolved
Comment thread src/prime_rl/monitors/prime.py Resolved
# Conflicts:
#	packages/prime-rl-configs/src/prime_rl/configs/orchestrator.py
Comment thread src/prime_rl/dashboard/static/app.js Resolved

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread src/prime_rl/monitors/prime.py Resolved
mikasenghaas and others added 2 commits October 29, 2026 22:05
Evaluations open when their epoch starts, so a missing key only showed
as a warning per env and the eval finished with nothing on Prime. Also
name the dashboard's orchestrator key pattern in full.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The submitter registered the run and exited after sbatch, and the SDK
never fails an attached run, so a failed SLURM job left its run
RUNNING. The supervising process now registers the run: the local
launcher, the in-job launcher on a single node, or a run owner that
wraps a multi-node job's step and fails the run when the step fails
or SLURM sends SIGTERM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread src/prime_rl/entrypoints/rl.py Resolved
mikasenghaas and others added 2 commits October 29, 2026 22:58
An rl launch registered only its orchestrator config, so the run lost
the trainer, inference and deployment sections. The launcher now
registers the full RL config. Multi-node SLURM jobs also write the
full rl.json and sft.json for the run owner; the SFT trainer runs its
own trainer.json, as the RL components already do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The process that registers a Prime run now also finishes it, once
every component exited: the rl or sft launcher, or the multi-node
SLURM run owner. Attached components only flush, so no component can
finish the run while another still logs to it, and
PRL_RUN_FINISHER goes away. The run owner forwards SLURM's SIGTERM to
srun and waits for the step instead of killing it.

An RL config can no longer turn a monitor on in one component only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7e0c55f. Configure here.

Comment thread src/prime_rl/entrypoints/run_owner.py Resolved
The step started before launch_run exported the run's id, so its
components had no $RUN_ID to attach to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mikasenghaas
mikasenghaas added this pull request to stack #4338 October 30, 2026 00:16

@hallerite hallerite left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the shared monitor configuration, run ownership, and local/SLURM launch wiring. The supervising process registering and finalizing the run, with components attaching and flushing, is a sound model.

@hallerite
hallerite merged commit ccff0a5 into main Nov 2, 2026
11 checks passed
@hallerite
hallerite deleted the feat/prime-monitor-unify branch November 2, 2026 17:20
hallerite pushed a commit that referenced this pull request Nov 2, 2026
## Summary

Part of the monitors stack: #4228 → **#4316** → #4317 → #4244. Stacked
on #4228.

- The RL trainer logs its per-step metrics to the run's Prime run. It
builds on #4170 by @kevinjosethomas and stacks on #4228.
- The trainer sets up the prime monitor from `trainer.monitors.prime`
and attaches to the run that the `rl` launcher registered (`$RUN_ID`).
  - The orchestrator still uploads the episodes and its own metrics.
- The trainer only flushes its uploads. The process that registered the
run finishes it once the trainer and the orchestrator exited (#4228).
- `monitors.prime` must be on or off in both components, like
`monitors.wandb` and `monitors.file`. `--orchestrator.no-monitors.prime`
alone is rejected, so the trainer never waits for a run the launcher did
not register.
- The shared `[monitors.prime]` reaches the trainer and the
orchestrator, like `[monitors.wandb]` and `[monitors.file]`.
`--no-monitors.prime` turns it off for both.
- A trainer started without the `rl` launcher has no run to attach to.
In online mode it fails with a hint, like the SFT online-eval process:
launch it with `rl`, or pass `--no-monitors.prime`.
- The trainer and the orchestrator keep their metric names. Both log
`time/step` and `time/save_ckpt`, as they do into the shared W&B run on
main. A rename is a separate change.

Compared with #4170, the trainer needs no `run.json` lookup, no
pending-metrics queue and no sidecar mode: it attaches through
`$RUN_ID`, like every other component since #4228.

## Breaking

- A standalone RL trainer (`torchrun src/prime_rl/trainer/rl/train.py
...`) needs `--no-monitors.prime`, or `PRIME_RUNS_MODE=disabled`.

## Verification

- Hosted RL, reverse-text, 10 steps, from the stack head:
[u68lwuthm9f7e87fxb2pnq5f](https://app.primeintellect.ai/dashboard/training/u68lwuthm9f7e87fxb2pnq5f)
COMPLETED. The hosted trainer attached to the platform's run;
`mismatch_kl/*`, `entropy/*`, `importance_ratio/*`, `loss/*`, `optim/*`
and `perf/mfu` are on Prime at every step 1–10, next to the
orchestrator's `train/*`.


- `uv run rl @ examples/basic/reverse-text/rl.toml --max-steps 5
--no-monitors.wandb`, local:
[ek4xpjtfdnghv2yuae0252au](https://app.primeintellect.ai/dashboard/training/ek4xpjtfdnghv2yuae0252au)
COMPLETED.
- `trainer.log` and `orchestrator.log` both show `(attached via
$RUN_ID)`.
- The Prime run has the trainer's metrics (`optim/grad_norm`,
`perf/mfu`, `loss/*`) next to the orchestrator's.
- Local RL, 3 steps, latest head:
[vconz7g0fm720wr9mtcrqyht](https://app.primeintellect.ai/dashboard/training/vconz7g0fm720wr9mtcrqyht)
COMPLETED, with the trainer's and the orchestrator's metrics at every
step.
- Colliding keys: in
[h9ia98nk6zc9ndruvpahy4gw](https://app.primeintellect.ai/dashboard/training/h9ia98nk6zc9ndruvpahy4gw),
Prime keeps the last write per key and step, so `time/step` and
`time/save_ckpt` show the trainer's values.

🤖 Generated with [Claude Code](https://claude.com/claude-code)





<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes default monitoring behavior for standalone RL trainer launches
and tightens cross-component Prime config validation; metrics upload is
best-effort but misconfiguration now fails fast at startup.
> 
> **Overview**
> The RL trainer now streams **per-step metrics** (loss, MFU,
`optim/grad_norm`, timing, etc.) into the same **Prime platform run** as
the orchestrator, instead of Prime being orchestrator-only.
> 
> Shared **`[monitors.prime]`** (and `--no-monitors.prime`) apply to
**both** trainer and orchestrator, with validation that Prime is enabled
or disabled on both—matching W&B and file monitors. The trainer
**attaches via `$RUN_ID`** from the `rl` launcher and only flushes
uploads; it does not register or finish the run.
> 
> **Breaking:** a standalone `torchrun … train.py` run must pass
**`--no-monitors.prime`** (or disable Prime runs in env), or online mode
errors with a launch hint. Docs and the parallel-invariance regression
test were updated accordingly.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
b577b55. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
hallerite pushed a commit that referenced this pull request Nov 2, 2026
## Summary

Part of the monitors stack: #4228 → #4316 → **#4317** → #4244. Stacked
on #4316.

- Adds `tests/unit/monitors/test_monitor_load.py`: one test,
parametrized over monitor (file, prime, W&B) × episode count (10, 100,
1000).
- Each episode is agentic and heavy: 100 turns, 100k tokens, numpy
streams as the env-server wire delivers them (about 2 MB per upload).
  - Interleaved with 2000 metric rows of 200 keys.
- The episodes share one trace object. Every monitor serializes each
episode separately, so the load is the same at a fraction of the memory.
- The test logs through `monitors.log` as the orchestrator does, under
the orchestrator's `GarbageCollector` (no automatic full collections).
An asyncio ticker measures how late the event loop wakes.
- The test checks that everything logged arrives:
- **File monitor:** every episode in the trace stream, and steps 1 to
2000 in `metrics.jsonl`.
- **Prime monitor:** the real SDK run and uploader threads against fake
Prime Traces and platform endpoints. The fake traces endpoint gzips like
the real client.
- **W&B monitor:** the real SDK in offline mode. W&B takes metrics only.
- The test fails when a monitor stalls the loop: p99 lag of 50 ms or
more, max lag of 250 ms or more, or one `log()` call of 250 ms or more.
- `prime-1000` is an expected failure (strict). The SDK uploader gzips
each episode at level 9, about 70 ms here, so 1000 episodes logged at
once fill its queue. `log()` then waits for room for up to 5 s on the
orchestrator's arrival path, and the SDK drops what stays queued. A TODO
in the test tracks the fix; PrimeIntellect-ai/prime#1129 is the first
part (gzip level 1).
- The test is CPU-only, needs no network and is marked `slow`. CI's unit
job runs it (it deselects only `gpu`). It takes about 110 s.

## Verification

`pytest -s tests/unit/monitors/test_monitor_load.py` on the dev box,
with main's `prime-traces` pin: 8 passed, 1 xfailed.

| Monitor | Episodes | Loop lag p99 | Loop lag max | `log()` p99 |
`log()` max |
|---|---|---|---|---|---|
| File | 10 / 100 / 1000 | 3.6 / 3.4 / 4.7 ms | 3.7 / 5.6 / 4.9 ms | 0.1
/ 5.0 / 5.3 ms | 7.9 / 9.9 / 7.8 ms |
| Prime | 10 / 100 | 5.2 / 7.9 ms | 6.0 / 10.5 ms | 0.4 / 0.4 ms | 10.1
/ 15.4 ms |
| Prime | 1000 | xfail: 7 of 1000 episodes dropped ("Upload queue full
after 5.0s"), one `log()` call blocked 5 s | | | |
| W&B (offline) | 10 / 100 / 1000 | 1.6 / 1.3 / 1.1 ms | 6.0 / 1.6 / 1.4
ms | 0.6 / 0.5 / 0.6 ms | 6.1 / 0.8 / 0.8 ms |

Without the orchestrator's GC setup, a full collection pauses the loop
for 220–260 ms in the 1000-episode cases.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Test-only change with no production code paths modified; it exercises
existing monitor integrations under load.
> 
> **Overview**
> Adds **`tests/unit/monitors/test_monitor_load.py`**, a slow unit test
that drives **file**, **Prime**, and **W&B (offline)** monitors through
**`monitors.log`** the way the orchestrator does—heavy agentic episodes
(up to 1000 × 100 turns / ~100k tokens) interleaved with **2000** wide
metric rows, under the orchestrator **`GarbageCollector`** fixture.
> 
> For each monitor × episode count (**10 / 100 / 1000**), it asserts
**complete delivery** (trace chunks / fake Prime endpoints / W&B metric
steps) and **event-loop responsiveness** (p99 lag **&lt; 50 ms**, max
lag and single **`log()`** **&lt; 250 ms**). **Prime @ 1000 episodes**
is **`xfail` (strict)** documenting **`prime_runs`** upload-queue
backpressure that can block **`log()`** for seconds and drop episodes.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
bb9dbad. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
hallerite pushed a commit that referenced this pull request Nov 2, 2026
)

## Summary

Part of the monitors stack: #4228 → #4316 → #4317 → **#4244**. Stacked
on #4317.

- No net change against #4317. Following review, a prime or W&B monitor
without credentials still fails the launch, as it does from #4228 on.
Most runs start from an agent, which can easily miss a warning. The
error has the login steps and the `--no-monitors.*` flag, so the agent
can act on it, and turning a monitor off stays explicit.
- The earlier warn-and-continue change is reverted in 9c2e9cd. This PR
can close without merging.

## Verification

- Without a Prime key, `launch_run` fails before the components start:
  ```
RuntimeError: The prime monitor found no Prime API key. Authenticate
with `prime login` (or set PRIME_API_KEY), or turn the monitor off with
--no-monitors.prime
  ```
- `pytest tests/unit/utils/test_links.py
tests/unit/train/sft/test_prime_monitor.py`: 19 passed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes default launch behavior for observability (credentials no
longer gate local runs) while keeping strict failure for attached
platform runs and authenticated registration errors.
> 
> **Overview**
> **rl**, **sft**, and standalone **eval** no longer abort when Prime or
W&B is enabled but no API key is present. Each monitor logs a one-line
warning (with `prime login` / `wandb login` or `--no-monitors.*`
guidance), runs in a **disabled** mode, and the rest of the launch
continues.
> 
> The **rl/sft launcher** (and multi-node **run_owner**) catches Prime
registration’s missing-key error once, warns, sets
**`PRIME_RUNS_MODE=disabled`**, and skips **`RUN_ID`** so child
processes don’t repeat the warning. **PrimeMonitor** and **W&B** map
their SDK’s “no API key” errors to the same disabled path; disabled
monitors register **no** platform links, so the launch banner doesn’t
block on them.
> 
> **Still fails at launch:** attaching via **`RUN_ID`** without a key
(empty platform page), Prime registration errors **with** a key
(team/allowlist), and other W&B **`UsageError`**s (e.g. bad project).
> 
> Docs for **training** and **eval** are updated to describe
warn-and-continue instead of hard failure; unit tests cover the new
paths.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
9c2e9cd. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
garrett361 added a commit that referenced this pull request Nov 4, 2026
Since #4228 every sft launch registers a run on the Prime platform and
streams its metrics there. The suite only reads metrics.jsonl from the
file monitor, so the Prime monitor would add a platform run per launch
with a key, and fail every launch at startup without one. Integration
tests and the parallel-invariance test already pass --no-monitors.prime.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants