Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8790a37
feat(opencode): record per-step token throughput metrics
Jul 21, 2026
2260c65
fix(vscode): wire token throughput toggle and drop unreachable provid…
Githubguy132010 Jul 21, 2026
08b0734
fix(opencode): centralize token throughput labels and tighten type guard
Githubguy132010 Jul 21, 2026
67bd528
feat(token-throughput-v2): wire aggregation through DisplayProvider a…
Githubguy132010 Jul 21, 2026
e34aecc
fix(token-throughput-v2): seed settings on hydration, drop dead provi…
Githubguy132010 Jul 21, 2026
f5e5496
fix(token-throughput-v2): drop PP display until llama.cpp wiring lands
Githubguy132010 Jul 21, 2026
bc96756
fix(token-throughput-v2): integrate TG into Tokens row, plain-text style
Githubguy132010 Jul 21, 2026
78645ce
fix(token-throughput-v2): finish removing memory badge from Assistant…
Githubguy132010 Jul 22, 2026
c71bb55
fix(token-throughput-v2): finish removing memory badge from Assistant…
Githubguy132010 Jul 22, 2026
4289127
formatting fixes
Githubguy132010 Jul 22, 2026
a310090
fix(token-throughput-v2): address Marius review comments
Githubguy132010 Jul 22, 2026
aa3fe36
fix(token-throughput-v2): close unclosed CSS block and apply prettier…
Githubguy132010 Jul 22, 2026
405a0c1
ci: re-run after fixing CSS unclosed-block + prettier drift
Githubguy132010 Jul 22, 2026
41270b0
fix(token-throughput-v2): address kilo-code-bot review
Githubguy132010 Jul 22, 2026
65e552c
feat(token-throughput-v2): weighted per-turn rate, plain text footer
Githubguy132010 Jul 22, 2026
d4a1fde
fix(token-throughput-v2): render t/s inline beside copy/feedback buttons
marius-kilocode Jul 23, 2026
582cf4d
fix(token-throughput-v2): correct changeset package name to @kilocode…
marius-kilocode Jul 23, 2026
5891f2b
fix(token-throughput-v2): annotate step-start time field with kilocod…
marius-kilocode Jul 23, 2026
a2a6efe
Merge branch 'main' into feat/token-throughput-v2
Githubguy132010 Jul 23, 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
6 changes: 6 additions & 0 deletions .changeset/token-throughput-v2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@kilocode/cli": minor
"@kilocode/sdk": minor
---

Show tokens-per-second text-generation throughput (TG) on each assistant message and in the usage sidebar, computed from step duration and tokens. The toggle "Show Token Throughput" in Display settings controls both surfaces. PP (prompt-processing) support lands in a follow-up once the upstream llama.cpp metadata wiring ships.
16 changes: 8 additions & 8 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions packages/core/src/v1/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,16 @@ export const StepStartPart = Schema.Struct({
...partBase,
type: Schema.Literal("step-start"),
snapshot: Schema.optional(Schema.String),
// kilocode_change start - wall-clock timestamps captured at the processor
// and consumed by the webview's weighted throughput aggregator. Marked
// optional so older persisted sessions (and synthetic messages) without
// timing still decode cleanly.
time: Schema.optional(
Schema.Struct({
start: NonNegativeInt,
}),
),
// kilocode_change end
}).annotate({ identifier: "StepStartPart" })
export type StepStartPart = Types.DeepMutable<Schema.Schema.Type<typeof StepStartPart>>

