Skip to content

feat(claude): add per-account 1M context toggle with global model whitelist#2247

Open
Minidoracat wants to merge 5 commits intorouter-for-me:mainfrom
Minidoracat:feat/claude-1m-context-toggle
Open

feat(claude): add per-account 1M context toggle with global model whitelist#2247
Minidoracat wants to merge 5 commits intorouter-for-me:mainfrom
Minidoracat:feat/claude-1m-context-toggle

Conversation

@Minidoracat
Copy link

@Minidoracat Minidoracat commented Mar 20, 2026

概述 / Summary

新增 Claude 1M context beta(context-1m-2025-08-07)的可控開關,支援全局設定 + 每帳號獨立切換,讓使用者可以精確控制哪些帳號和哪些模型啟用 1M context。

Adds configurable control for Claude's 1M context beta (context-1m-2025-08-07) with a two-layer mechanism: global config toggle with model whitelist + per-account opt-in. This gives operators precise control over which accounts and models use 1M context, avoiding errors on models that don't support the feature.

改動詳情 / Changes

1. internal/config/config.go — 新增設定結構 / New config struct

  • 新增 Claude1MContext struct(Enabled bool + Models []string
  • 加入 Config 的 claude-1m-context YAML 欄位
  • Adds Claude1MContext struct with global toggle and model whitelist

2. internal/runtime/executor/claude_executor.go — 核心邏輯 / Core logic

  • applyClaudeHeaders 新增 model 參數,3 個呼叫點同步更新
  • 新增 should1MContext() 判斷函數:config enabled + auth attribute + model whitelist
  • 滿足條件時自動在 Anthropic-Beta header 中加入 context-1m-2025-08-07
  • X-CPA-CLAUDE-1M header 覆蓋機制保留(向後相容)

3. internal/watcher/synthesizer/file.go — 合成階段 / Synthesis

  • 從 auth JSON 的 enable_1m_context 欄位讀取(支援 bool/string 格式)
  • 寫入 auth.Attributes["enable_1m_context"],模式與 priority/note 一致

4. internal/api/handlers/management/auth_files.go — 管理 API / Management API

  • buildAuthFileEntry 回傳 enable_1m_context 欄位(Attributes 優先,Metadata 回退)
  • PatchAuthFileFields 新增 Enable1MContext *bool 請求欄位

5. config.example.yaml / .gitignore

  • 新增 claude-1m-context 設定範例
  • .gitignore 加入 servercoverage.out

改動文件 / Changed files

文件 / File 改動 / Change
internal/config/config.go 新增 Claude1MContext struct 和 Config 欄位
internal/runtime/executor/claude_executor.go 新增 1M context 條件判斷邏輯
internal/watcher/synthesizer/file.go 合成階段讀取 enable_1m_context 到 Attributes
internal/api/handlers/management/auth_files.go 列表回傳 + PATCH 寫入 enable_1m_context
config.example.yaml 新增設定範例
.gitignore 加入建置產物

使用方式 / Usage

config.yaml:

claude-1m-context: enabled: true models: - claude-opus-4-6 - claude-sonnet-4-6

Auth JSON 檔案:

{ "type": "claude", "enable_1m_context": true }

Management API:

# 啟用 curl -X PATCH /v0/management/auth-files/fields \ -d '{"name": "claude-xxx@gmail.com.json", "enable_1m_context": true}' # 停用 curl -X PATCH /v0/management/auth-files/fields \ -d '{"name": "claude-xxx@gmail.com.json", "enable_1m_context": false}'

啟用條件(AND):

  1. claude-1m-context.enabled = true(全局開關)
  2. auth file enable_1m_context = true(帳號開關)
  3. 模型在 claude-1m-context.models 白名單中(空 = 全部允許)

或:客戶端發送 X-CPA-CLAUDE-1M header(直接覆蓋,向後相容)

測試計劃 / Test plan

  • go build ./cmd/server/ 編譯通過
  • go test ./internal/runtime/executor/... 通過
  • go test ./internal/watcher/... 通過
  • go test ./internal/api/handlers/management/... 通過
  • go test ./internal/config/... 通過
  • go test ./sdk/... 通過
  • go test ./test/... 整合測試通過
  • 設定 claude-1m-context.enabled: true + auth JSON enable_1m_context: true,白名單模型請求帶 1M beta header
  • 未設定的帳號/模型不帶 1M beta header
  • X-CPA-CLAUDE-1M header 覆蓋機制正常運作
  • 管理面板 PATCH API 可正確讀寫 enable_1m_context

前端配套(待做)/ Frontend follow-up

管理面板 UI(Cli-Proxy-API-Management-Center)需要在 Claude 卡片操作列新增 1M toggle,調用本 PR 新增的 API。

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors the handling of Claude's 1M context beta header, moving away from a hardcoded approach that caused compatibility issues with older models. It introduces a robust, two-tiered control mechanism: a global configuration toggle with an optional model whitelist, and a per-account opt-in setting. This change empowers operators with precise control over which Claude accounts and models utilize the 1M context, preventing errors and improving flexibility.

Highlights

  • Flexible 1M Context Control: Implemented a new configuration structure for fine-grained control over Claude's 1M context beta feature, replacing a hardcoded approach.
  • Conditional Beta Header Application: Decoupled the 1M context beta header from hardcoded settings, enabling conditional application based on global configuration and per-account opt-in.
  • Per-Account Opt-in: Introduced a per-account toggle for the 1M context feature, allowing users to enable or disable it for specific Claude accounts.
  • Model Whitelisting: Added a model whitelist to the 1M context configuration, ensuring the beta header is only applied to supported models.
  • Management API Integration: Updated the management API to expose and allow modification of the 'enable_1m_context' setting for individual authentication files.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@Minidoracat
Copy link
Author

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a well-designed, two-layer control mechanism for enabling Claude's 1M context feature, moving away from a hardcoded approach. The changes are comprehensive, spanning configuration, core execution logic, and management APIs, which provides great flexibility. My review includes a few suggestions to enhance robustness in boolean parsing and to optimize the performance of model whitelist checks, ensuring the new feature is both powerful and efficient.

Comment on lines +466 to +479
if v := strings.TrimSpace(authAttribute(auth, "enable_1m_context")); strings.EqualFold(v, "true") {
entry["enable_1m_context"] = true
} else if auth.Metadata != nil {
switch raw := auth.Metadata["enable_1m_context"].(type) {
case bool:
if raw {
entry["enable_1m_context"] = true
}
case string:
if strings.EqualFold(strings.TrimSpace(raw), "true") {
entry["enable_1m_context"] = true
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The logic for parsing the enable_1m_context boolean value is spread across a few checks and relies on strings.EqualFold for string values. This could be consolidated and made more robust by using strconv.ParseBool, which handles a wider range of boolean string representations (e.g., "true", "t", "1"). This refactoring improves readability and maintainability.

 var is1MEnabled bool if v := strings.TrimSpace(authAttribute(auth, "enable_1m_context")); v != "" { is1MEnabled, _ = strconv.ParseBool(v)	} else if auth.Metadata != nil { if val, ok := auth.Metadata["enable_1m_context"]; ok { switch v := val.(type) { case bool: is1MEnabled = v case string: is1MEnabled, _ = strconv.ParseBool(strings.TrimSpace(v))	}	}	} if is1MEnabled { entry["enable_1m_context"] = true	}
Comment on lines +937 to +941
for _, m := range cfg.Claude1MContext.Models {
if strings.EqualFold(strings.TrimSpace(m), model) {
return true
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This loop over the model whitelist runs on every request. For better performance, consider converting cfg.Claude1MContext.Models into a map[string]struct{} when the configuration is loaded in internal/config/config.go. This would make the model check an O(1) map lookup instead of an O(n) slice iteration, which is more efficient, especially if the whitelist grows.

Comment on lines +161 to +172
if raw1M, ok := metadata["enable_1m_context"]; ok {
switch v := raw1M.(type) {
case bool:
if v {
a.Attributes["enable_1m_context"] = "true"
}
case string:
if strings.EqualFold(strings.TrimSpace(v), "true") {
a.Attributes["enable_1m_context"] = "true"
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The logic to parse the enable_1m_context boolean from metadata can be made more robust. Currently, it only checks for the string "true" (case-insensitively). Using strconv.ParseBool would align with Go's standard boolean parsing and handle other valid boolean string representations like "1", "t", etc., improving robustness.

Suggested change
if raw1M, ok := metadata["enable_1m_context"]; ok {
switch v := raw1M.(type) {
case bool:
if v {
a.Attributes["enable_1m_context"] = "true"
}
case string:
if strings.EqualFold(strings.TrimSpace(v), "true") {
a.Attributes["enable_1m_context"] = "true"
}
}
}
if raw1M, ok := metadata["enable_1m_context"]; ok {
var isTrue bool
switch v := raw1M.(type) {
case bool:
isTrue = v
case string:
isTrue, _ = strconv.ParseBool(strings.TrimSpace(v))
}
if isTrue {
a.Attributes["enable_1m_context"] = "true"
}
}
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f95c13468d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

if auth == nil || auth.Attributes == nil {
return false
}
if !strings.EqualFold(strings.TrimSpace(auth.Attributes["enable_1m_context"]), "true") {

Choose a reason for hiding this comment

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

P1 Badge Fall back to metadata when checking 1M opt-in

should1MContext only reads auth.Attributes["enable_1m_context"], but auths created via UploadAuthFile are registered through registerAuthFromFile with metadata populated and no synthesized attributes. In that flow, buildAuthFileEntry now reports enable_1m_context: true from metadata, yet request execution never enables the context-1m-2025-08-07 beta header because this check returns false. This makes the new toggle silently ineffective for uploaded Claude auth files unless another path later writes the attribute.

Useful? React with 👍 / 👎.

return resp, err
}
applyClaudeHeaders(httpReq, auth, apiKey, false, extraBetas, e.cfg)
applyClaudeHeaders(httpReq, auth, apiKey, false, extraBetas, e.cfg, baseModel)

Choose a reason for hiding this comment

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

P2 Badge Use effective payload model for 1M whitelist matching

The 1M gating now depends on model name, but this call passes baseModel captured before payload overrides. Since applyPayloadConfigWithRoot runs earlier and can rewrite model, should1MContext may evaluate whitelist rules against a different model than the one actually sent upstream, causing incorrect addition/removal of the 1M beta header when payload rules remap models.

Useful? React with 👍 / 👎.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a configurable toggle for Claude's 1M context beta feature, with changes spanning configuration, API handlers, and core executor logic. The implementation is well-structured and covers the necessary areas. I've included a couple of suggestions to enhance maintainability and performance by refactoring some duplicated code and optimizing the model whitelist check.

Comment on lines +466 to +479
if v := strings.TrimSpace(authAttribute(auth, "enable_1m_context")); strings.EqualFold(v, "true") {
entry["enable_1m_context"] = true
} else if auth.Metadata != nil {
switch raw := auth.Metadata["enable_1m_context"].(type) {
case bool:
if raw {
entry["enable_1m_context"] = true
}
case string:
if strings.EqualFold(strings.TrimSpace(raw), "true") {
entry["enable_1m_context"] = true
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This block of code for checking enable_1m_context is a bit repetitive and contains logic that is also duplicated in internal/watcher/synthesizer/file.go.

To improve code clarity and maintainability, I suggest refactoring this block to be more concise. Additionally, you could extract the logic for parsing the enable_1m_context value from metadata into a shared helper function to avoid duplication across files.

For example, a helper function could look like this:

// In a shared package, e.g., internal/util func Is1MContextEnabledInMetadata(metadata map[string]any) bool { if metadata == nil { return false } raw1M, ok := metadata["enable_1m_context"] if !ok { return false } switch v := raw1M.(type) { case bool: return v case string: return strings.EqualFold(strings.TrimSpace(v), "true") } return false }
Suggested change
if v := strings.TrimSpace(authAttribute(auth, "enable_1m_context")); strings.EqualFold(v, "true") {
entry["enable_1m_context"] = true
} else if auth.Metadata != nil {
switch raw := auth.Metadata["enable_1m_context"].(type) {
case bool:
if raw {
entry["enable_1m_context"] = true
}
case string:
if strings.EqualFold(strings.TrimSpace(raw), "true") {
entry["enable_1m_context"] = true
}
}
}
enable1M := false
if v := strings.TrimSpace(authAttribute(auth, "enable_1m_context")); strings.EqualFold(v, "true") {
enable1M = true
} else if auth.Metadata != nil {
switch raw := auth.Metadata["enable_1m_context"].(type) {
case bool:
enable1M = raw
case string:
enable1M = strings.EqualFold(strings.TrimSpace(raw), "true")
}
}
if enable1M {
entry["enable_1m_context"] = true
}
Comment on lines +922 to +943
func should1MContext(auth *cliproxyauth.Auth, cfg *config.Config, model string) bool {
if cfg == nil || !cfg.Claude1MContext.Enabled {
return false
}
if auth == nil || auth.Attributes == nil {
return false
}
if !strings.EqualFold(strings.TrimSpace(auth.Attributes["enable_1m_context"]), "true") {
return false
}
// If models whitelist is empty, allow all models.
if len(cfg.Claude1MContext.Models) == 0 {
return true
}
// Check if the request model is in the whitelist.
for _, m := range cfg.Claude1MContext.Models {
if strings.EqualFold(strings.TrimSpace(m), model) {
return true
}
}
return false
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The current implementation of should1MContext iterates through the Models slice on every request to check the whitelist. For better performance, especially if the model list grows, this check can be optimized by converting the slice to a map for O(1) lookups.

You can achieve this by adding a ModelSet map[string]struct{} to the Claude1MContext struct in internal/config/config.go and populating it once when the configuration is loaded. This avoids repeated string operations and iterations in the request hot path.

Here's how you could modify internal/config/config.go:

// In internal/config/config.go // 1. Add ModelSet to the struct type Claude1MContext struct { Enabled bool `yaml:"enabled" json:"enabled"` Models []string `yaml:"models" json:"models"` ModelSet map[string]struct{} `yaml:"-" json:"-"` } // 2. Populate the set in LoadConfig() after unmarshalling if len(cfg.Claude1MContext.Models) > 0 { cfg.Claude1MContext.ModelSet = make(map[string]struct{}, len(cfg.Claude1MContext.Models)) for _, m := range cfg.Claude1MContext.Models { cfg.Claude1MContext.ModelSet[strings.ToLower(strings.TrimSpace(m))] = struct{}{} } } else { cfg.Claude1MContext.ModelSet = nil }
func should1MContext(auth *cliproxyauth.Auth, cfg *config.Config, model string) bool { if cfg == nil || !cfg.Claude1MContext.Enabled { return false	} if auth == nil || auth.Attributes == nil { return false	} if !strings.EqualFold(strings.TrimSpace(auth.Attributes["enable_1m_context"]), "true") { return false	} // If models whitelist is empty (ModelSet is nil), allow all models. if cfg.Claude1MContext.ModelSet == nil { return true	} // Check if the request model is in the whitelist using the pre-computed set. _, ok := cfg.Claude1MContext.ModelSet[strings.ToLower(model)] return ok }
@chencheng-li
Copy link

hi we need this plzzz

Copy link
Collaborator

@luispater luispater left a comment

Choose a reason for hiding this comment

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

Summary:
This PR introduces a useful two-layer 1M-context control for Claude (global config + per-account opt-in + model whitelist), and keeps header override compatibility. The overall direction is good, but there is one behavior gap that can make the feature appear enabled while not actually applying at runtime.

Key findings:

  • Blocking: enable_1m_context is not consistently wired for UploadAuthFile-created auths.

    • registerAuthFromFile stores full JSON in Metadata but does not project enable_1m_context into Attributes.
    • should1MContext uses only auth.Attributes["enable_1m_context"] to decide whether to add context-1m-2025-08-07.
    • buildAuthFileEntry already falls back to Metadata, so management output can report enable_1m_context: true while runtime still does not enable 1M.
    • Please align read/write paths so runtime and management API reflect the same source of truth (e.g., populate attribute during registration, or let runtime also read metadata fallback).
  • Non-blocking: Missing tests for new behavior matrix.

    • Recommended coverage:
      • global enabled + account enabled + whitelisted model => header injected
      • account disabled / global disabled / non-whitelisted model => header not injected
      • X-CPA-CLAUDE-1M override => header injected regardless of account toggle
      • UploadAuthFile path specifically (metadata-only input) to prevent this regression class

Test plan:
I reviewed PR metadata, changed files, full patch, and CI checks via gh (ignoring translator-path-guard per request). I did not execute local runtime tests in this review pass.

@Minidoracat
Copy link
Author

Thanks for the thorough review @luispater!

Blocking issue — fixed in 46ade7b:

should1MContext now falls back to auth.Metadata["enable_1m_context"] when Attributes is empty, aligning the runtime read path with buildAuthFileEntry. This ensures auths registered via UploadAuthFile / registerAuthFromFile (which only populate Metadata) correctly enable the 1M context beta header.

Non-blocking — test coverage:

Good call on the test matrix. Will add coverage for the behavior combinations (global/account/model whitelist/header override/UploadAuthFile path) in a follow-up commit.

@Minidoracat Minidoracat force-pushed the feat/claude-1m-context-toggle branch from c7dfc3d to b1eb655 Compare March 23, 2026 14:35
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b1eb65509e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

if auth.Attributes != nil {
enabled, _ = strconv.ParseBool(strings.TrimSpace(auth.Attributes["enable_1m_context"]))
}
if !enabled && auth.Metadata != nil {

Choose a reason for hiding this comment

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

P2 Badge Honor explicit false toggle in should1MContext

should1MContext currently falls back to auth.Metadata["enable_1m_context"] whenever the parsed attribute value is false (if !enabled && auth.Metadata != nil). This makes a conflicting state (Attributes["enable_1m_context"] = "false" with metadata true) evaluate to enabled and incorrectly append context-1m-2025-08-07, even though the account-level toggle is explicitly off. The metadata fallback should be conditioned on attribute absence (or parse failure), not on the boolean result itself, so a deliberate false attribute remains authoritative.

Useful? React with 👍 / 👎.

@Minidoracat Minidoracat force-pushed the feat/claude-1m-context-toggle branch from 5ff3181 to d7fbdcb Compare March 24, 2026 20:12
Remove hardcoded context-1m-2025-08-07 from baseBetas and replace with a two-layer control mechanism: 1. Global config (claude-1m-context.enabled + models whitelist) 2. Per-account toggle (enable_1m_context in auth JSON / management API) The X-CPA-CLAUDE-1M header override is preserved for backward compat. Changes: - config: add Claude1MContext struct (enabled + models whitelist) - executor: applyClaudeHeaders now conditionally includes 1M beta - synthesizer: parse enable_1m_context from auth JSON into Attributes - management API: PatchAuthFileFields accepts enable_1m_context - management API: buildAuthFileEntry exposes enable_1m_context - gitignore: add server binary and coverage.out
- Use strconv.ParseBool for more robust boolean parsing (auth_files, synthesizer) - Use map lookup for O(1) model whitelist check (executor)
registerAuthFromFile stores JSON fields in Metadata but does not project enable_1m_context into Attributes. Without this fallback, the 1M toggle was silently ineffective for uploaded auth files even though buildAuthFileEntry correctly reported it as enabled. Addresses review from luispater (CHANGES_REQUESTED).
12 test cases covering: - global enabled/disabled - account enabled/disabled (Attributes path) - Metadata fallback for UploadAuthFile path (bool + string) - model whitelist match/miss/empty - nil auth/config guards - Attributes precedence over Metadata - case-insensitive model matching Addresses non-blocking review feedback from luispater.
An explicit Attributes["enable_1m_context"] = "false" is now authoritative and will not be overridden by Metadata. The Metadata fallback only triggers when the key does not exist in Attributes (i.e. UploadAuthFile path where synthesizer never ran). Addresses Codex P2 review.
@Minidoracat Minidoracat force-pushed the feat/claude-1m-context-toggle branch from d7fbdcb to 2bfa05d Compare March 25, 2026 19:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants