diff --git a/packages/ai/src/usage/xai-oauth.ts b/packages/ai/src/usage/xai-oauth.ts index 63bb8006f..161533d1f 100644 --- a/packages/ai/src/usage/xai-oauth.ts +++ b/packages/ai/src/usage/xai-oauth.ts @@ -202,6 +202,10 @@ function parseMonthlyBillingConfig(raw: Record): XaiMonthlyBill onDemandUsed: parseOnDemandAmount(raw.onDemandUsed), }; } +function hasPositiveMonthlyLimit(raw: Record): boolean { + const limit = parseOnDemandAmount(raw.monthlyLimit); + return limit !== undefined && limit > 0; +} function buildOnDemandLimit( onDemandCap: number | undefined, @@ -384,8 +388,14 @@ export const xaiOauthUsageProvider: UsageProvider = { if (weekly?.inferredPercent && creditsLooksUnified) { if (monthly) { effectiveWeekly = null; - } else if (!monthlyPayload || !isRecord(monthlyPayload) || !isRecord(monthlyPayload.config)) { - effectiveWeekly = null; + } else { + const monthlyConfig = + monthlyPayload && isRecord(monthlyPayload) && isRecord(monthlyPayload.config) + ? monthlyPayload.config + : null; + if (!monthlyConfig || hasPositiveMonthlyLimit(monthlyConfig)) { + effectiveWeekly = null; + } } } if (!effectiveWeekly && !monthly) return null; diff --git a/packages/ai/test/xai-oauth-usage.test.ts b/packages/ai/test/xai-oauth-usage.test.ts index 30cc1539b..09674d4ff 100644 --- a/packages/ai/test/xai-oauth-usage.test.ts +++ b/packages/ai/test/xai-oauth-usage.test.ts @@ -305,6 +305,36 @@ describe("xai-oauth usage provider", () => { expect(report).toBeNull(); }); + it("rejects inferred unified weekly usage when monthly config has a positive limit but malformed fields", async () => { + const periodEnd = new Date(Date.now() + 6 * 24 * 60 * 60 * 1000).toISOString(); + const periodStart = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(); + const report = await xaiOauthUsageProvider.fetchUsage( + { provider: "xai-oauth", credential: makeCredential() }, + { + fetch: dualBillingFetch( + { + config: { + currentPeriod: { + end: periodEnd, + start: periodStart, + type: "USAGE_PERIOD_TYPE_WEEKLY", + }, + isUnifiedBillingUser: true, + }, + }, + { + config: { + isUnifiedBillingUser: true, + monthlyLimit: { val: 15000 }, + // Missing 'used' and billingPeriod dates + }, + }, + ).fetch, + }, + ); + + expect(report).toBeNull(); + }); it("falls back to monthly included quota when credits has no percent fields", async () => { const { fetch, calls } = dualBillingFetch(makeUnifiedCreditsPayload(), makeUnifiedMonthlyPayload());