Skip to content

Discard infrastructure failures in agentic training and evaluation - #7

Merged
celve merged 6 commits into
LIN-1985/investigate-existing-recipes-forfrom
LIN-2121/stop-training-cua-infrastructure
Oct 21, 2026
Merged

celve merged 6 commits into
LIN-1985/investigate-existing-recipes-forfrom
LIN-2121/stop-training-cua-infrastructure

Conversation

@celve

@celve celve commented Oct 21, 2026

Copy link
Copy Markdown
Owner

An agentic episode that fails because the sandbox service or the model service broke used to train as an attempt the policy failed. GRPO then pushed down whatever the policy did in that episode. Evaluation scored the attempt 0, and crashed outright when every attempt of a dataset aborted. This PR lets harnesses discard such episodes, discards them in Miles's bundled Harbor and HUD paths, and leaves them out of evaluation.

The rule

Discard only what the policy cannot have caused. Anything it can cause stays scored, or it would learn to trigger that outcome to escape a low reward.

Outcome Result
The sandbox was never ready, or the harness could not be set up, before the agent started discarded
The model service or the environment host became unreachable discarded
A session that recorded no model call, or whose samples could not be collected discarded, as main already did
A wall-clock timeout after the agent started, a step or turn limit, max_seq_len scored 0
The sandbox was lost mid-episode, or the verifier crashed scored 0

Changes

Training:

  • InfraAbort(exit_status) in miles.rollout.agentic.agent_function. An agent function raises it to mark its sample ABORTED with the cause in metadata["exit_status"], so Miles drops the group. It keeps its cause across the subprocess agent boundary, and when collecting the session's samples then fails. Returning None or raising anything else still keeps the session, scored by the reward model.
  • On the session path, Miles's own aborts now record a cause: NoModelCalls, CollectFailed or AllTruncated.
  • rollout/aborted/drop_<exit_status> counts dropped groups by cause, in both synchronous loops and the fully-async buffer.
  • --max-consecutive-aborted-groups fails a synchronous rollout once that many groups in a row abort. It first aborts in-flight generation, agent abort hook included. The default is 4 × the rollout batch size, and 0 disables it. Before this, a sandbox outage made the loop draw new prompts forever.

Examples:

  • Harbor, in-process (the path computer-1 runs on):
    • A trial that failed before its agent started raises SandboxUnavailable or AgentSetupFailed. Harbor stamps each phase's timing when the phase begins; a multi-step trial counts as started once any step ran.
    • A trial whose in-process agent lost the model service raises ServerUnreachable. That means litellm's APIConnectionError or ServiceUnavailableError, on the trial or on any step of a multi-step trial.
    • Everything else after the agent started still scores 0. That includes installed agents' Api* errors: Harbor derives those by regex over the agent's own output, which the policy writes.
  • HUD: some episodes are now ABORTED instead of FAILED with reward 0, whose 0 still entered the group's baseline under remove_sample. They are: an episode that never launched (HUD 0.6 returns a run with no runtime rather than raising), one whose first model request failed (HUD records that as an AgentStep with an error and no response), and one where task.run raised. A graded episode keeps its real reward.

Evaluation (log_eval_rollout_data):

  • Every sample of an aborted attempt is left out of the score.
  • eval/<dataset>-aborted_ratio (per attempt) and eval/<dataset>-aborted/<exit_status> counts are logged.
  • A dataset whose every attempt aborted logs no score rather than 0. On main it crashed on round(None).
  • pass@k is computed over the prompts whose attempts were all scored, taking one reward per attempt. Attempts are grouped by the dataset's own n_samples_per_eval_prompt, the value the eval loops number attempts by.
  • Datasets without samples, and samples that did not abort but have no reward, are scored as before.

Relation to upstream

The training-side interface (InfraAbort, the exit_status names, the drop metric) matches Tao Lin's radixark/miles#2801 and radixark/miles#2802, so the two can converge. Both have been open since August, and #2801 no longer applies to main. For Harbor, this departs from nanjiangwill's radixark/miles#3617. That PR discards installed agents' ApiInternalServerError and kin, which a policy can trigger by printing the matching text. Nothing upstream covers evaluation.

Left out:

  • The NeMo Gym and OpenEnv agent-function changes in #2802, which aren't on the computer-use path.
  • Keeping a group's surviving trajectories when one aborts (nanjiangwill's partial-group PRs), which only saves compute.

Verification

All on CPU: macOS, Python 3.12, with the CPU-CI requirements and sglang-miles source on PYTHONPATH.

  • New tests cover each piece. On main, the eval tests fail, including the round(None) crash.
  • Neighbouring suites: tests/fast/rollout, tests/fast/ray/rollout, tests/fast/examples/experimental, tests/fast/utils and tests/fast/router pass, except tests that fail the same way on main on this machine: the session-server benchmark, the Anthropic conversion contract, process and worker lifecycle, Ray object-store and node-IP fixtures, and HTTP concurrency timing.
  • End to end: Miles's real rollout function, session server and subprocess agent runner, against a fake model backend. The first prompt's agent made one model call, then raised InfraAbort("SandboxUnavailable"). The rollout dropped that group, refilled with the next prompt, kept its three-turn episode, and reported rollout/aborted/drop_SandboxUnavailable: 1.
  • Review: a second agent reviewed twice, read-only. Round 1 found four pass@k and wording issues in the eval change. Round 2 found the installed-agent regex loophole, multi-step step exceptions, HUD's error AgentStep, a lost cause on HTTP collection errors, and a guard that stopped without draining. All are fixed in the last commit.

