mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-20 13:53:32 +08:00
fix: align transcript context fallback with cli totals
The live desktop context path already routes through the CLI get_context_usage control request and the shared analyzeContextUsage implementation. The offline transcript fallback was still using its own prompt-token-only estimate, so disconnected sessions could report a lower context total than the live /context path. The fallback now reuses the same current-context total helper and includes output tokens as next-turn context. Constraint: Desktop must still show context estimates when the CLI process is not running Rejected: Keep transcript fallback prompt-only | it diverges from the live CLI /context total and can drop after responses Confidence: high Scope-risk: narrow Directive: Transcript context fallback must stay aligned with calculateCurrentContextTokenTotal Tested: bun test src/server/__tests__/conversations.test.ts --test-name-pattern "context-only|structured session inspection|reconstruct Sonnet 4.6 transcript usage" Tested: bun test src/utils/__tests__/context.test.ts Tested: bun run check:server
This commit is contained in:
parent
f02fe83e28
commit
1029c9e7bc
@ -372,8 +372,9 @@ describe('ConversationService', () => {
|
|||||||
expect(usage?.models[0]?.model).toBe('claude-sonnet-4-6')
|
expect(usage?.models[0]?.model).toBe('claude-sonnet-4-6')
|
||||||
expect(usage?.models[0]?.contextWindow).toBe(200_000)
|
expect(usage?.models[0]?.contextWindow).toBe(200_000)
|
||||||
expect(contextEstimate?.model).toBe('claude-sonnet-4-6')
|
expect(contextEstimate?.model).toBe('claude-sonnet-4-6')
|
||||||
expect(contextEstimate?.totalTokens).toBe(100)
|
expect(contextEstimate?.totalTokens).toBe(120)
|
||||||
expect(contextEstimate?.rawMaxTokens).toBe(200_000)
|
expect(contextEstimate?.rawMaxTokens).toBe(200_000)
|
||||||
|
expect(contextEstimate?.categories.some((category) => category.name === 'Output tokens' && category.tokens === 20)).toBe(true)
|
||||||
} finally {
|
} finally {
|
||||||
if (previousConfigDir === undefined) {
|
if (previousConfigDir === undefined) {
|
||||||
delete process.env.CLAUDE_CONFIG_DIR
|
delete process.env.CLAUDE_CONFIG_DIR
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { sanitizePath as sanitizePortablePath } from '../../utils/sessionStorage
|
|||||||
import type { FileHistorySnapshot } from '../../utils/fileHistory.js'
|
import type { FileHistorySnapshot } from '../../utils/fileHistory.js'
|
||||||
import { calculateUSDCost, MODEL_COSTS } from '../../utils/modelCost.js'
|
import { calculateUSDCost, MODEL_COSTS } from '../../utils/modelCost.js'
|
||||||
import {
|
import {
|
||||||
|
calculateCurrentContextTokenTotal,
|
||||||
MODEL_CONTEXT_WINDOW_DEFAULT,
|
MODEL_CONTEXT_WINDOW_DEFAULT,
|
||||||
getContextWindowForModel,
|
getContextWindowForModel,
|
||||||
getModelMaxOutputTokens,
|
getModelMaxOutputTokens,
|
||||||
@ -891,12 +892,19 @@ export class SessionService {
|
|||||||
if (!latest) return null
|
if (!latest) return null
|
||||||
|
|
||||||
const rawMaxTokens = this.getTranscriptContextWindow(latest.model)
|
const rawMaxTokens = this.getTranscriptContextWindow(latest.model)
|
||||||
const totalTokens = latest.inputTokens + latest.cacheReadInputTokens + latest.cacheCreationInputTokens
|
const promptTokens = latest.inputTokens + latest.cacheReadInputTokens + latest.cacheCreationInputTokens
|
||||||
|
const totalTokens = calculateCurrentContextTokenTotal(promptTokens, {
|
||||||
|
input_tokens: latest.inputTokens,
|
||||||
|
output_tokens: latest.outputTokens,
|
||||||
|
cache_read_input_tokens: latest.cacheReadInputTokens,
|
||||||
|
cache_creation_input_tokens: latest.cacheCreationInputTokens,
|
||||||
|
})
|
||||||
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' },
|
||||||
{ name: 'Cache read', tokens: latest.cacheReadInputTokens, color: '#0f5c8f' },
|
{ name: 'Cache read', tokens: latest.cacheReadInputTokens, color: '#0f5c8f' },
|
||||||
{ name: 'Cache write', tokens: latest.cacheCreationInputTokens, color: '#7c3aed' },
|
{ name: 'Cache write', tokens: latest.cacheCreationInputTokens, color: '#7c3aed' },
|
||||||
|
{ name: 'Output tokens', tokens: latest.outputTokens, color: '#2f7d32' },
|
||||||
{ name: 'Free space', tokens: Math.max(0, rawMaxTokens - totalTokens), color: '#a1a1aa', isDeferred: true },
|
{ name: 'Free space', tokens: Math.max(0, rawMaxTokens - totalTokens), color: '#a1a1aa', isDeferred: true },
|
||||||
].filter((category) => category.tokens > 0)
|
].filter((category) => category.tokens > 0)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user