From fcc381dee84f2f5817154223f5169e50096bc6ef Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Thu, 17 Jul 2025 14:20:11 -0700 Subject: [PATCH] feat: Start rendering thread errors (#445) * feat: Start rendering thread errors * cr --- apps/web/src/components/ui/alert.tsx | 14 ++++++++++-- .../src/components/v2/actions-renderer.tsx | 22 +++++++++++++++++++ apps/web/src/components/v2/manager-chat.tsx | 20 ++++++++++++++++- apps/web/src/components/v2/thread-view.tsx | 14 ++++++++++++ 4 files changed, 67 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/ui/alert.tsx b/apps/web/src/components/ui/alert.tsx index aa7de24d..8bdbbe3f 100644 --- a/apps/web/src/components/ui/alert.tsx +++ b/apps/web/src/components/ui/alert.tsx @@ -8,9 +8,14 @@ const alertVariants = cva( { variants: { variant: { - default: "bg-card text-card-foreground", + default: "bg-card text-card-foreground border-border", destructive: - "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", + "border-destructive/50 bg-destructive/10 text-destructive dark:border-red-400 dark:bg-red-950/50 dark:text-red-300 [&>svg]:text-destructive dark:[&>svg]:text-red-400", + warning: + "border-yellow-500/50 bg-yellow-50 text-yellow-900 dark:border-yellow-500 dark:bg-yellow-950 dark:text-yellow-100 [&>svg]:text-yellow-600 dark:[&>svg]:text-yellow-400", + info: "border-blue-500/50 bg-blue-50 text-blue-900 dark:border-blue-500 dark:bg-blue-950 dark:text-blue-100 [&>svg]:text-blue-600 dark:[&>svg]:text-blue-400", + success: + "border-green-500/50 bg-green-50 text-green-900 dark:border-green-500 dark:bg-green-950 dark:text-green-100 [&>svg]:text-green-600 dark:[&>svg]:text-green-400", }, }, defaultVariants: { @@ -27,6 +32,7 @@ function Alert({ return (
({ ); const joinedRunId = useRef(undefined); const [streamLoading, setStreamLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(""); const stream = useStream({ apiUrl: process.env.NEXT_PUBLIC_API_URL, @@ -130,6 +133,18 @@ export function ActionsRenderer({ fetchStateHistory: false, }); + useEffect(() => { + if (stream.error) { + const errorMessage = + typeof stream.error === "object" && "message" in stream.error + ? (stream.error.message as string) + : "An unknown error occurred in the manager"; + setErrorMessage(errorMessage); + } else { + setErrorMessage(""); + } + }, [stream.error]); + const { cancelRun } = useCancelStream({ stream, threadId, @@ -304,6 +319,13 @@ export function ActionsRenderer({ thread={stream as UseStream>} /> ) : null} + {errorMessage ? ( + + + An error occurred: + {errorMessage} + + ) : null}
); } diff --git a/apps/web/src/components/v2/manager-chat.tsx b/apps/web/src/components/v2/manager-chat.tsx index 0c3fb6a9..45950b1e 100644 --- a/apps/web/src/components/v2/manager-chat.tsx +++ b/apps/web/src/components/v2/manager-chat.tsx @@ -5,12 +5,21 @@ import { useState } from "react"; import { StickToBottom } from "use-stick-to-bottom"; import { TooltipIconButton } from "../ui/tooltip-icon-button"; import { AnimatePresence, motion } from "framer-motion"; -import { Bot, Copy, CopyCheck, Send, User, Loader2 } from "lucide-react"; +import { + Bot, + Copy, + CopyCheck, + Send, + User, + Loader2, + AlertCircle, +} from "lucide-react"; import { Textarea } from "../ui/textarea"; import { Button } from "../ui/button"; import { cn } from "@/lib/utils"; import { isAIMessageSDK } from "@/lib/langchain-messages"; import { BasicMarkdownText } from "../thread/markdown-text"; +import { Alert, AlertDescription, AlertTitle } from "../ui/alert"; function MessageCopyButton({ content }: { content: string }) { const [copied, setCopied] = useState(false); @@ -66,6 +75,7 @@ interface ManagerChatProps { handleSendMessage: () => void; isLoading: boolean; cancelRun: () => void; + errorMessage?: string; } function extractResponseFromMessage(message: Message): string { @@ -88,6 +98,7 @@ export function ManagerChat({ handleSendMessage, isLoading, cancelRun, + errorMessage, }: ManagerChatProps) { return (
@@ -136,6 +147,13 @@ export function ManagerChat({
); })} + {errorMessage ? ( + + + An error occurred: + {errorMessage} + + ) : null} } footer={ diff --git a/apps/web/src/components/v2/thread-view.tsx b/apps/web/src/components/v2/thread-view.tsx index 12f0f252..46ced88b 100644 --- a/apps/web/src/components/v2/thread-view.tsx +++ b/apps/web/src/components/v2/thread-view.tsx @@ -52,6 +52,7 @@ export function ThreadView({ useState(); const [programmerSession, setProgrammerSession] = useState(); + const [errorMessage, setErrorMessage] = useState(""); useEffect(() => { if ( @@ -69,6 +70,18 @@ export function ThreadView({ } }, [stream?.values]); + useEffect(() => { + if (stream.error) { + const errorMessage = + typeof stream.error === "object" && "message" in stream.error + ? (stream.error.message as string) + : "An unknown error occurred in the manager"; + setErrorMessage(errorMessage); + } else { + setErrorMessage(""); + } + }, [stream.error]); + const { status: realTimeStatus } = useThreadStatus(displayThread.id); const getStatusDotColor = (status: string) => { @@ -170,6 +183,7 @@ export function ThreadView({ handleSendMessage={handleSendMessage} isLoading={stream.isLoading} cancelRun={cancelRun} + errorMessage={errorMessage} /> {/* Right Side - Actions & Plan */}