From 5cbfaa6c04135e8bfba577f26a249d30643df016 Mon Sep 17 00:00:00 2001 From: yetone Date: Fri, 25 Sep 2026 05:45:51 +0800 Subject: [PATCH] =?UTF-8?q?gui(providers):=20an=20agent=20on=20a=20routing?= =?UTF-8?q?=20group=20shows=20on=20each=20provider=20one=20of=20its=20memb?= =?UTF-8?q?ers=20is=20on,=20as=20one=20on=20a=20provider's=20own=20model?= =?UTF-8?q?=20does=20=E2=80=94=20its=20icon=20in=20the=20list,=20a=20chip?= =?UTF-8?q?=20in=20the=20editor=20naming=20the=20group,=20the=20gateway=20?= =?UTF-8?q?counting=20it=20routed=20through=20=E2=80=94=20and=20clicking?= =?UTF-8?q?=20it=20opens=20the=20whole=20picker,=20the=20group=20first,=20?= =?UTF-8?q?rather=20than=20only=20that=20provider's=20models,=20which=20wo?= =?UTF-8?q?uld=20take=20it=20off=20the=20group?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/gui/assets/app.js | 11 ++++++++--- internal/gui/assets/i18n.js | 2 ++ internal/gui/providers.go | 23 +++++++++++++++++++---- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/internal/gui/assets/app.js b/internal/gui/assets/app.js index d6bbae9b..0b394704 100644 --- a/internal/gui/assets/app.js +++ b/internal/gui/assets/app.js @@ -852,7 +852,8 @@ function renderProviders() { const uses = el("div", "uses"); for (const a of using) { const b = el("button", "use"); - b.title = t("{name} · {model} — click to change", { name: a.name, model: a.model }); + b.title = a.group ? t("{name} · {model}, through the routing group {group} — click to change", { name: a.name, model: a.model, group: a.group }) + : t("{name} · {model} — click to change", { name: a.name, model: a.model }); b.append(icon(a.icon)); b.onclick = (ev) => pickForAgent(a, p, b, ev); uses.append(b); @@ -1339,6 +1340,9 @@ function pickForAgent(a, p, btn, ev) { const agent = state.agents.find((x) => x.id === a.id); const field = modelField(a); if (!field) return; + // on a routing group: the whole picker, the group first, not only this + // provider's models — picking one would take the agent off the group + if (a.group) return openPicker(agent, field, btn, ev); const pre = ofProvider(p); if (!field.options.some((o) => pre.test(o.value))) { ev.stopPropagation(); @@ -1726,8 +1730,9 @@ function renderEditor(p, presetID) { const chips = el("div", "achips"); for (const a of on) { const c = el("button", "achip on"); - c.append(icon(a.icon), el("span", "n", a.name), el("span", "m", a.model)); - c.title = t("{agent} is on {model} — click to change", { agent: a.name, model: a.model }); + c.append(icon(a.icon), el("span", "n", a.name), el("span", "m", a.group || a.model)); + c.title = a.group ? t("{agent} is on the routing group {group}, {model} here among its members — click to change", { agent: a.name, group: a.group, model: a.model }) + : t("{agent} is on {model} — click to change", { agent: a.name, model: a.model }); c.onclick = (ev) => pickForAgent(a, p, c, ev); chips.append(c); } diff --git a/internal/gui/assets/i18n.js b/internal/gui/assets/i18n.js index 5fadec7d..750b682b 100644 --- a/internal/gui/assets/i18n.js +++ b/internal/gui/assets/i18n.js @@ -86,6 +86,7 @@ const I18N = { "no models exposed": "未暴露模型", "signed in as {user}": "已登录 {user}", "{name} · {model} — click to change": "{name} · {model} — 点击更改", + "{name} · {model}, through the routing group {group} — click to change": "{name} · {model},经由路由组 {group} — 点击更改", "{agent} is signed in; its models are here for every other agent": "{agent} 已登录;它的模型在这里供其他 Agent 使用", "no key": "无需密钥", "needs a key": "需要密钥", @@ -117,6 +118,7 @@ const I18N = { "Anthropic Messages — what Claude Code speaks": "Anthropic Messages — Claude Code 使用", "Custom provider": "自定义供应商", "{agent} is on {model} — click to change": "{agent} 正在使用 {model} — 点击更改", + "{agent} is on the routing group {group}, {model} here among its members — click to change": "{agent} 正在使用路由组 {group},这里的 {model} 是它的成员之一 — 点击更改", "e.g. My Relay": "例如 My Relay", "Name": "名称", "OpenAI compatible": "OpenAI 兼容", diff --git a/internal/gui/providers.go b/internal/gui/providers.go index 3aeeab37..13c055b9 100644 --- a/internal/gui/providers.go +++ b/internal/gui/providers.go @@ -76,6 +76,7 @@ type providerAgent struct { Icon string `json:"icon"` Current bool `json:"current"` // this agent is on one of this provider's models now Model string `json:"model,omitempty"` + Group string `json:"group,omitempty"` // through this routing group, one of whose members it is } type presetJSON struct { @@ -114,11 +115,16 @@ type providersJSON struct { } // currentProvider reads which provider (and model) an agent is routed to now. -func currentProvider(a *agent.Agent) (string, string) { +// agentModel is the model an agent is on, as magpie's catalog names it. +func agentModel(a *agent.Agent) string { if len(a.Fields) == 0 { - return "", "" + return "" } - v := strings.TrimPrefix(a.Fields[0].Get(), "magpie/") + return strings.TrimPrefix(a.Fields[0].Get(), "magpie/") +} + +func currentProvider(a *agent.Agent) (string, string) { + v := agentModel(a) if strings.HasPrefix(v, provider.GroupPrefix) { return "", "" // a routing group: no one provider } @@ -191,7 +197,16 @@ func providerInfo(p provider.Provider, agents []*agent.Agent) providerJSON { } for _, a := range agents { pid, model := currentProvider(a) - out.Agents = append(out.Agents, providerAgent{ID: a.ID, Name: a.Name, Icon: a.Icon, Current: pid == p.ID, Model: model}) + pa := providerAgent{ID: a.ID, Name: a.Name, Icon: a.Icon, Current: pid == p.ID, Model: model} + if g, ms, ok := provider.FindGroup(agentModel(a)); ok { + for _, m := range ms { + if m.Provider.ID == p.ID { + pa.Current, pa.Model, pa.Group = true, m.Model, g.Name + break + } + } + } + out.Agents = append(out.Agents, pa) } return out }