feat(grok): improve free quota probing and usage

This commit is contained in:
superman2003
2026-07-13 19:49:56 +08:00
parent 7d239d62e8
commit c896cacf6d
27 changed files with 2140 additions and 104 deletions
+10 -9
View File
@@ -4,6 +4,9 @@
*/
import { apiClient } from '../client'
import type { GrokBillingSummary, GrokQuotaWindow, WindowStats } from '@/types'
export type { GrokBillingSummary, GrokQuotaWindow } from '@/types'
export interface GrokAuthUrlResponse {
auth_url: string
@@ -39,13 +42,6 @@ export interface GrokTokenInfo {
[key: string]: unknown
}
export interface GrokQuotaWindow {
limit?: number | null
remaining?: number | null
reset_unix?: number | null
reset_at?: string | null
}
export interface GrokQuotaSnapshot {
requests?: GrokQuotaWindow | null
tokens?: GrokQuotaWindow | null
@@ -62,13 +58,18 @@ export interface GrokQuotaSnapshot {
}
export interface GrokQuotaProbeResult {
source: 'active_probe'
model: string
source: 'active_probe' | 'billing_probe' | 'hybrid_probe'
model?: string
billing?: GrokBillingSummary | null
snapshot?: GrokQuotaSnapshot | null
local_usage_7d?: WindowStats | null
local_usage_monthly?: WindowStats | null
status_code?: number
headers_observed: boolean
reset_supported: boolean
fetched_at: number
persisted?: boolean
probe_error?: string
}
export interface GrokQuotaResetResult {
@@ -382,7 +382,15 @@
</div>
</div>
<UsageProgressBar
v-if="grokRequestQuotaBar"
v-if="grokWeeklyBillingBar"
label="7d"
:utilization="grokWeeklyBillingBar.utilization"
:resets-at="grokWeeklyBillingBar.resetsAt"
:show-now-when-idle="true"
color="indigo"
/>
<UsageProgressBar
v-if="!grokWeeklyBillingBar && !grokIsFree && grokRequestQuotaBar"
:label="t('admin.accounts.usageWindow.grokRequests')"
:utilization="grokRequestQuotaBar.utilization"
:resets-at="grokRequestQuotaBar.resetsAt"
@@ -390,13 +398,20 @@
color="indigo"
/>
<UsageProgressBar
v-if="grokTokenQuotaBar"
v-if="!grokWeeklyBillingBar && !grokIsFree && grokTokenQuotaBar"
:label="t('admin.accounts.usageWindow.grokTokens')"
:utilization="grokTokenQuotaBar.utilization"
:resets-at="grokTokenQuotaBar.resetsAt"
:remaining-capacity="true"
color="emerald"
/>
<UsageProgressBar
v-if="grokFreeTokenBar"
label="2M"
:utilization="grokFreeTokenBar.utilization"
:show-now-when-idle="true"
color="emerald"
/>
<div v-if="grokRetryAfterLabel" class="text-[10px] text-amber-600 dark:text-amber-400">
{{ t('admin.accounts.usageWindow.grokRetryAfter', { time: grokRetryAfterLabel }) }}
</div>
@@ -409,7 +424,7 @@
<div v-if="grokQuotaStatusLine" class="text-[10px] text-gray-500 dark:text-gray-400">
{{ grokQuotaStatusLine }}
</div>
<GrokQuotaProbeCell :account="account" />
<GrokQuotaProbeCell :account="account" @probed="handleGrokProbed" />
</div>
<div v-else class="text-xs text-gray-400">-</div>
</template>
@@ -602,6 +617,7 @@
import { ref, computed, onMounted, onBeforeUnmount, onUnmounted, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { adminAPI } from '@/api/admin'
import type { GrokQuotaProbeResult } from '@/api/admin/grok'
import type { Account, AccountUsageInfo, GeminiCredentials, WindowStats } from '@/types'
import { buildOpenAIUsageRefreshKey } from '@/utils/accountUsageRefresh'
import { enqueueUsageRequest } from '@/utils/usageLoadQueue'
@@ -614,6 +630,8 @@ import GrokQuotaProbeCell from './GrokQuotaProbeCell.vue'
// Module-level cache shared across all AccountUsageCell instances
const _usageCache = new Map<number, { data: AccountUsageInfo; ts: number }>()
const USAGE_CACHE_TTL = 5 * 60 * 1000 // 5 minutes
// xAI Free billing exposes a window without usage_percent, so estimate it from local tokens.
const GROK_FREE_TOKEN_LIMIT = 2_000_000
const props = withDefaults(
defineProps<{
@@ -1047,9 +1065,62 @@ const makeGrokQuotaBar = (quota?: { limit?: number | null; remaining?: number |
const grokRequestQuotaBar = computed(() => makeGrokQuotaBar(usageInfo.value?.grok_request_quota))
const grokTokenQuotaBar = computed(() => makeGrokQuotaBar(usageInfo.value?.grok_token_quota))
const grokLocalUsage = computed(() =>
props.todayStats ||
usageInfo.value?.grok_local_usage ||
usageInfo.value?.grok_local_usage_7d ||
usageInfo.value?.grok_local_usage_monthly ||
null
)
const grokFreeQuotaUsage = computed(() =>
usageInfo.value?.grok_local_usage_7d ||
props.todayStats ||
usageInfo.value?.grok_local_usage ||
null
)
const grokBilling = computed(() => usageInfo.value?.grok_billing || null)
const grokWeeklyBillingBar = computed((): GrokQuotaBarInfo | null => {
const billing = grokBilling.value
if (billing?.period_type?.toLowerCase() !== 'weekly' || billing.usage_percent == null) {
return null
}
return {
utilization: Math.min(100, Math.max(0, billing.usage_percent)),
resetsAt: billing.period_end || null
}
})
const grokPlanLabelIsFree = (value: string) => value.includes('free') || value.includes('basic')
const grokPlanLabelIsPaid = (value: string) => {
return value !== '' && !grokPlanLabelIsFree(value) && !value.includes('unknown')
}
const grokIsFree = computed(() => {
if (props.account.platform !== 'grok' || props.account.type !== 'oauth') return false
const billing = grokBilling.value
if (
billing?.usage_percent != null ||
billing?.used_percent != null ||
(billing?.monthly_limit_cents != null && billing.monthly_limit_cents > 0)
) return false
const plan = (billing?.plan || '').trim().toLowerCase()
const tier = (usageInfo.value?.subscription_tier || '').trim().toLowerCase()
const entitlement = (usageInfo.value?.grok_entitlement_status || '').toLowerCase()
if (grokPlanLabelIsPaid(plan) || grokPlanLabelIsPaid(tier)) return false
if (
grokPlanLabelIsFree(plan) ||
grokPlanLabelIsFree(tier) ||
grokPlanLabelIsFree(entitlement)
) return true
return billing != null
})
const grokFreeTokenBar = computed(() => {
if (!grokIsFree.value || !grokFreeQuotaUsage.value) return null
const used = Math.max(0, grokFreeQuotaUsage.value.tokens || 0)
return { utilization: Math.min(100, (used / GROK_FREE_TOKEN_LIMIT) * 100) }
})
const grokQuotaUnknown = computed(() => {
if (props.account.platform !== 'grok') return false
if (grokRequestQuotaBar.value || grokTokenQuotaBar.value) return false
if (grokBilling.value || grokFreeTokenBar.value || grokRequestQuotaBar.value || grokTokenQuotaBar.value) return false
return usageInfo.value?.grok_quota_snapshot_state !== 'observed'
})
const grokQuotaUnknownLabel = computed(() => {
@@ -1080,7 +1151,6 @@ const grokQuotaStatusLine = computed(() => {
}
return parts.length > 0 ? parts.join(' | ') : null
})
const grokLocalUsage = computed(() => usageInfo.value?.grok_local_usage || props.todayStats || null)
const grokEntitlementLabel = computed(() => {
const status = (usageInfo.value?.grok_entitlement_status || '').trim()
return status || null
@@ -1283,6 +1353,34 @@ const loadActiveUsage = async () => {
}
}
const handleGrokProbed = (result: GrokQuotaProbeResult) => {
const current = usageInfo.value
if (!current) return
const snapshot = result.snapshot
const merged: AccountUsageInfo = {
...current,
grok_billing: result.billing ?? current.grok_billing,
grok_local_usage_7d: result.local_usage_7d ?? current.grok_local_usage_7d,
grok_local_usage_monthly: result.local_usage_monthly ?? current.grok_local_usage_monthly,
grok_request_quota: snapshot?.requests ?? current.grok_request_quota,
grok_token_quota: snapshot?.tokens ?? current.grok_token_quota,
grok_retry_after_seconds: snapshot?.retry_after_seconds ?? current.grok_retry_after_seconds,
grok_entitlement_status: snapshot?.entitlement_status || current.grok_entitlement_status,
grok_quota_snapshot_state: result.billing
? 'billing_observed'
: snapshot?.headers_observed
? 'observed'
: current.grok_quota_snapshot_state,
grok_last_quota_probe_at: result.billing?.fetched_at ?? snapshot?.last_probe_at ?? current.grok_last_quota_probe_at,
grok_last_headers_seen_at: snapshot?.last_headers_seen_at ?? current.grok_last_headers_seen_at,
grok_last_status_code: result.status_code ?? snapshot?.status_code ?? current.grok_last_status_code,
error: result.billing || snapshot ? undefined : current.error,
error_code: result.billing || snapshot ? undefined : current.error_code
}
usageInfo.value = merged
_usageCache.set(props.account.id, { data: merged, ts: Date.now() })
}
// ===== API Key quota progress bars =====
interface QuotaBarInfo {
@@ -55,6 +55,8 @@ const props = defineProps<{
account: Account
}>()
const emit = defineEmits<{ probed: [result: GrokQuotaProbeResult] }>()
const { t } = useI18n()
const visible = computed(() => props.account.platform === 'grok' && props.account.type === 'oauth')
@@ -92,18 +94,27 @@ const retryAfterLabel = computed(() => {
const summary = computed(() => {
const snapshot = data.value?.snapshot
if (!data.value) return ''
if (!snapshot) return t('admin.accounts.usageWindow.grokNoHeaders')
const parts = [
formatWindow(t('admin.accounts.usageWindow.grokRequests'), snapshot.requests),
formatWindow(t('admin.accounts.usageWindow.grokTokens'), snapshot.tokens)
].filter(Boolean)
const billing = data.value.billing
const parts: Array<string | null> = []
if (billing?.period_type?.toLowerCase() === 'weekly' && billing.usage_percent != null) {
parts.push(t('admin.accounts.usageWindow.grokWeeklyUsage', {
percent: Math.round(Math.min(100, Math.max(0, billing.usage_percent)))
}))
}
if (snapshot) {
parts.push(
formatWindow(t('admin.accounts.usageWindow.grokRequests'), snapshot.requests),
formatWindow(t('admin.accounts.usageWindow.grokTokens'), snapshot.tokens)
)
}
if (retryAfterLabel.value) {
parts.push(t('admin.accounts.usageWindow.grokRetryAfter', { time: retryAfterLabel.value }))
}
if (snapshot.entitlement_status) {
if (snapshot?.entitlement_status) {
parts.push(snapshot.entitlement_status)
}
return parts.length > 0 ? parts.join(' | ') : t('admin.accounts.usageWindow.grokNoHeaders')
const visibleParts = parts.filter((part): part is string => Boolean(part))
return visibleParts.length > 0 ? visibleParts.join(' | ') : t('admin.accounts.usageWindow.grokNoHeaders')
})
const truncatedError = computed(() => {
@@ -117,6 +128,8 @@ const handleProbe = async () => {
error.value = null
try {
data.value = await adminAPI.grok.queryQuota(props.account.id)
error.value = data.value.probe_error || null
emit('probed', data.value)
} catch (e) {
error.value = extractErrorMessage(e)
} finally {
@@ -660,6 +660,339 @@ describe('AccountUsageCell', () => {
expect(wrapper.text()).toContain('admin.accounts.usageWindow.grokTokens|25|true')
})
it('Grok OAuth uses the official weekly billing percentage when available', async () => {
getUsage.mockResolvedValue({
grok_billing: {
period_type: 'weekly',
usage_percent: 37,
period_end: '2026-07-16T03:25:00Z',
plan: 'SuperGrok'
},
grok_local_usage: {
requests: 5,
tokens: 2_200_000,
cost: 4.42,
standard_cost: 4.42,
user_cost: 0.44
},
grok_request_quota: { limit: 100, remaining: 100 },
grok_token_quota: { limit: 2_000_000, remaining: 2_000_000 }
})
const wrapper = mount(AccountUsageCell, {
props: {
account: makeAccount({ id: 4201, platform: 'grok', type: 'oauth', extra: {} })
},
global: {
stubs: {
UsageProgressBar: {
props: ['label', 'utilization', 'resetsAt', 'remainingCapacity'],
template: '<div class="usage-bar">{{ label }}|{{ utilization }}|{{ resetsAt }}|{{ remainingCapacity }}</div>'
},
AccountQuotaInfo: true,
GrokQuotaProbeCell: true
}
}
})
await flushPromises()
expect(wrapper.text()).toContain('7d|37|2026-07-16T03:25:00Z')
expect(wrapper.text()).not.toContain('admin.accounts.usageWindow.grokRequests|')
expect(wrapper.text()).not.toContain('admin.accounts.usageWindow.grokTokens|')
expect(wrapper.text()).not.toContain('2M|')
})
it.each([
{ tokens: 0, expected: 0, compact: '0' },
{ tokens: 1_000_000, expected: 50, compact: '1.0M' },
{ tokens: 2_000_000, expected: 100, compact: '2.0M' },
{ tokens: 2_200_000, expected: 100, compact: '2.2M' }
])('Grok Free derives its 2M quota from local tokens: $tokens -> $expected%', async ({ tokens, expected, compact }) => {
getUsage.mockResolvedValue({
grok_billing: {
period_type: 'weekly',
usage_percent: null,
plan: ''
},
grok_local_usage: {
requests: 5,
tokens,
cost: 0,
standard_cost: 0,
user_cost: 0
},
grok_request_quota: { limit: 100, remaining: 100 },
grok_token_quota: { limit: 2_000_000, remaining: 2_000_000 }
})
const wrapper = mount(AccountUsageCell, {
props: {
account: makeAccount({ id: 4300 + expected, platform: 'grok', type: 'oauth', extra: {} })
},
global: {
stubs: {
UsageProgressBar: {
props: ['label', 'utilization'],
template: '<div class="usage-bar">{{ label }}|{{ utilization }}</div>'
},
AccountQuotaInfo: true,
GrokQuotaProbeCell: true
}
}
})
await flushPromises()
expect(wrapper.text()).toContain(`2M|${expected}`)
expect(wrapper.findAll('span').filter((node) => node.text() === compact)).toHaveLength(1)
expect(wrapper.findAll('.usage-bar')).toHaveLength(1)
expect(wrapper.text()).not.toContain('admin.accounts.usageWindow.grokRequests|')
expect(wrapper.text()).not.toContain('admin.accounts.usageWindow.grokTokens|')
})
it('Grok Free uses the weekly billing window instead of today-only usage', async () => {
getUsage.mockResolvedValue({
grok_billing: { period_type: 'weekly', usage_percent: null, plan: '' },
grok_local_usage: {
requests: 2,
tokens: 200_000,
cost: 0,
standard_cost: 0
},
grok_local_usage_7d: {
requests: 12,
tokens: 1_500_000,
cost: 0,
standard_cost: 0
}
})
const wrapper = mount(AccountUsageCell, {
props: {
account: makeAccount({ id: 4398, platform: 'grok', type: 'oauth', extra: {} })
},
global: {
stubs: {
UsageProgressBar: {
props: ['label', 'utilization'],
template: '<div class="usage-bar">{{ label }}|{{ utilization }}</div>'
},
AccountQuotaInfo: true,
GrokQuotaProbeCell: true
}
}
})
await flushPromises()
expect(wrapper.text()).toContain('2M|75')
expect(wrapper.text()).toContain('200.0K')
})
it('Grok Free falls back to refreshed today stats when weekly usage is unavailable', async () => {
getUsage.mockResolvedValue({
grok_billing: { period_type: 'weekly', usage_percent: null, plan: '' },
grok_local_usage: {
requests: 1,
tokens: 250_000,
cost: 0,
standard_cost: 0,
user_cost: 0
}
})
const wrapper = mount(AccountUsageCell, {
props: {
account: makeAccount({ id: 4399, platform: 'grok', type: 'oauth', extra: {} }),
todayStats: {
requests: 4,
tokens: 1_000_000,
cost: 0,
standard_cost: 0,
user_cost: 0
}
},
global: {
stubs: {
UsageProgressBar: {
props: ['label', 'utilization'],
template: '<div class="usage-bar">{{ label }}|{{ utilization }}</div>'
},
AccountQuotaInfo: true,
GrokQuotaProbeCell: true
}
}
})
await flushPromises()
expect(wrapper.text()).toContain('2M|50')
expect(wrapper.text()).toContain('1.0M')
expect(wrapper.text()).not.toContain('250K')
})
it('Grok paid plans are not mistaken for Free when weekly usage is temporarily missing', async () => {
getUsage.mockResolvedValue({
grok_billing: {
period_type: 'weekly',
usage_percent: null,
plan: 'SuperGrok Heavy'
},
grok_entitlement_status: 'free',
grok_local_usage: {
requests: 2,
tokens: 2_000_000,
cost: 1,
standard_cost: 1
},
grok_token_quota: { limit: 1_000, remaining: 250 }
})
const wrapper = mount(AccountUsageCell, {
props: {
account: makeAccount({ id: 4401, platform: 'grok', type: 'oauth', extra: {} })
},
global: {
stubs: {
UsageProgressBar: {
props: ['label', 'utilization'],
template: '<div class="usage-bar">{{ label }}|{{ utilization }}</div>'
},
AccountQuotaInfo: true,
GrokQuotaProbeCell: true
}
}
})
await flushPromises()
expect(wrapper.text()).toContain('admin.accounts.usageWindow.grokTokens|25')
expect(wrapper.text()).not.toContain('2M|')
})
it('Grok custom paid monthly limits override stale Free entitlement', async () => {
getUsage.mockResolvedValue({
grok_billing: {
period_type: 'weekly',
usage_percent: null,
monthly_limit_cents: 25_000,
plan: ''
},
grok_entitlement_status: 'free',
grok_local_usage: {
requests: 2,
tokens: 2_000_000,
cost: 1,
standard_cost: 1
},
grok_token_quota: { limit: 1_000, remaining: 250 }
})
const wrapper = mount(AccountUsageCell, {
props: {
account: makeAccount({ id: 4402, platform: 'grok', type: 'oauth', extra: {} })
},
global: {
stubs: {
UsageProgressBar: {
props: ['label', 'utilization'],
template: '<div class="usage-bar">{{ label }}|{{ utilization }}</div>'
},
AccountQuotaInfo: true,
GrokQuotaProbeCell: true
}
}
})
await flushPromises()
expect(wrapper.text()).toContain('admin.accounts.usageWindow.grokTokens|25')
expect(wrapper.text()).not.toContain('2M|')
})
it('Grok credential Free tier keeps the 2M fallback when billing is unavailable', async () => {
getUsage.mockResolvedValue({
subscription_tier: 'FREE',
grok_local_usage: {
requests: 3,
tokens: 1_000_000,
cost: 0,
standard_cost: 0
}
})
const wrapper = mount(AccountUsageCell, {
props: {
account: makeAccount({ id: 4403, platform: 'grok', type: 'oauth', extra: {} })
},
global: {
stubs: {
UsageProgressBar: {
props: ['label', 'utilization'],
template: '<div class="usage-bar">{{ label }}|{{ utilization }}</div>'
},
AccountQuotaInfo: true,
GrokQuotaProbeCell: true
}
}
})
await flushPromises()
expect(wrapper.text()).toContain('2M|50')
})
it('Grok manual probes merge billing, quota headers, and local usage', async () => {
getUsage.mockResolvedValue({
grok_quota_snapshot_state: 'no_headers',
error: 'stale error',
error_code: 'quota_unknown'
})
const wrapper = mount(AccountUsageCell, {
props: {
account: makeAccount({ id: 4501, platform: 'grok', type: 'oauth', extra: {} })
},
global: {
stubs: {
UsageProgressBar: {
props: ['label', 'utilization', 'resetsAt'],
template: '<div class="usage-bar">{{ label }}|{{ utilization }}|{{ resetsAt }}</div>'
},
AccountQuotaInfo: true,
GrokQuotaProbeCell: {
emits: ['probed'],
template: `<button class="probe" @click="$emit('probed', {
source: 'hybrid_probe',
billing: { period_type: 'weekly', usage_percent: 42, period_end: '2026-07-17T00:00:00Z' },
snapshot: {
headers_observed: true,
updated_at: '2026-07-13T00:00:00Z',
entitlement_status: 'ACTIVE',
requests: { limit: 100, remaining: 20 }
},
local_usage_7d: { requests: 4, tokens: 1000000, cost: 1, standard_cost: 1, user_cost: 0.5 },
local_usage_monthly: { requests: 7, tokens: 1500000, cost: 2, standard_cost: 2, user_cost: 1 },
status_code: 200,
headers_observed: true,
reset_supported: false,
fetched_at: 1
})">probe</button>`
}
}
}
})
await flushPromises()
await wrapper.get('.probe').trigger('click')
expect(wrapper.text()).toContain('7d|42|2026-07-17T00:00:00Z')
expect(wrapper.text()).toContain('1.0M')
expect(wrapper.text()).toContain('ACTIVE')
expect(wrapper.text()).not.toContain('stale error')
})
it('Key 账号在 today stats loading 时显示骨架屏', async () => {
const wrapper = mount(AccountUsageCell, {
props: {
@@ -0,0 +1,54 @@
import { flushPromises, mount } from '@vue/test-utils'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import GrokQuotaProbeCell from '../GrokQuotaProbeCell.vue'
import type { Account } from '@/types'
const { queryQuota } = vi.hoisted(() => ({
queryQuota: vi.fn()
}))
vi.mock('@/api/admin', () => ({
adminAPI: {
grok: { queryQuota }
}
}))
vi.mock('vue-i18n', () => ({
useI18n: () => ({
t: (key: string, params?: Record<string, unknown>) =>
params?.percent == null ? key : `${key}:${params.percent}`
})
}))
const account = {
id: 99,
platform: 'grok',
type: 'oauth'
} as Account
describe('GrokQuotaProbeCell', () => {
beforeEach(() => {
queryQuota.mockReset()
})
it('keeps billing data while exposing a failed Free quota fallback', async () => {
queryQuota.mockResolvedValue({
source: 'hybrid_probe',
billing: { period_type: 'weekly', usage_percent: null },
headers_observed: false,
reset_supported: false,
fetched_at: 1,
probe_error: 'upstream returned 402 for probe model "grok-4.5"'
})
const wrapper = mount(GrokQuotaProbeCell, { props: { account } })
await wrapper.get('button').trigger('click')
await flushPromises()
expect(wrapper.text()).toContain('upstream returned 402 for probe model "grok-4.5"')
expect(wrapper.emitted('probed')?.[0]?.[0]).toMatchObject({
billing: { period_type: 'weekly', usage_percent: null },
probe_error: 'upstream returned 402 for probe model "grok-4.5"'
})
})
})
@@ -1202,6 +1202,7 @@ export default {
claude: 'Claude',
grokRequests: 'Req',
grokTokens: 'Tok',
grokWeeklyUsage: 'Weekly {percent}%',
grokUnknown: 'Grok quota is unknown until the first upstream response includes xAI rate-limit headers.',
grokRetryAfter: 'Retry after {time}',
grokProbe: 'Probe',
@@ -317,6 +317,7 @@ export default {
claude: 'Claude',
grokRequests: '请求',
grokTokens: 'Token',
grokWeeklyUsage: '周额度已用 {percent}%',
grokUnknown: 'Grok 配额需等待首次上游响应返回 xAI rate-limit 头后显示。',
grokRetryAfter: '{time} 后重试',
grokProbe: '探测',
+37 -4
View File
@@ -1020,10 +1020,38 @@ export interface AntigravityModelQuota {
}
export interface GrokQuotaWindow {
limit?: number
remaining?: number
reset_unix?: number
reset_at?: string
limit?: number | null
remaining?: number | null
reset_unix?: number | null
reset_at?: string | null
}
export interface GrokBillingProductUsage {
product: string
usage_percent?: number | null
}
export interface GrokBillingSummary {
period_type?: string
usage_percent?: number | null
period_start?: string
period_end?: string
product_usage?: GrokBillingProductUsage[]
monthly_limit_cents?: number | null
used_cents?: number | null
included_used_cents?: number | null
billing_period_start?: string
billing_period_end?: string
used_percent?: number | null
plan?: string
status_code?: number
source?: string
fetched_at?: string
updated_at?: string
weekly_updated_at?: string
monthly_updated_at?: string
partial?: boolean
failed_windows?: string[]
}
export interface AccountUsageInfo {
@@ -1049,6 +1077,11 @@ export interface AccountUsageInfo {
grok_last_headers_seen_at?: string
grok_last_status_code?: number
grok_local_usage?: WindowStats | null
grok_local_usage_7d?: WindowStats | null
grok_local_usage_monthly?: WindowStats | null
grok_billing?: GrokBillingSummary | null
subscription_tier?: string
subscription_tier_raw?: string
ai_credits?: Array<{
credit_type?: string
amount?: number