Files
magpie/internal/agent/codex_thread_test.go
T
yetone 5cf4d1dae1 codex: on magpie API, Codex's own model as the default keeps magpie as its provider and magpie's models in its picker (#701, nullburn: Codex 选回原生默认模型后,第三方模型目录消失(已选择 magpie API)) — with Codex's sign-in set to magpie API, picking one of Codex's own models (gpt-6-luna) as its default in the Agents row ran unroute(): model_provider, model_catalog_json, openai_base_url and magpie-models.json all went, so every third-party model left Codex's picker while the row still said magpie API; and choosing magpie API while on Codex's own model wired nothing (login.Set only re-ran set for a magpie model). Now, on magpie API, Codex's own model goes as magpie serves it on the ChatGPT account there (codexOwnViaMagpie: gpt-6-luna -> codex/gpt-6-luna, the entries shown to Codex first), so set() takes the magpie branch and only the default changes; the picker's OpenAI group offers those ids under their own names (Label gpt-6-luna) so the value matches; one magpie doesn't serve (the ChatGPT subscription off in magpie) is refused with a message naming both ways out, the config untouched, rather than quietly unwired; choosing magpie API on Codex's own model wires it at once; Sync wires a config an older magpie left so (#701's: model = "gpt-6-luna", sign-in api). Back on ChatGPT, a codex/… default turns into Codex's own model again, straight to OpenAI as before (codexOwnOf); the ChatGPT sign-in's paths are otherwise unchanged. TestCodexAPIKeepsMagpieOnOwnModel (api on an own model, magpie model then own model again, picker values, refusal, back to ChatGPT) and TestCodexAPISyncWiresOwnModel fail on the old code and pass; TestCodexProviderAlsoSetsBaseURL's api case now expects magpie to stay wired on Codex's own model. End to end in a temp HOME with a copy of a real models_cache.json: api, fake/m1, then gpt-6-luna leaves model = "codex/gpt-6-luna" with model_provider, model_catalog_json and openai_base_url, and codex-cli 0.160.0's codex debug models with CODEX_HOME on that sandbox (gateway ports pointed at :1) lists fake/m1 beside codex/gpt-6-luna and the other codex/ models. go vet, builds (darwin, GOOS=linux, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass. Not tried in the Codex app or with a turn sent through a real ChatGPT account.
2026-10-03 23:40:06 +08:00

123 lines
4.2 KiB
Go

package agent
import (
"os"
"path/filepath"
"strings"
"testing"
)
// #259: magpie as Codex's provider, a thread started on Codex's built-in
// provider is opened on it again (Codex keeps a thread's provider), and a
// magpie model picked there went to the ChatGPT backend, which refused it
// ("… is not supported when using Codex with a ChatGPT account"). The base
// URL is magpie's now in that state too, so the built-in provider's request
// reaches magpie; back on Codex's own model, it goes — but on magpie API,
// where Codex's own model goes through magpie on its ChatGPT account and
// magpie stays wired (#701).
func TestCodexProviderAlsoSetsBaseURL(t *testing.T) {
for _, tc := range []struct {
name, auth string
api bool
}{
{"signed out", "", false},
{"api", `{"tokens":{"access_token":"x","id_token":"x.e30.x"}}`, true},
} {
t.Run(tc.name, func(t *testing.T) {
home, read := codexHome(t, tc.auth, "model = \"gpt-5.5\"\n")
cx := codex(home)
if tc.api {
os.WriteFile(filepath.Join(home, ".codex", "models_cache.json"), []byte(`{"models":[{"slug":"gpt-5.4","display_name":"5.4","priority":1}]}`), 0o644)
if err := cx.Field("login").Set("api"); err != nil {
t.Fatal(err)
}
}
if err := cx.Fields[0].Set("fake/m1"); err != nil {
t.Fatal(err)
}
cfg := read()
if !strings.Contains(cfg, `model_provider = "magpie"`) || !strings.Contains(cfg, "model_catalog_json") ||
!strings.Contains(cfg, `openai_base_url = "`+codexGatewayURL()+`"`) {
t.Fatalf("\n%s", cfg)
}
if msg := cx.Check(); msg != "" {
t.Fatalf("check: %s", msg)
}
if err := cx.Fields[0].Set("gpt-5.4"); err != nil {
t.Fatal(err)
}
if tc.api {
if cfg = read(); !strings.Contains(cfg, `openai_base_url = "`+codexGatewayURL()+`"`) ||
!strings.Contains(cfg, `model_provider = "magpie"`) || !strings.Contains(cfg, `model = "codex/gpt-5.4"`) {
t.Fatalf("own model on magpie API:\n%s", cfg)
}
return
}
if cfg = read(); strings.Contains(cfg, "openai_base_url") || strings.Contains(cfg, "model_provider =") ||
!strings.Contains(cfg, `model = "gpt-5.4"`) {
t.Fatalf("own model:\n%s", cfg)
}
})
}
}
// A base URL of the user's own — a proxy of theirs in front of OpenAI — is
// left as it is when magpie becomes the provider, and after.
func TestCodexProviderKeepsOwnBaseURL(t *testing.T) {
const own = `openai_base_url = "https://proxy.example/v1"`
home, read := codexHome(t, "", "model = \"gpt-5.5\"\n"+own+"\n")
cx := codex(home)
if err := cx.Fields[0].Set("fake/m1"); err != nil {
t.Fatal(err)
}
if cfg := read(); !strings.Contains(cfg, own) || strings.Count(cfg, "openai_base_url") != 1 ||
!strings.Contains(cfg, `model_provider = "magpie"`) {
t.Fatalf("\n%s", cfg)
}
if err := cx.Fields[0].Set(""); err != nil {
t.Fatal(err)
}
if cfg := read(); !strings.Contains(cfg, own) || strings.Contains(cfg, "model_provider =") {
t.Fatalf("reset:\n%s", cfg)
}
}
// A Codex that an older magpie made its provider, with no base URL, gets
// magpie's at the next sync; a provider set up by hand with a catalog of
// the user's own is not touched.
func TestCodexSyncAddsBaseURLToProvider(t *testing.T) {
home, read := codexHome(t, "", "")
cx := codex(home)
if err := cx.Fields[0].Set("fake/m1"); err != nil {
t.Fatal(err)
}
// as an older magpie left it
cfg := read()
i := strings.Index(cfg, "openai_base_url")
if i < 0 {
t.Fatalf("\n%s", cfg)
}
j := strings.Index(cfg[i:], "\n")
if err := os.WriteFile(filepath.Join(home, ".codex", "config.toml"), []byte(cfg[:i]+cfg[i+j+1:]), 0o644); err != nil {
t.Fatal(err)
}
if strings.Contains(read(), "openai_base_url") {
t.Fatal("base URL still there")
}
if err := cx.Sync(); err != nil {
t.Fatal(err)
}
if cfg = read(); !strings.Contains(cfg, `openai_base_url = "`+codexGatewayURL()+`"`) || !strings.Contains(cfg, `model_provider = "magpie"`) {
t.Fatalf("sync:\n%s", cfg)
}
const mine = "model = \"fake/m1\"\nmodel_provider = \"magpie\"\nmodel_catalog_json = \"/elsewhere/models.json\"\n"
home, read = codexHome(t, "", mine)
if err := codex(home).Sync(); err != nil {
t.Fatal(err)
}
if cfg = read(); cfg != mine {
t.Fatalf("hand-made provider changed:\n%s", cfg)
}
}