Compare commits

...

1 Commits

Author SHA1 Message Date
Simon Klee 0e13f39e7c fix(tui): measure complete turn duration 2026-08-10 08:44:48 +00:00
3 changed files with 27 additions and 4 deletions
+3 -3
View File
@@ -89,6 +89,7 @@ import {
createSessionRows,
messageBoundaryIDs,
resolvePart,
turnDuration,
type CacheUsage,
type PartRef,
type SessionRow,
@@ -1597,6 +1598,7 @@ function SessionGroupView(props: {
function AssistantFooter(props: { message: SessionMessageAssistant }) {
const ctx = use()
const data = useData()
const local = useLocal()
const dimensions = useTerminalDimensions()
const theme = useTheme("elevated")
@@ -1607,9 +1609,7 @@ function AssistantFooter(props: { message: SessionMessageAssistant }) {
.find((model) => model.providerID === props.message.model.providerID && model.id === props.message.model.id)
?.name ?? `${props.message.model.providerID}/${props.message.model.id}`,
)
const duration = createMemo(() =>
props.message.time.completed ? props.message.time.completed - props.message.time.created : 0,
)
const duration = createMemo(() => turnDuration(props.message, data.session.message.list(ctx.sessionID)))
const interrupted = createMemo(() => props.message.error?.message === "Step interrupted")
return (
<>
+9
View File
@@ -348,6 +348,15 @@ export function cacheReuseDrop(previous: CacheUsage | undefined, current: CacheU
return drop > 0 ? drop : undefined
}
export function turnDuration(message: SessionMessageAssistant, messages: SessionMessageInfo[]) {
if (message.time.completed === undefined) return 0
const index = messages.findIndex((item) => item.id === message.id)
const input = messages
.slice(0, index === -1 ? messages.length : index)
.findLast((item) => item.type === "user" || item.type === "synthetic")
return Math.max(0, message.time.completed - (input?.time.created ?? message.time.created))
}
function hasTokenUsage(
message: SessionMessageAssistant,
): message is SessionMessageAssistant & { tokens: NonNullable<SessionMessageAssistant["tokens"]> } {
+15 -1
View File
@@ -1,6 +1,20 @@
import { expect, test } from "bun:test"
import type { SessionMessageAssistant, SessionMessageInfo } from "@opencode-ai/client"
import { cacheReuseDrop, messageBoundaryIDs, reduceSessionRows } from "../../../src/routes/session/rows"
import { cacheReuseDrop, messageBoundaryIDs, reduceSessionRows, turnDuration } from "../../../src/routes/session/rows"
test("measures turn duration from the user prompt across assistant steps", () => {
const first = assistant("assistant-1", [])
first.time = { created: 8_000, completed: 11_000 }
const final = assistant("assistant-2", [])
final.time = { created: 27_000, completed: 30_000 }
const messages: SessionMessageInfo[] = [
{ type: "user", id: "user-1", text: "Question", time: { created: 1_000 } },
first,
final,
]
expect(turnDuration(final, messages)).toBe(29_000)
})
test("filters OpenAI cache quantization from cache reuse drops", () => {
const openai = { id: "gpt", providerID: "openai" }