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:
1 parent
3fad3d21eb
commit
15102c809f
3 files changed
+94
-11
No files matched your search
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user