mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-19 13:33:35 +08:00
fix: clamp context token display to model context window size
Providers like DeepSeek with 1M context windows can report input_tokens that, when combined with output_tokens, exceed the declared context window — causing the UI to show >100% usage. Add an optional contextWindow parameter to calculateCurrentContextTokenTotal that clamps the result to the window size. Pass it from analyzeContext and sessionService where the window size is already available. Backward compatible: callers that omit the parameter keep existing behavior. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ffef5de9ef
commit
1aa0774cbf
@ -967,7 +967,7 @@ export class SessionService {
|
|||||||
output_tokens: latest.outputTokens,
|
output_tokens: latest.outputTokens,
|
||||||
cache_read_input_tokens: latest.cacheReadInputTokens,
|
cache_read_input_tokens: latest.cacheReadInputTokens,
|
||||||
cache_creation_input_tokens: latest.cacheCreationInputTokens,
|
cache_creation_input_tokens: latest.cacheCreationInputTokens,
|
||||||
})
|
}, rawMaxTokens)
|
||||||
const percentage = rawMaxTokens > 0 ? Math.round((totalTokens / rawMaxTokens) * 100) : 0
|
const percentage = rawMaxTokens > 0 ? Math.round((totalTokens / rawMaxTokens) * 100) : 0
|
||||||
const categories: TranscriptContextEstimate['categories'] = [
|
const categories: TranscriptContextEstimate['categories'] = [
|
||||||
{ name: 'Input tokens', tokens: latest.inputTokens, color: '#8f3217' },
|
{ name: 'Input tokens', tokens: latest.inputTokens, color: '#8f3217' },
|
||||||
|
|||||||
@ -19,4 +19,38 @@ describe('calculateCurrentContextTokenTotal', () => {
|
|||||||
output_tokens: 0,
|
output_tokens: 0,
|
||||||
})).toBe(29_000)
|
})).toBe(29_000)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('clamps to contextWindow when total would exceed it', () => {
|
||||||
|
// DeepSeek scenario: input_tokens already near 1M limit, adding output pushes over
|
||||||
|
expect(calculateCurrentContextTokenTotal(990_000, {
|
||||||
|
input_tokens: 995_000,
|
||||||
|
cache_creation_input_tokens: 0,
|
||||||
|
cache_read_input_tokens: 0,
|
||||||
|
output_tokens: 8_000,
|
||||||
|
}, 1_000_000)).toBe(1_000_000)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('does not clamp when total is within contextWindow', () => {
|
||||||
|
expect(calculateCurrentContextTokenTotal(26_000, {
|
||||||
|
input_tokens: 24_000,
|
||||||
|
cache_creation_input_tokens: 1_000,
|
||||||
|
cache_read_input_tokens: 1_000,
|
||||||
|
output_tokens: 3_000,
|
||||||
|
}, 200_000)).toBe(29_000)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('without contextWindow arg behaves as before — no clamping', () => {
|
||||||
|
// Passing no contextWindow should not clamp, preserving backward compatibility
|
||||||
|
expect(calculateCurrentContextTokenTotal(990_000, {
|
||||||
|
input_tokens: 995_000,
|
||||||
|
cache_creation_input_tokens: 0,
|
||||||
|
cache_read_input_tokens: 0,
|
||||||
|
output_tokens: 8_000,
|
||||||
|
})).toBe(1_003_000)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('returns estimatedTokens when currentUsage is null', () => {
|
||||||
|
expect(calculateCurrentContextTokenTotal(50_000, null)).toBe(50_000)
|
||||||
|
expect(calculateCurrentContextTokenTotal(50_000, null, 200_000)).toBe(50_000)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1202,9 +1202,12 @@ export async function analyzeContextUsage(
|
|||||||
// Use the larger of the local estimate and the latest API-reported context.
|
// Use the larger of the local estimate and the latest API-reported context.
|
||||||
// This keeps the context meter from dropping when a response completes and
|
// This keeps the context meter from dropping when a response completes and
|
||||||
// the output tokens become part of the next turn's context.
|
// the output tokens become part of the next turn's context.
|
||||||
|
// Clamp to contextWindow so providers whose input_tokens already approach the
|
||||||
|
// limit (e.g. DeepSeek with 1M context) don't push the display over 100%.
|
||||||
const finalTotalTokens = calculateCurrentContextTokenTotal(
|
const finalTotalTokens = calculateCurrentContextTokenTotal(
|
||||||
totalIncludingReserved,
|
totalIncludingReserved,
|
||||||
apiUsage,
|
apiUsage,
|
||||||
|
contextWindow,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Pre-calculate grid based on model context window and terminal width
|
// Pre-calculate grid based on model context window and terminal width
|
||||||
|
|||||||
@ -170,6 +170,10 @@ export function calculateContextPercentages(
|
|||||||
* immediately after the model finishes responding. The local estimate is kept
|
* immediately after the model finishes responding. The local estimate is kept
|
||||||
* as a lower bound because it includes system/tool/message material that some
|
* as a lower bound because it includes system/tool/message material that some
|
||||||
* provider usage payloads under-report.
|
* provider usage payloads under-report.
|
||||||
|
*
|
||||||
|
* Pass `contextWindow` to clamp the result to the model's context window size.
|
||||||
|
* This prevents display values from exceeding 100% for providers (e.g. DeepSeek)
|
||||||
|
* whose input_tokens already approach the window limit before output is added.
|
||||||
*/
|
*/
|
||||||
export function calculateCurrentContextTokenTotal(
|
export function calculateCurrentContextTokenTotal(
|
||||||
estimatedTokens: number,
|
estimatedTokens: number,
|
||||||
@ -179,6 +183,7 @@ export function calculateCurrentContextTokenTotal(
|
|||||||
cache_creation_input_tokens: number
|
cache_creation_input_tokens: number
|
||||||
cache_read_input_tokens: number
|
cache_read_input_tokens: number
|
||||||
} | null,
|
} | null,
|
||||||
|
contextWindow?: number,
|
||||||
): number {
|
): number {
|
||||||
if (!currentUsage) return estimatedTokens
|
if (!currentUsage) return estimatedTokens
|
||||||
|
|
||||||
@ -188,7 +193,8 @@ export function calculateCurrentContextTokenTotal(
|
|||||||
currentUsage.cache_read_input_tokens +
|
currentUsage.cache_read_input_tokens +
|
||||||
(currentUsage.output_tokens ?? 0)
|
(currentUsage.output_tokens ?? 0)
|
||||||
|
|
||||||
return Math.max(estimatedTokens, totalFromAPI)
|
const total = Math.max(estimatedTokens, totalFromAPI)
|
||||||
|
return contextWindow !== undefined ? Math.min(total, contextWindow) : total
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user