agent: Codex on one of magpie's models beside its ChatGPT sign-in stays connected when Codex writes one of its own models back, and connecting keeps the model it was on through magpie where it can't join (#940, xybio)
Picked "via magpie" on a ChatGPT-signed Codex, a magpie model is set beside the sign-in: openai_base_url to the gateway, the magpie table, no model_provider. When Codex itself (its /model, or a Codex app still running on its old pick) then wrote one of its own models back (gpt-6.1-sol), Wired found nothing to say magpie had put it there, so the row read Not connected while its config still pointed at magpie's gateway. Set so, Codex keeps a codex.beside mark (gone when magpie becomes its provider, on a pick of its own through the picker, or on disconnect), and Agent.Beside makes Wired true on it; the model written back is still told as a change from outside (#750's drift stays). Connecting where Codex can't join (its ChatGPT allowance used up, or a model_provider of its own) went to the first of magpie's other models (Grok 4.7, "magpie doesn't serve the model it was on"), since Codex's field leaves out magpie's codex/* models: Agent.OwnVia now finds the model it is on as magpie serves it on the user's ChatGPT account (codex/gpt-6.1-sol), and Connect keeps it there. TestCodexOwnPickAfterMagpieModelStaysConnected and TestCodexConnectKeepsItsOwnModelThroughMagpie are new; with the change taken out the first reads the reported config (model gpt-6.1-sol, openai_base_url, [model_providers.magpie], no model_provider) as not connected and the second connects to fake/m1 "first". Tested in temp HOMEs only. go vet, go build (GOOS=linux nogui, GOOS=windows) and go test -tags nogui ./... under a temp HOME pass (internal/proc TestUserPath timed out waiting on a login shell under load once, and passes on its own).
This commit is contained in:
1 parent
4c170cbad6
commit
ab47042ba8
4 files changed
+115
-4
No files matched your search
@@ -121,6 +121,17 @@ type Agent struct {
|
||||
// Joined reports an agent Join connected: magpie is in its config
|
||||
// though no field is on one of magpie's models.
|
||||
Joined func() bool
|
||||
// Beside reports an agent set on one of magpie's models beside its own
|
||||
// (Codex by the base URL beside its ChatGPT sign-in), now on one of its
|
||||
// own written in by the agent: still connected, as joined, though the
|
||||
// change is told as drift (#940).
|
||||
Beside func() bool
|
||||
// OwnVia is the catalog id magpie serves one of the agent's own models
|
||||
// by on a sign-in of the user's, which its model field lists as its
|
||||
// own rather than as magpie's ("codex/gpt-5.5" for Codex's gpt-5.5),
|
||||
// "" for none: Connect keeps the agent on that model through magpie
|
||||
// where it can't Join (#940: Codex went to an unrelated model).
|
||||
OwnVia func(model string) string
|
||||
// Routed reports that the agent's config sends whatever model it
|
||||
// names to magpie's gateway (Codex's openai_base_url or magpie as its
|
||||
// provider), so a model's name the gateway takes as a routing group
|
||||
|
||||
@@ -347,7 +347,7 @@ func (a *Agent) Wired() bool {
|
||||
if a.Native != nil {
|
||||
return a.Native.Read().Provider == "connected"
|
||||
}
|
||||
if a.Joined != nil && a.Joined() {
|
||||
if a.Joined != nil && a.Joined() || a.Beside != nil && a.Beside() {
|
||||
return true
|
||||
}
|
||||
vals := a.Values()
|
||||
@@ -544,6 +544,12 @@ func (a *Agent) connect() (Connection, error) {
|
||||
sub = o.Value
|
||||
}
|
||||
}
|
||||
// the model it is on, of its own, as magpie serves it on a sign-in of
|
||||
// the user's, which its field lists as its own (Codex's on a ChatGPT
|
||||
// account it can't join, #940)
|
||||
if same == "" && alike == "" && cur != "" && a.OwnVia != nil {
|
||||
same = a.OwnVia(cur)
|
||||
}
|
||||
how := "first"
|
||||
switch {
|
||||
case same != "":
|
||||
|
||||
+15
-3
@@ -113,13 +113,20 @@ func codexIn(at place) *Agent {
|
||||
// joined: connected by Join, on a model of Codex's own (its last pick)
|
||||
// with magpie's beside it, by the base URL
|
||||
joined := func() bool { return viaBase() && stashLoad()[at.key("codex.joined")] == "1" }
|
||||
// beside: set on one of magpie's models beside the ChatGPT sign-in, by
|
||||
// the base URL, Codex's own models in its list too: one of them written
|
||||
// in by Codex (its /model, or a Codex app still running on its old
|
||||
// pick) leaves it connected, its config still on magpie's gateway
|
||||
// (#940: it read as not connected), the change told as drift. One
|
||||
// picked on the Agents page still takes magpie out (set).
|
||||
beside := func() bool { return viaBase() && stashLoad()[at.key("codex.beside")] == "1" }
|
||||
// magpie is in Codex's config: on one of magpie's models, or joined;
|
||||
// or, routed, on the model of the group magpie set it to as Codex
|
||||
// spells it (gpt-6.1-sol for group/auto-gpt-6-1-sol, #750), which the
|
||||
// gateway takes as that group
|
||||
wired := func() bool {
|
||||
m := get("model")
|
||||
return isMagpie(m) || joined() || routed() && setAsGroup(cmp.Or(at.id, "codex"), "model", m)
|
||||
return isMagpie(m) || joined() || beside() || routed() && setAsGroup(cmp.Or(at.id, "codex"), "model", m)
|
||||
}
|
||||
models := func() []catalog.Model {
|
||||
switch {
|
||||
@@ -476,7 +483,7 @@ func codexIn(at place) *Agent {
|
||||
// provider, catalog and effort); it answers the model Codex was on
|
||||
// then, for Unwire to go back to
|
||||
unroute := func() (string, error) {
|
||||
forget(at.key("codex.out"), at.key("codex.joined"))
|
||||
forget(at.key("codex.out"), at.key("codex.joined"), at.key("codex.beside"))
|
||||
if err := giveTables(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -529,7 +536,7 @@ func codexIn(at place) *Agent {
|
||||
return err
|
||||
}
|
||||
os.Remove(catalogPath)
|
||||
forget(at.key("codex.model"), at.key("codex.effort"), at.key("codex.provider"), at.key("codex.catalog"), at.key("codex.out"), at.key("codex.joined"))
|
||||
forget(at.key("codex.model"), at.key("codex.effort"), at.key("codex.provider"), at.key("codex.catalog"), at.key("codex.out"), at.key("codex.joined"), at.key("codex.beside"))
|
||||
return nil
|
||||
}
|
||||
// magpie API: magpie stays Codex's provider whichever model is the
|
||||
@@ -577,6 +584,7 @@ func codexIn(at place) *Agent {
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
stash(map[string]string{at.key("codex.beside"): "1"})
|
||||
if err := takeTables(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -587,6 +595,8 @@ func codexIn(at place) *Agent {
|
||||
} else {
|
||||
forget(at.key("codex.out"))
|
||||
}
|
||||
// magpie is its provider now, not beside the sign-in
|
||||
forget(at.key("codex.beside"))
|
||||
if err := putProvider(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -707,7 +717,9 @@ func codexIn(at place) *Agent {
|
||||
return true, settle()
|
||||
},
|
||||
Joined: joined,
|
||||
Beside: beside,
|
||||
Routed: routed,
|
||||
OwnVia: codexOwnViaMagpie,
|
||||
Sync: func() error {
|
||||
// model_provider = "magpie" left with its table gone (taken by
|
||||
// another tool, or a hand edit), which keeps Codex from loading
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/yetone/magpie/internal/edit"
|
||||
)
|
||||
|
||||
// Codex signed in with ChatGPT, connected on one of magpie's models beside
|
||||
// its sign-in, stays connected when Codex writes one of its own models in
|
||||
// (its /model, or a Codex app still running on its old pick): its own
|
||||
// models are in its list beside magpie's, as when it joined (#940: Codex
|
||||
// read as not connected then, its config still on magpie's gateway).
|
||||
// Disconnect leaves it on that model. Made magpie's provider (its
|
||||
// allowance used up), it is not beside the sign-in.
|
||||
func TestCodexOwnPickAfterMagpieModelStaysConnected(t *testing.T) {
|
||||
home, read := codexHome(t, `{"tokens":{"access_token":"x","id_token":"x.e30.x"}}`, "model = \"gpt-5.5\"\n")
|
||||
cx := codex(home)
|
||||
if err := cx.Apply("model", "fake/m1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !cx.Wired() {
|
||||
t.Fatalf("not connected on fake/m1:\n%s", read())
|
||||
}
|
||||
if err := edit.SetTOMLTop(filepath.Join(home, ".codex", "config.toml"), edit.KV{Path: "model", Value: "gpt-6.1-sol"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !cx.Wired() {
|
||||
t.Fatalf("Codex's own pick disconnected it:\n%s", read())
|
||||
}
|
||||
// told as a change from outside, the row still connected
|
||||
if d := cx.Drift(); d == nil || d.Kind != "replaced" || d.Now != "gpt-6.1-sol" {
|
||||
t.Fatalf("drift: %+v", d)
|
||||
}
|
||||
if err := cx.Disconnect(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cfg := read(); cx.Wired() || strings.Contains(cfg, "openai_base_url") || !strings.Contains(cfg, `model = "gpt-6.1-sol"`) {
|
||||
t.Fatalf("disconnected:\n%s", cfg)
|
||||
}
|
||||
|
||||
// out of its allowance, magpie is its provider: an own model written
|
||||
// back is a change from outside, not a pick beside magpie's
|
||||
codexUsedUp = func() bool { return true }
|
||||
if err := cx.Fields[0].Set("fake/m1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
path := filepath.Join(home, ".codex", "config.toml")
|
||||
if err := edit.SetTOMLTop(path, edit.KV{Path: "model", Value: "gpt-6.1-sol"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := edit.DelTOMLTop(path, "model_provider"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cx.Wired() {
|
||||
t.Fatalf("still beside the sign-in after magpie became its provider:\n%s", read())
|
||||
}
|
||||
}
|
||||
|
||||
// Connected while Codex can't join (its ChatGPT account used up, or a
|
||||
// provider of its own in config.toml), Codex stays on the model it was on
|
||||
// as magpie serves it on the ChatGPT account, not the first of magpie's
|
||||
// other models (#940: it went to Grok 4.7, "magpie doesn't serve the model
|
||||
// it was on").
|
||||
func TestCodexConnectKeepsItsOwnModelThroughMagpie(t *testing.T) {
|
||||
home, read := codexHome(t, `{"tokens":{"access_token":"x","id_token":"x.e30.x"}}`, "model = \"gpt-b\"\n")
|
||||
os.WriteFile(filepath.Join(home, ".codex", "models_cache.json"), []byte(`{"models":[
|
||||
{"slug":"gpt-a","display_name":"A","priority":1},
|
||||
{"slug":"gpt-b","display_name":"B","priority":2}]}`), 0o644)
|
||||
codexUsedUp = func() bool { return true }
|
||||
cx := codex(home)
|
||||
c, err := cx.ConnectHow()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c.How != "same" || c.Value != "codex/gpt-b" || !strings.Contains(read(), `model = "codex/gpt-b"`) {
|
||||
t.Fatalf("connected %+v:\n%s", c, read())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user