diff --git a/packages/happy-app/sources/-session/SessionView.tsx b/packages/happy-app/sources/-session/SessionView.tsx index ee39874f2a..8d9653a0be 100644 --- a/packages/happy-app/sources/-session/SessionView.tsx +++ b/packages/happy-app/sources/-session/SessionView.tsx @@ -571,6 +571,7 @@ function SessionViewLoaded({ sessionId, session }: { sessionId: string, session: cacheCreation: source.cacheCreation, cacheRead: source.cacheRead, contextSize: source.contextSize, + model: source.model, }; }, [sessionUsage, session.latestUsage]); diff --git a/packages/happy-app/sources/components/AgentInput.tsx b/packages/happy-app/sources/components/AgentInput.tsx index f5f4c35a37..8b34949e47 100644 --- a/packages/happy-app/sources/components/AgentInput.tsx +++ b/packages/happy-app/sources/components/AgentInput.tsx @@ -72,6 +72,7 @@ interface AgentInputProps { cacheCreation: number; cacheRead: number; contextSize: number; + model?: string; }; alwaysShowContextSize?: boolean; onFileViewerPress?: () => void; @@ -93,7 +94,18 @@ interface AgentInputProps { onAddImages?: (images: AttachmentPreview[]) => void; } -const MAX_CONTEXT_SIZE = 190000; +// Effective usable context per model. Anthropic 1M-context variants +// expose ~950K usable tokens; everything else is treated as 200K (~190K usable). +// See https://github.com/slopus/happy/issues/910 +const DEFAULT_MAX_CONTEXT_SIZE = 190000; +const ONE_MILLION_MAX_CONTEXT_SIZE = 950000; + +function getMaxContextSize(model?: string): number { + if (model && /\[1m\]/i.test(model)) { + return ONE_MILLION_MAX_CONTEXT_SIZE; + } + return DEFAULT_MAX_CONTEXT_SIZE; +} const stylesheet = StyleSheet.create((theme, runtime) => ({ container: { @@ -300,8 +312,9 @@ const stylesheet = StyleSheet.create((theme, runtime) => ({ }, })); -const getContextWarning = (contextSize: number, alwaysShow: boolean = false, theme: Theme) => { - const percentageUsed = (contextSize / MAX_CONTEXT_SIZE) * 100; +const getContextWarning = (contextSize: number, alwaysShow: boolean = false, theme: Theme, model?: string) => { + const maxContextSize = getMaxContextSize(model); + const percentageUsed = (contextSize / maxContextSize) * 100; const percentageRemaining = Math.max(0, Math.min(100, 100 - percentageUsed)); if (percentageRemaining <= 5) { @@ -596,9 +609,10 @@ export const AgentInput = React.memo(React.forwardRef state.latestUsage.timestamp) { state.latestUsage = { @@ -1152,6 +1155,7 @@ function processUsageData(state: ReducerState, usage: UsageData, timestamp: numb cacheCreation: usage.cache_creation_input_tokens || 0, cacheRead: usage.cache_read_input_tokens || 0, contextSize: (usage.cache_creation_input_tokens || 0) + (usage.cache_read_input_tokens || 0) + usage.input_tokens, + model, timestamp: timestamp }; } diff --git a/packages/happy-app/sources/sync/storageTypes.ts b/packages/happy-app/sources/sync/storageTypes.ts index b3e9eaa715..498be942db 100644 --- a/packages/happy-app/sources/sync/storageTypes.ts +++ b/packages/happy-app/sources/sync/storageTypes.ts @@ -125,6 +125,7 @@ export interface Session { cacheCreation: number; cacheRead: number; contextSize: number; + model?: string; timestamp: number; } | null; } diff --git a/packages/happy-app/sources/sync/typesRaw.ts b/packages/happy-app/sources/sync/typesRaw.ts index ef05147f64..77b11c4c06 100644 --- a/packages/happy-app/sources/sync/typesRaw.ts +++ b/packages/happy-app/sources/sync/typesRaw.ts @@ -526,6 +526,7 @@ export type NormalizedMessage = ({ isSidechain: boolean, meta?: MessageMeta, usage?: UsageData, + model?: string, /** * Underlying Claude `uuid` for this message — used as the rewind point * for the session fork / duplicate flow. Optional because some message @@ -835,7 +836,8 @@ export function normalizeRawMessage(id: string, localId: string | null, createdA isSidechain: raw.content.data.isSidechain ?? false, content, meta: raw.meta, - usage: raw.content.data.message.usage + usage: raw.content.data.message.usage, + model: raw.content.data.message.model }; } else if (raw.content.data.type === 'user') { if (!raw.content.data.uuid) {