feat(agent): bounded auto and cold fast modes - #89991
Conversation
Pure refactor: lift the inline Anthropic Fast Mode block in build_anthropic_kwargs into a dedicated _apply_fast_mode_to_kwargs helper. The helper is idempotent and revocable — it strips any prior extra_body["speed"] and the fast-mode beta token from anthropic-beta before deciding, so applying it twice is stable and applying it with enabled=False cleanly reverts a prior application. This is needed because a later slice toggles fast mode per-turn on already-built kwargs; the strip-first design keeps that safe. No behavior change when enabled: the enabled path is byte-identical to the pre-refactor inline block (verified by test), and disabled on fresh kwargs is a no-op. Second slice of the bounded-fast-modes series (T1 = NousResearch#89960). Receipts: tests/agent/test_anthropic_adapter.py: 96 passed, 0 failed new test: test_apply_fast_mode_helper_idempotent_revocable_and_byte_identical built on pin 13ce0c5
Add keyword-only provider/api_mode/base_url to resolve_fast_mode_overrides so that, when a caller supplies the runtime endpoint identity, service_tier: priority is granted only for allow-listed first-party origins: native OpenAI (openai/openai-api @ api.openai.com), ChatGPT-Codex (openai-codex @ chatgpt.com), and xAI Grok 4.6 (xai/xai-oauth @ api.x.ai). A gpt- or grok-shaped model name on an OpenAI-compatible proxy no longer earns the field — it fails closed to None. Anthropic speed=fast is likewise gated to the anthropic_messages transport. This gate is INERT until a caller actually passes identity (a later slice in this series wires it through). All three params default to None, so every existing zero-arg call site is byte-identical and the legacy static `fast` path is unchanged: this is hardening-in-advance, not a live user-visible change. The xAI Grok branch closes a gap where the upstream whitelist (which predated Grok fast-mode support) would have silently returned None for a model the resolver already routes into fast mode. First slice of a bounded-fast-modes series split from a larger PR for single-concern review.
Add a provider-neutral, turn-local fast-mode policy on top of the existing
normal/priority (fast) tiers. Two dynamic modes:
- auto: opens a time-bounded fast window (default 60s, configurable via
fast_auto_on_seconds) on every user turn.
- cold: opens the same window, but only for the first logical-session turn.
The window is resolved at each user-turn boundary (begin_fast_mode_turn at
run_conversation ingress) and re-validated immediately before every provider
dispatch (revalidate_fast_mode_request at the non-streaming, streaming,
Anthropic, Bedrock, and codex-summary boundaries). Re-validating per dispatch
means an expired window drops fast metadata even when relay/middleware
finalizes kwargs after the cutoff, so a fast turn can never leak across
turns. The policy touches request metadata only: conversation messages,
prompts, tools, and persisted overrides stay stable.
The new policy is inert for the existing normal and priority modes — their
dispatch path stays byte-identical to today. Only service_tier in
{auto, cold} activates the window.
Stacks on NousResearch#89960 (fail-closed fast-mode endpoints; resolve_fast_mode_overrides
identity kwargs) and NousResearch#89982 (idempotent _apply_fast_mode_to_kwargs helper),
both of which this policy calls.
The user-facing config/CLI/UI surface that exposes auto/cold to users
(service_tier: auto/cold across the CLI, gateway, tui, and desktop app) is a
deferred follow-up; this change wires the policy internally so it is exercised
by the default agent path and its unit + integration tests.
|
Folded the config/CLI surface into this PR (commit |
da7bc56 to
87914a6
Compare
Expose the NousResearch#89991 bounded-auto/cold fast-mode feature on the generic config, CLI, and gateway surface (headless / CLI / gateway users), so `service_tier: auto` / `cold` and `fast_auto_on_seconds` are settable via config.yaml and selectable via the `/fast` command across the CLI, messaging gateway, and TUI/desktop backend gateway. - config_defaults.py / cli.py / cli-config.yaml.example: default fast_auto_on_seconds=60; parse+persist normal/fast/auto/cold. - cli_agent_setup_mixin / cli_commands_mixin / commands / web_server: thread fast_auto_on_seconds into the agent; /fast accepts auto/cold. - gateway/run.py / session_state.py / slash_commands.py: thread fast_auto_on_seconds; defer dynamic modes to the agent; persist "priority"/"auto"/"cold" session overrides. - tui_gateway/server.py / methods_session.py / methods_config.py: accept + persist auto/cold via session.create and config.set/get, invalidating the fast-mode turn clock on live mode changes. Reuses the feature's normalize_fast_auto_on_seconds and the fail-closed resolve_fast_mode_overrides; existing normal/priority paths unchanged. The desktop model-settings UI (ui-tui/*, apps/desktop/*) is deliberately excluded and remains a separate follow-up. Stacks on NousResearch#89991.
|
Fixed both real CI failures here (
Green after the fix: 55 passed across The remaining red check ( |
Expose the NousResearch#89991 bounded-auto/cold fast-mode feature on the generic config, CLI, and gateway surface (headless / CLI / gateway users), so `service_tier: auto` / `cold` and `fast_auto_on_seconds` are settable via config.yaml and selectable via the `/fast` command across the CLI, messaging gateway, and TUI/desktop backend gateway. - config_defaults.py / cli.py / cli-config.yaml.example: default fast_auto_on_seconds=60; parse+persist normal/fast/auto/cold. - cli_agent_setup_mixin / cli_commands_mixin / commands / web_server: thread fast_auto_on_seconds into the agent; /fast accepts auto/cold. - gateway/run.py / session_state.py / slash_commands.py: thread fast_auto_on_seconds; defer dynamic modes to the agent; persist "priority"/"auto"/"cold" session overrides. - tui_gateway/server.py / methods_session.py / methods_config.py: accept + persist auto/cold via session.create and config.set/get, invalidating the fast-mode turn clock on live mode changes. Reuses the feature's normalize_fast_auto_on_seconds and the fail-closed resolve_fast_mode_overrides; existing normal/priority paths unchanged. The desktop model-settings UI (ui-tui/*, apps/desktop/*) is deliberately excluded and remains a separate follow-up. Stacks on NousResearch#89991. (cherry picked from commit 87914a6)
What does this PR do?
Adds two time-bounded fast-mode policies on top of the existing
normal/prioritymodes:The policy is revalidated at every provider-dispatch boundary, so a mode change (or the window closing) takes effect on the next request and can't leak stale fast-mode settings across turns. It's inert by default: for any
service_tieroutside{auto, cold}the dispatch path is byte-identical to today.Stacked PR — read this
This branch has three commits, the first two of which are already open as standalone PRs:
refactor(anthropic): extract idempotent fast-mode kwargs helper→ refactor(anthropic): extract idempotent fast-mode kwargs helper #89982fix(models): fail closed on unverified fast-mode endpoints→ fix(models): fail closed on unverified fast-mode endpoints #89960feat(agent): bounded auto and cold fast modes→ this PR's actual contributionThe feature needs both (it calls the idempotent helper to apply/revoke per turn, and the endpoint verifier to stay fail-closed). Simplest path: take #89960 and #89982 first (they stand on their own — a fix and a refactor), and this rebases down to the single feature commit. I can rebase it to one commit on request. This is the last piece of the original #74730, split for reviewability.
Related Issue
Completes #64785 / #74730 (minus the user-facing surface — see below).
Type of Change
Changes Made (feature commit only)
agent/fast_mode.py(new) — the policy module:normalize_fast_auto_on_seconds(with a bool-is-not-a-float guard),has_prior_session_activity,begin_fast_mode_turn/invalidate_fast_mode_turn,effective_request_overrides,revalidate_fast_mode_request.agent/agent_init.py,run_agent.py—fast_auto_on_secondsparam + normalization on the agent.agent/conversation_loop.py—begin_fast_mode_turnat run ingress.agent/chat_completion_helpers.py— revalidate at the provider-dispatch boundaries;build_api_kwargsreads the effective overrides.tests/agent/test_fast_mode_auto.py) + 5 dispatch-revalidation integration (tests/run_agent/test_run_agent.py).Now includes the config/CLI surface
Updated: this PR now also exposes the modes on the generic surface (a 4th commit,
da7bc56750), so the feature is actually usable, not just wired:agent.service_tieracceptsauto/cold(alongsidenormal/fast/priority) in config, CLI, and the gateway.agent.fast_auto_on_seconds(default 60) is a config key threaded to the agent./fast [normal|fast|auto|cold|status]works across the CLI, the messaging gateway, and the TUI/desktop backend gateway.The one piece still deferred is the desktop model-settings menu UI (ui-tui / apps/desktop) — a small follow-up that doesn't affect headless, CLI, or gateway users. Surface tests:
tests/cli/test_fast_command.py+tests/gateway/test_fast_command.py+tests/tui_gateway/test_fast_session_scope.py→ 36 passed; a 546-test regression sweep across tui_gateway/gateway/cli is green.How to Test
(One unrelated red on this machine,
test_interruptible_anthropic_interrupt_never_closes_shared_client, is a missinganthropicSDK in the venv — it fails identically at the base commit.)test_normal_and_fast_modes_remain_backwards_compatibleasserts the normal/priority path is unchanged.Checklist
Code
Documentation & Housekeeping
cli-config.yaml.example— deferred (no config key exposed yet)CONTRIBUTING.md/AGENTS.md— N/ASalvage note: the feature is one commit (
ec09a3af98) on top of #89960 + #89982 — cherry-pick the prerequisites, then this.