This is a follow-up to https://github.com/openai/codex/pull/15922. That previous PR deleted the old `tui` directory and left the new `tui_app_server` directory in place. This PR renames `tui_app_server` to `tui` and fixes up all references.
4.0 KiB
TUI Stream Chunking
This document explains how stream chunking in the TUI works and why it is implemented this way.
Problem
Streaming output can arrive faster than a one-line-per-tick animation can show it. If commit speed stays fixed while arrival speed spikes, queued lines grow and visible output lags behind received output.
Design goals
- Preserve existing baseline behavior under normal load.
- Reduce display lag when backlog builds.
- Keep output order stable.
- Avoid abrupt single-frame flushes that look jumpy.
- Keep policy transport-agnostic and based only on queue state.
Non-goals
- The policy does not schedule animation ticks.
- The policy does not depend on upstream source identity.
- The policy does not reorder queued output.
Where the logic lives
codex-rs/tui/src/streaming/chunking.rs- Adaptive policy, mode transitions, and drain-plan selection.
codex-rs/tui/src/streaming/commit_tick.rs- Orchestration for each commit tick: snapshot, decide, drain, trace.
codex-rs/tui/src/streaming/controller.rs- Queue/drain primitives used by commit-tick orchestration.
codex-rs/tui/src/chatwidget.rs- Integration point that invokes commit-tick orchestration and handles UI lifecycle events.
Runtime flow
On each commit tick:
- Build a queue snapshot across active controllers.
queued_lines: total queued lines.oldest_age: max age of the oldest queued line across controllers.
- Ask adaptive policy for a decision.
- Output: current mode and a drain plan.
- Apply drain plan to each controller.
- Emit drained
HistoryCells for insertion by the caller. - Emit trace logs for observability.
In CatchUpOnly scope, policy state still advances, but draining is skipped
unless mode is currently CatchUp.
Modes and transitions
Two modes are used:
Smooth- Baseline behavior: one line drained per baseline commit tick.
- Baseline tick interval currently comes from
tui/src/app.rs:COMMIT_ANIMATION_TICK(~8.3ms, ~120fps).
CatchUp- Drain current queued backlog per tick via
Batch(queued_lines).
- Drain current queued backlog per tick via
Entry and exit use hysteresis:
- Enter
CatchUpwhen queue depth or queue age exceeds enter thresholds. - Exit requires both depth and age to be below exit thresholds for a hold
window (
EXIT_HOLD).
This prevents oscillation when load hovers near thresholds.
Current experimental tuning values
These are the current values in streaming/chunking.rs plus the baseline
commit tick in tui/src/app.rs. They are
experimental and may change as we gather more trace data.
- Baseline commit tick:
~8.3ms(COMMIT_ANIMATION_TICKinapp.rs) - Enter catch-up:
queued_lines >= 8ORoldest_age >= 120ms
- Exit catch-up eligibility:
queued_lines <= 2ANDoldest_age <= 40ms
- Exit hold (
CatchUp -> Smooth):250ms - Re-entry hold after catch-up exit:
250ms - Severe backlog thresholds:
queued_lines >= 64ORoldest_age >= 300ms
Drain planning
In Smooth, plan is always Single.
In CatchUp, plan is Batch(queued_lines), which drains the currently queued
backlog for immediate convergence.
Why this design
This keeps normal animation semantics intact, while making backlog behavior adaptive:
- Under normal load, behavior stays familiar and stable.
- Under pressure, queue age is reduced quickly without sacrificing ordering.
- Hysteresis avoids rapid mode flapping.
Invariants
- Queue order is preserved.
- Empty queue resets policy back to
Smooth. CatchUpexits only after sustained low pressure.- Catch-up drains are immediate while in
CatchUp.
Observability
Trace events are emitted from commit-tick orchestration:
stream chunking commit tickmode,queued_lines,oldest_queued_age_ms,drain_plan,has_controller,all_idle
stream chunking mode transitionprior_mode,new_mode,queued_lines,oldest_queued_age_ms,entered_catch_up
These events are intended to explain display lag by showing queue pressure, selected drain behavior, and mode transitions over time.