Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
c27724f
feat: Added experimental TPS for model responses [FEATURE]: Adding Ex…
OpeOginni Dec 24, 2025
240df9b
refactor: enhance token calculation for multiple assistant messages c…
OpeOginni Dec 24, 2025
1a22b66
feat: replace TPS display environment variable with config setting
ariane-emory Dec 27, 2025
4765b46
feat: replace TPS display environment variable with config setting
ariane-emory Dec 27, 2025
4382af5
Merge dev branch into feat/opeoginni--display-message-tps
ariane-emory Dec 27, 2025
3a420ec
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 27, 2025
99f6c07
Merge remote-tracking branch 'origin/dev' into feat/opeoginni--displa…
ariane-emory Dec 28, 2025
fb56e05
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 28, 2025
4610eb3
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 28, 2025
815d812
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 28, 2025
de864a4
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 29, 2025
62046db
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 29, 2025
2880e3d
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 29, 2025
143491d
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 29, 2025
5f7298f
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 30, 2025
192ea29
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 30, 2025
5aa7475
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 30, 2025
100fb18
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 30, 2025
98d8bc5
Merge branch 'feat/opeoginni--display-message-tps' of github.com:aria…
ariane-emory Dec 30, 2025
2818e67
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 30, 2025
ffdc68a
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 30, 2025
0b2d5cd
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Dec 31, 2025
3e9f342
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 1, 2026
9c3909c
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 1, 2026
21fb80e
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 1, 2026
3a44a71
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 2, 2026
5f03a6f
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 3, 2026
0d89859
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 3, 2026
916cfca
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 4, 2026
26b6fcb
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 4, 2026
2dc2a02
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 4, 2026
4abfbf0
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 5, 2026
4ac13e9
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 5, 2026
e08ddc1
Merge dev into feat/opeoginni--display-message-tps
ariane-emory Jan 5, 2026
e710b51
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 5, 2026
434c55b
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 5, 2026
fafed56
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 6, 2026
354f53d
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 6, 2026
37ebe3c
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 6, 2026
3f7653d
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 6, 2026
4844ca9
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 6, 2026
73371d5
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 6, 2026
0418b75
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 7, 2026
9bf07d2
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 7, 2026
7066a14
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 7, 2026
bca9e4e
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 8, 2026
f0e8686
Fix TPS calculation to use current message instead of other messages
ariane-emory Jan 8, 2026
49c1a65
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 11, 2026
ddad607
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 13, 2026
c39b0b3
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 13, 2026
37f5f5e
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 13, 2026
a0d38de
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 15, 2026
d6e9d2a
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 16, 2026
d5787fb
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 17, 2026
fedca72
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 17, 2026
e4b2a2d
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 19, 2026
f376cc9
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 19, 2026
a208f53
Merge branch 'dev' into feat/opeoginni--display-message-tps
ariane-emory Jan 22, 2026
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
65 changes: 65 additions & 0 deletions packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1241,6 +1241,10 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
const sync = useSync()
const messages = createMemo(() => sync.data.message[props.message.sessionID] ?? [])

function getParts(messageID: string) {
return sync.data.part[messageID] ?? []
}

const final = createMemo(() => {
return props.message.finish && !["tool-calls", "unknown"].includes(props.message.finish)
})
Expand All @@ -1253,6 +1257,64 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
return props.message.time.completed - user.time.created
})

const TPS = createMemo(() => {
if (!final()) return 0
if (!props.message.time.completed) return 0
if (!(sync.data.config.tui as any)?.display_message_tps) return 0

// Get parts for the current message only
const allParts = getParts(props.message.id)

const INVALID_REASONING_TEXTS = ["[REDACTED]", "", null, undefined] as const

// Filter for actual streaming parts (reasoning + text), exclude tool/step markers
const streamingParts = allParts.filter((part): part is TextPart | ReasoningPart => {
// Only text and reasoning parts have streaming time data
if (part.type !== "text" && part.type !== "reasoning") return false

// Skip parts without valid timestamps
if (!part.time?.start || !part.time?.end) return false

// Include text parts with content
if (part.type === "text" && (part.text?.trim().length ?? 0) > 0) return true

// Include reasoning parts with valid (non-empty) text
if (part.type === "reasoning" && !INVALID_REASONING_TEXTS.includes(part.text as any)) {
return true
}

return false
})

if (streamingParts.length === 0) return 0

// Sum individual part durations (excludes tool execution time between parts)
let totalStreamingTimeMs = 0
let hasValidReasoning = false

for (const part of streamingParts) {
totalStreamingTimeMs += part.time!.end! - part.time!.start!
if (part.type === "reasoning") {
hasValidReasoning = true
}
}

if (totalStreamingTimeMs === 0) return 0

// Use token counts from the current message
const outputTokens = props.message.tokens.output
const reasoningTokens = hasValidReasoning ? props.message.tokens.reasoning : 0
const totalTokens = outputTokens + reasoningTokens

if (totalTokens === 0) return 0

// Calculate tokens per second
const totalStreamingTimeSec = totalStreamingTimeMs / 1000
const tokensPerSecond = totalTokens / totalStreamingTimeSec

return Number(tokensPerSecond.toFixed(2))
})

return (
<>
<For each={props.parts}>
Expand Down Expand Up @@ -1303,6 +1365,9 @@ function AssistantMessage(props: { message: AssistantMessage; parts: Part[]; las
<Show when={duration()}>
<span style={{ fg: theme.textMuted }}> · {Locale.duration(duration())}</span>
</Show>
<Show when={(sync.data.config.tui as any)?.display_message_tps && TPS()}>
<span style={{ fg: theme.textMuted }}> · {TPS()} tps</span>
</Show>
<Show when={props.message.error?.name === "MessageAbortedError"}>
<span style={{ fg: theme.textMuted }}> · interrupted</span>
</Show>
Expand Down
8 changes: 8 additions & 0 deletions packages/opencode/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -810,8 +810,14 @@ export namespace Config {
.enum(["auto", "stacked"])
.optional()
.describe("Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column"),
display_message_tps: z
.boolean()
.optional()
.describe("Display tokens per second in assistant message footer"),
})

export type TUI = z.infer<typeof TUI>

export const Server = z
.object({
port: z.number().int().positive().optional().describe("Port to listen on"),
Expand All @@ -824,6 +830,8 @@ export namespace Config {
ref: "ServerConfig",
})

export type Server = z.infer<typeof Server>

export const Layout = z.enum(["auto", "stretch"]).meta({
ref: "LayoutConfig",
})
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/session/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ export namespace SessionProcessor {
)
currentText.text = textOutput.text
currentText.time = {
start: Date.now(),
start: currentText.time?.start ?? Date.now(), // No need to set start time here, it's already set in the text-start event
end: Date.now(),
}
if (value.providerMetadata) currentText.metadata = value.providerMetadata
Expand Down