Skip to content

fix(tps): hold live rate across tools and steps - #1

Merged
P-Theo merged 3 commits into
mainfrom
fix/tps-tool-call-rate-drop
Sep 22, 2026
Merged

P-Theo merged 3 commits into
mainfrom
fix/tps-tool-call-rate-drop

Conversation

@P-Theo

@P-Theo P-Theo commented Sep 22, 2026

Copy link
Copy Markdown
Owner

The TPS label kept decaying while tools ran, and step settlement replaced it with the run-wide average — the rate slid downward during tool waits or jumped mid-run. It now freezes the live estimate at the stream-end boundary and holds it across tool execution and between steps; only new observable bytes resume the live calculation. The completed run still freezes to the weighted run average. Display-only: token and duration accounting are unchanged.

What changed

  • markStreamed captures the live rate at the host's session.step.streamed boundary. For hosts that never publish it, settleActiveStep falls back to the last content boundary — and skips the capture when that guess predates the newest sample, since those newer bytes would be divided by a duration clamped to the 250 ms minimum and inflate the hold (probe: 1600 t/s held vs 250 t/s on screen moments earlier).
  • A resumed attempt reusing the message ID reopens live calculation when new bytes arrive after an earlier boundary, instead of staying frozen behind it.
  • The footer refresh timer stops once the boundary is known (a held rate cannot change with time); lifecycle events still flush one final render.

Validation

  • bun test — 613 pass, including new TpsTracker unit tests and a footer render test covering hold across tools, settlement, and the frozen run average.

The live estimate kept decaying after the stream-end event while tools
ran, and step settlement swapped it for the run-wide average. Capture
the live value at the stream-end (or content) boundary, clamp the live
clock there, and fall back to the held rate between steps. Final run
average and token accounting are unchanged.
Hosts without session.step.streamed settle against the last content
boundary, which can predate the newest live sample. Capturing the hold
there counts those newer bytes but divides by a duration clamped to
the 250 ms minimum — a half-second burst shows double or worse (probe:
1600 vs 250 t/s). Skip capture when the boundary predates the newest
sample and leave any prior hold alone. Display-only: token and
duration accounting are unchanged.
The running-display semantics were stated twice in tps.ts and again in
the footer timer comment; settleActiveStep and value() restated their
fallback chains. Keep the invariants the code cannot express and the
test arithmetic that pins the magic numbers.
@P-Theo
P-Theo merged commit 7e32e3a into main Sep 22, 2026
4 checks passed
@P-Theo
P-Theo deleted the fix/tps-tool-call-rate-drop branch September 22, 2026 13:56
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