fix: SuperGrokPro 用 4.5 窗口区分 Heavy,容量抖动只封单模型

JWT 的 SuperGrokPro 同时覆盖 SuperGrok 与 Heavy,账单月额度又滞后,
账号会误升/误降。multi-agent 容量抖动还会把整号提出调度。

- CanonicalGrokPlan:明确 JWT 优先;模糊档仅采新鲜的 grok-4.5 Responses 窗口
- 配额快照写入 plan_from_45_responses 时间戳,过期信号不用
- engine_overloaded 对 multi-agent 只封当前模型 0.5s–5min
This commit is contained in:
IanShaw027
2026-08-13 07:49:14 +08:00
parent f3d9491071
commit 363cc4994b
18 changed files with 601 additions and 35 deletions
+67 -3
View File
@@ -1481,15 +1481,79 @@ const { pause: pauseAutoRefresh, resume: resumeAutoRefresh } = useIntervalFn(
{ immediate: false }
)
// Persisted credentials.subscription_tier outranks lagging billing/quota snapshots.
const GROK_QUOTA_SIGNAL_MAX_AGE_MS = 24 * 60 * 60 * 1000
const GROK_QUOTA_SIGNAL_MAX_FUTURE_SKEW_MS = 5 * 60 * 1000
function normalizeGrokPlanKey(value: unknown): string {
return String(value ?? '')
.trim()
.toLowerCase()
.replace(/[\s_-]+/g, '')
}
function isGrokQuotaTimestampFresh(raw: unknown): boolean {
const value = String(raw || '').trim()
if (!value) return false
const observedAt = Date.parse(value)
if (!Number.isFinite(observedAt)) return false
const age = Date.now() - observedAt
return age <= GROK_QUOTA_SIGNAL_MAX_AGE_MS && age >= -GROK_QUOTA_SIGNAL_MAX_FUTURE_SKEW_MS
}
function isGrok45ResponsesQuotaModel(model: unknown): boolean {
const value = String(model || '')
.trim()
.toLowerCase()
.replace(/^(x-ai|xai)\//, '')
return value === 'grok-4.5' || value.startsWith('grok-4.5-')
}
function grokQuotaLooksHeavy(snapshot: Record<string, any> | undefined): boolean {
const req = Number(snapshot?.requests?.limit ?? 0)
const tok = Number(snapshot?.tokens?.limit ?? 0)
return req >= 8300 || tok >= 53_000_000
}
function grok45ResponsesPlanIsHeavy(snapshot: Record<string, any> | undefined): boolean {
if (!snapshot) return false
const hint = normalizeGrokPlanKey(snapshot.plan_from_45_responses)
if (hint === 'supergrokheavy' && isGrokQuotaTimestampFresh(snapshot.plan_from_45_responses_at)) {
return true
}
const observedAt = snapshot.last_headers_seen_at || snapshot.updated_at
return (
isGrok45ResponsesQuotaModel(snapshot.model) &&
isGrokQuotaTimestampFresh(observedAt) &&
grokQuotaLooksHeavy(snapshot)
)
}
// JWT / unambiguous credentials outrank snapshots. SuperGrokPro is ambiguous
// (covers SuperGrok and Heavy). 8300/53M only upgrades when the window came
// from grok-4.5 Responses (or a carried 4.5 hint).
function getAccountPlanType(row: any): string | undefined {
if (!row) return undefined
if (row.platform === 'grok') {
const extra = (row.extra || {}) as Record<string, any>
const billing = extra.grok_billing_snapshot as Record<string, any> | undefined
const quota = extra.grok_quota_snapshot as Record<string, any> | undefined
const quota = (extra.grok_quota_snapshot || extra.grok_usage_snapshot) as Record<string, any> | undefined
const cred = row.credentials?.subscription_tier
const credKey = normalizeGrokPlanKey(cred)
if (credKey && credKey !== 'supergrokpro') {
return cred
}
if (
grok45ResponsesPlanIsHeavy(quota) &&
(credKey === 'supergrokpro' ||
normalizeGrokPlanKey(billing?.plan) === 'supergrok' ||
normalizeGrokPlanKey(billing?.plan) === 'supergrokpro')
) {
return 'SuperGrok Heavy'
}
if (credKey === 'supergrokpro') {
return billing?.plan || 'SuperGrok'
}
return (
row.credentials?.subscription_tier ||
billing?.plan ||
quota?.subscription_tier ||
extra.subscription_tier ||
@@ -396,6 +396,38 @@ describe('admin AccountsView — 账号行展示', () => {
type: 'oauth',
credentials: { plan_type: 'SuperGrok' },
},
{
id: 206,
name: 'supergrokpro-responses-quota',
platform: 'grok',
type: 'oauth',
credentials: { subscription_tier: 'SuperGrokPro' },
extra: {
grok_billing_snapshot: { plan: 'SuperGrok' },
grok_quota_snapshot: {
model: 'grok-4.5',
last_headers_seen_at: new Date().toISOString(),
requests: { limit: 8300 },
tokens: { limit: 53_000_000 },
},
},
},
{
id: 207,
name: 'supergrokpro-other-model-quota',
platform: 'grok',
type: 'oauth',
credentials: { subscription_tier: 'SuperGrokPro' },
extra: {
grok_billing_snapshot: { plan: 'SuperGrok' },
grok_quota_snapshot: {
model: 'grok-4.6',
last_headers_seen_at: new Date().toISOString(),
requests: { limit: 8300 },
tokens: { limit: 53_000_000 },
},
},
},
]
listAccounts.mockResolvedValue({
@@ -416,6 +448,8 @@ describe('admin AccountsView — 账号行展示', () => {
'FREE',
'BASIC',
'SuperGrok',
'SuperGrok Heavy',
'SuperGrok',
])
wrapper.unmount()