Skip to content

feat: per-rollout token limits (max_input/output/total_tokens) - #1591

Merged
mikasenghaas merged 4 commits into
feat/nano-as-v1from
feat/v1-token-limits
Jun 9, 2026
Merged

feat: per-rollout token limits (max_input/output/total_tokens)#1591
mikasenghaas merged 4 commits into
feat/nano-as-v1from
feat/v1-token-limits

Conversation

@mikasenghaas

@mikasenghaas mikasenghaas commented Jun 9, 2026

Copy link
Copy Markdown
Member

Summary

  • Add framework-enforced per-rollout token budgets to EnvConfig, alongside the existing max_turns:
    • max_input_tokens — caps the trace's prompt_len
    • max_output_tokens — caps the trace's completion_len
    • max_total_tokens — caps the trace's total_tokens
  • Introduce RolloutLimits (a small frozen bundle in interception.py) that holds all framework limits — turns + the three token budgets — and exposes reached(trace), returning the name of the first limit reached (or None). This subsumes the old standalone max_turns check so all framework limits go through one place and thread as a single object (EnvironmentRolloutInterceptionServer).
  • The interception server checks limits.reached(trace) before serving each turn (same mechanism as a @stop): the first limit reached refuses the turn, halting any harness, and is recorded as the trace's stop_condition.
  • Trace.is_truncated now treats the token-limit stop conditions (max_input_tokens / max_output_tokens / max_total_tokens) as truncation, like max_turns / harness_timeout.

The checks read the trace's existing computed properties (prompt_len / completion_len / total_tokens), so they apply uniformly to any harness. Token caps are soft by one turn: they're checked between turns, so the turn that crosses a cap still completes.

Breaking

  • InterceptionServer(...) and Rollout(...) no longer take a max_turns keyword — they take limits: RolloutLimits | None instead. Environment.max_turns is replaced by Environment.limits. These are internal constructors (only Environment.episode / Rollout.run call them); the public EnvConfig.max_turns config field and its CLI flag are unchanged.

Verification

  • ruff check --isolated clean on all changed files.
  • Smoke-tested RolloutLimits.reached: >= boundary semantics, the input/output/total → prompt_len/completion_len/total_tokens mapping, and limit priority (turns before tokens when both are reached). Imports of env / rollout / trace / interception confirmed (no circular import from the new envinterception edge).

Follow-up (separate PR, prime-rl side): surface these fields on the orchestrator's env config so they can be set from training TOMLs.

Note

Add per-rollout token limits (max_input/output/total_tokens) to rollout execution

  • Introduces a RolloutLimits frozen dataclass in interception.py that holds optional caps for turns, input tokens, output tokens, and total tokens, with a reached() method that checks a Trace against all limits.
  • Adds max_input_tokens, max_output_tokens, and max_total_tokens fields to EnvConfig in env.py, which are aggregated into a RolloutLimits object and passed down to each Rollout.
  • The InterceptionServer now calls limits.reached() before serving each turn; if any limit is hit, it returns HTTP 400 with rollout stopped: {limit} and sets the trace stop condition to the limit name.
  • Trace.is_truncated in trace.py now returns True for the three new token-limit stop conditions in addition to max_turns and harness_timeout.

Macroscope summarized 5ae2372.


Note

Medium Risk
Changes rollout halt behavior and internal Rollout/InterceptionServer APIs; misconfigured limits could truncate eval rollouts unexpectedly, but enforcement mirrors existing max_turns.

Overview
Adds framework-enforced per-rollout token budgets on EnvConfig (max_input_tokens, max_output_tokens, max_total_tokens) alongside existing max_turns, exposed as top-level eval/serve CLI flags like other env limits.

Introduces RolloutLimits with reached(trace) so turn and token caps are checked in one place before each intercepted chat turn (same halt path as @stop). Environment builds limits from config and passes them through Rollout to InterceptionServer instead of a standalone max_turns argument.

Trace.is_truncated now treats token-limit stop conditions (max_input_tokens, max_output_tokens, max_total_tokens) as truncation, consistent with max_turns and harness_timeout. Token caps are evaluated between turns (soft by one turn).

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

mikasenghaas and others added 4 commits June 9, 2026 19:57
…}_tokens)

Add framework-enforced token budgets alongside max_turns: max_input_tokens,
max_output_tokens, max_total_tokens on EnvConfig. The interception server checks
them before each turn via a new RolloutLimits bundle (which also subsumes
max_turns), capping the trace's prompt_len / completion_len / total_tokens
computed properties. Reaching any limit refuses the turn and records it as the
stop condition, and is_truncated now treats the token-limit conditions as
truncation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mikasenghaas
mikasenghaas marked this pull request as ready for review June 9, 2026 20:01
@mikasenghaas
mikasenghaas merged commit fba4642 into feat/nano-as-v1 Jun 9, 2026
3 checks passed

@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.

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 5ae2372. Configure here.

Comment thread verifiers/v1/env.py
`completion_len`; framework-enforced between turns."""
max_total_tokens: int | None = None
"""Max total (prompt + completion) tokens per rollout (None = no limit). Caps the
trace's `total_tokens`; framework-enforced between turns."""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing docs for token limits

Medium Severity

This PR adds framework max_input_tokens, max_output_tokens, and max_total_tokens on EnvConfig (top-level eval/TOML flags like max_turns), but none of the core docs under docs/ describe them. Users have no reference for configuring or interpreting these limits and truncation stop conditions.

Fix in Cursor Fix in Web

Triggered by project rule: BugBot Instructions

Reviewed by Cursor Bugbot for commit 5ae2372. Configure here.

@macroscopeapp

macroscopeapp Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new framework-enforced token limits that change rollout termination behavior - a new feature capability. While the implementation is straightforward and well-structured, new features introducing user-facing behavior changes warrant human review.

You can customize Macroscope's approvability policy. Learn more.

pull Bot pushed a commit to Stars1233/verifiers that referenced this pull request Jun 23, 2026
…Intellect-ai#1591)

* feat(v1): per-rollout token limits (EnvConfig.max_{input,output,total}_tokens)

Add framework-enforced token budgets alongside max_turns: max_input_tokens,
max_output_tokens, max_total_tokens on EnvConfig. The interception server checks
them before each turn via a new RolloutLimits bundle (which also subsumes
max_turns), capping the trace's prompt_len / completion_len / total_tokens
computed properties. Reaching any limit refuses the turn and records it as the
stop condition, and is_truncated now treats the token-limit conditions as
truncation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(v1): drop 'like max_turns' from token-limit field docstrings

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* style(v1): trim limit-check comment in interception

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* style(v1): ruff format interception

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <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.

1 participant