Files
Aether/frontend/src/utils/__tests__/providerKeyQuota.spec.ts
T
stabeyandClaude Opus 5 e83399db2f feat(providers): add xAI provider with device code OAuth
Add a separate `xai` provider type for xAI Grok CLI subscription accounts.
It is independent of the existing `grok` provider, which reverse-proxies
grok.com with browser cookies; behavior of `grok` is unchanged.

Account binding uses the xAI device code flow, so no local callback
listener is needed and headless deployments can bind accounts. Refresh
tokens can also be imported individually or in batches, and are rotated
on refresh.

OAuth requests default to the cli-chat-proxy Responses API; API keys and
compact stay on api.x.ai. Explicit custom gateways are preserved. Only
`openai:responses` and `openai:responses:compact` are exposed; Chat,
Claude and Gemini clients reach the provider through Aether's existing
cross-format conversion rather than new native endpoints.

Upstream Responses payloads are sanitized for what xAI actually rejects:
`previous_response_id` and `metadata.user_id` are dropped, hosted
`tool_choice` is rewritten, `web_search` is restored for converted
clients, `image_generation` is stripped on older Grok conversation
models, unsupported reasoning effort is removed, and requested
`reasoning.encrypted_content` is preserved with a replay policy keyed on
the configured provider type rather than the model name.

Quota refresh reads /user and /billing?format=credits and stores a
structured usage snapshot; a prepaid balance keeps an account selectable
after the weekly allowance is exhausted. API-key accounts skip the
subscription billing surface. The admin UI shows remaining weekly quota
as a labeled bar in the provider drawer and the pool list.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-14 21:09:03 +08:00

