agent: Hermes keeps magpie's provider when its model goes to one of its own, so it stays connected and its session picker still offers magpie's models; Disconnect takes it out (lijiho96940561 on X: changing Hermes's default model marked it Not connected and magpie's models left its picker)

This commit is contained in:
yetone committed 2026-10-08 00:27:03 +08:00
1 parent 8731dd9f38
commit ed4476b981
2 files changed
+61 -13

No files matched your search

+24 -4
View File
@@ -7,7 +7,12 @@ package agent
// with the catalog as its models) and points model.provider at it; a model
// through magpie is "magpie/<provider>/<model>" here and model.default holds
// "<provider>/<model>". The provider and model the user had are stashed and
// put back when magpie steps out.
// put back when magpie steps out. A model of the user's own picked after
// that, in Hermes or on the Agents page, leaves magpie's provider in
// providers: Hermes stays connected, its session model picker still offers
// magpie's models beside its own (lijiho96940561 on X: Hermes went to Not
// connected and magpie's models left its picker), and only Disconnect
// takes the provider out.
import (
"path/filepath"
@@ -31,7 +36,11 @@ func hermesIn(at place) *Agent {
key := "hermes:" + path + ":"
getKey := func(k string) string { v, _ := edit.GetYAML(path, k); return v }
onMagpie := func() bool { return getKey("model.provider") == magpieID }
// restore puts back the provider and model the user had before magpie
// joined: magpie's provider is in providers, though the model is one of
// Hermes's own
joined := func() bool { _, ok := edit.GetYAML(path, "providers."+magpieID+".base_url"); return ok }
// restore puts back the provider and model the user had before magpie,
// magpie's provider left beside them
restore := func() error {
var del []string
var kvs []edit.KV
@@ -42,8 +51,10 @@ func hermesIn(at place) *Agent {
del = append(del, k)
}
}
if err := edit.DelYAML(path, append(del, "providers."+magpieID)...); err != nil {
return err
if len(del) > 0 {
if err := edit.DelYAML(path, del...); err != nil {
return err
}
}
if len(kvs) == 0 {
return nil
@@ -63,6 +74,15 @@ func hermesIn(at place) *Agent {
}
return ""
},
Joined: joined,
// Disconnect takes magpie's provider out; the model, when it is
// magpie's, goes back by the field
Unwire: func() error {
if !joined() {
return nil
}
return edit.DelYAML(path, "providers."+magpieID)
},
Check: func() string {
if !onMagpie() {
return ""
+37 -9
View File
@@ -6,6 +6,7 @@ import (
"strings"
"testing"
"github.com/yetone/magpie/internal/edit"
"github.com/yetone/magpie/internal/provider"
"gopkg.in/yaml.v3"
)
@@ -62,33 +63,60 @@ func TestHermes(t *testing.T) {
t.Fatal(err)
}
// its own model: the user's provider comes back, magpie's entry goes
// its own model: the user's provider comes back, magpie's entry stays
// beside it, so Hermes's session picker still offers magpie's models
// and it is still connected (lijiho96940561 on X)
if err := f.Set("moonshotai/kimi-k3"); err != nil {
t.Fatal(err)
}
c, m, raw = read()
if m["default"] != "moonshotai/kimi-k3" || m["provider"] != "openrouter" || providers(c)["magpie"] != nil || providers(c)["mine"] == nil {
if m["default"] != "moonshotai/kimi-k3" || m["provider"] != "openrouter" || providers(c)["magpie"] == nil || providers(c)["mine"] == nil {
t.Fatalf("own:\n%s", raw)
}
a := hermes(home)
if !a.Wired() || a.Drift() != nil {
t.Fatalf("own model: wired %v, drift %+v", a.Wired(), a.Drift())
}
// reset from magpie: back to what the user had
f.Set("magpie/deepseek/flash")
if err := f.Set(""); err != nil {
// the default changed in Hermes itself (`hermes model`): still connected,
// not told as drift
if err := a.Apply("model", "magpie/deepseek/flash"); err != nil {
t.Fatal(err)
}
if err := edit.SetYAML(path, edit.KV{Path: "model.provider", Value: "anthropic"}, edit.KV{Path: "model.default", Value: "claude-opus-5"}); err != nil {
t.Fatal(err)
}
if !a.Wired() || a.Drift() != nil {
t.Fatalf("changed in Hermes: wired %v, drift %+v", a.Wired(), a.Drift())
}
// Disconnect takes magpie's entry out
if err := a.Disconnect(); err != nil {
t.Fatal(err)
}
c, m, raw = read()
if m["default"] != "moonshotai/kimi-k3" || m["provider"] != "openrouter" || providers(c)["magpie"] != nil {
t.Fatalf("reset:\n%s", raw)
if m["default"] != "claude-opus-5" || m["provider"] != "anthropic" || providers(c)["magpie"] != nil || providers(c)["mine"] == nil || a.Wired() {
t.Fatalf("disconnect, own model:\n%s", raw)
}
// Disconnect from magpie's model: back to what the user had
f.Set("magpie/deepseek/flash")
if err := a.Disconnect(); err != nil {
t.Fatal(err)
}
c, m, raw = read()
if m["default"] != "claude-opus-5" || m["provider"] != "anthropic" || providers(c)["magpie"] != nil {
t.Fatalf("disconnect:\n%s", raw)
}
// a config with no provider of its own: none comes back
os.WriteFile(path, []byte("agent:\n max_turns: 60\n"), 0o644)
f.Set("magpie/deepseek/pro")
if err := f.Set(""); err != nil {
if err := a.Disconnect(); err != nil {
t.Fatal(err)
}
c, m, raw = read()
if m["provider"] != nil || m["default"] != nil || providers(c)["magpie"] != nil || c["agent"] == nil {
t.Fatalf("reset, no provider:\n%s", raw)
t.Fatalf("disconnect, no provider:\n%s", raw)
}
}