Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## [Unreleased]
### Fixed
- Generation speed counts the time a model spends writing a tool call's
arguments. Those tokens were counted but their streaming time was not, so
a step that wrote a file showed several times its real speed (3,672 tokens
"at 162.9 tok/s" against the engine's 36.4), and the Session average was
inflated with it. Affects OpenCode's figures, not an engine's own. On
MTPLX, a turn that wrote a file now gives 36.4 tok/s from OpenCode's
figures against the engine's 36.1.

## [0.3.2] – 2026-09-25
### Fixed
- A reply is recorded when it ends, not when OpenCode's execution does. A
Expand Down
32 changes: 31 additions & 1 deletion tui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1094,7 +1094,17 @@ export default Plugin.define({
off.push(
ctx.data.on("session.step.streamed", (evt) => {
const id = stepID(evt)
if (id) readStep(id, "step.streamed")
if (!id) return
if (HUD_DEBUG) {
const t = turns.get(id)
const now = Date.now()
dbg(
` stream window ${id}: ${t?.firstAt !== undefined && t.lastAt !== undefined ? ((t.lastAt - t.firstAt) / 1000).toFixed(2) : "?"}s; ` +
`last mark ${t?.lastAt !== undefined ? now - t.lastAt : "?"}ms before step.streamed; tool deltas ${toolDeltas.get(id) ?? 0}`
)
toolDeltas.delete(id)
}
readStep(id, "step.streamed")
})
)
off.push(
Expand All @@ -1105,6 +1115,26 @@ export default Plugin.define({
)
off.push(ctx.data.on("session.text.delta", mark))
off.push(ctx.data.on("session.reasoning.delta", mark))
// A tool call's arguments are generated tokens too, counted in the
// step's output. Unwatched, a step that wrote a file had its tokens
// divided by only its text's streaming time (measured: 3,672 tokens "at
// 162.9 tok/s" on a `write` step, against the engine's 36.4 for the
// turn), inflating the session's speed. The argument deltas never
// reach a plugin (measured: 0 on every step of an 8-step turn that
// wrote a file); the start and end of the arguments do, and with them
// the window's last mark lands 0-1ms before the stream ends. Session
// 36.4 tok/s against the engine's 36.1, from 55.1 against 36.4. The
// delta subscription is kept in case a later OpenCode forwards them.
const toolDeltas = new Map<string, number>()
off.push(
ctx.data.on("session.tool.input.delta", (evt) => {
mark(evt)
const id = stepID(evt)
if (id) toolDeltas.set(id, (toolDeltas.get(id) ?? 0) + 1)
})
)
off.push(ctx.data.on("session.tool.input.started", mark))
off.push(ctx.data.on("session.tool.input.ended", mark))

// Diagnostics only (OPENCODE_HUD_DEBUG): the per-step events, to design
// reading the engine once per step instead of once per turn. On
Expand Down
Loading