provider: a ZCode account's spent gift bucket doesn't hold up a model another of its plans still has (ganlerk on Discord) — an account with ZCode Trust Build (GLM-5.3-Flash 14.5% used) and the Start Plan (GLM-5.3-Flash all used) read GLM-5.3-Flash, and so the account, as 100% used: every bucket's window counted its model, and routing takes the fullest. The server picks the bucket (ZCode sends no plan with a request), so a model now counts on one bucket, a live one before a spent one, the least used; a bucket no model counts on stays on the card set aside. The plugin does the same from 0.1.10 (magpie-community/plugins 6b13dc0), and the mover's min is raised to it. TestZCodeStartSpentSiblingAside fails without the change. go vet, go build (darwin, GOOS=linux nogui, GOOS=windows) and go test -tags nogui ./... under a temp HOME pass.

This commit is contained in:
yetone committed 2026-10-05 20:02:13 +08:00
1 parent 3fad3d21eb
commit 15102c809f
3 files changed
+94 -11

No files matched your search

+1 -1
View File
@@ -21,7 +21,7 @@ import (
func init() {
movers["zcode"] = &mover{
pkg: "@magpie-community/opencode-zcode-auth",
min: "0.1.9", // the gift's models are trial entries (GLM-5.3-Flash-Trial) the user picks; a plain model spends the GLM Coding Plan (plugins #21)
min: "0.1.10", // a gift-only account's spent bucket (the Start Plan's GLM-5.3-Flash) doesn't hold up a model another plan (Trust Build) still has; 0.1.9's trial entries (plugins #21) kept
agents: []string{"zcode"},
// a Start Plan account was never served GLM-5.3, by the built-in
// or by ZCode, and the plugin lists it no more than they do; its
+52 -10
View File
@@ -487,6 +487,7 @@ func zcodeStartQuota(ctx context.Context, l Login, jwt string) SubscriptionQuota
if until != nil {
q.Until, q.Renew = until, "off"
}
var buckets []zcodeBucket
for _, x := range b.Balances {
total, hasTotal := zcodeNum(x.Total)
used, hasUsed := zcodeNum(x.Used)
@@ -529,17 +530,58 @@ func zcodeStartQuota(ctx context.Context, l Login, jwt string) SubscriptionQuota
w.Span = time.Duration(e-s) * time.Second
}
}
if len(models) > 0 {
w.matches = func(model string) bool {
for _, m := range models {
if strings.EqualFold(m, model) {
return true
}
}
return false
}
}
q.Windows = append(q.Windows, w)
buckets = append(buckets, zcodeBucket{models, hasLeft && left <= 0 || hasTotal && total > 0 && used >= total})
}
zcodeAlternatives(q.Windows, buckets)
return q
}
// zcodeBucket is what zcodeAlternatives reads of a window's bucket.
type zcodeBucket struct {
models []string
spent bool
}
// zcodeAlternatives scopes each bucket's window to the models a request
// spends it on. Two plans can give one model (ZCode Trust Build's and the
// Start Plan's GLM-5.3-Flash), and the server picks the bucket — ZCode
// sends no plan with a request — so a model counts on one bucket: a live
// one before a spent one, the least used of those. A bucket that is no
// model's stays on the card, Aside, so a spent one doesn't hold up the
// model, or the account, another still has (ganlerk on Discord).
func zcodeAlternatives(ws []QuotaWindow, bs []zcodeBucket) {
best := map[string]int{}
for i, b := range bs {
for _, m := range b.models {
k := strings.ToLower(m)
j, ok := best[k]
if !ok || bs[j].spent && !b.spent || bs[j].spent == b.spent && ws[i].Used < ws[j].Used {
best[k] = i
}
}
}
for i, b := range bs {
if len(b.models) == 0 {
continue
}
var mine []string
for _, m := range b.models {
if best[strings.ToLower(m)] == i {
mine = append(mine, m)
}
}
if len(mine) == 0 {
ws[i].Aside = true
continue
}
ws[i].matches = func(model string) bool {
for _, m := range mine {
if strings.EqualFold(m, model) {
return true
}
}
return false
}
}
}
+41
View File
@@ -303,3 +303,44 @@ func TestZCodeStartHTTP1(t *testing.T) {
t.Fatalf("sent:\n%s\nwant:\n%s", strings.Join(got, "\n"), strings.Join(want, "\n"))
}
}
// ganlerk (Discord): an account with ZCode Trust Build and the Start Plan,
// both giving GLM-5.3-Flash, the Start Plan's all used and Trust Build's
// 14.5%. The server spends whichever has some, so GLM-5.3-Flash counts on
// Trust Build's bucket and the spent one is set aside: neither the model
// nor the account reads as used up.
func TestZCodeStartSpentSiblingAside(t *testing.T) {
signIn(t)
jwt := zcodeTestJWT(time.Now().Add(24 * time.Hour))
u := newZCodeStartUpstream(t, jwt)
now := time.Now()
u.balance = map[string]any{
"server_time": now.Unix(),
"plans": []any{
map[string]any{"plan_id": "zai-start-plan", "user_plan_id": "s1", "name": "ZCode Start Plan", "status": "active", "ends_at": now.Add(7 * 24 * time.Hour).Unix()},
map[string]any{"plan_id": "zai-trust-build", "user_plan_id": "t1", "name": "ZCode Trust Build", "status": "active", "ends_at": now.Add(24 * time.Hour).Unix()},
},
"balances": []any{
map[string]any{"plan_id": "zai-start-plan", "user_plan_id": "s1", "show_name": "GLM-5.3", "capabilities": []any{"model:GLM-5.3"},
"total_units": 3000000, "used_units": 0, "remaining_units": 3000000},
map[string]any{"plan_id": "zai-start-plan", "user_plan_id": "s1", "show_name": "GLM-5.3-Flash", "capabilities": []any{"model:GLM-5.3-Flash"},
"total_units": 5000000, "used_units": 5000000, "remaining_units": 0},
map[string]any{"plan_id": "zai-trust-build", "user_plan_id": "t1", "show_name": "GLM-5.3-Flash", "capabilities": []any{"model:GLM-5.3-Flash"},
"total_units": 100000000, "used_units": 14501196, "remaining_units": 85498804, "expires_at": now.Add(time.Hour).Unix()},
},
}
q := zcodeStartQuota(context.Background(), Login{User: "u@example.com"}, jwt)
if q.Error != "" || len(q.Windows) != 3 {
t.Fatalf("usage: %+v", q)
}
if w := q.Windows[1]; w.Used != 100 || !w.Aside {
t.Fatalf("the spent Start Plan bucket isn't set aside: %+v", w)
}
a := allowanceOf(q.Windows, now)
if used, _ := a.For("glm-5.3-flash", now); used < 14 || used > 15 {
t.Fatalf("GLM-5.3-Flash counts %v%% used, not Trust Build's 14.5%%", used)
}
if used, _ := a.For("glm-5.3", now); used != 0 {
t.Fatalf("GLM-5.3: %v", used)
}
}