From 446c77c7dc0a8b384686d1ca5404151cf7887cf7 Mon Sep 17 00:00:00 2001 From: Abdulmuiz Adeyemo Date: Sun, 5 Apr 2026 17:16:57 -0700 Subject: [PATCH] Stabilize credit charging and handle chat save failures gracefully --- app/actions/generate.ts | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/app/actions/generate.ts b/app/actions/generate.ts index 2d0ed16..d7e9b75 100644 --- a/app/actions/generate.ts +++ b/app/actions/generate.ts @@ -142,22 +142,11 @@ export async function generateAnswer({ prompt, tool, authorId, authorEmail, atta // Generate the AI response const generationResult = await generateTeacherResponse({ prompt, tool, attachments, history, userId: authorId, enableWebSearch, researchMode }) const answer = generationResult.text - const tokenCost = Math.max(1, generationResult.usage.totalTokens || 0) - - // Enforce token credit limit before returning content if request exceeded remaining balance - if (tokenCost > creditsRemaining) { - const cap = getPlanCreditCap(userProfile.subscriptionPlan) - const resetLabel = resetDate - ? new Date(resetDate).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) - : 'in 30 days' - const errorMessage = `You've reached your monthly credit cap (${cap}). Upgrade your plan now, or wait until your credits reset on ${resetLabel}.` - return { - answer: errorMessage, - sessionId: sessionId, - chatId: chatId, - error: errorMessage - } - } + const rawTokenCost = Number(generationResult.usage.totalTokens ?? 0) + const tokenCost = Number.isFinite(rawTokenCost) + ? Math.max(1, Math.min(Math.round(rawTokenCost), 2_147_483_647)) + : 1 + const creditsToCharge = Math.max(1, Math.min(tokenCost, creditsRemaining)) const currentSessionId = sessionId || crypto.randomUUID() @@ -195,7 +184,14 @@ export async function generateAnswer({ prompt, tool, authorId, authorEmail, atta .eq('user_id', authorId) if (error) { - throw error + console.error('[chat_update_failed]', { userId: authorId, chatId, error }) + const errorMessage = 'Your response was generated, but we could not save this chat. Please try again.' + return { + answer: errorMessage, + sessionId: sessionId, + chatId: chatId, + error: errorMessage + } } } else { // Insert new row @@ -214,7 +210,14 @@ export async function generateAnswer({ prompt, tool, authorId, authorEmail, atta .single() if (error) { - throw error + console.error('[chat_insert_failed]', { userId: authorId, sessionId: currentSessionId, error }) + const errorMessage = 'Your response was generated, but we could not save this chat. Please try again.' + return { + answer: errorMessage, + sessionId: currentSessionId, + chatId: chatId, + error: errorMessage + } } if (data?.id) { savedChatId = data.id @@ -234,7 +237,7 @@ export async function generateAnswer({ prompt, tool, authorId, authorEmail, atta let usageAccountingSucceeded = false for (let attempt = 1; attempt <= maxAccountingAttempts; attempt += 1) { - usageAccountingSucceeded = await incrementUserCredits(authorId, tokenCost) + usageAccountingSucceeded = await incrementUserCredits(authorId, creditsToCharge) if (usageAccountingSucceeded) { break }