Files
magpie/groups_fast_cli_test.go
T
yetone 10b940233c routing: a group's member can be sent in its vendor's fast mode, beside its own reasoning (vs on Discord: a member "gpt-6.1 sol · high · fast") — a member's effort was already its own (#189, "provider/model:effort") but there was no way to ask for speed, so a group could not hold "gpt-6.1-sol at high, priority" next to slower members. A group now lists in fast the members it sends fast, as Members spells them, kept beside Members rather than in their ids so an older version still routes to them, only not fast (checked: HEAD's build on the same providers.json sends the same members at their effort, no tier, and leaves the file as it was). When the gateway sends to such a member it asks in the upstream's own words, only where the model has a fast mode magpie can ask for (provider.CanFast): service_tier "priority" for a ChatGPT account's GPT models and an OpenAI key's GPT/o-series models on its Responses API, Cursor's -fast variant, and speed "fast" with the fast-mode-2026-02-01 beta for Claude Opus 4.8/5/5.5 on an Anthropic key (api.anthropic.com); a relay, Bedrock, a Claude subscription, a model without one, or a provider since moved off those hosts is sent untouched. It is applied in the agent's own API and carried through translation (parseAnthropic now reads speed), so Chat, Responses and Anthropic agents all get it. A translated request for OpenAI's Chat API is still told no tier (TestCursorPluginFast), hence the Responses requirement. The trace marks the try, the order and the group (fast), shown as "fast" beside the reasoning in Requests and the order. Typed, a member is provider/model[:effort]:fast: magpie group add/set models=… takes it, fast=<m1>,… sets them, models-= takes either spelling, and the group lists and shows members as typed with "· fast"; a model with no fast mode, or a group in the group, is refused. The GUI group editor shows a "Standard speed"/"Fast" toggle (标准速度/快速) beside a member's reasoning only for a model that has one, the card reads "· fast" after the reasoning, and fast follows a member's reasoning change and leaves with a removed member; the TUI's group view takes E to step a member's effort and F to toggle fast. Provider renames carry it. TestFastMemberOpenAI, TestFastMemberAnthropic, TestWithFastWire (gateway, per wire format, a fake vendor recording body and anthropic-beta), TestCanFast, TestSaveGroupFast, TestGroupMembersFast, TestGroupMemberEffortAndFast, TestGroupSaveFast and group-member-fast.test.cjs fail without the change. A sandbox magpie (own HOME, gateway on 3966) with providers at http://api.openai.com and http://api.anthropic.com through a local recording proxy sent priority+high to OpenAI and speed fast + the beta to Anthropic from Chat, Responses and Anthropic clients, and nothing extra for Sonnet or a non-fast member. go vet, go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test -tags nogui ./... pass; group-member-fast, click-scroll, old-webkit, routing-levels, routing-manual, routing-effort, settings-groups pass (Chromium, WebKit, en/zh).
2026-10-01 17:13:34 +08:00

46 lines
1.8 KiB
Go

package main
import (
"slices"
"strings"
"testing"
"github.com/yetone/magpie/internal/provider"
)
// A member typed with :fast after it (and its effort) is sent in its
// vendor's fast mode; fast= says which members are, models-= takes one
// out typed either way, and the group reads them back typed as they were.
func TestGroupMembersFast(t *testing.T) {
groupsHome(t)
if err := provider.Save(provider.Provider{ID: "oa", Name: "OpenAI", Key: "k", Responses: "https://api.openai.com/v1", Models: []string{"gpt-6.1-sol"}}); err != nil {
t.Fatal(err)
}
g, err := addGroup("Sol", []string{"models=gpt-6.1-sol:high:fast,a/m"})
if err != nil {
t.Fatal(err)
}
if !slices.Equal(g.Members, []string{"oa/gpt-6.1-sol:high", "a/m"}) || !slices.Equal(g.Fast, []string{"oa/gpt-6.1-sol:high"}) {
t.Fatalf("members %v fast %v", g.Members, g.Fast)
}
out, err := said(t, func() error { return showGroup(g) })
if err != nil || !strings.Contains(out, "oa/gpt-6.1-sol:high:fast") || !strings.Contains(out, "· fast") {
t.Fatalf("shown %v:\n%s", err, out)
}
if out, _ = said(t, groups); !strings.Contains(out, "oa/gpt-6.1-sol:high:fast") {
t.Fatalf("listed:\n%s", out)
}
if g, err = setGroup("sol", []string{"fast="}); err != nil || len(g.Fast) != 0 {
t.Fatalf("fast= %v %v", g.Fast, err)
}
if g, err = setGroup("sol", []string{"fast=oa/gpt-6.1-sol:high"}); err != nil || !g.IsFast("oa/gpt-6.1-sol:high") {
t.Fatalf("fast=… %v %v", g.Fast, err)
}
if _, err = setGroup("sol", []string{"fast=a/m"}); err == nil || !strings.Contains(err.Error(), "no fast mode") {
t.Fatalf("a/m on a relay sent fast: %v", err)
}
if g, err = setGroup("sol", []string{"models-=gpt-6.1-sol:high:fast"}); err != nil || !slices.Equal(g.Members, []string{"a/m"}) || len(g.Fast) != 0 {
t.Fatalf("models-= %v %v %v", g.Members, g.Fast, err)
}
}