Skip to content

Slow-but-alive provider streams are undetectable: a ~2 tok/s turn never times out, never retries, and never falls back #1739

Description

@code-yeongyu

Symptom

A gpt-6-astra turn that keeps streaming at about 2.1 tokens per second never fails, never retries, and never walks a fallback chain. The session looks healthy (working line only shows elapsed time) while remaining effectively unusable.

Why

Every in-progress stream watchdog is a silence detector:

  • Stream-start (retry.provider.streamStartTimeoutMs, default 300s) applies only until the first event.
  • Inter-event idle (retry.provider.timeoutMs / httpIdleTimeoutMs, default 300s) resets on every event, including thinking deltas.
  • There is no total-turn budget on the agent loop.
  • WebSocket liveness and HTTP body idle likewise reset on any bytes.
  • Fallback runs only after a retryable assistant error. A trickle produces none.

Compaction already documents the missing class ("a stream that keeps trickling events never trips the idle watchdog") and bounds it with a wall-clock budget. That bound was not applied to the main turn, and a wall-clock total-turn cap would be the wrong shape anyway (tool-using turns are long).

The suite currently pins the opposite contract: packages/agent/test/agent-loop-stream-start-timeout.test.ts (lets slow-but-alive streams finish despite gaps above the start bound).

Live tok/s is not shown in the working line or footer. The builtin TPS extension posts TPS N tok/s. … only on agent_end.

Shipped retry.fallbackChains is empty (no implicit/wildcard lane). Category builtins pin ultrabrain/deep to a single Astra model in config; resolve-time chains can add gpt-5.6-sol only when that id is in the live registry, and only after an error. architect is a single-rung pin with no second model.

Expected

  1. After the first stream event, the working line shows live tok/s (Working (12s • 42 tok/s • Esc to interrupt)).
  2. If streamed text+thinking stays under 8 tok/s for a 20s window (5s grace after first event, tool time excluded), abort with Provider stream throughput degraded: … tok/s over 20s (floor 8 tok/s).
  3. Do not spend the same-model retry budget. Fall back immediately when a chain candidate exists; if none, end the turn with that error and the existing "No fallback chain configured — set one with /fallback." copy.
  4. A control stream at tens of tok/s is unaffected. Silence is still start/idle, not this path.

Acceptance tests (failing first)

  • packages/agent/test/agent-loop-throughput-watchdog.test.ts: 2 tok/s mock for >25s → stopReason === "error" and throughput wording; 40 tok/s control → stop.
  • packages/coding-agent/test/suite/retry-fallback-throughput-degraded.test.ts: one primary call, then retry_fallback_applied; empty chain → no second model, error still names tok/s.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions