* 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>
84 lines
2.8 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|