From 039770e89fd2bfc72cc9a7b16854cf6ceb34f6c1 Mon Sep 17 00:00:00 2001 From: Tony Giorgio Date: Mon, 13 Oct 2025 19:05:01 -0500 Subject: [PATCH 1/2] Add context limit dialog for 413 errors Handle backend's new 413 error (Message exceeds context limit) with a user-friendly dialog. The dialog provides contextual tips based on whether a document was uploaded and whether the user is using Gemma (smaller context window). User's message and attachments are preserved in the input when the error occurs. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- frontend/src/components/ComparisonChart.tsx | 8 +- .../src/components/ContextLimitDialog.tsx | 80 +++++++++++++++++++ frontend/src/components/UnifiedChat.tsx | 47 ++++++++++- 3 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 frontend/src/components/ContextLimitDialog.tsx diff --git a/frontend/src/components/ComparisonChart.tsx b/frontend/src/components/ComparisonChart.tsx index e9176c924..d13187aa9 100644 --- a/frontend/src/components/ComparisonChart.tsx +++ b/frontend/src/components/ComparisonChart.tsx @@ -169,13 +169,11 @@ export function ComparisonChart() { {/* Header Row */}
-
- -
+
{products.map((product) => (
diff --git a/frontend/src/components/ContextLimitDialog.tsx b/frontend/src/components/ContextLimitDialog.tsx new file mode 100644 index 000000000..3ed7b32ae --- /dev/null +++ b/frontend/src/components/ContextLimitDialog.tsx @@ -0,0 +1,80 @@ +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { AlertCircle, MessageCircle } from "lucide-react"; + +interface ContextLimitDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + currentModel?: string; + hasDocument?: boolean; +} + +export function ContextLimitDialog({ + open, + onOpenChange, + currentModel, + hasDocument +}: ContextLimitDialogProps) { + const isGemma = currentModel?.includes("gemma"); + + return ( + + + +
+
+ +
+ Message Too Large +
+ + Your message exceeds the context limit for the current model. + +
+ +
+
+

Here's what you can try:

+
    +
  • + + + Shorten your message - Try reducing the amount of text or content + +
  • + {hasDocument && ( +
  • + + + Use a smaller document - Try uploading a shorter document or + extracting only the relevant sections + +
  • + )} + {isGemma && ( +
  • + + + Switch to a model with more context - Try DeepSeek R1, Mistral, + or other models that support 128k tokens + +
  • + )} +
+
+
+ + + + +
+
+ ); +} diff --git a/frontend/src/components/UnifiedChat.tsx b/frontend/src/components/UnifiedChat.tsx index fb7714e11..104ea7d83 100644 --- a/frontend/src/components/UnifiedChat.tsx +++ b/frontend/src/components/UnifiedChat.tsx @@ -50,6 +50,7 @@ import { useLocalState } from "@/state/useLocalState"; import { useOpenSecret } from "@opensecret/react"; import { UpgradePromptDialog } from "@/components/UpgradePromptDialog"; import { DocumentPlatformDialog } from "@/components/DocumentPlatformDialog"; +import { ContextLimitDialog } from "@/components/ContextLimitDialog"; import { RecordingOverlay } from "@/components/RecordingOverlay"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { AlertCircle } from "lucide-react"; @@ -351,6 +352,7 @@ export function UnifiedChat() { "image" | "document" | "voice" | "usage" | "tokens" >("image"); const [documentPlatformDialogOpen, setDocumentPlatformDialogOpen] = useState(false); + const [contextLimitDialogOpen, setContextLimitDialogOpen] = useState(false); // Audio recording states const [isRecording, setIsRecording] = useState(false); @@ -1525,16 +1527,49 @@ export function UnifiedChat() { console.error("Failed to send message:", error); // Handle usage limit errors with upsell dialogs - // The SDK throws errors with the message "Request failed with status 403: {json}" + // The SDK throws errors with the message "Request failed with status 403: {json}" or "Request failed with status 413: {json}" // We need to parse this to extract the actual error details let errorMessage = error instanceof Error ? error.message : "Something went wrong"; // Also check the cause property if it exists const causeMessage = (error as Error & { cause?: { message?: string } })?.cause?.message; - if (causeMessage && causeMessage.includes("Request failed with status 403:")) { + if (causeMessage && causeMessage.includes("Request failed with status")) { errorMessage = causeMessage; } + // Check for 413 error (Message exceeds context limit) + let status413Error: { status: number; message: string } | null = null; + if (errorMessage.includes("Request failed with status 413:")) { + try { + // Extract the JSON part from the error message + const jsonMatch = errorMessage.match(/Request failed with status 413:\s*({.*})/); + if (jsonMatch && jsonMatch[1]) { + status413Error = JSON.parse(jsonMatch[1]); + } + } catch (parseError) { + console.error("Failed to parse 413 error:", parseError); + } + } + + if (status413Error && status413Error.message === "Message exceeds context limit") { + // Remove the user message from history and restore input + setMessages((prev) => prev.filter((msg) => msg.id !== localMessageId)); + + // Restore the original input and attachments + if (!overrideInput) { + setInput(originalInput); + setDraftImages(originalImages); + setImageUrls(originalImageUrls); + setDocumentText(originalDocumentText); + setDocumentName(originalDocumentName); + } + + // Show the context limit dialog + setContextLimitDialogOpen(true); + setError("Your message exceeds the context limit for this model."); + return; // Exit early, don't continue to other error handling + } + let status403Error: { status: number; message: string } | null = null; // Check if this is a 403 error from the SDK @@ -2235,6 +2270,14 @@ export function UnifiedChat() { hasProAccess={canUseDocuments || false} /> + {/* Context limit dialog for 413 errors */} + + {/* Hidden file inputs - must be outside conditional rendering to work in both views */} Date: Mon, 13 Oct 2025 20:02:37 -0500 Subject: [PATCH 2/2] Fix image preview bug after 413/403 error restoration Re-create object URLs when restoring attachments after errors, since the original URLs were revoked by clearAllAttachments(). This fixes the issue where image thumbnails would fail to render after a 413 or 403 error. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- frontend/src/components/UnifiedChat.tsx | 29 ++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/UnifiedChat.tsx b/frontend/src/components/UnifiedChat.tsx index 104ea7d83..fd174bae9 100644 --- a/frontend/src/components/UnifiedChat.tsx +++ b/frontend/src/components/UnifiedChat.tsx @@ -1469,7 +1469,6 @@ export function UnifiedChat() { // Store the original input and attachments in case we need to restore them const originalInput = trimmedInput; const originalImages = [...draftImages]; - const originalImageUrls = new Map(imageUrls); const originalDocumentText = documentText; const originalDocumentName = documentName; @@ -1559,7 +1558,12 @@ export function UnifiedChat() { if (!overrideInput) { setInput(originalInput); setDraftImages(originalImages); - setImageUrls(originalImageUrls); + // Re-create object URLs since originals were revoked by clearAllAttachments() + const restoredUrlMap = new Map(); + for (const file of originalImages) { + restoredUrlMap.set(file, URL.createObjectURL(file)); + } + setImageUrls(restoredUrlMap); setDocumentText(originalDocumentText); setDocumentName(originalDocumentName); } @@ -1593,7 +1597,12 @@ export function UnifiedChat() { if (!overrideInput) { setInput(originalInput); setDraftImages(originalImages); - setImageUrls(originalImageUrls); + // Re-create object URLs since originals were revoked by clearAllAttachments() + const restoredUrlMap = new Map(); + for (const file of originalImages) { + restoredUrlMap.set(file, URL.createObjectURL(file)); + } + setImageUrls(restoredUrlMap); setDocumentText(originalDocumentText); setDocumentName(originalDocumentName); } @@ -1692,7 +1701,12 @@ export function UnifiedChat() { if (!overrideInput) { setInput(originalInput); setDraftImages(originalImages); - setImageUrls(originalImageUrls); + // Re-create object URLs since originals were revoked by clearAllAttachments() + const restoredUrlMap = new Map(); + for (const file of originalImages) { + restoredUrlMap.set(file, URL.createObjectURL(file)); + } + setImageUrls(restoredUrlMap); setDocumentText(originalDocumentText); setDocumentName(originalDocumentName); } @@ -1713,7 +1727,12 @@ export function UnifiedChat() { if (!overrideInput) { setInput(originalInput); setDraftImages(originalImages); - setImageUrls(originalImageUrls); + // Re-create object URLs since originals were revoked by clearAllAttachments() + const restoredUrlMap = new Map(); + for (const file of originalImages) { + restoredUrlMap.set(file, URL.createObjectURL(file)); + } + setImageUrls(restoredUrlMap); setDocumentText(originalDocumentText); setDocumentName(originalDocumentName); }