mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-06 09:27:46 +08:00
Add Gemini CLI v1internal quota support
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { getQuotaDisplayText } from '../providerKeyQuota'
|
||||
import {
|
||||
getGeminiCliAccountCreditsText,
|
||||
getQuotaDisplayText,
|
||||
} from '../providerKeyQuota'
|
||||
|
||||
describe('providerKeyQuota', () => {
|
||||
it('includes Codex Spark quota windows in display text', () => {
|
||||
@@ -104,4 +107,38 @@ describe('providerKeyQuota', () => {
|
||||
},
|
||||
}, '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: {
|
||||
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: {
|
||||
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')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,10 +3,12 @@ import type {
|
||||
QuotaStatusSnapshot,
|
||||
QuotaWindowSnapshot,
|
||||
} from '@/api/endpoints/types/statusSnapshot'
|
||||
import type { UpstreamMetadata } from '@/api/endpoints/types/provider'
|
||||
|
||||
export interface ProviderKeyQuotaCarrier {
|
||||
account_quota?: string | null
|
||||
status_snapshot?: ProviderKeyStatusSnapshot | null
|
||||
upstream_metadata?: UpstreamMetadata | null
|
||||
}
|
||||
|
||||
function normalizeText(value: unknown): string | null {
|
||||
@@ -76,6 +78,28 @@ function getQuotaWindow(
|
||||
return getQuotaWindows(quota).find(window => normalizeText(window.code)?.toLowerCase() === normalizedCode) ?? null
|
||||
}
|
||||
|
||||
function finiteNumber(value: unknown): number | null {
|
||||
if (typeof value === 'number') return Number.isFinite(value) ? value : null
|
||||
if (typeof value === 'string') {
|
||||
const parsed = Number(value.trim())
|
||||
return Number.isFinite(parsed) ? parsed : null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function firstFiniteNumber(...values: unknown[]): number | null {
|
||||
for (const value of values) {
|
||||
const parsed = finiteNumber(value)
|
||||
if (parsed != null) return parsed
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function objectValue(value: unknown): Record<string, unknown> | null {
|
||||
return value && typeof value === 'object' && !Array.isArray(value)
|
||||
? value as Record<string, unknown>
|
||||
: null
|
||||
}
|
||||
function getQuotaWindowsByScope(
|
||||
quota: QuotaStatusSnapshot | null | undefined,
|
||||
scope: string,
|
||||
@@ -105,6 +129,58 @@ function getQuotaWindowValueText(window: QuotaWindowSnapshot | null | undefined)
|
||||
return null
|
||||
}
|
||||
|
||||
function getGeminiCliCreditsTextFromQuota(quota: QuotaStatusSnapshot | null | undefined): string | null {
|
||||
const credits = quota?.credits
|
||||
if (!credits) return null
|
||||
const remaining = firstFiniteNumber(credits.remaining, credits.balance)
|
||||
if (remaining != null) return `AI Credits 剩余 ${formatQuotaValue(remaining)}`
|
||||
if (credits.unlimited === true) return 'AI Credits 不限量'
|
||||
if (credits.has_credits === true) return 'AI Credits 可用'
|
||||
if (credits.has_credits === false) return 'AI Credits 已用尽'
|
||||
return null
|
||||
}
|
||||
|
||||
export function getGeminiCliAccountCreditsText(
|
||||
input: ProviderKeyQuotaCarrier,
|
||||
fallbackProviderType?: string | null,
|
||||
): string | null {
|
||||
const quota = getQuotaSnapshot(input)
|
||||
if (getQuotaProviderType(quota, fallbackProviderType) !== 'gemini_cli') return null
|
||||
|
||||
const quotaText = getGeminiCliCreditsTextFromQuota(quota)
|
||||
if (quotaText) return quotaText
|
||||
|
||||
const metadata = input.upstream_metadata?.gemini_cli
|
||||
const credits = objectValue(metadata?.credits)
|
||||
const paidTier = objectValue(metadata?.paidTier)
|
||||
const currentTier = objectValue(metadata?.currentTier)
|
||||
const remaining = firstFiniteNumber(
|
||||
credits?.remaining,
|
||||
credits?.remainingCredits,
|
||||
credits?.available,
|
||||
credits?.availableCredits,
|
||||
credits?.balance,
|
||||
paidTier?.availableCredits,
|
||||
paidTier?.remainingCredits,
|
||||
currentTier?.availableCredits,
|
||||
currentTier?.remainingCredits,
|
||||
)
|
||||
if (remaining != null) return `AI Credits 剩余 ${formatQuotaValue(remaining)}`
|
||||
|
||||
const hasCredits = typeof credits?.has_credits === 'boolean'
|
||||
? credits.has_credits
|
||||
: typeof paidTier?.hasCredits === 'boolean'
|
||||
? paidTier.hasCredits
|
||||
: typeof currentTier?.hasCredits === 'boolean'
|
||||
? currentTier.hasCredits
|
||||
: null
|
||||
if (hasCredits === true) return 'AI Credits 可用'
|
||||
if (hasCredits === false) return 'AI Credits 已用尽'
|
||||
|
||||
const unlimited = credits?.unlimited === true || paidTier?.unlimited === true || currentTier?.unlimited === true
|
||||
return unlimited ? 'AI Credits 不限量' : null
|
||||
}
|
||||
|
||||
const GROK_QUOTA_MODE_LABELS: Record<string, string> = {
|
||||
quota_auto: 'Auto',
|
||||
auto: 'Auto',
|
||||
@@ -234,6 +310,7 @@ function getAntigravityQuotaText(quota: QuotaStatusSnapshot): string | null {
|
||||
}
|
||||
|
||||
function getGeminiCliQuotaText(quota: QuotaStatusSnapshot): string | null {
|
||||
const creditsText = getGeminiCliCreditsTextFromQuota(quota)
|
||||
const modelWindows = getQuotaWindowsByScope(quota, 'model')
|
||||
const activeCoolingModels = modelWindows
|
||||
.filter((window) => {
|
||||
@@ -257,9 +334,10 @@ function getGeminiCliQuotaText(quota: QuotaStatusSnapshot): string | null {
|
||||
const remainingList = modelWindows
|
||||
.map(getQuotaWindowRemainingPercent)
|
||||
.filter((value): value is number => value != null)
|
||||
if (remainingList.length === 0) return normalizeText(quota.label)
|
||||
if (remainingList.length === 0) return creditsText || normalizeText(quota.label)
|
||||
|
||||
const minimumRemaining = Math.min(...remainingList)
|
||||
if (creditsText) return creditsText
|
||||
if (remainingList.length === 1) {
|
||||
return `剩余 ${formatPercent(minimumRemaining)}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user