389 lines
10 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
getGeminiCliAccountCreditsText,
getQuotaDisplayText,
} from '../providerKeyQuota'
describe('providerKeyQuota', () => {
it('includes Codex Spark quota windows in display text', () => {
expect(getQuotaDisplayText({
status_snapshot: {
oauth: {
code: 'valid',
},
account: {
code: 'ok',
blocked: false,
},
quota: {
provider_type: 'codex',
code: 'ok',
exhausted: false,
windows: [
{
code: 'weekly',
remaining_ratio: 0.9,
},
{
code: '5h',
remaining_ratio: 0.8,
},
{
code: 'spark_5h',
remaining_ratio: 0.6,
},
{
code: 'spark_weekly',
remaining_ratio: 0.95,
},
],
},
},
}, 'codex')).toBe('周剩余 90.0% | 5H剩余 80.0% | Spark5H剩余 60.0% | Spark周剩余 95.0%')
})
it('uses actual Codex window durations and ignores zero placeholders', () => {
expect(getQuotaDisplayText({
status_snapshot: {
oauth: { code: 'valid' },
account: { code: 'ok', blocked: false },
quota: {
provider_type: 'codex',
code: 'ok',
exhausted: false,
windows: [
{
code: 'weekly',
label: '周',
window_minutes: 0,
remaining_ratio: 1,
},
{
code: '5h',
label: '5H',
window_minutes: 43_800,
remaining_ratio: 0.86,
},
],
},
},
}, 'codex')).toBe('月剩余 86.0%')
})
it('keeps account and model Codex weekly quotas distinct in display text', () => {
expect(getQuotaDisplayText({
status_snapshot: {
oauth: { code: 'valid' },
account: { code: 'ok', blocked: false },
quota: {
provider_type: 'codex',
code: 'ok',
exhausted: false,
windows: [
{
code: 'weekly',
label: '周',
scope: 'account',
window_minutes: 10_080,
remaining_ratio: 0.9,
},
{
code: 'additional_0_primary',
label: 'gpt-reserve',
scope: 'model',
model: 'gpt-reserve',
window_minutes: 10_080,
remaining_ratio: 0.4,
},
],
},
},
}, 'codex')).toBe('周剩余 90.0% | gpt-reserve 周剩余 40.0%')
})
it('formats Grok account quota from structured quota windows', () => {
expect(getQuotaDisplayText({
status_snapshot: {
oauth: {
code: 'valid',
},
account: {
code: 'ok',
blocked: false,
},
quota: {
provider_type: 'grok',
code: 'ok',
exhausted: false,
windows: [
{
code: 'account',
scope: 'account',
used_value: 2,
limit_value: 10,
remaining_ratio: 0.8,
},
],
},
},
}, 'grok')).toBe('剩余 80.0% (8/10)')
})
it('formats Grok mode quota from model-scoped windows', () => {
expect(getQuotaDisplayText({
status_snapshot: {
oauth: {
code: 'valid',
},
account: {
code: 'ok',
blocked: false,
},
quota: {
provider_type: 'grok',
code: 'ok',
exhausted: false,
plan_type: 'heavy',
windows: [
{
code: 'model:quota_auto',
label: 'auto',
scope: 'model',
remaining_ratio: 0.4,
used_value: 90,
limit_value: 150,
},
{
code: 'model:quota_heavy',
label: 'heavy',
scope: 'model',
remaining_ratio: 0,
used_value: 20,
limit_value: 20,
},
],
},
},
}, 'grok')).toBe('Auto剩余 40.0% (60/150) | Heavy剩余 0.0% (0/20)')
})
it('formats Gemini CLI AI credits from status snapshot and upstream metadata', () => {
expect(getQuotaDisplayText({
status_snapshot: {
oauth: { code: 'none' },
account: { code: 'ok', blocked: false },
quota: {
provider_type: 'gemini_cli',
code: 'ok',
exhausted: false,
credits: {
remaining: 123.5,
consumed: 7,
},
},
},
}, 'gemini_cli')).toBe('AI Credits 剩余 123.5')
expect(getGeminiCliAccountCreditsText({
status_snapshot: {
oauth: { code: 'none' },
account: { code: 'ok', blocked: false },
quota: {
provider_type: 'gemini_cli',
code: 'ok',
exhausted: false,
},
},
upstream_metadata: {
gemini_cli: {
paidTier: {
id: 'g1-pro-tier',
availableCredits: '41.5',
},
},
},
}, 'gemini_cli')).toBe('AI Credits 剩余 41.5')
})
it('formats ChatGPT Web image quota as remaining count', () => {
expect(getQuotaDisplayText({
status_snapshot: {
oauth: { code: 'none' },
account: { code: 'ok', blocked: false },
quota: {
provider_type: 'chatgpt_web',
code: 'ok',
exhausted: false,
windows: [
{
code: 'image_gen',
scope: 'account',
remaining_ratio: 0.96,
used_value: 1,
remaining_value: 24,
limit_value: 25,
},
],
},
},
}, 'chatgpt_web')).toBe('生图剩余 24/25')
})
it('surfaces Windsurf hard account states', () => {
expect(getQuotaDisplayText({
status_snapshot: {
oauth: { code: 'none' },
account: { code: 'ok', blocked: false },
quota: {
provider_type: 'windsurf',
code: 'quarantined',
label: '账号隔离中',
exhausted: false,
},
},
}, 'windsurf')).toBe('账号隔离中')
expect(getQuotaDisplayText({
status_snapshot: {
oauth: { code: 'none' },
account: { code: 'ok', blocked: false },
quota: {
provider_type: 'windsurf',
code: 'cooldown',
label: '冷却中',
exhausted: false,
},
},
}, 'windsurf')).toBe('冷却中')
expect(getQuotaDisplayText({
status_snapshot: {
oauth: { code: 'none' },
account: { code: 'ok', blocked: false },
quota: {
provider_type: 'windsurf',
code: 'cooldown',
exhausted: false,
},
},
}, 'windsurf')).toBe('冷却中')
})
it('includes Windsurf quota windows and model availability in display text', () => {
expect(getQuotaDisplayText({
status_snapshot: {
oauth: { code: 'none' },
account: { code: 'ok', blocked: false },
quota: {
provider_type: 'windsurf',
code: 'ok',
exhausted: false,
allowed_models_count: 7,
windows: [
{
code: 'daily',
remaining_ratio: 0.75,
},
{
code: 'weekly',
remaining_ratio: 0.5,
},
{
code: 'prompt',
remaining_value: 12,
limit_value: 20,
},
{
code: 'flex',
used_value: 2,
limit_value: 5,
},
],
},
},
}, 'windsurf')).toBe('日剩余 75.0% | 周剩余 50.0% | Prompt 剩余 12/20 | Flex 剩余 3/5 | 可用模型 7 个')
expect(getQuotaDisplayText({
status_snapshot: {
oauth: { code: 'none' },
account: { code: 'ok', blocked: false },
quota: {
provider_type: 'windsurf',
code: 'cooldown',
label: '冷却中',
exhausted: false,
rate_limit: {
limited: true,
has_capacity: true,
messages_remaining: -1,
max_messages: -1,
},
allowed_models_count: 118,
windows: [
{
code: 'daily',
remaining_ratio: 0.99,
},
{
code: 'weekly',
remaining_ratio: 1,
},
{
code: 'prompt',
remaining_value: 100,
limit_value: 100,
},
{
code: 'rate_limit',
reset_seconds: null,
is_exhausted: false,
},
],
},
},
}, 'windsurf')).toBe('日剩余 99.0% | 周剩余 100.0% | Prompt 剩余 100/100 | 可用模型 118 个')
})
it('uses Windsurf model availability when no quota window is present', () => {
expect(getQuotaDisplayText({
status_snapshot: {
oauth: { code: 'none' },
account: { code: 'ok', blocked: false },
quota: {
provider_type: 'windsurf',
code: 'ok',
exhausted: false,
allowed_models_count: 3,
},
},
}, 'windsurf')).toBe('可用模型 3 个')
})
it('formats xAI weekly credits as remaining percent', () => {
expect(getQuotaDisplayText({
status_snapshot: {
oauth: { code: 'valid' },
account: { code: 'ok', blocked: false },
quota: {
provider_type: 'xai',
code: 'ok',
exhausted: false,
windows: [
{
code: 'usage',
scope: 'account',
used_ratio: 0.46,
remaining_ratio: 0.54,
},
{
code: 'prepaid',
remaining_value: 12.5,
},
],
},
},
}, 'xai')).toBe('剩余 54.0% | 预付剩余 12.5')
})
})