MiniMax Code 3.1.0 checks a SubAgent's model against /^[^/\s]+\/[^/\s]+$/ (uQn in @minimax-ai/code's chunk-2XPMNZVR.js), and magpie wrote its models under custom_provider.magpie by catalog id, so custom_provider:magpie/bb-codex/gpt-6.1-sol was refused (AGENT_CONFIG_INVALID). - The writer keys each model by its catalog id with "~" for each slash (bb-codex~gpt-6.1-sol, provider.FlatID), when exactly one served id spells that way (provider.Flattened); else it keeps the slashed id. - The gateway takes such an id back to that one provider's model or group in validateModel and unprefixed (unflat / provider.Unflat): only an id with "~", no slash, that resolves as nothing else; two ids spelled alike are neither. Never a bare-name first hit. Key limits, middleware and usage see the catalog id, so a key held to bb-codex/* is refused other~gpt-6.1-sol on every route, count_tokens too. - A config an older magpie wrote moves on the next sync: the user's keys on a model go to its new key, defaultModel, defaultLightModel and memory.captureModel/consolidationModel follow, and the slashed key stays with enabled: false, so a session begun on it still resumes (checked with mcode exec --continue). Get still reads magpie/<catalog id>, and Disconnect still puts back the stashed default and variant. Other agents: no installed agent bundle here (cline, command-code, qwen-code, deepseek, pi, omp, codebuff, openclaw) has this regex; native agents were not checked. Verified: - go test -tags nogui ./internal/agent ./internal/gateway ./internal/provider (temp HOME), gofmt -l, go vet darwin/linux/windows. - New TestMiniMaxCodeOneSlash uses the reporter's regex and ids; TestFlatModelIDsReachTheirProvider has two providers serving gpt-6.1-sol. Breaking unflat, mcodeKey, the key carry-over, the legacy enabled:false and the ref rewrite each turned the tests red. - Real run: built magpie serve in a temp HOME on 47812, two fake upstreams; `magpie mcode magpie/other/gpt-6.1-sol` wrote other~gpt-6.1-sol; real mcode 0.6.5 exec reached other's upstream with gpt-6.1-sol (usage: other/gpt-6.1-sol); its agent loader accepted an agent.md with custom_provider:magpie/other~gpt-6.1-sol and refused the two-slash one; `magpie mcode default` put minimax/MiniMax-M2.7 back.
69 lines
2.0 KiB
Go
69 lines
2.0 KiB
Go
package provider
|
|
|
|
import "strings"
|
|
|
|
// FlatSep stands for each slash of a catalog id in its flat spelling.
|
|
//
|
|
// MiniMax Code takes a SubAgent's model only as "<provider>/<model>" with
|
|
// one slash in the whole (/^[^/\s]+\/[^/\s]+$/, #1387), and its provider
|
|
// for magpie is "custom_provider:magpie", so a model under it can't hold a
|
|
// slash: "bb-codex/gpt-6.1-sol" is written there as "bb-codex~gpt-6.1-sol",
|
|
// and the gateway takes that back to the catalog id (Unflat). A tilde is in
|
|
// no id a provider was seen to serve but OpenRouter's "~anthropic/…"
|
|
// latest-aliases, which flatten to "openrouter~~anthropic~…", still one
|
|
// entry's.
|
|
const FlatSep = "~"
|
|
|
|
// FlatID is a catalog id with no slash in it.
|
|
func FlatID(id string) string { return strings.ReplaceAll(id, "/", FlatSep) }
|
|
|
|
// Unflat is the served id (a provider's model or a routing group) whose
|
|
// flat spelling is id, when exactly one has it. An id with a slash, or
|
|
// with no tilde, is never a flat one.
|
|
func Unflat(id string) (string, bool) {
|
|
if !strings.Contains(id, FlatSep) || strings.Contains(id, "/") {
|
|
return "", false
|
|
}
|
|
return unflatIn(Served(), id)
|
|
}
|
|
|
|
func unflatIn(entries []Entry, id string) (string, bool) {
|
|
found := ""
|
|
for _, e := range entries {
|
|
if e.ID == found || FlatID(e.ID) != id {
|
|
continue
|
|
}
|
|
if found != "" {
|
|
return "", false // two ids flatten alike: neither is meant
|
|
}
|
|
found = e.ID
|
|
}
|
|
return found, found != ""
|
|
}
|
|
|
|
// Flattened maps each served id that can be spelled flat, and taken back
|
|
// by Unflat, to that spelling; an id two served ids flatten to alike is
|
|
// left out, as is one already without a slash.
|
|
func Flattened() map[string]string {
|
|
by := map[string]map[string]bool{}
|
|
for _, e := range Served() {
|
|
f := FlatID(e.ID)
|
|
if f == e.ID {
|
|
continue
|
|
}
|
|
if by[f] == nil {
|
|
by[f] = map[string]bool{}
|
|
}
|
|
by[f][e.ID] = true
|
|
}
|
|
out := map[string]string{}
|
|
for f, ids := range by {
|
|
if len(ids) == 1 {
|
|
for id := range ids {
|
|
out[id] = f
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|