codex: the ChatGPT account's own models Codex lists follow the user's picks too (在magpie里把ChatGPT提供商只勾选GPT-6-Sol,codex里还是显示所有模型 — @vemsux) — signed into ChatGPT with magpie as the base URL, Codex asked the backend for its model list and magpie passed every model the account can reach straight through, so picking among them on the codex provider only ever narrowed magpie's own added models, never ChatGPT's. Now the gateway keeps that /models answer to the slugs the user kept (provider.CodexNativePicked, from the codex provider's picks); untouched picks leave the list whole, as before. The dedup that skips a ChatGPT account's models in CodexListed is unchanged — the backend still lists them, now only the kept ones

This commit is contained in:
yetone committed 2026-09-28 19:06:32 +08:00
1 parent c8d7a995d1
commit aed12ac60f
3 files changed
+69

No files matched your search

+14
View File
@@ -417,6 +417,20 @@ func (s *Server) codexModels(w http.ResponseWriter, r *http.Request) {
own = append(own, e)
}
}
// The backend lists every model the ChatGPT account can reach. When the
// user picked among them on the codex provider, keep the list to those:
// their pick governs Codex's own models, not just magpie's added ones.
if keep, narrowed := provider.CodexNativePicked(); narrowed {
kept := own[:0]
for _, m := range own {
o, _ := m.(map[string]any)
if slug, _ := o["slug"].(string); slug != "" && !keep[slug] {
continue
}
kept = append(kept, m)
}
own = kept
}
ms := provider.CodexListed()
// the list is the backend's and magpie's, and so is its ETag
w.Header().Set("ETag", codexcat.WithTag(etag, codexcat.Tag(ms)))
+38
View File
@@ -357,6 +357,44 @@ func TestCodexModelList(t *testing.T) {
}
}
// The user's model picks on the ChatGPT account narrow the backend's own
// list too: Codex is shown only the native models kept, not every one the
// account can reach.
func TestCodexModelListNarrowedByPicks(t *testing.T) {
codexSignedIn(t)
if err := provider.Save(provider.Provider{ID: "codex", Models: []string{"gpt-6-sol"}}); err != nil {
t.Fatal(err)
}
up := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, `{"models":[{"slug":"gpt-6-sol","priority":1},{"slug":"gpt-5.5","priority":2},{"slug":"gpt-5-codex","priority":3}]}`)
}))
defer up.Close()
was := provider.CodexBase
provider.CodexBase = up.URL + "/backend-api/codex"
defer func() { provider.CodexBase = was }()
rec := httptest.NewRecorder()
req := httptest.NewRequest("GET", CodexPath+"/models", nil)
req.Header.Set("Authorization", "Bearer chatgpt-token")
New().Handler().ServeHTTP(rec, req)
var list struct {
Models []map[string]any `json:"models"`
}
json.Unmarshal(rec.Body.Bytes(), &list)
var native []string
for _, m := range list.Models {
slug, _ := m["slug"].(string)
// the backend's own models are the gpt-* ones; magpie's added
// entries carry their own slugs and stay
if strings.HasPrefix(slug, "gpt-") {
native = append(native, slug)
}
}
if rec.Code != 200 || len(native) != 1 || native[0] != "gpt-6-sol" {
t.Errorf("native models after picks = %v (want just gpt-6-sol); code %d", native, rec.Code)
}
}
// A reply's X-Models-Etag carries magpie's list too: Codex refetches its
// model list on a new one, and only on the backend's it never would when a
// provider was added.
+17
View File
@@ -202,6 +202,23 @@ func CodexListed() []catalog.Model {
})
}
// CodexNativePicked is the set of the ChatGPT account's own model slugs the
// user kept, and whether they narrowed that list at all. The backend lists
// every model the account can reach; when the user has picked among them on
// the codex provider, the gateway keeps its /models answer to those (see
// codexModels). Not narrowed — the account's list is left whole.
func CodexNativePicked() (map[string]bool, bool) {
p, ok := find(All(), "codex")
if !ok || len(p.Models) == 0 {
return nil, false
}
keep := make(map[string]bool, len(p.Models))
for _, id := range p.Models {
keep[id] = true
}
return keep, true
}
// codexListed marks a group Fast when a ChatGPT account's GPT model is in
// it, so Codex offers /fast there too; the tier goes out only to that
// account (buildResponses).