Files
magpie/providers_decide_cli_test.go
T
a998a36367 provider, gateway: remote magpie serves decision and retrieval APIs (#1282)
* provider, gateway: remote magpie serves decision and retrieval APIs

Configure System One from the remote address and discover explicitly marked
decision models, including Clef and custom names. Keep exposed retrieval
models when importing the remote catalog, without treating decisions as
chat or image generators. Refresh removes stale decisions and the editor
receives an explicit empty decision list when none remain.

Forward caller/session attribution for decision, retrieval and media APIs.
Preserve TUI model-list errors for a remote that cannot be reached. Document
the HTTP API surface and old-peer compatibility.

Verified with the full nogui Go suite, focused race checks, go vet, native
macOS and Linux/Windows cross-builds, and the Remote magpie browser tests in
Chromium and WebKit in English and Chinese. The new System One integration
test fails on the old code with an empty model list, and the retrieval test
fails because the remote embedding model is filtered out. Integration tests
exercise two gateways and simulated upstreams; live vendor accounts were
not used.

* gateway, provider: remote forwards keep official client headers

Claude Code and Codex keep their User-Agent through both gateways, so a
relay behind the remote that admits only official clients accepts their
requests. Only decision, retrieval and media calls set magpie's User-Agent
at their own call sites. The two-hop test covers Messages, Responses, Chat
Completions and a Codex originator with another User-Agent.

Keep entryFor limited to remote decision providers. Test rejection of
unlisted remote decisions and remove the redundant resolveDecideModel
early return: the explicit model marker and list-only Jev already exclude
vendor aliases. Document retrieval entries remaining in the shared agent
catalog and TUI list errors being shown for other mixed providers too.

Verified with the Remote magpie provider/gateway tests. Before the fix,
TestRemoteMagpieClientHeaders fails all four cases with the relay receiving
magpie/dev and returning 403 official client required; all pass after it.
Follow-up to yetone's review of #1282.

* provider, gateway, gui: remote discovery reports only the decisions it lists (#1282)

Show CLI list failures and catalog counts for mixed providers. Derive the
remote decision endpoint from its one editable address, name custom decisions
by their ids, and reuse successful discovery in endpoint tests without an
empty decide OK row. Bare-provider decision selection is documented.

Simplify decision classification around the explicit remote list and reuse
model markers while building Available, Catalog and Deciders, avoiding a
whole-list copy and scan for every model. Other providers keep their rules.

The regression tests failed before the fixes on swallowed CLI errors, blank
labels, extra endpoint fields, empty decide results and repeated discovery.
Deliberately removing retrieval/video User-Agent or System One caller
attribution makes all three receiving-side tests fail with lost agent/via.

Verified with sandboxed Go tests, targeted race checks, Linux GUI/nogui and
Windows vet/build, Chromium/WebKit en/zh browser tests and zh-TW/ja/de checks.
The two-hop tests use real gateway handlers with simulated vendor replies;
real vendor accounts and native Windows/macOS runs were not used.

Co-authored-by: XYenon <register@xyenon.bid>
Amp-Thread-ID: https://ampcode.com/threads/T-01a11cd6-93aa-767c-9d6e-f0db2a9c5bab

* gui, gateway tests: remote decision refresh leaves only its current model choices (#1282)

The empty-decision browser fixture cleared deciders but kept decision
entries in models, so its screenshot still showed the same chips. Remove
those entries and assert the visible choices in both states. Check the
real providerInfo model list after refresh too, with Jev Router retained
as chat, and verify an unnamed remote decision reaches the local picker
with its id and provider label.

No production code changes. Verified all Remote magpie tests in root,
provider, gateway and gui under a sandbox HOME with -race; the new
assertions in gateway/gui pass. Chromium and WebKit pass all six browser
cases in English and Chinese; the corrected screenshot run passes 3/3.
Targeted Linux and Windows nogui vet and gofmt are clean.

Amp-Thread-ID: https://ampcode.com/threads/T-01a11cd6-93aa-767c-9d6e-f0db2a9c5bab
Co-authored-by: XYenon <register@xyenon.bid>

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-10-10 03:55:45 +08:00

84 lines
2.8 KiB
Go

package main
import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/yetone/magpie/internal/provider"
)
// Bailian's decision model is added with its workspace or plan, and a
// System One API of another vendor with decide= (#647).
func TestProviderDecidePairs(t *testing.T) {
p, err := provider.FromPreset("bailian-decision")
if err != nil {
t.Fatal(err)
}
if err := applyPairs(&p, []string{"workspace=ws-1"}); err != nil {
t.Fatal(err)
}
if p.Decide != "https://ws-1.cn-beijing.maas.aliyuncs.com/compatible-mode/v1" {
t.Fatalf("workspace: %s", p.Decide)
}
p, _ = provider.FromPreset("bailian-decision")
if err := applyPairs(&p, []string{"workspace=ws-2", "region=ap-southeast-1"}); err != nil {
t.Fatal(err)
}
if p.Decide != "https://ws-2.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1" {
t.Fatalf("Singapore: %s", p.Decide)
}
p, _ = provider.FromPreset("bailian-decision")
if err := applyPairs(&p, []string{"plan=token-plan"}); err != nil {
t.Fatal(err)
}
if p.Decide != "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1" || !strings.Contains(p.KeysURL, "token-plan") {
t.Fatalf("Token Plan: %s %s", p.Decide, p.KeysURL)
}
if err := applyPairs(&p, []string{"workspace=ws-3"}); err == nil {
t.Fatal("a workspace taken for the Token Plan")
}
if err := applyPairs(&p, []string{"region=mars"}); err == nil || !strings.Contains(err.Error(), "token-plan") {
t.Fatalf("unknown region: %v", err)
}
c := provider.Provider{Name: "My Decider"}
if err := applyPairs(&c, []string{"decide=https://decide.example.com/v1", "models=my-decision-model", "key=sk-1"}); err != nil {
t.Fatal(err)
}
if !c.DecideOnly() || c.Jev() != "my-decision-model" || !c.DecidesModel("my-decision-model") {
t.Fatalf("custom: %+v asked as %s", c, c.Jev())
}
}
func TestRemoteMagpieAddReportsModels(t *testing.T) {
for _, tc := range []struct{ name, list, want string }{
{"unreachable", "", "no models exposed yet"},
{"chat only", `{"data":[{"id":"lib/m1"}]}`, "1 models in the catalog"},
} {
t.Run(tc.name, func(t *testing.T) {
groupsHome(t)
up := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, tc.list)
}))
t.Cleanup(up.Close)
if tc.list == "" {
up.Close()
}
out, err := stdoutOf(t, func() error {
return providerCmd([]string{"provider", "add", "remote-magpie", "remote-key", "url=" + up.URL, "id=office"})
})
if err != nil {
t.Fatal(err)
}
if !strings.Contains(out, tc.want) || strings.Contains(out, "classifier=office/") {
t.Errorf("remote add said %q; want %q and no empty classifier", out, tc.want)
}
if tc.list == "" && !strings.Contains(out, "refused") {
t.Errorf("remote list error hidden: %q", out)
}
})
}
}