usage, gateway: a model the vendor spells with other separators isn't marked as another one (vincentzhang on Discord: 这个说明有点奇怪。看不明白。) — Volcengine Ark answers deepseek-v4.1-flash as deepseek-v4-1-flash, the dots written as hyphens, and the Usage page's Requests and the Routing page marked every such row amber 「实际 deepseek-v4-1-flash」, saying another model had answered, not the one asked for under a dated name. Swapped now compares the bare names without their separators (. - _ and spaces) and case, so deepseek-v4-1-flash, deepseek_v4_1_flash, GLM-4-6 for glm-4.6 and gpt-4-1-2025-04-14 for gpt-4.1 are the model asked for, while deepseek-v4-flash or deepseek-v4.1-pro for deepseek-v4.1-flash still are another. Rows and routes kept marked swapped before are read back as not swapped when the names differ only so (usage.SameSpelled, in rowChunk.row and Route.routedAgain). The tag's explanation says it in plainer words: asked for {sent}, the reply says {served} answered, likely another model; the same model under a dated name or spelled otherwise isn't marked (zh, ja, de). TestSwappedIgnoresSeparators fails on the old code; TestKeptRowSameSpelledNotSwapped, TestRouteKeptSpelledOtherwiseNotSwapped. usage-ledger (its expected title updated), remote-group-served, panel-routing, gui-ja and gui-de pass on WebKit. go vet, builds (darwin, GOOS=linux nogui, GOOS=windows) and go test -tags nogui -count=1 ./... under a temp HOME pass (TestCodexVersion timed out once under load and passes alone).
This commit is contained in:
1 parent
7d92013a15
commit
4a87b30610
8 files changed
+94
-7
No files matched your search
@@ -192,13 +192,20 @@ func History(day string) (days []HistoryDay, routes []Route, cut bool) {
|
||||
|
||||
// routedAgain reads a route kept before Routed was: a try that asked
|
||||
// another magpie's routing group was marked swapped for the member it
|
||||
// routed to, and is routed (usage.GroupRouted).
|
||||
// routed to, and is routed (usage.GroupRouted). A try kept marked swapped
|
||||
// for its model spelled with other separators (deepseek-v4.1-flash
|
||||
// answered as deepseek-v4-1-flash) wasn't swapped (usage.SameSpelled).
|
||||
func (r *Route) routedAgain() {
|
||||
for i := range r.Tries {
|
||||
if tr := &r.Tries[i]; tr.Swapped && usage.GroupRouted(tr.Model, tr.Served) {
|
||||
tr.Swapped, tr.Routed = false, true
|
||||
} else if tr.Swapped && usage.SameSpelled(tr.Model, tr.Served) {
|
||||
tr.Swapped = false
|
||||
}
|
||||
}
|
||||
if n := len(r.Tries); n > 0 && r.Swapped && !r.Tries[n-1].Swapped && !r.Tries[n-1].Routed && r.Served == r.Tries[n-1].Served {
|
||||
r.Swapped = false
|
||||
}
|
||||
if n := len(r.Tries); n > 0 && r.Swapped && r.Tries[n-1].Routed && r.Served == r.Tries[n-1].Served {
|
||||
r.Swapped, r.Routed = false, true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package gateway
|
||||
|
||||
import "testing"
|
||||
|
||||
// A route kept with a try marked swapped for its model spelled with other
|
||||
// separators (Volcengine Ark: deepseek-v4.1-flash answered as
|
||||
// deepseek-v4-1-flash) is read as not swapped; another model stays swapped.
|
||||
func TestRouteKeptSpelledOtherwiseNotSwapped(t *testing.T) {
|
||||
r := &Route{Swapped: true, Served: "deepseek-v4-1-flash", Tries: []Try{{Model: "deepseek-v4.1-flash", Served: "deepseek-v4-1-flash", Swapped: true}}}
|
||||
r.routedAgain()
|
||||
if r.Swapped || r.Tries[0].Swapped {
|
||||
t.Fatalf("still swapped: %+v", r)
|
||||
}
|
||||
r = &Route{Swapped: true, Served: "deepseek-v4-flash", Tries: []Try{{Model: "deepseek-v4.1-flash", Served: "deepseek-v4-flash", Swapped: true}}}
|
||||
r.routedAgain()
|
||||
if !r.Swapped || !r.Tries[0].Swapped {
|
||||
t.Fatalf("a real swap was cleared: %+v", r)
|
||||
}
|
||||
}
|
||||
@@ -2585,7 +2585,7 @@ const I18N = {
|
||||
"served {served}": "实际 {served}",
|
||||
"{sent} is a routing group of the remote magpie, and it routed the request to {served}: the group picking one of its models, not the vendor swapping the model.": "{sent} 是远程 magpie 的路由组,请求被路由给 {served}:这是路由组选择了组内模型,并非服务商替换了模型。",
|
||||
"Open Routing": "打开路由",
|
||||
"The vendor was asked for {sent}, and its reply says {served} answered it: another model, not just {sent} under a dated name.": "向服务商请求的是 {sent},回复却写明由 {served} 作答:这是另一个模型,而非 {sent} 的带日期版本名。",
|
||||
"The vendor was asked for {sent}, but its reply says {served} answered: likely another model. The same model under a dated name or spelled otherwise isn't marked.": "向服务商请求的是 {sent},但回复说是 {served} 作答,很可能换成了别的模型。(同一模型只是带了日期或写法不同,不会这样标出。)",
|
||||
"{n} min": "{n} 分钟",
|
||||
"{h} h {m} min": "{h} 小时 {m} 分钟",
|
||||
"{n} h": "{n} 小时",
|
||||
@@ -5696,7 +5696,7 @@ const I18N = {
|
||||
"served {served}": "応答 {served}",
|
||||
"{sent} is a routing group of the remote magpie, and it routed the request to {served}: the group picking one of its models, not the vendor swapping the model.": "{sent} はリモート magpie のルーティンググループで、リクエストを {served} にルーティングしました。グループがモデルを選んだもので、ベンダーがモデルを差し替えたのではありません。",
|
||||
"Open Routing": "ルーティングを開く",
|
||||
"The vendor was asked for {sent}, and its reply says {served} answered it: another model, not just {sent} under a dated name.": "ベンダーには {sent} をリクエストしましたが、応答には {served} が応答したとあります。日付付きの名前の {sent} ではなく、別のモデルです。",
|
||||
"The vendor was asked for {sent}, but its reply says {served} answered: likely another model. The same model under a dated name or spelled otherwise isn't marked.": "ベンダーには {sent} をリクエストしましたが、応答では {served} が答えたとあり、別のモデルに替えられた可能性があります。(同じモデルに日付が付いただけ、または表記が違うだけなら表示しません。)",
|
||||
"{n} min": "{n} 分",
|
||||
"{h} h {m} min": "{h} 時間 {m} 分",
|
||||
"{n} h": "{n} 時間",
|
||||
@@ -8807,7 +8807,7 @@ const I18N = {
|
||||
"served {served}": "geliefert {served}",
|
||||
"{sent} is a routing group of the remote magpie, and it routed the request to {served}: the group picking one of its models, not the vendor swapping the model.": "{sent} ist eine Weiterleitungsgruppe des entfernten magpie und hat die Anfrage an {served} weitergeleitet: Die Gruppe wählt eines ihrer Modelle, nicht der Anbieter tauscht das Modell.",
|
||||
"Open Routing": "Weiterleitung öffnen",
|
||||
"The vendor was asked for {sent}, and its reply says {served} answered it: another model, not just {sent} under a dated name.": "Beim Anbieter wurde {sent} angefragt, laut Antwort hat aber {served} geantwortet: ein anderes Modell, nicht nur {sent} unter einem Namen mit Datum.",
|
||||
"The vendor was asked for {sent}, but its reply says {served} answered: likely another model. The same model under a dated name or spelled otherwise isn't marked.": "Beim Anbieter wurde {sent} angefragt, laut Antwort hat aber {served} geantwortet – wahrscheinlich ein anderes Modell. (Dasselbe Modell mit Datum oder anderer Schreibweise wird nicht markiert.)",
|
||||
"{n} min": "{n} min",
|
||||
"{h} h {m} min": "{h} h {m} min",
|
||||
"{n} h": "{n} h",
|
||||
|
||||
@@ -1269,7 +1269,7 @@
|
||||
k.title = swapWhy(tr);
|
||||
return k;
|
||||
}
|
||||
const swapWhy = (tr) => t("The vendor was asked for {sent}, and its reply says {served} answered it: another model, not just {sent} under a dated name.", { sent: tr.model, served: tr.served });
|
||||
const swapWhy = (tr) => t("The vendor was asked for {sent}, but its reply says {served} answered: likely another model. The same model under a dated name or spelled otherwise isn't marked.", { sent: tr.model, served: tr.served });
|
||||
window.swapWhy = swapWhy; // the Usage page's Requests say it too
|
||||
// a try that asked a remote magpie for one of its routing groups: the
|
||||
// reply names the member the group routed to, which is the group
|
||||
|
||||
@@ -103,7 +103,7 @@ const L = {
|
||||
tabs: ["Overview", "Requests", "Sessions"],
|
||||
cols: ["Time", "Agent", "Requested", "Provider · account", "Sent", "Served", "Effort", "In", "Out", "Cache write", "Cache read", "Cost", "Duration", "Status"],
|
||||
sum: "130 requests", pager: "1–100 of 130", older: "Older", newer: "Newer", failed: "Failed", export: "Export CSV",
|
||||
why: "The vendor was asked for gpt-6-sol, and its reply says gpt-6-luna answered it", saved: "Saved 130 requests to ~/Downloads/magpie-requests-30d-2026-09-29.csv",
|
||||
why: "The vendor was asked for gpt-6-sol, but its reply says gpt-6-luna answered",saved: "Saved 130 requests to ~/Downloads/magpie-requests-30d-2026-09-29.csv",
|
||||
none: "No requests match these filters.", bad: "Failed: the agent was answered 429", via: "Codex · via office-mac",
|
||||
},
|
||||
zh: {
|
||||
|
||||
@@ -132,6 +132,9 @@ func (c *rowChunk) row(i int) Row {
|
||||
for i, s := range rowText(&r) {
|
||||
*s = c.Strings[p.Text[i]]
|
||||
}
|
||||
if r.Swapped && SameSpelled(r.Model, r.Served) {
|
||||
r.Swapped = false // kept before a name spelled otherwise was the same
|
||||
}
|
||||
r.Computer = c.Computer
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -51,7 +51,21 @@ func bareModel(m string) string {
|
||||
// is the member another magpie's routing group sent it to (GroupRouted).
|
||||
func Swapped(sent, served string) bool {
|
||||
a, b := bareModel(sent), bareModel(served)
|
||||
return a != "" && b != "" && a != b && a != "auto" && bareModel(provider.EffortFamily(a)) != b && !GroupRouted(sent, served)
|
||||
return a != "" && b != "" && squash(a) != squash(b) && a != "auto" && squash(bareModel(provider.EffortFamily(a))) != squash(b) && !GroupRouted(sent, served)
|
||||
}
|
||||
|
||||
// squash is a bare name without its separators, so a vendor that writes
|
||||
// the dots of a version as hyphens (Volcengine Ark: deepseek-v4.1-flash
|
||||
// answered as deepseek-v4-1-flash, vincentzhang on Discord) or underscores
|
||||
// names the same model.
|
||||
var squash = strings.NewReplacer(".", "", "-", "", "_", "", " ", "").Replace
|
||||
|
||||
// SameSpelled reports whether served is sent spelled with other separators
|
||||
// or case: a row or route kept marked swapped before Swapped knew it is
|
||||
// read as not swapped.
|
||||
func SameSpelled(sent, served string) bool {
|
||||
a := squash(bareModel(sent))
|
||||
return a != "" && a == squash(bareModel(served))
|
||||
}
|
||||
|
||||
// GroupRouted reports whether sent is a routing group of another magpie
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package usage
|
||||
|
||||
import "testing"
|
||||
|
||||
// A vendor that writes a version's dots as hyphens or underscores answered
|
||||
// with the model asked for (vincentzhang on Discord: Volcengine Ark's
|
||||
// deepseek-v4.1-flash answered as deepseek-v4-1-flash, marked another
|
||||
// model); another model is still one.
|
||||
func TestSwappedIgnoresSeparators(t *testing.T) {
|
||||
for _, c := range [][2]string{
|
||||
{"volcengine/deepseek-v4.1-flash", "deepseek-v4-1-flash"},
|
||||
{"deepseek-v4.1-flash", "deepseek_v4_1_flash"},
|
||||
{"glm-4.6", "GLM-4-6"},
|
||||
{"gpt-4.1", "gpt-4-1-2025-04-14"},
|
||||
{"doubao-seed-1.6", "doubao-seed-1-6-250615"},
|
||||
} {
|
||||
if Swapped(c[0], c[1]) {
|
||||
t.Errorf("%s answered as %s is the same model", c[0], c[1])
|
||||
}
|
||||
}
|
||||
for _, c := range [][2]string{
|
||||
{"deepseek-v4.1-flash", "deepseek-v4-flash"},
|
||||
{"deepseek-v4.1-flash", "deepseek-v4.1-pro"},
|
||||
{"glm-4.6", "glm-4.5"},
|
||||
{"claude-opus-5-5", "claude-sonnet-5-5"},
|
||||
} {
|
||||
if !Swapped(c[0], c[1]) {
|
||||
t.Errorf("%s answered as %s is another model", c[0], c[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A row kept marked swapped before is read back as not swapped when its
|
||||
// model was only spelled otherwise; a real swap stays one.
|
||||
func TestKeptRowSameSpelledNotSwapped(t *testing.T) {
|
||||
c := &rowChunk{}
|
||||
c.add(Row{Record: Record{Model: "deepseek-v4.1-flash", Served: "deepseek-v4-1-flash"}, Swapped: true}, "", 0, false)
|
||||
c.add(Row{Record: Record{Model: "deepseek-v4.1-flash", Served: "deepseek-v4-flash"}, Swapped: true}, "", 1, false)
|
||||
for _, chunk := range []*rowChunk{c, c.pack().unpack()} {
|
||||
if chunk.row(0).Swapped || !chunk.row(1).Swapped {
|
||||
t.Fatalf("same-spelled %v, other model %v", chunk.row(0).Swapped, chunk.row(1).Swapped)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user