mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix(web): clarify token usage labels in message details (#1062)
This commit is contained in:
@@ -58,7 +58,7 @@ describe('MessageActions', () => {
|
||||
expect(screen.getByRole('button', { name: 'Message details' })).toBeTruthy()
|
||||
expect(screen.getByText('Duration: 1.3s')).toBeTruthy()
|
||||
expect(screen.getByText('Model: gpt-5.2-codex')).toBeTruthy()
|
||||
expect(screen.getByText('Usage: 125 billable tokens (100 in / 25 out)')).toBeTruthy()
|
||||
expect(screen.getByText('Tokens: 125 total (100 in / 25 out)')).toBeTruthy()
|
||||
expect(screen.queryByText(/^Invoke:/)).toBeNull()
|
||||
})
|
||||
|
||||
|
||||
@@ -41,12 +41,12 @@ describe('buildMessageMetadataLabels', () => {
|
||||
expect(buildMessageMetadataLabels({})).toEqual([])
|
||||
})
|
||||
|
||||
it('labels token totals as billable to clarify that cache I/O is intentionally excluded', () => {
|
||||
it('renders the token total and input/output breakdown without billing claims', () => {
|
||||
const parts = buildMessageMetadataLabels({
|
||||
usage: { input_tokens: 100, output_tokens: 200 }
|
||||
})
|
||||
expect(parts.some(p => /\bbillable tokens\b/.test(p))).toBe(true)
|
||||
expect(parts.some(p => p.includes('300 billable tokens (100 in / 200 out)'))).toBe(true)
|
||||
expect(parts).toContain('Tokens: 300 total (100 in / 200 out)')
|
||||
expect(parts.some(p => /\bbillable\b/.test(p))).toBe(false)
|
||||
})
|
||||
|
||||
it('does not drop a Duration line when durationMs is exactly 0', () => {
|
||||
@@ -83,7 +83,7 @@ describe('buildMessageMetadataLabels', () => {
|
||||
expect(parts).toEqual([
|
||||
'Duration: 1.2s',
|
||||
'Model: claude-sonnet-4-6',
|
||||
'Usage: 22 billable tokens (3 in / 19 out)'
|
||||
'Tokens: 22 total (3 in / 19 out)'
|
||||
])
|
||||
})
|
||||
|
||||
@@ -95,8 +95,7 @@ describe('buildMessageMetadataLabels', () => {
|
||||
})
|
||||
expect(parts).toContain('Models: claude-sonnet-4-6, claude-haiku-4-5-20251001')
|
||||
expect(parts.some(p => p.startsWith('Model:'))).toBe(false)
|
||||
expect(parts).toContain('Total: 300 billable tokens (100 in / 200 out)')
|
||||
expect(parts.some(p => p.startsWith('Usage:'))).toBe(false)
|
||||
expect(parts).toContain('Tokens: 300 total (100 in / 200 out)')
|
||||
expect(parts).toContain('3 turns')
|
||||
})
|
||||
|
||||
|
||||
@@ -37,14 +37,9 @@ export function buildMessageMetadataLabels({ durationMs, usage, model, turnCount
|
||||
}
|
||||
|
||||
if (usage) {
|
||||
// "Billable" because cache_read tokens are not part of the input
|
||||
// figure used for billing; surfacing only input + output here is
|
||||
// intentional. If we ever surface cache I/O, keep this label and
|
||||
// add a separate `Cache:` line.
|
||||
const total = usage.input_tokens + usage.output_tokens
|
||||
const formatToken = (n: number) => n.toLocaleString()
|
||||
const usageLabel = isAggregated ? 'Total' : 'Usage'
|
||||
parts.push(`${usageLabel}: ${formatToken(total)} billable tokens (${formatToken(usage.input_tokens)} in / ${formatToken(usage.output_tokens)} out)`)
|
||||
parts.push(`Tokens: ${formatToken(total)} total (${formatToken(usage.input_tokens)} in / ${formatToken(usage.output_tokens)} out)`)
|
||||
}
|
||||
|
||||
if (isAggregated) {
|
||||
|
||||
Reference in New Issue
Block a user