lgtm set Codex · WSL Ubuntu's 推理强度 to 默认 and it came back as 中.
Two things did that, on every platform (the WSL row is the same codexIn
code as the local one):
- The effort field's Get answered an unset model_reasoning_effort on a
magpie model with codexcat.DefaultEffort (medium), so right after the
pick the slider and the row read 中, not 默认.
- settle() counted an unset effort as one the model doesn't take and
wrote DefaultEffort into config.toml: on every Sync for a magpie
model, on Join, and on a model pick, Codex's own models included. So
Default was pinned as medium, and on Codex's own models whose default
is low (gpt-6.1-sol, gpt-6-astra, gpt-5.6-sol in 0.162's models.json)
magpie overrode Codex's own default.
With model_reasoning_effort unset, Codex takes the catalog entry's
default_reasoning_level (core/src/client.rs build_reasoning). Checked
for real with codex-cli 0.162.0 in a sandbox CODEX_HOME: a
model_catalog_json entry with default_reasoning_level "low" and no
model_reasoning_effort sent {"reasoning":{"effort":"low"}} to a stand-in
Responses endpoint.
Now settle replaces only a set effort the model doesn't take, Get
returns the unset value, and the effort control's options start with
Default carrying Takes: the level Codex then takes, from the entry it
reads (codexcat.TakesEffort: Codex's own models_cache entry for its
models and a ChatGPT account's codex/ ones, else the one Entries writes).
The GUI shows it as "default (medium)" / 默认(中) / 預設(中) /
デフォルト(中) / Standard (mittel). A stopped WSL distro's options get the
same Default (codexEfforts).
Tests: TestCodexDefaultEffortStaysDefault fails on the old settle
("Default written over: model_reasoning_effort = \"high\"", "Default
shows as \"high\"") and passes now. TestCodexCustomProviderModelHasEfforts
now expects Default first with Takes "high" and an unset value read as
"". codex-default-effort.test.cjs (window at 560 and 360px, tray panel;
en, zh, ja, de; Chromium and WebKit) fails on the old effortName and
passes now.
Run: gofmt -l (clean); go vet -tags nogui on darwin, GOOS=linux and
GOOS=windows for ./internal/agent ./internal/codexcat ./internal/gui
./internal/tui and the main package; go test -tags nogui of those and
./internal/catalog under a scratch HOME (all ok); the GUI effort, gui-ja,
gui-de, gui-zh-tw and i18n-keys tests (all pass), and the whole GUI suite
in both engines: 4711 pass, 8 fail, all in library-servers-bulk
(chromium, ".lib-everyon moved from 290 to 288") and purpose-filter
(webkit), which fail the same way run alone on origin/main c1c88779.
Not run on a real WSL distro.
161 lines
5.4 KiB
Go
161 lines
5.4 KiB
Go
package agent
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/yetone/magpie/internal/catalog"
|
|
"github.com/yetone/magpie/internal/provider"
|
|
)
|
|
|
|
// A custom provider serving a model models.dev knows from others (a
|
|
// Volcengine endpoint's glm-5.3-flash) hands Codex the reasoning levels
|
|
// those give it: the catalog entry had none, so the Codex app offered no
|
|
// effort and magpie's own control showed a level Codex never used.
|
|
func TestCodexCustomProviderModelHasEfforts(t *testing.T) {
|
|
home, read := codexHome(t, "", "model_reasoning_effort = \"none\"\n")
|
|
os.MkdirAll(filepath.Dir(catalog.CachePath()), 0o755)
|
|
os.WriteFile(catalog.CachePath(), []byte(`{
|
|
"zai":{"models":{"glm-5.3-flash":{"id":"glm-5.3-flash","reasoning_options":[{"type":"effort","values":["low","high","max"]}]},
|
|
"glm-4.5-air":{"id":"glm-4.5-air"}}}}`), 0o644)
|
|
catalog.Reset()
|
|
t.Cleanup(catalog.Reset)
|
|
if err := provider.Save(provider.Provider{ID: "volc", Name: "Volc", Key: "k", Chat: "http://127.0.0.1:1/api/v3",
|
|
Models: []string{"glm-5.3-flash", "glm-4.5-air"}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cx := codex(home)
|
|
if err := cx.Fields[0].Set("volc/glm-5.3-flash"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if cfg := read(); !strings.Contains(cfg, `model_reasoning_effort = "high"`) {
|
|
t.Fatalf("effort not settled on the model's default:\n%s", cfg)
|
|
}
|
|
b, _ := os.ReadFile(filepath.Join(home, ".codex", "magpie-models.json"))
|
|
var cat struct {
|
|
Models []struct {
|
|
Slug string `json:"slug"`
|
|
Default *string `json:"default_reasoning_level"`
|
|
Levels []struct {
|
|
Effort string `json:"effort"`
|
|
} `json:"supported_reasoning_levels"`
|
|
} `json:"models"`
|
|
}
|
|
if err := json.Unmarshal(b, &cat); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := map[string]string{}
|
|
for _, m := range cat.Models {
|
|
var ls []string
|
|
for _, l := range m.Levels {
|
|
ls = append(ls, l.Effort)
|
|
}
|
|
d := ""
|
|
if m.Default != nil {
|
|
d = *m.Default
|
|
}
|
|
got[m.Slug] = strings.Join(ls, ",") + " / " + d
|
|
}
|
|
if got["volc/glm-5.3-flash"] != "low,high,max / high" {
|
|
t.Errorf("glm-5.3-flash: %q", got["volc/glm-5.3-flash"])
|
|
}
|
|
// a model no one says reasons keeps none
|
|
if got["volc/glm-4.5-air"] != " / " {
|
|
t.Errorf("glm-4.5-air: %q", got["volc/glm-4.5-air"])
|
|
}
|
|
effort := cx.Fields[1]
|
|
var opts []string
|
|
ef := effort.Options(map[string]string{"model": "volc/glm-5.3-flash"})
|
|
for _, o := range ef {
|
|
opts = append(opts, o.Value)
|
|
}
|
|
if strings.Join(opts, ",") != ",low,high,max" {
|
|
t.Errorf("effort options: %v", opts)
|
|
}
|
|
// Default says what Codex takes then: the catalog's default
|
|
if ef[0].Takes != "high" {
|
|
t.Errorf("default takes %q", ef[0].Takes)
|
|
}
|
|
|
|
// unset, the control shows Default, which is what was picked
|
|
if err := effort.Set(""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v := effort.Get(); v != "" {
|
|
t.Errorf("unset effort shows %q", v)
|
|
}
|
|
|
|
// a level the model doesn't take, left from before, is settled when
|
|
// the catalog is synced
|
|
if err := effort.Set("none"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := cx.Sync(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if cfg := read(); !strings.Contains(cfg, `model_reasoning_effort = "high"`) {
|
|
t.Errorf("sync left the effort:\n%s", cfg)
|
|
}
|
|
}
|
|
|
|
// Default on Codex's effort control stays Default (lgtm on Discord: picked
|
|
// on Codex · WSL Ubuntu, it turned into medium): an effort left unset is
|
|
// not written in when a model is picked or the catalog synced, and the
|
|
// control reads it as Default, with the level Codex then takes beside it —
|
|
// for Codex's own model, its own default (gpt-6.1-sol's low), not medium.
|
|
func TestCodexDefaultEffortStaysDefault(t *testing.T) {
|
|
home, read := codexHome(t, `{"tokens":{"access_token":"x","id_token":"x.e30.x"}}`, "")
|
|
os.WriteFile(filepath.Join(home, ".codex", "models_cache.json"), []byte(`{"models":[
|
|
{"slug":"gpt-6.1-sol","display_name":"GPT-6.1 Sol","priority":1,"default_reasoning_level":"low",
|
|
"supported_reasoning_levels":[{"effort":"low"},{"effort":"medium"},{"effort":"high"}]}]}`), 0o644)
|
|
os.MkdirAll(filepath.Dir(catalog.CachePath()), 0o755)
|
|
os.WriteFile(catalog.CachePath(), []byte(`{
|
|
"zai":{"models":{"glm-5.3-flash":{"id":"glm-5.3-flash","reasoning_options":[{"type":"effort","values":["low","high","max"]}]}}}}`), 0o644)
|
|
catalog.Reset()
|
|
t.Cleanup(catalog.Reset)
|
|
if err := provider.Save(provider.Provider{ID: "volc", Name: "Volc", Key: "k", Chat: "http://127.0.0.1:1/api/v3",
|
|
Models: []string{"glm-5.3-flash"}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cx := codex(home)
|
|
model, effort := cx.Fields[0], cx.Fields[1]
|
|
unset := func(when string) {
|
|
t.Helper()
|
|
if cfg := read(); strings.Contains(cfg, "model_reasoning_effort") {
|
|
t.Errorf("%s: Default written over:\n%s", when, cfg)
|
|
}
|
|
if v := effort.Get(); v != "" {
|
|
t.Errorf("%s: Default shows as %q", when, v)
|
|
}
|
|
}
|
|
if err := model.Set("volc/glm-5.3-flash"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
unset("magpie's model picked")
|
|
if err := cx.Sync(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
unset("catalog synced")
|
|
if err := model.Set("gpt-6.1-sol"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
unset("Codex's own model picked")
|
|
if d := effort.Options(map[string]string{"model": "gpt-6.1-sol"})[0]; d.Value != "" || d.Takes != "low" {
|
|
t.Errorf("Codex's own model's default: %+v", d)
|
|
}
|
|
|
|
// a level set that the model doesn't take is still settled
|
|
if err := effort.Set("max"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := model.Set("gpt-6.1-sol"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v := effort.Get(); v == "max" || v == "" {
|
|
t.Errorf("max left on a model without it: %q", v)
|
|
}
|
|
}
|