mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-10-16 14:57:05 +08:00
fix(quota): clarify unavailable xAI usage (#441)
The xAI billing response can provide a valid weekly reset without a usage percentage. Preserve that unknown state instead of presenting a blank or fabricated zero, and hide the legacy zero-value monthly row only when a weekly window is present. Refs: vraj-ai/fleet#63 Generated with [Devin](https://devin.ai) Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -133,9 +133,10 @@ export function XaiQuotaBody({ quota, classes }: QuotaBodyProps<XaiQuotaState>)
|
||||
billing.periodType === 'weekly' &&
|
||||
(weeklyUsed !== null || Boolean(billing.periodEnd) || billing.productUsage.length > 0);
|
||||
const hasMonthlyData =
|
||||
billing.monthlyLimitCents !== null ||
|
||||
billing.usedCents !== null ||
|
||||
Boolean(billing.billingPeriodEnd);
|
||||
(billing.monthlyLimitCents !== null ||
|
||||
billing.usedCents !== null ||
|
||||
Boolean(billing.billingPeriodEnd)) &&
|
||||
!(hasWeeklyData && billing.monthlyLimitCents === 0 && billing.usedCents === 0);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -156,16 +157,18 @@ export function XaiQuotaBody({ quota, classes }: QuotaBodyProps<XaiQuotaState>)
|
||||
<span className={classes.quotaModel}>{t('xai_quota.weekly_limit')}</span>
|
||||
<div className={classes.quotaMeta}>
|
||||
<span className={classes.quotaPercent}>
|
||||
{t('xai_quota.used_percent', {
|
||||
percent: formatXaiPercent(weeklyUsed),
|
||||
})}
|
||||
{weeklyUsed === null
|
||||
? t('xai_quota.usage_unavailable')
|
||||
: t('xai_quota.used_percent', { percent: formatXaiPercent(weeklyUsed) })}
|
||||
</span>
|
||||
{weeklyResetDisplay && (
|
||||
<QuotaResetLabel display={weeklyResetDisplay} classes={classes} soon={weeklySoon} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<QuotaMeter percent={weeklyRemaining} classes={classes} index={0} />
|
||||
{weeklyRemaining !== null && (
|
||||
<QuotaMeter percent={weeklyRemaining} classes={classes} index={0} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{billing.productUsage.map((item, index) => {
|
||||
|
||||
@@ -581,6 +581,7 @@
|
||||
"weekly_limit": "Weekly limit",
|
||||
"monthly_credits": "Monthly credits",
|
||||
"used_percent": "Used {{percent}}",
|
||||
"usage_unavailable": "Usage unavailable from xAI",
|
||||
"reset_at": "Resets {{time}}",
|
||||
"product_usage": "{{product}} usage",
|
||||
"plan_label": "Plan",
|
||||
|
||||
@@ -584,6 +584,7 @@
|
||||
"weekly_limit": "Недельный лимит",
|
||||
"monthly_credits": "Месячные кредиты",
|
||||
"used_percent": "Использовано {{percent}}",
|
||||
"usage_unavailable": "Данные об использовании недоступны от xAI",
|
||||
"reset_at": "Сброс {{time}}",
|
||||
"product_usage": "Использование {{product}}",
|
||||
"plan_label": "План",
|
||||
|
||||
@@ -579,6 +579,7 @@
|
||||
"weekly_limit": "周限额",
|
||||
"monthly_credits": "月度积分",
|
||||
"used_percent": "已用 {{percent}}",
|
||||
"usage_unavailable": "xAI 用量暂不可用",
|
||||
"reset_at": "重置 {{time}}",
|
||||
"product_usage": "{{product}} 使用",
|
||||
"plan_label": "套餐",
|
||||
|
||||
@@ -579,6 +579,7 @@
|
||||
"weekly_limit": "週限額",
|
||||
"monthly_credits": "月度點數",
|
||||
"used_percent": "已用 {{percent}}",
|
||||
"usage_unavailable": "xAI 用量暫不可用",
|
||||
"reset_at": "重置 {{time}}",
|
||||
"product_usage": "{{product}} 使用",
|
||||
"plan_label": "方案",
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* xAI quota body: unknown usage must not masquerade as a zero.
|
||||
*
|
||||
* When the weekly endpoint omits creditUsagePercent the summary keeps
|
||||
* usagePercent null; the body should say the usage is unavailable and hide
|
||||
* the meter rather than render a fabricated "Used 0%". The legacy
|
||||
* zero-limit/zero-used monthly row is hidden only while weekly data exists.
|
||||
*/
|
||||
|
||||
import { beforeAll, describe, expect, test } from 'bun:test';
|
||||
import { createElement } from 'react';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import i18n from '@/i18n';
|
||||
import { XaiQuotaBody } from '@/features/quota/providers/xai/XaiQuotaBody';
|
||||
import { QUOTA_CLASS_KEYS, bindQuotaClasses } from '@/features/quota/types';
|
||||
import {
|
||||
buildXaiBillingSummary,
|
||||
formatQuotaResetTime,
|
||||
mergeXaiBillingSummaries,
|
||||
} from '@/utils/quota';
|
||||
import type { XaiBillingConfig, XaiQuotaState } from '@/types';
|
||||
|
||||
const classes = bindQuotaClasses(
|
||||
Object.fromEntries(QUOTA_CLASS_KEYS.map((key) => [key, key])),
|
||||
'test-host'
|
||||
);
|
||||
|
||||
const WEEKLY_PERIOD_START = '2026-09-17T13:32:42.093205+00:00';
|
||||
const WEEKLY_PERIOD_END = '2026-09-24T13:32:42.093205+00:00';
|
||||
|
||||
const weeklyConfig = (extra: XaiBillingConfig = {}): XaiBillingConfig => ({
|
||||
currentPeriod: {
|
||||
type: 'USAGE_PERIOD_TYPE_WEEKLY',
|
||||
start: WEEKLY_PERIOD_START,
|
||||
end: WEEKLY_PERIOD_END,
|
||||
},
|
||||
billingPeriodStart: WEEKLY_PERIOD_START,
|
||||
billingPeriodEnd: WEEKLY_PERIOD_END,
|
||||
onDemandCap: { val: 0 },
|
||||
...extra,
|
||||
});
|
||||
|
||||
const monthlyConfig = (extra: XaiBillingConfig = {}): XaiBillingConfig => ({
|
||||
monthlyLimit: { val: 0 },
|
||||
used: { val: 0 },
|
||||
onDemandCap: { val: 0 },
|
||||
billingPeriodStart: '2026-09-01T00:00:00+00:00',
|
||||
billingPeriodEnd: '2026-10-01T00:00:00+00:00',
|
||||
...extra,
|
||||
});
|
||||
|
||||
const quotaFor = (
|
||||
weekly: XaiBillingConfig | null,
|
||||
monthly: XaiBillingConfig | null
|
||||
): XaiQuotaState => ({
|
||||
status: 'success',
|
||||
billing: mergeXaiBillingSummaries(
|
||||
buildXaiBillingSummary(weekly),
|
||||
buildXaiBillingSummary(monthly)
|
||||
),
|
||||
});
|
||||
|
||||
const render = (quota: XaiQuotaState): string =>
|
||||
renderToStaticMarkup(createElement(XaiQuotaBody, { quota, classes }));
|
||||
|
||||
beforeAll(async () => {
|
||||
await i18n.changeLanguage('en');
|
||||
});
|
||||
|
||||
describe('XaiQuotaBody unavailable weekly usage', () => {
|
||||
test('reports unavailable instead of a fabricated zero when usagePercent is missing', () => {
|
||||
const quota = quotaFor(weeklyConfig(), monthlyConfig());
|
||||
expect(quota.billing?.usagePercent).toBeNull();
|
||||
|
||||
const markup = render(quota);
|
||||
|
||||
expect(markup).toContain('Usage unavailable from xAI');
|
||||
expect(markup).toContain(formatQuotaResetTime(WEEKLY_PERIOD_END));
|
||||
expect(markup).not.toContain('Used --');
|
||||
expect(markup).not.toContain('Used 0%');
|
||||
// Zero-budget zero-used monthly row is hidden while weekly data exists.
|
||||
expect(markup).not.toContain('Monthly credits');
|
||||
expect(markup).not.toContain('$0.00 / $0.00');
|
||||
});
|
||||
|
||||
test('keeps usagePercent null for a malformed percentage string', () => {
|
||||
const quota = quotaFor(weeklyConfig({ creditUsagePercent: 'not-a-number' }), monthlyConfig());
|
||||
expect(quota.billing?.usagePercent).toBeNull();
|
||||
|
||||
const markup = render(quota);
|
||||
|
||||
expect(markup).toContain('Usage unavailable from xAI');
|
||||
expect(markup).not.toContain('Used 0%');
|
||||
});
|
||||
|
||||
test('renders an explicit zero percent as Used 0%', () => {
|
||||
const markup = render(quotaFor(weeklyConfig({ creditUsagePercent: 0 }), monthlyConfig()));
|
||||
|
||||
expect(markup).toContain('Used 0%');
|
||||
expect(markup).not.toContain('Usage unavailable from xAI');
|
||||
});
|
||||
|
||||
test('renders an explicit percent as Used 37%', () => {
|
||||
const markup = render(quotaFor(weeklyConfig({ creditUsagePercent: 37 }), monthlyConfig()));
|
||||
|
||||
expect(markup).toContain('Used 37%');
|
||||
expect(markup).not.toContain('Usage unavailable from xAI');
|
||||
});
|
||||
|
||||
test('shows the monthly row for a nonzero limit', () => {
|
||||
const markup = render(
|
||||
quotaFor(weeklyConfig(), monthlyConfig({ monthlyLimit: { val: 15000 } }))
|
||||
);
|
||||
|
||||
expect(markup).toContain('Monthly credits');
|
||||
});
|
||||
|
||||
test('shows the monthly row when the limit is zero but usage is nonzero', () => {
|
||||
const markup = render(quotaFor(weeklyConfig(), monthlyConfig({ used: { val: 100 } })));
|
||||
|
||||
expect(markup).toContain('Monthly credits');
|
||||
});
|
||||
|
||||
test('keeps the monthly-only zero row when no weekly data exists', () => {
|
||||
const markup = render(quotaFor(null, monthlyConfig()));
|
||||
|
||||
expect(markup).toContain('Monthly credits');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user