show subagent token metrics

This commit is contained in:
kingwl
2026-08-03 01:19:36 +08:00
parent c831c99981
commit 57e2433f45
14 changed files with 114 additions and 37 deletions

View File

@@ -173,19 +173,30 @@
}
.summary,
.time {
.metrics {
color: var(--dsw-alias-label-tertiary);
font-size: 11px;
line-height: 16px;
}
.time {
.metrics {
display: grid;
grid-template-rows: 18px 16px;
flex: none;
margin-top: 16px;
font-variant-numeric: tabular-nums;
text-align: right;
white-space: nowrap;
}
.metricToken {
grid-row: 1;
line-height: 18px;
}
.metricDuration {
grid-row: 2;
}
.children {
position: relative;
margin-left: 18px;

View File

@@ -12,6 +12,7 @@ import type { PropsLocale, PropsRuntime, TranslateNS } from '@deepseek-ai/dsh-cl
import { NS } from './locales.ts'
import type {} from '@deepseek-ai/dsh-client-ui-conversation/client'
import type {} from '@deepseek-ai/dsh-subagent/client'
import type {} from '@deepseek-ai/dsh-token-meter/client'
import css from './SubagentCatalogAction.module.css'
type CatalogEntry = SubagentCatalogSnapshot['entries'][number]
@@ -59,6 +60,26 @@ function treeItems(root: HTMLDivElement | null): HTMLElement[] {
: Array.from(root.querySelectorAll<HTMLElement>('[role="treeitem"]:not([aria-disabled="true"])'))
}
/** Compact token count shared in shape with the conversation stats strip. */
function formatTokens(value: number): string {
const scaled = (next: number): string => next >= 100
? String(Math.round(next))
: String(Math.round(next * 10) / 10)
if (value < 1_000) return String(value)
if (value < 1_000_000) return `${scaled(value / 1_000)}K`
return `${scaled(value / 1_000_000)}M`
}
/** Sum the four disjoint durable provider-usage buckets. */
function tokenTotal(
usage: SessionProjectionMap['tokenUsage'] | undefined,
): number | undefined {
return usage === undefined
? undefined
: usage.uncachedInputTokens + usage.outputTokens
+ usage.cacheReadTokens + usage.cacheWriteTokens
}
/** Exact whole-second active-turn duration for one catalog row. */
function activityDuration(
summary: SessionSummary | undefined,
@@ -222,14 +243,21 @@ function CatalogRows({
const secondary = [summary?.title, mode, activity]
.filter(value => value !== undefined)
.join(' · ')
const totalTokens = tokenTotal(summary?.projectionValues?.tokenUsage)
const durationMs = activityDuration(
summary,
entry.activity,
now,
)
const duration = durationMs === undefined
const tokenMetric = totalTokens === undefined
? undefined
: `${formatTokens(totalTokens)} tok`
const durationMetric = durationMs === undefined
? undefined
: formatDuration(durationMs, t)
const metrics = [tokenMetric, durationMetric]
.filter(value => value !== undefined)
.join(' · ')
const open = (): void => {
openChild({ parentSessionId, childSessionId: entry.id, mode: entry.mode })
@@ -261,9 +289,7 @@ function CatalogRows({
role="treeitem"
tabIndex={0}
aria-level={level}
aria-label={[label, secondary, duration]
.filter(value => value !== undefined)
.join(' ')}
aria-label={[label, secondary, metrics].filter(value => value !== '').join(' ')}
{...knownLeaf ? {} : { 'aria-expanded': isExpanded }}
className={css.row}
onClick={open}
@@ -288,7 +314,12 @@ function CatalogRows({
<span className={css.label}>{label}</span>
<span className={css.summary}>{secondary}</span>
</span>
{duration !== undefined && <span className={css.time}>{duration}</span>}
{metrics !== '' && (
<span className={css.metrics}>
{tokenMetric !== undefined && <span className={css.metricToken}>{tokenMetric}</span>}
{durationMetric !== undefined && <span className={css.metricDuration}>{durationMetric}</span>}
</span>
)}
</div>
</div>
{isExpanded && !knownLeaf && (