diff --git a/internal/agent/claude.go b/internal/agent/claude.go index 935af47d..1839ee1a 100644 --- a/internal/agent/claude.go +++ b/internal/agent/claude.go @@ -141,7 +141,8 @@ const claudeContextEnv = "CLAUDE_CODE_MAX_CONTEXT_TOKENS" // of it and the model's window, a [1m] one's too. magpie sets it to // settings.WorkingWindow, so a 1M model isn't run to 1M with every turn // sending all of it (X: Chen, turns of ~550K tokens waiting 70–90s for a -// first token); settings.FullContext leaves it out. +// first token); settings.FullContext leaves it out, and so does a Claude +// model, which Anthropic runs to its whole window. const claudeCompactEnv = "CLAUDE_CODE_AUTO_COMPACT_WINDOW" // claudeCapsEnv tells Claude Code what a model it doesn't know can do, as @@ -412,13 +413,16 @@ func claudeIn(at place) *Agent { return nil } // writeCompact has Claude Code compact at the working window - // (settings.WorkingWindow) on magpie, unless settings.FullContext + // (settings.WorkingWindow) on magpie, unless settings.FullContext or the + // main model is one of Anthropic's Claude models: Anthropic runs a [1m] + // one to its whole 1M, so it gets its own window (Max on Discord: a + // [1m] model stopped at 272K) writeCompact := func() error { if env(claudeCompactEnv) != "" && !compactOurs() { forget(compactKey) return nil } - if settings.Load().FullContext { + if settings.Load().FullContext || claudeModel(mainModel()) { return dropCompact() } w := strconv.Itoa(settings.WorkingWindow) @@ -1271,6 +1275,15 @@ func claude1MFor(agent string) func(ref string) string { } } +// claudeModel says a model ref (magpie/v/claude-opus-5-5[1m], or Claude +// Code's own claude-opus-5-5) is one of Anthropic's Claude models, by +// whichever provider it comes. +func claudeModel(ref string) bool { + m := strings.TrimSuffix(ref, "[1m]") + m = m[strings.LastIndex(m, "/")+1:] + return strings.HasPrefix(strings.ToLower(m), "claude") +} + // claudeWindow is the context window to tell Claude Code for the models it // runs on, 0 when magpie doesn't know it. One value serves every model not // marked [1m]: the main model's, or when that one is marked, the smallest of diff --git a/internal/agent/claude_compact_test.go b/internal/agent/claude_compact_test.go index 907470d2..ca3ba23e 100644 --- a/internal/agent/claude_compact_test.go +++ b/internal/agent/claude_compact_test.go @@ -11,9 +11,9 @@ import ( ) // TestClaudeCompactWindow: on magpie Claude Code compacts at the working -// window (CLAUDE_CODE_AUTO_COMPACT_WINDOW), a [1m] model too, so a 1M model's +// window (CLAUDE_CODE_AUTO_COMPACT_WINDOW), another vendor's [1m] model too, so a 1M model's // conversation isn't run to 1M with every turn sending all of it (X: Chen); -// settings.FullContext takes it away, a value the user set is theirs, and +// settings.FullContext takes it away, a Claude model runs to its own, a value the user set is theirs, and // Claude Code as installed has none. func TestClaudeCompactWindow(t *testing.T) { home := t.TempDir() @@ -22,10 +22,10 @@ func TestClaudeCompactWindow(t *testing.T) { t.Setenv("XDG_CONFIG_HOME", filepath.Join(home, ".config")) t.Setenv("XDG_CACHE_HOME", filepath.Join(home, ".cache")) if err := provider.Save(provider.Provider{ID: "v", Name: "V", Chat: "https://example.test/v1", Key: "k", - Models: []string{"big"}}); err != nil { + Models: []string{"big", "claude-opus-5-5"}}); err != nil { t.Fatal(err) } - if err := catalog.SaveLive("v", "https://example.test/v1", []catalog.Model{{ID: "big", Context: 1000000}}); err != nil { + if err := catalog.SaveLive("v", "https://example.test/v1", []catalog.Model{{ID: "big", Context: 1000000}, {ID: "claude-opus-5-5", Context: 1000000}}); err != nil { t.Fatal(err) } path := filepath.Join(home, ".claude", "settings.json") @@ -53,6 +53,21 @@ func TestClaudeCompactWindow(t *testing.T) { if compact() != "272000" { t.Fatalf("back to the working window: %q", compact()) } + // a Claude model runs to its whole window, as Anthropic runs a [1m] + // one (Max on Discord: a [1m] model stopped at 272K), and another + // vendor's is capped again after it + must(a.Field("model").Set("v/claude-opus-5-5[1m]")) + if compact() != "" { + t.Fatalf("a Claude model: %q", compact()) + } + must(a.Sync()) + if compact() != "" { + t.Fatalf("a Claude model, synced: %q", compact()) + } + must(a.Field("model").Set("v/big[1m]")) + if compact() != "272000" { + t.Fatalf("after a Claude model: %q", compact()) + } must(a.Field("model").Set("")) if compact() != "" { t.Fatalf("Claude Code as installed: %q", compact()) diff --git a/internal/gui/assets/i18n.js b/internal/gui/assets/i18n.js index 919d46d5..6a19b76e 100644 --- a/internal/gui/assets/i18n.js +++ b/internal/gui/assets/i18n.js @@ -403,7 +403,7 @@ const I18N = { "Not on names I set": "自定义名称不带供应商", "Codex subagents on any model": "Codex 子代理可用任意模型", "Long conversations": "长对话", - "Codex and Claude Code compact a conversation at 272K tokens on a model with a larger window, as they do on OpenAI’s and Anthropic’s own; the model’s full window lets it grow further, with every turn slower and costlier": "模型窗口超过 272K 时,Codex 和 Claude Code 在 272K tokens 处压缩对话,和它们在 OpenAI、Anthropic 自家模型上一样;用模型的完整窗口则对话可以继续变长,但每一轮都更慢、更贵", + "Codex and Claude Code compact a conversation at 272K tokens on a model with a larger window; in Claude Code a Claude model runs to its own window, 1M for a [1m] one, as Anthropic runs it. The model’s full window lets any conversation grow further, with every turn slower and costlier": "模型窗口超过 272K 时,Codex 和 Claude Code 在 272K tokens 处压缩对话;Claude Code 里的 Claude 模型按它自己的窗口走([1m] 的就是 1M),和 Anthropic 自家一样。用模型的完整窗口则任何对话都可以继续变长,但每一轮都更慢、更贵", "Compact at 272K": "272K 时压缩", "Full window": "完整窗口", "Codex thread titles": "Codex 会话标题", @@ -3382,7 +3382,7 @@ const I18N = { "Not on names I set": "自分で付けた名前には付けない", "Codex subagents on any model": "Codex のサブエージェントで任意のモデル", "Long conversations": "長い会話", - "Codex and Claude Code compact a conversation at 272K tokens on a model with a larger window, as they do on OpenAI’s and Anthropic’s own; the model’s full window lets it grow further, with every turn slower and costlier": "ウィンドウが大きいモデルでも、Codex と Claude Code は OpenAI や Anthropic 自身のモデルと同じく 272K トークンで会話を圧縮します。モデルのコンテキストウィンドウ全体を使うと会話はさらに伸ばせますが、ターンごとに遅く、高くなります", + "Codex and Claude Code compact a conversation at 272K tokens on a model with a larger window; in Claude Code a Claude model runs to its own window, 1M for a [1m] one, as Anthropic runs it. The model’s full window lets any conversation grow further, with every turn slower and costlier": "ウィンドウが大きいモデルでは、Codex と Claude Code は 272K トークンで会話を圧縮します。Claude Code の Claude モデルは Anthropic と同じく自身のウィンドウ([1m] なら 1M)まで使います。モデルのウィンドウ全体を使うとどの会話もさらに伸ばせますが、ターンごとに遅く、高くなります", "Compact at 272K": "272K で圧縮", "Full window": "ウィンドウ全体", "Codex thread titles": "Codex のスレッドタイトル", @@ -6346,7 +6346,7 @@ const I18N = { "Not on names I set": "Nicht bei eigenen Namen", "Codex subagents on any model": "Codex-Subagenten auf jedem Modell", "Long conversations": "Lange Unterhaltungen", - "Codex and Claude Code compact a conversation at 272K tokens on a model with a larger window, as they do on OpenAI’s and Anthropic’s own; the model’s full window lets it grow further, with every turn slower and costlier": "Codex und Claude Code komprimieren eine Unterhaltung bei einem Modell mit größerem Kontextfenster ab 272K Token, wie bei den eigenen Modellen von OpenAI und Anthropic; mit dem vollen Fenster des Modells wächst sie weiter, wobei jede Runde langsamer und teurer wird", + "Codex and Claude Code compact a conversation at 272K tokens on a model with a larger window; in Claude Code a Claude model runs to its own window, 1M for a [1m] one, as Anthropic runs it. The model’s full window lets any conversation grow further, with every turn slower and costlier": "Codex und Claude Code komprimieren eine Unterhaltung bei einem Modell mit größerem Kontextfenster ab 272K Token; in Claude Code nutzt ein Claude-Modell sein eigenes Fenster, 1M bei einem [1m]-Modell, wie Anthropic es betreibt. Mit dem vollen Fenster des Modells wächst jede Unterhaltung weiter, wobei jede Runde langsamer und teurer wird", "Compact at 272K": "Bei 272K komprimieren", "Full window": "Volles Fenster", "Codex thread titles": "Codex-Thread-Titel", diff --git a/internal/gui/assets/index.html b/internal/gui/assets/index.html index badc1deb..072b017a 100644 --- a/internal/gui/assets/index.html +++ b/internal/gui/assets/index.html @@ -258,7 +258,7 @@