Skip to content

feat: add Opus [1M] model with correct 1M context window reporting#649

Open
bbesner wants to merge 1 commit intositeboon:mainfrom
bbesner:feat/opus-1m-context-window
Open

feat: add Opus [1M] model with correct 1M context window reporting#649
bbesner wants to merge 1 commit intositeboon:mainfrom
bbesner:feat/opus-1m-context-window

Conversation

@bbesner
Copy link
Copy Markdown

@bbesner bbesner commented Apr 12, 2026

Summary

Closes #579

Adds opus[1m] to the Claude model picker and fixes context window reporting so 1M-context models display the correct token budget instead of showing 160K/200K.

Problem

When using Opus with 1M context (claude-opus-4-6[1m]), the token usage pie chart incorrectly showed 160K or 200K as the total context window, making it appear 100% full even with plenty of room remaining.

Root Cause

The fix required addressing four layers:

  1. Model listopus[1m] was missing from CLAUDE_MODELS.OPTIONS
  2. Server-side token budgetextractTokenBudget() hardcoded 160000 instead of reading contextWindow from the SDK's modelUsage result (which already reports 1000000 for 1M models)
  3. Frontend fallbackTokenUsagePie used 160000 as fallback before the first server response. Now checks the selected model name for 1m suffix
  4. Frontend state race — After the WebSocket correctly sets total: 1000000, the HTTP token-usage endpoint re-fires and overwrites it with 200000. Fixed by preventing HTTP from overwriting a higher WebSocket value

Changes

  • shared/modelConstants.js — Add opus[1m] option
  • server/claude-sdk.js — Read contextWindow from SDK result's modelUsage data; pass selectedModel for fallback detection
  • src/.../ChatInputControls.tsx — Accept selectedModel prop, use model-aware context window fallback
  • src/.../ChatComposer.tsx — Thread selectedModel prop
  • src/.../ChatInterface.tsx — Pass claudeModel to composer
  • src/.../useChatSessionState.ts — Prevent HTTP token-usage fetch from overwriting WebSocket value

Testing

Tested with Claude Code CLI 2.1.92 + @anthropic-ai/claude-agent-sdk 0.2.104.

  • New session with opus[1m] → pie shows 1,000,000 immediately ✓
  • After Claude responds → pie stays at 1,000,000 (not reverted to 200K) ✓
  • Sessions with sonnet → pie correctly shows 200,000 ✓
  • Sessions with sonnet[1m] → pie correctly shows 1,000,000 ✓

Note

The SDK (0.2.104) already reports contextWindow: 1000000 in the modelUsage result for 1M models. This PR makes CloudCLI read that value instead of ignoring it. Users on older SDK versions may need to upgrade for the server-side detection to work; the frontend fallback handles the case where the SDK doesn't report contextWindow.

Summary by CodeRabbit

  • New Features

    • Added Claude Opus [1M] model to available options, providing access to an expanded 1M token context window.
  • Improvements

    • Enhanced token budget detection with intelligent model-aware fallback logic.
    • Improved token usage state management with cancellation tracking and state preservation across model selections.

Closes siteboon#579

Adds `opus[1m]` to the Claude model picker and fixes context window
reporting so 1M-context models display the correct token budget.

Changes:
- Add opus[1m] option to CLAUDE_MODELS.OPTIONS in modelConstants.js
- Read contextWindow from SDK result's modelUsage data instead of
  hardcoding 160K — the SDK already reports the correct value
- Pass selectedModel to extractTokenBudget for fallback detection
- Thread selectedModel through ChatInterface → ChatComposer →
  ChatInputControls so the pie chart shows 1M before first response
- Prevent HTTP token-usage fetch from overwriting the correct
  WebSocket-provided context window (race condition where HTTP
  returns 200K default after WebSocket already set 1M)

The context window fix required four layers:
1. Model list entry (trivial)
2. Server-side: read contextWindow from SDK modelUsage
3. Frontend: model-aware fallback in TokenUsagePie
4. Frontend: prevent HTTP/WebSocket state race on token budget

Tested with Claude Code CLI 2.1.92 + SDK 0.2.104.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 12, 2026

📝 Walkthrough

Walkthrough

This pull request adds support for Claude Opus with 1M token context window in the model picker UI, improves server-side token budget calculation to leverage selected model information, and enhances client-side token usage fetching with cancellation tracking and state preservation.

Changes

Cohort / File(s) Summary
Model Constants
shared/modelConstants.js
Added Opus [1M] option (opus[1m]) to the selectable Claude model variants.
Token Budget Calculation
server/claude-sdk.js
Enhanced extractTokenBudget to accept selectedModel parameter and implement multi-source context-window precedence: environment variable → model data → heuristic check for "1m" in model name → fallback to 200000.
Token Usage State Management
src/components/chat/hooks/useChatSessionState.ts
Added cancellation tracking for async token-usage fetches to prevent stale updates, and implemented state preservation logic that prefers existing higher token budgets over newly fetched lower ones.
Component Prop Wiring
src/components/chat/view/ChatInterface.tsx, src/components/chat/view/subcomponents/ChatComposer.tsx, src/components/chat/view/subcomponents/ChatInputControls.tsx
Propagated selectedModel prop down the component hierarchy from ChatInterface through ChatComposer to ChatInputControls.
Token Usage Display
src/components/chat/view/subcomponents/ChatInputControls.tsx
Updated TokenUsagePie fallback logic to derive default context-window total from selectedModel (checking for "1m" to use 1000000, otherwise 200000) instead of parsing environment variable.

Possibly related PRs

Suggested reviewers

  • viper151

Poem

🐰 A rabbit hops through token streams,
With Opus large in UI dreams,
Context windows, one million wide,
Model selection, state with pride,
Cancellation guards the flow so clean! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding Opus [1M] model support with correct 1M context window reporting.
Linked Issues check ✅ Passed The PR fully implements the primary requirement from #579: adds Opus [1M] to CLAUDE_MODELS.OPTIONS and ensures UI/backend correctly support the 1M context window.
Out of Scope Changes check ✅ Passed All changes directly support the linked issue: adding Opus [1M] model, fixing context window reporting, threading selectedModel through components, and preventing WebSocket race conditions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/components/chat/view/subcomponents/ChatInputControls.tsx (1)

83-83: Consider centralizing context-window fallback inference.

Line 83 duplicates model-suffix fallback logic that now also exists server-side. A shared helper/constants path would reduce frontend/backend drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/chat/view/subcomponents/ChatInputControls.tsx` at line 83, The
TokenUsagePie invocation is duplicating the context-window fallback logic
(ternary using selectedModel.includes('1m')); extract that inference into a
shared helper or constant (e.g., getDefaultContextWindow or
DEFAULT_CONTEXT_WINDOW) used by both frontend and backend and replace the inline
fallback in TokenUsagePie props (where tokenBudget?.total || (selectedModel &&
selectedModel.includes('1m') ? 1000000 : 200000)) with a call to the shared
helper or the shared constant so both sides use the same source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@server/claude-sdk.js`:
- Around line 305-307: The override for CONTEXT_WINDOW currently uses
parseInt(process.env.CONTEXT_WINDOW) || 0 which accepts negative or non-integer
values; update the logic that sets contextWindow so it parses the env var,
verifies it's a finite positive integer (>0) (e.g., use Number.isInteger(parsed)
&& parsed > 0) and only applies it when valid, otherwise fall back to 0 or the
existing model-derived value; modify the code where contextWindow is assigned
(the parseInt(...) usage and the subsequent if (!contextWindow) check) to
perform this validation and fall back safely.

In `@src/components/chat/hooks/useChatSessionState.ts`:
- Around line 565-567: The code is clearing an existing valid WebSocket-provided
token budget on transient fetch failures by calling setTokenBudget(null) in the
fetch error path; update the error/else path inside useChatSessionState so it
does not overwrite a current tokenBudget on transient failures—either remove the
setTokenBudget(null) call or guard it so you only clear tokenBudget when you
have a definitive “no budget” response (e.g., only setTokenBudget(null) when the
fetch returned a confirmed null budget), and leave tokenBudget untouched when
the fetch failed or was cancelled (check cancelled and the fetched value
variable before calling setTokenBudget).

---

