feat: add model-aware context budget calculation for status bar warnings

This commit is contained in:
weishu
2025-12-18 08:50:07 +08:00
parent 7f07bdc796
commit f75a7d46bb
3 changed files with 42 additions and 10 deletions
+28
View File
@@ -0,0 +1,28 @@
import type { ModelMode } from '@/types/api'
/**
* Context windows vary by model/provider and may change over time.
*
* The UI only needs this to compute a conservative "context remaining" warning.
* We intentionally keep a headroom budget to avoid false confidence near the limit
* (system prompts, tool overhead, and other hidden tokens can consume extra space).
*
* If/when the server provides an explicit per-session context limit, prefer that
* and use this only as a fallback.
*/
const CONTEXT_HEADROOM_TOKENS = 10_000
const MODEL_CONTEXT_WINDOWS: Record<NonNullable<ModelMode>, number> = {
// Claude Code modes used in this app; currently treated as ~200k context.
default: 200_000,
sonnet: 200_000,
opus: 200_000
}
export function getContextBudgetTokens(modelMode: ModelMode): number | null {
const mode: NonNullable<ModelMode> = modelMode ?? 'default'
const windowTokens = MODEL_CONTEXT_WINDOWS[mode]
if (!windowTokens) return null
return Math.max(1, windowTokens - CONTEXT_HEADROOM_TOKENS)
}
@@ -401,6 +401,7 @@ export function HappyComposer(props: {
thinking={thinking}
agentState={agentState}
contextSize={contextSize}
modelMode={modelMode}
permissionMode={permissionMode}
/>
+13 -10
View File
@@ -1,5 +1,6 @@
import { useMemo } from 'react'
import type { AgentState, PermissionMode } from '@/types/api'
import type { AgentState, ModelMode, PermissionMode } from '@/types/api'
import { getContextBudgetTokens } from '@/chat/modelConfig'
const PERMISSION_MODE_LABELS: Record<string, string> = {
default: 'Default',
@@ -8,9 +9,6 @@ const PERMISSION_MODE_LABELS: Record<string, string> = {
bypassPermissions: 'Bypass All'
}
// Max context size for percentage calculation
const MAX_CONTEXT_SIZE = 190000
// Vibing messages for thinking state
const VIBING_MESSAGES = [
"Accomplishing", "Actioning", "Actualizing", "Baking", "Booping", "Brewing",
@@ -73,9 +71,9 @@ function getConnectionStatus(
}
}
function getContextWarning(contextSize: number): { text: string; color: string } | null {
const percentageUsed = (contextSize / MAX_CONTEXT_SIZE) * 100
const percentageRemaining = 100 - percentageUsed
function getContextWarning(contextSize: number, maxContextSize: number): { text: string; color: string } | null {
const percentageUsed = (contextSize / maxContextSize) * 100
const percentageRemaining = Math.max(0, 100 - percentageUsed)
if (percentageRemaining <= 5) {
return { text: `${Math.round(percentageRemaining)}% left`, color: 'text-red-500' }
@@ -91,6 +89,7 @@ export function StatusBar(props: {
thinking: boolean
agentState: AgentState | null | undefined
contextSize?: number
modelMode?: ModelMode
permissionMode?: PermissionMode
}) {
const connectionStatus = useMemo(
@@ -99,8 +98,13 @@ export function StatusBar(props: {
)
const contextWarning = useMemo(
() => props.contextSize !== undefined ? getContextWarning(props.contextSize) : null,
[props.contextSize]
() => {
if (props.contextSize === undefined) return null
const maxContextSize = getContextBudgetTokens(props.modelMode)
if (!maxContextSize) return null
return getContextWarning(props.contextSize, maxContextSize)
},
[props.contextSize, props.modelMode]
)
const permissionMode = props.permissionMode
@@ -136,4 +140,3 @@ export function StatusBar(props: {
</div>
)
}