provider: an account's "only through routing groups" holds, and one removed stays removed — All() merged a signed-in account's picks without Unlisted, so provider listed claude no saved but claude/* stayed in /v1/models; and any save of an account rebuilt its entry without Hidden, so a removed one came back; a save now keeps it removed, and Add it back (and magpie provider add <account>, which made claude-2) goes through ShowAccount, keeping its picks; and magpie provider set <id> k=v… changes a provider with add's pairs (the v0.1.77 notes named it, it didn't exist)
This commit is contained in:
5 files changed
+74
-4
No files matched your search
@@ -1260,7 +1260,7 @@ function renderExcluded() {
|
||||
r.lastChild.append(el("b", "", t("{agent} is signed in, but stays out of this list. ", { agent: x.agentName })), t(x.why));
|
||||
if (x.provider) {
|
||||
const back = el("button", "link", t("Add it back"));
|
||||
back.onclick = () => providerAction("save", { id: x.provider }, t("{name} added back", { name: x.agentName }));
|
||||
back.onclick = () => providerAction("show", { id: x.provider }, t("{name} added back", { name: x.agentName }));
|
||||
r.lastChild.append(" ", back);
|
||||
}
|
||||
box.append(r);
|
||||
|
||||
@@ -342,6 +342,12 @@ func providerRoutes(mux *http.ServeMux, w Windows, gw *gateway.Server) {
|
||||
}
|
||||
in := req.Provider
|
||||
switch r.PathValue("action") {
|
||||
case "show":
|
||||
// a signed-in account the user removed, back with its picks
|
||||
if err := provider.ShowAccount(in.ID); err != nil {
|
||||
fail(rw, err)
|
||||
return
|
||||
}
|
||||
case "save":
|
||||
// a preset needs nothing but the key; a saved provider keeps
|
||||
// its key when the form left it blank
|
||||
|
||||
@@ -133,12 +133,34 @@ func TestAccountsAreProviders(t *testing.T) {
|
||||
if x := Excluded(); len(x) != 1 || x[0].Provider != "codex" || x[0].Agent != "codex" {
|
||||
t.Fatalf("excluded: %+v", x)
|
||||
}
|
||||
// saving its picks again (anything that saves it) keeps it removed
|
||||
if err := Save(Provider{ID: "codex", Models: []string{"gpt-5.5"}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := find(All(), "codex"); ok || len(Excluded()) != 1 {
|
||||
t.Fatal("a save brought a removed codex back")
|
||||
}
|
||||
if err := ShowAccount("codex"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if codex, ok = find(All(), "codex"); !ok || len(codex.Models) != 1 || codex.Account == nil || len(Excluded()) != 0 {
|
||||
t.Fatalf("after add back: %+v", codex)
|
||||
}
|
||||
// only through routing groups holds for an account too
|
||||
codex.Unlisted = true
|
||||
if err := Save(codex); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if codex, _ = find(All(), "codex"); !codex.Unlisted {
|
||||
t.Fatal("unlisted lost on an account")
|
||||
}
|
||||
for _, e := range Catalog() {
|
||||
if e.Provider.ID == "codex" {
|
||||
t.Fatalf("unlisted account in the catalog: %s", e.ID)
|
||||
}
|
||||
}
|
||||
codex.Unlisted = false
|
||||
Save(codex)
|
||||
|
||||
// signed out: gone, and a stale picks entry is not a provider
|
||||
Save(Provider{ID: "copilot", Models: []string{"gpt-5.5"}})
|
||||
|
||||
@@ -189,7 +189,8 @@ func All() []Provider {
|
||||
if _, taken := find(out, a.ID); taken || picks[a.ID].Hidden {
|
||||
continue
|
||||
}
|
||||
a.Models, a.Fallback, a.Routing, a.Affinity = picks[a.ID].Models, picks[a.ID].Fallback, picks[a.ID].Routing, picks[a.ID].Affinity
|
||||
pk := picks[a.ID]
|
||||
a.Models, a.Unlisted, a.Fallback, a.Routing, a.Affinity = pk.Models, pk.Unlisted, pk.Fallback, pk.Routing, pk.Affinity
|
||||
out = append(out, a)
|
||||
}
|
||||
return out
|
||||
@@ -256,8 +257,9 @@ func Save(p Provider) error {
|
||||
}
|
||||
if a, ok := find(Accounts(), p.ID); ok {
|
||||
// an account keeps only the user's model picks; the rest is the
|
||||
// agent's own sign-in. Saving it again brings a removed one back.
|
||||
p = Provider{ID: a.ID, Models: p.Models, Unlisted: p.Unlisted, Fallback: p.Fallback, Routing: p.Routing, Affinity: p.Affinity}
|
||||
// agent's own sign-in. One the user removed stays removed: only
|
||||
// ShowAccount brings it back.
|
||||
p = Provider{ID: a.ID, Models: p.Models, Unlisted: p.Unlisted, Fallback: p.Fallback, Routing: p.Routing, Affinity: p.Affinity, Hidden: hiddenAccount(a.ID)}
|
||||
} else {
|
||||
if slices.Contains(accountIDs, p.ID) && !stored(p.ID) {
|
||||
// taken, it would hide that subscription once signed in
|
||||
@@ -333,6 +335,15 @@ func stored(id string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func hiddenAccount(id string) bool {
|
||||
for _, p := range load().Providers {
|
||||
if p.ID == id {
|
||||
return p.Hidden
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ShowAccount brings back the signed-in account of an agent the user had
|
||||
// removed from magpie.
|
||||
func ShowAccount(id string) error {
|
||||
|
||||
@@ -26,6 +26,7 @@ const providerUsage = `usage:
|
||||
magpie provider add <preset> <key> add a preset vendor e.g. magpie provider add deepseek sk-…
|
||||
again, it adds another (deepseek-2); k=v pairs too: id, name, header.X-Foo
|
||||
magpie provider add <name> k=v… add a custom vendor k: url, anthropic, responses, key, models, catalog, icon, header.X-Foo, balance, balance.path, models.url
|
||||
magpie provider set <id> k=v… change a provider's settings, with the same k=v pairs as add
|
||||
magpie provider key <id> <key> change the API key
|
||||
magpie provider icon <id> <file|name> give a custom provider a picture (PNG, JPEG, SVG…) or a built-in icon
|
||||
magpie provider fallback <id> <provider/model>… where requests go when it's out of quota or down (none clears)
|
||||
@@ -38,6 +39,7 @@ const providerUsage = `usage:
|
||||
magpie provider add "Own Claude" anthropic=https://gw.example.com key=sk-… catalog=anthropic
|
||||
magpie provider add "My Relay" url=https://relay.example.com/v1 key=sk-… header.X-Org-Id=acme
|
||||
magpie provider add anthropic sk-… id=anthropic-ws2 name="Anthropic WS2" header.anthropic-workspace-id=wrkspc_…
|
||||
magpie provider set my-relay models.url=https://relay.example.com/api/models catalog=
|
||||
magpie provider add "My Relay" url=https://relay.example.com/v1 key=sk-… balance=https://relay.example.com/api/usage/token balance.path='$data.total_available / 500000'`
|
||||
|
||||
// providers: `magpie providers`
|
||||
@@ -191,6 +193,28 @@ func providerCmd(args []string) error {
|
||||
switch verb {
|
||||
case "add":
|
||||
return addProvider(rest)
|
||||
case "set":
|
||||
// the same k=v pairs as add, on a provider already here
|
||||
if len(rest) < 2 {
|
||||
return fmt.Errorf("magpie provider set <id> k=v…\n\n%s", providerUsage)
|
||||
}
|
||||
p, err := provider.Find(rest[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, kv := range rest[1:] {
|
||||
if k, _, _ := strings.Cut(kv, "="); strings.EqualFold(k, "id") {
|
||||
return fmt.Errorf("a provider's id can't change; groups and agents name it by it")
|
||||
}
|
||||
}
|
||||
if err := applyPairs(p, rest[1:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := provider.Save(*p); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(green.Render("✓"), "saved", p.Name, muted.Render("("+p.ID+")"))
|
||||
return nil
|
||||
case "key":
|
||||
if len(rest) != 2 {
|
||||
return fmt.Errorf("magpie provider key <id> <key>")
|
||||
@@ -362,6 +386,13 @@ func addProvider(rest []string) error {
|
||||
if len(rest) == 0 {
|
||||
return fmt.Errorf("magpie provider add <preset> <key> or magpie provider add <name> k=v…\n\n%s", providerUsage)
|
||||
}
|
||||
if len(rest) == 1 && slices.ContainsFunc(provider.Excluded(), func(x provider.Exclusion) bool { return x.Provider == strings.ToLower(rest[0]) }) {
|
||||
// a signed-in account the user removed comes back with its picks
|
||||
if err := provider.ShowAccount(strings.ToLower(rest[0])); err != nil {
|
||||
return err
|
||||
}
|
||||
return announce(strings.ToLower(rest[0]))
|
||||
}
|
||||
var p provider.Provider
|
||||
if pr, err := provider.FromPreset(strings.ToLower(rest[0])); err == nil {
|
||||
p = pr
|
||||
|
||||
Reference in new issue
Block a user