Nitpick comments:
In `@src/components/chat/view/subcomponents/ChatInputControls.tsx`:
- Line 83: The TokenUsagePie invocation is duplicating the context-window
fallback logic (ternary using selectedModel.includes('1m')); extract that
inference into a shared helper or constant (e.g., getDefaultContextWindow or
DEFAULT_CONTEXT_WINDOW) used by both frontend and backend and replace the inline
fallback in TokenUsagePie props (where tokenBudget?.total || (selectedModel &&
selectedModel.includes('1m') ? 1000000 : 200000)) with a call to the shared
helper or the shared constant so both sides use the same source of truth.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fa4587b2-427e-4ef4-a765-a6f3330dc513

📥 Commits

Reviewing files that changed from the base of the PR and between e2459cb and 602785b.

📒 Files selected for processing (6)
  • server/claude-sdk.js
  • shared/modelConstants.js
  • src/components/chat/hooks/useChatSessionState.ts
  • src/components/chat/view/ChatInterface.tsx
  • src/components/chat/view/subcomponents/ChatComposer.tsx
  • src/components/chat/view/subcomponents/ChatInputControls.tsx

Comment on lines +305 to +307
// Determine context window: env override > model usage data > selected model > default
let contextWindow = parseInt(process.env.CONTEXT_WINDOW) || 0;
if (!contextWindow) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Validate CONTEXT_WINDOW as a positive integer before applying override.

At Line 306, parseInt(process.env.CONTEXT_WINDOW) || 0 treats negative values as valid and can emit an invalid budget total.

Suggested fix
-  let contextWindow = parseInt(process.env.CONTEXT_WINDOW) || 0;
+  const envContextWindow = Number.parseInt(process.env.CONTEXT_WINDOW ?? '', 10);
+  let contextWindow = Number.isFinite(envContextWindow) && envContextWindow > 0 ? envContextWindow : 0;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Determine context window: env override > model usage data > selected model > default
let contextWindow = parseInt(process.env.CONTEXT_WINDOW) || 0;
if (!contextWindow) {
// Determine context window: env override > model usage data > selected model > default
const envContextWindow = Number.parseInt(process.env.CONTEXT_WINDOW ?? '', 10);
let contextWindow = Number.isFinite(envContextWindow) && envContextWindow > 0 ? envContextWindow : 0;
if (!contextWindow) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/claude-sdk.js` around lines 305 - 307, The override for CONTEXT_WINDOW
currently uses parseInt(process.env.CONTEXT_WINDOW) || 0 which accepts negative
or non-integer values; update the logic that sets contextWindow so it parses the
env var, verifies it's a finite positive integer (>0) (e.g., use
Number.isInteger(parsed) && parsed > 0) and only applies it when valid,
otherwise fall back to 0 or the existing model-derived value; modify the code
where contextWindow is assigned (the parseInt(...) usage and the subsequent if
(!contextWindow) check) to perform this validation and fall back safely.

Comment on lines +565 to 567
} else if (!cancelled) {
setTokenBudget(null);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid clearing existing token budget on transient fetch failures.

At Line 565-Line 567, setting tokenBudget to null can erase a valid WebSocket-provided budget and cause UI regression.

Suggested fix
-        } else if (!cancelled) {
-          setTokenBudget(null);
-        }
+        } else if (!cancelled) {
+          // Preserve existing budget (especially realtime WS updates) on HTTP fetch failures.
+          setTokenBudget((prev: Record<string, unknown> | null) => prev);
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if (!cancelled) {
setTokenBudget(null);
}
} else if (!cancelled) {
// Preserve existing budget (especially realtime WS updates) on HTTP fetch failures.
setTokenBudget((prev: Record<string, unknown> | null) => prev);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/chat/hooks/useChatSessionState.ts` around lines 565 - 567, The
code is clearing an existing valid WebSocket-provided token budget on transient
fetch failures by calling setTokenBudget(null) in the fetch error path; update
the error/else path inside useChatSessionState so it does not overwrite a
current tokenBudget on transient failures—either remove the setTokenBudget(null)
call or guard it so you only clear tokenBudget when you have a definitive “no
budget” response (e.g., only setTokenBudget(null) when the fetch returned a
confirmed null budget), and leave tokenBudget untouched when the fetch failed or
was cancelled (check cancelled and the fetched value variable before calling
setTokenBudget).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Opus [1M] option to model picker

1 participant