From b2dfd5b890d4ddb527584415d0cdf74c9bd849eb Mon Sep 17 00:00:00 2001 From: Dayuan Jiang <34411969+DayuanJiang@users.noreply.github.com> Date: Tue, 23 Dec 2025 21:08:21 +0900 Subject: [PATCH] fix: display correct quota values in limit toast (#383) - Parse JSON error response from server to get actual used/limit values - Previously showed 0/0 due to race condition (config fetch vs error) - AI SDK puts full response body in error.message for non-OK responses - Updated all quota toasts (request, token, TPM) to use server values --- components/chat-panel.tsx | 22 +++++++++++++- lib/use-quota-manager.tsx | 63 ++++++++++++++++++++++----------------- 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/components/chat-panel.tsx b/components/chat-panel.tsx index 744fa3b..6bcfb77 100644 --- a/components/chat-panel.tsx +++ b/components/chat-panel.tsx @@ -557,12 +557,32 @@ Continue from EXACTLY where you stopped.`, }, onError: (error) => { // Handle server-side quota limit (429 response) + // AI SDK puts the full response body in error.message for non-OK responses + try { + const data = JSON.parse(error.message) + if (data.type === "request") { + quotaManager.showQuotaLimitToast(data.used, data.limit) + return + } + if (data.type === "token") { + quotaManager.showTokenLimitToast(data.used, data.limit) + return + } + if (data.type === "tpm") { + quotaManager.showTPMLimitToast(data.limit) + return + } + } catch { + // Not JSON, fall through to string matching for backwards compatibility + } + + // Fallback to string matching if (error.message.includes("Daily request limit")) { quotaManager.showQuotaLimitToast() return } if (error.message.includes("Daily token limit")) { - quotaManager.showTokenLimitToast(dailyTokenLimit) + quotaManager.showTokenLimitToast() return } if ( diff --git a/lib/use-quota-manager.tsx b/lib/use-quota-manager.tsx index bda083b..33a7527 100644 --- a/lib/use-quota-manager.tsx +++ b/lib/use-quota-manager.tsx @@ -18,36 +18,39 @@ export interface QuotaConfig { * This hook only provides UI feedback when limits are exceeded. */ export function useQuotaManager(config: QuotaConfig): { - showQuotaLimitToast: () => void - showTokenLimitToast: (used: number) => void - showTPMLimitToast: () => void + showQuotaLimitToast: (used?: number, limit?: number) => void + showTokenLimitToast: (used?: number, limit?: number) => void + showTPMLimitToast: (limit?: number) => void } { const { dailyRequestLimit, dailyTokenLimit, tpmLimit } = config const dict = useDictionary() // Show quota limit toast (request-based) - const showQuotaLimitToast = useCallback(() => { - toast.custom( - (t) => ( - toast.dismiss(t)} - /> - ), - { duration: 15000 }, - ) - }, [dailyRequestLimit]) + const showQuotaLimitToast = useCallback( + (used?: number, limit?: number) => { + toast.custom( + (t) => ( + toast.dismiss(t)} + /> + ), + { duration: 15000 }, + ) + }, + [dailyRequestLimit], + ) // Show token limit toast const showTokenLimitToast = useCallback( - (used: number) => { + (used?: number, limit?: number) => { toast.custom( (t) => ( toast.dismiss(t)} /> ), @@ -58,15 +61,21 @@ export function useQuotaManager(config: QuotaConfig): { ) // Show TPM limit toast - const showTPMLimitToast = useCallback(() => { - const limitDisplay = - tpmLimit >= 1000 ? `${tpmLimit / 1000}k` : String(tpmLimit) - const message = formatMessage(dict.quota.tpmMessageDetailed, { - limit: limitDisplay, - seconds: 60, - }) - toast.error(message, { duration: 8000 }) - }, [tpmLimit, dict]) + const showTPMLimitToast = useCallback( + (limit?: number) => { + const effectiveLimit = limit ?? tpmLimit + const limitDisplay = + effectiveLimit >= 1000 + ? `${effectiveLimit / 1000}k` + : String(effectiveLimit) + const message = formatMessage(dict.quota.tpmMessageDetailed, { + limit: limitDisplay, + seconds: 60, + }) + toast.error(message, { duration: 8000 }) + }, + [tpmLimit, dict], + ) return { showQuotaLimitToast,