celve added 5 commits October 21, 2026 13:19
An eval attempt that ends ABORTED (a sandbox that never came up, a session
that could not be collected, an agent function's InfraAbort) has reward None,
and the default eval logger scored it 0.0, so one dead sandbox lowered the
held-out score as if the policy had failed the task. When every attempt of a
dataset aborted, the sample metrics crashed on round(None).

The default logger now drops every sample of an aborted attempt before
scoring, logs eval/<dataset>-aborted_ratio (per attempt, not per sample) and
eval/<dataset>-aborted/<exit_status> counts, logs no score when nothing is
left, and computes pass@k over prompts whose attempts were all scored.
Datasets without samples, and samples with a missing reward that did not
abort, are scored as before.
…count

With aborted attempts left out, pass@k decided a prompt was complete by
counting sample rows, so an attempt split into several samples counted
several times. It also grouped indices by the global
--n-samples-per-eval-prompt, while both eval loops number a dataset's
attempts by its own resolved value, so a dataset with an override was cut
into fabricated prompts. A custom eval output without sample indices crashed.

Rewards are now taken once per attempt, prompts are grouped with the
dataset's resolved n_samples_per_eval_prompt (the global value when no
dataset config matches), and pass@k is skipped with a warning when samples
carry no index. The fixed-size reshape for datasets with nothing aborted
uses the dataset's value too.
An agent function had no way to say that an episode failed for a reason the
policy could not have caused. Returning None, or raising, kept whatever the
session recorded, and the reward model then scored it 0, so GRPO pushed down
what the policy did in an episode the sandbox service broke.

- InfraAbort(exit_status), in miles.rollout.agentic.agent_function: an agent
  function raises it to mark its sample ABORTED with the cause in
  metadata["exit_status"], so the group is dropped. It keeps its cause across
  the subprocess agent boundary.
- Miles's own aborts on the session path record a cause too: NoModelCalls,
  CollectFailed and AllTruncated.
- rollout/aborted/drop_<exit_status> counts dropped groups by cause, in both
  synchronous rollout loops and the fully-async buffer.
- --max-consecutive-aborted-groups, 4 x the rollout batch size by default and
  0 to disable, fails a synchronous rollout whose groups keep aborting, rather
  than drawing new prompts forever while the sandbox service is down.

The interface matches Tao Lin's upstream InfraAbort pull request (2801), so
the two can converge.
Harbor, in-process (the path computer-1 runs on):
- A trial that failed before its agent started raises InfraAbort:
  SandboxUnavailable when the sandbox never became ready, AgentSetupFailed
  when the harness could not be installed. Harbor stamps each phase's timing
  when the phase begins, and a multi-step trial counts as started once any
  step ran.
- A trial whose agent lost the model service also raises it
  (ServerUnreachable). That covers Harbor's typed errors for installed agents
  and litellm's APIConnectionError and ServiceUnavailableError for in-process
  ones such as computer-1.
- Everything else after the agent started still scores 0, timeouts included.

HUD: an episode that never ran, or recorded no model call, is now ABORTED
rather than FAILED with reward 0. HUD 0.6 returns a failed run instead of
raising when provisioning, connecting or starting the task fails, and
remove_sample kept that 0 in its group's baseline.
- Harbor no longer discards installed agents' Api* errors. Harbor gets them by
  regex over the agent's own output, which the policy writes, so a policy could
  print a provider error to escape a low reward. Only litellm's exceptions from
  in-process agents remain. Multi-step trials now read step exceptions too,
  where Harbor records a step's model-service failure.
- HUD discards a run only when it never launched (no runtime) or its first
  model request failed. An AgentStep alone does not prove a response: HUD
  records a failed request as one.
- On the session path, an InfraAbort keeps its cause when collecting the
  samples then fails with any error, not only a timeout.
- The aborted-group guard now aborts in-flight generation, agent abort hook
  included, before failing the rollout.
Copilot AI balanced review requested due to automatic review settings October 21, 2026 08:52

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-21T08:55:21.554275Z 5f53498 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5f53498151

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread miles/utils/arguments.py
Comment on lines +889 to +891
"--max-consecutive-aborted-groups",
type=int,
default=None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject negative aborted-group limits

When a user passes a negative value such as --max-consecutive-aborted-groups -1, the parser accepts it, and aborted_streak_error treats it as enabled: even a successful group produces streak 0, which is not less than -1, so the rollout immediately aborts with the misleading message that zero groups aborted. Validate this launch constraint as nonnegative at startup, since only 0 and positive limits have defined behavior. .claude/rules/launch-and-request-args.mdL69-L71

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in f255def: miles_validate_args now refuses a negative value at startup. 0 still disables the guard, and a test covers the default, 0, and -1.

A negative limit parsed fine and then stopped the rollout on its first group,
whatever that group's outcome, with a message claiming zero groups had
aborted. Only 0, which disables the guard, and positive counts mean anything.
@celve
celve changed the base branch from upstream/main to LIN-1985/investigate-existing-recipes-for October 21, 2026 13:42
@celve
celve merged commit e47e96e into LIN-1985/investigate-existing-recipes-for Oct 21, 2026
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