Expand All @@ -243,6 +253,25 @@ export const StepFinishPart = Schema.Struct({
modelID: ModelV2.ID,
}),
),
metrics: Schema.optional(
Schema.Struct({
prompt: Schema.optional(Schema.Finite),
generation: Schema.optional(Schema.Finite),
source: Schema.Literals(["provider", "computed"]),
}),
),
// Wall-clock timestamps + active generation duration captured at the
// session processor. The webview's weighted throughput aggregator uses
// `time.elapsed` (active model-generation duration in milliseconds,
// excluding tool execution and idle waiting) to weight the per-turn
// rate. Optional so legacy persisted sessions keep decoding.
time: Schema.optional(
Schema.Struct({
start: NonNegativeInt,
end: NonNegativeInt,
elapsed: Schema.Finite,
}),
),
// kilocode_change end
cost: Schema.Finite,
tokens: Schema.Struct({
Expand Down
4 changes: 4 additions & 0 deletions packages/kilo-ui/src/components/icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ const icons: Record<string, { path: string; viewBox: string }> = {
viewBox: "0 0 24 24",
path: `<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><path d="M21 3v5h-5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 21v-5h5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>`,
},
gauge: {
viewBox: "0 0 24 24",
path: `<path d="M12 14L9 10M12 14L15 10M21 15C21 18.866 17.866 22 14 22H10C6.134 22 3 18.866 3 15V9C3 5.134 6.134 2 10 2H14C17.866 2 21 5.134 21 9V15Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>`,
},
}

type Name = keyof typeof icons
Expand Down
8 changes: 8 additions & 0 deletions packages/kilo-ui/src/components/message-part.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@
[data-component="icon-button"][data-icon="thumbs-down"]:hover [data-slot="icon-svg"] path {
fill: currentColor;
}

/* Throughput badge sits to the right of the copy/feedback buttons,
beside them rather than beneath the message. */
[data-slot="assistant-throughput-inline"] {
margin-left: 6px;
display: flex;
align-items: center;
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions packages/kilo-ui/src/components/message-part.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export interface MessagePartProps {
animate?: boolean
working?: boolean
feedback?: MessageFeedbackControls
throughput?: JSX.Element
}

export type PartComponent = Component<MessagePartProps>
Expand Down Expand Up @@ -991,6 +992,7 @@ export function Part(props: MessagePartProps) {
animate={props.animate}
working={props.working}
feedback={props.feedback}
throughput={props.throughput}
/>
</Show>
)
Expand Down Expand Up @@ -1448,6 +1450,9 @@ PART_MAPPING["text"] = function TextPartDisplay(props) {
/>
</Tooltip>
</Show>
<Show when={props.throughput}>
{(el) => <span data-slot="assistant-throughput-inline">{el()}</span>}
</Show>
</div>
</Show>
<Show when={summary()}>
Expand Down
5 changes: 5 additions & 0 deletions packages/kilo-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,11 @@
"default": true,
"description": "Show the task timeline graph in the chat header"
},
"kilo-code.new.showTokenThroughput": {
"type": "boolean",
"default": false,
"description": "Show tokens-per-second (prompt-processing / text-generation) badges on assistant messages and the task header"
},
"kilo-code.new.chat.shiftTabCyclesVariant": {
"type": "boolean",
"default": true,
Expand Down
10 changes: 10 additions & 0 deletions packages/kilo-vscode/src/KiloProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ import {
watchIndexingConfig,
} from "./kilo-provider/indexing-settings"
import { buildChatSettingsMessage, validChatSetting, watchChatConfig } from "./kilo-provider/chat-settings"
import { buildThroughputSettingMessage, watchThroughputConfig } from "./kilo-provider/throughput-settings"

let maxCost = 0

Expand Down Expand Up @@ -393,6 +394,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
private autocompleteConfigDisposable: vscode.Disposable | null = null
private indexingConfigDisposable: vscode.Disposable | null = null
private chatConfigDisposable: vscode.Disposable | null = null
private throughputConfigDisposable: vscode.Disposable | null = null
private telemetryStateDisposable: vscode.Disposable | null = null
private viewStateDisposable: vscode.Disposable | null = null
private visibilityDisposable: vscode.Disposable | null = null
Expand Down Expand Up @@ -917,6 +919,8 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.indexingConfigDisposable = watchIndexingConfig((msg) => this.postMessage(msg))
this.chatConfigDisposable?.dispose()
this.chatConfigDisposable = watchChatConfig((msg) => this.postMessage(msg))
this.throughputConfigDisposable?.dispose()
this.throughputConfigDisposable = watchThroughputConfig((msg) => this.postMessage(msg))
this.telemetryStateDisposable?.dispose()
this.telemetryStateDisposable = watchTelemetryState((msg) => this.postMessage(msg))
this.webviewMessageDisposable = webview.onDidReceiveMessage(async (message) => {
Expand Down Expand Up @@ -1351,6 +1355,9 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
case "requestTimelineSetting":
this.sendTimelineSetting()
break
case "requestThroughputSetting":
this.postMessage(buildThroughputSettingMessage())
break
case "requestNotifications":
this.fetchAndSendNotifications().catch((e) =>
console.error("[Kilo New] fetchAndSendNotifications failed:", e),
Expand Down Expand Up @@ -1734,6 +1741,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.postMessage({ type: "gitStatus", repo: this.cachedGitRepo })
this.sendNotificationSettings()
this.sendTimelineSetting()
this.postMessage(buildThroughputSettingMessage())
this.postMessage({ type: "extensionDataReady" })

if (this.cachedGitRepo) this.startStatsPolling()
Expand Down Expand Up @@ -3724,6 +3732,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.sendBrowserSettings()
this.sendNotificationSettings()
this.sendTimelineSetting()
this.postMessage(buildThroughputSettingMessage())
this.sendWorkStyle()
await ModelState.reset(this.client, (msg) => this.postMessage(msg))

Expand Down Expand Up @@ -4518,6 +4527,7 @@ export class KiloProvider implements vscode.WebviewViewProvider, TelemetryProper
this.autocompleteConfigDisposable?.dispose()
this.indexingConfigDisposable?.dispose()
this.chatConfigDisposable?.dispose()
this.throughputConfigDisposable?.dispose()
this.telemetryStateDisposable?.dispose()
this.autoApproveBridge?.dispose()
this.visibleTaskStreams.clear()
Expand Down
19 changes: 19 additions & 0 deletions packages/kilo-vscode/src/kilo-provider/throughput-settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as vscode from "vscode"

type Post = (msg: unknown) => void

export function buildThroughputSettingMessage() {
const config = vscode.workspace.getConfiguration("kilo-code.new")
return {
type: "throughputSettingLoaded" as const,
visible: config.get<boolean>("showTokenThroughput", false),
}
}

export function watchThroughputConfig(post: Post): vscode.Disposable {
return vscode.workspace.onDidChangeConfiguration((event) => {
if (event.affectsConfiguration("kilo-code.new.showTokenThroughput")) {
post(buildThroughputSettingMessage())
}
})
}
Loading
Loading