claude: a Claude model runs to its own window in Claude Code, not 272K, and magpie model compact on|off is the Long conversations switch from the terminal (Max on Discord: a [1m] model's context stopped at 272K, magpie wrote CLAUDE_CODE_AUTO_COMPACT_WINDOW back when it was taken out, and there seemed to be no way to change it) — on magpie, Claude Code was told CLAUDE_CODE_AUTO_COMPACT_WINDOW=272000 whatever its model, so a [1m] Claude model compacted at 272K, though Anthropic runs it to 1M (the cap was meant for other vendors' long windows: Chen's DeepSeek turns of ~550K). Now a main model that is one of Anthropic's Claude models, from whichever provider, has the env left out (magpie's own value taken away), and Claude Code compacts at the model's own window; another vendor's model of a longer window keeps the 272K cap unless Settings → Long conversations is on Full window. A value the user writes there is still kept. The switch was only in the app's Settings; magpie model compact [on|off] (alias full-context) shows and sets it, and the Settings row's description says how Claude models are treated (en, zh, ja, de). TestClaudeCompactWindow (a Claude [1m] model gets no 272000, set and synced; another vendor's after it is capped again) and TestModelCompactCmd fail on the old code. The Long conversations row read right in WebKit, en and zh. go vet, builds (darwin, GOOS=linux nogui, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass (TestClaudeFailureKeepsClaudeCodesStatus flaked once under load, passes alone).
This commit is contained in:
1 parent
b93cc1623a
commit
531dc0aa33
6 files changed
+112
-11
No files matched your search
@@ -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
|
// 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
|
// 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
|
// 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"
|
const claudeCompactEnv = "CLAUDE_CODE_AUTO_COMPACT_WINDOW"
|
||||||
|
|
||||||
// claudeCapsEnv tells Claude Code what a model it doesn't know can do, as
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
// writeCompact has Claude Code compact at the working window
|
// 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 {
|
writeCompact := func() error {
|
||||||
if env(claudeCompactEnv) != "" && !compactOurs() {
|
if env(claudeCompactEnv) != "" && !compactOurs() {
|
||||||
forget(compactKey)
|
forget(compactKey)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if settings.Load().FullContext {
|
if settings.Load().FullContext || claudeModel(mainModel()) {
|
||||||
return dropCompact()
|
return dropCompact()
|
||||||
}
|
}
|
||||||
w := strconv.Itoa(settings.WorkingWindow)
|
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
|
// 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
|
// 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
|
// marked [1m]: the main model's, or when that one is marked, the smallest of
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// TestClaudeCompactWindow: on magpie Claude Code compacts at the working
|
// 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);
|
// 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.
|
// Claude Code as installed has none.
|
||||||
func TestClaudeCompactWindow(t *testing.T) {
|
func TestClaudeCompactWindow(t *testing.T) {
|
||||||
home := t.TempDir()
|
home := t.TempDir()
|
||||||
@@ -22,10 +22,10 @@ func TestClaudeCompactWindow(t *testing.T) {
|
|||||||
t.Setenv("XDG_CONFIG_HOME", filepath.Join(home, ".config"))
|
t.Setenv("XDG_CONFIG_HOME", filepath.Join(home, ".config"))
|
||||||
t.Setenv("XDG_CACHE_HOME", filepath.Join(home, ".cache"))
|
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",
|
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)
|
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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
path := filepath.Join(home, ".claude", "settings.json")
|
path := filepath.Join(home, ".claude", "settings.json")
|
||||||
@@ -53,6 +53,21 @@ func TestClaudeCompactWindow(t *testing.T) {
|
|||||||
if compact() != "272000" {
|
if compact() != "272000" {
|
||||||
t.Fatalf("back to the working window: %q", compact())
|
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(""))
|
must(a.Field("model").Set(""))
|
||||||
if compact() != "" {
|
if compact() != "" {
|
||||||
t.Fatalf("Claude Code as installed: %q", compact())
|
t.Fatalf("Claude Code as installed: %q", compact())
|
||||||
|
|||||||
@@ -403,7 +403,7 @@ const I18N = {
|
|||||||
"Not on names I set": "自定义名称不带供应商",
|
"Not on names I set": "自定义名称不带供应商",
|
||||||
"Codex subagents on any model": "Codex 子代理可用任意模型",
|
"Codex subagents on any model": "Codex 子代理可用任意模型",
|
||||||
"Long conversations": "长对话",
|
"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 时压缩",
|
"Compact at 272K": "272K 时压缩",
|
||||||
"Full window": "完整窗口",
|
"Full window": "完整窗口",
|
||||||
"Codex thread titles": "Codex 会话标题",
|
"Codex thread titles": "Codex 会话标题",
|
||||||
@@ -3382,7 +3382,7 @@ const I18N = {
|
|||||||
"Not on names I set": "自分で付けた名前には付けない",
|
"Not on names I set": "自分で付けた名前には付けない",
|
||||||
"Codex subagents on any model": "Codex のサブエージェントで任意のモデル",
|
"Codex subagents on any model": "Codex のサブエージェントで任意のモデル",
|
||||||
"Long conversations": "長い会話",
|
"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 で圧縮",
|
"Compact at 272K": "272K で圧縮",
|
||||||
"Full window": "ウィンドウ全体",
|
"Full window": "ウィンドウ全体",
|
||||||
"Codex thread titles": "Codex のスレッドタイトル",
|
"Codex thread titles": "Codex のスレッドタイトル",
|
||||||
@@ -6346,7 +6346,7 @@ const I18N = {
|
|||||||
"Not on names I set": "Nicht bei eigenen Namen",
|
"Not on names I set": "Nicht bei eigenen Namen",
|
||||||
"Codex subagents on any model": "Codex-Subagenten auf jedem Modell",
|
"Codex subagents on any model": "Codex-Subagenten auf jedem Modell",
|
||||||
"Long conversations": "Lange Unterhaltungen",
|
"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",
|
"Compact at 272K": "Bei 272K komprimieren",
|
||||||
"Full window": "Volles Fenster",
|
"Full window": "Volles Fenster",
|
||||||
"Codex thread titles": "Codex-Thread-Titel",
|
"Codex thread titles": "Codex-Thread-Titel",
|
||||||
|
|||||||
@@ -258,7 +258,7 @@
|
|||||||
<div id="codexAgentsV1Segs"></div>
|
<div id="codexAgentsV1Segs"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row pref">
|
<div class="row pref">
|
||||||
<div class="who"><div class="name" data-t>Long conversations</div><div class="sub" data-t>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</div></div>
|
<div class="who"><div class="name" data-t>Long conversations</div><div class="sub" data-t>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</div></div>
|
||||||
<div id="fullContextSegs"></div>
|
<div id="fullContextSegs"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row pref" id="codexTitlesRow">
|
<div class="row pref" id="codexTitlesRow">
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ const modelUsage = `usage:
|
|||||||
own, a name you gave a model just as you wrote it, "Opus 5.5",
|
own, a name you gave a model just as you wrote it, "Opus 5.5",
|
||||||
the others as on; off, "Sol" alone — but two a list would name
|
the others as on; off, "Sol" alone — but two a list would name
|
||||||
the same keep it
|
the same keep it
|
||||||
|
magpie model compact [on|off] whether Codex and Claude Code compact a long conversation at
|
||||||
|
272K: on, as by default, for a model of a longer window (in
|
||||||
|
Claude Code, a Claude model runs to its own); off, at the
|
||||||
|
model's whole window, 1M for a [1m] one. The app's Settings →
|
||||||
|
Long conversations is the same switch
|
||||||
|
|
||||||
Each is looked for in this order: this model, then <provider id>/*, then the provider's own
|
Each is looked for in this order: this model, then <provider id>/*, then the provider's own
|
||||||
list, then models.dev. --reset removes only the first, and says so when a <provider id>/* value
|
list, then models.dev. --reset removes only the first, and says so when a <provider id>/* value
|
||||||
@@ -90,6 +95,8 @@ func modelCmd(args []string) error {
|
|||||||
return modelWires()
|
return modelWires()
|
||||||
case "suffix", "suffixes":
|
case "suffix", "suffixes":
|
||||||
return modelSuffix(args[1:])
|
return modelSuffix(args[1:])
|
||||||
|
case "compact", "full-context":
|
||||||
|
return modelCompact(args[1:])
|
||||||
case "context", "ctx":
|
case "context", "ctx":
|
||||||
return modelContext(args[1:])
|
return modelContext(args[1:])
|
||||||
case "output", "max-output":
|
case "output", "max-output":
|
||||||
@@ -514,6 +521,37 @@ func modelSuffix(args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// modelCompact says whether long conversations are compacted at the
|
||||||
|
// working window (settings.WorkingWindow), or sets it: off is the app's
|
||||||
|
// Full window, every model run to its whole window.
|
||||||
|
func modelCompact(args []string) error {
|
||||||
|
if len(args) == 0 {
|
||||||
|
if settings.Load().FullContext {
|
||||||
|
fmt.Println("off", muted.Render("· Codex and Claude Code run a conversation to the model's whole window · magpie model compact on"))
|
||||||
|
} else {
|
||||||
|
fmt.Println("on", muted.Render(fmt.Sprintf("· Codex and Claude Code compact at %dK on a longer window; in Claude Code a Claude model runs to its own · magpie model compact off", settings.WorkingWindow/1000)))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var full bool
|
||||||
|
switch strings.ToLower(args[0]) {
|
||||||
|
case "on", "yes", "true":
|
||||||
|
case "off", "no", "false", "full":
|
||||||
|
full = true
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("magpie model compact on|off, not %q", args[0])
|
||||||
|
}
|
||||||
|
if err := provider.SetFullContext(full); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if full {
|
||||||
|
fmt.Println(green.Render("✓"), "Codex and Claude Code run a conversation to the model's whole window", muted.Render("· every turn sends all of it"))
|
||||||
|
} else {
|
||||||
|
fmt.Println(green.Render("✓"), fmt.Sprintf("Codex and Claude Code compact at %dK again", settings.WorkingWindow/1000))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func modelNames() error {
|
func modelNames() error {
|
||||||
s := settings.Load()
|
s := settings.Load()
|
||||||
keys := slices.Sorted(maps.Keys(s.ModelNames))
|
keys := slices.Sorted(maps.Keys(s.ModelNames))
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/yetone/magpie/internal/catalog"
|
||||||
|
"github.com/yetone/magpie/internal/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// magpie model compact off runs Codex's and Claude Code's conversations to
|
||||||
|
// the model's whole window, on compacts them at the working window again
|
||||||
|
// (Max on Discord found no way to it but the app's Settings); anything else
|
||||||
|
// is refused.
|
||||||
|
func TestModelCompactCmd(t *testing.T) {
|
||||||
|
groupsHome(t)
|
||||||
|
catalog.Changed = nil // no agent's files are rewritten here
|
||||||
|
if err := modelCmd([]string{"compact"}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := modelCmd([]string{"compact", "off"}); err != nil || !settings.Load().FullContext {
|
||||||
|
t.Fatal(err, settings.Load().FullContext)
|
||||||
|
}
|
||||||
|
if err := modelCmd([]string{"compact"}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := modelCmd([]string{"compact", "on"}); err != nil || settings.Load().FullContext {
|
||||||
|
t.Fatal(err, settings.Load().FullContext)
|
||||||
|
}
|
||||||
|
if err := modelCmd([]string{"full-context", "full"}); err != nil || !settings.Load().FullContext {
|
||||||
|
t.Fatal(err, settings.Load().FullContext)
|
||||||
|
}
|
||||||
|
if err := modelCmd([]string{"compact", "maybe"}); err == nil {
|
||||||
|
t.Fatal("took maybe")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user