mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-03 20:13:21 +00:00
feat: Start rendering thread errors (#445)
* feat: Start rendering thread errors * cr
This commit is contained in:
parent
f847e6b443
commit
fcc381dee8
4 changed files with 67 additions and 3 deletions
|
|
@ -8,9 +8,14 @@ const alertVariants = cva(
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default: "bg-card text-card-foreground",
|
default: "bg-card text-card-foreground border-border",
|
||||||
destructive:
|
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: {
|
defaultVariants: {
|
||||||
|
|
@ -27,6 +32,7 @@ function Alert({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-slot="alert"
|
data-slot="alert"
|
||||||
|
data-variant={variant}
|
||||||
role="alert"
|
role="alert"
|
||||||
className={cn(alertVariants({ variant }), className)}
|
className={cn(alertVariants({ variant }), className)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -56,6 +62,10 @@ function AlertDescription({
|
||||||
data-slot="alert-description"
|
data-slot="alert-description"
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
|
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
|
||||||
|
"data-[variant=destructive]:text-destructive/90 dark:data-[variant=destructive]:text-red-200",
|
||||||
|
"data-[variant=warning]:text-yellow-800 dark:data-[variant=warning]:text-yellow-200",
|
||||||
|
"data-[variant=info]:text-blue-800 dark:data-[variant=info]:text-blue-200",
|
||||||
|
"data-[variant=success]:text-green-800 dark:data-[variant=success]:text-green-200",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ import { GraphState, PlanItem } from "@open-swe/shared/open-swe/types";
|
||||||
import { HumanResponse } from "@langchain/langgraph/prebuilt";
|
import { HumanResponse } from "@langchain/langgraph/prebuilt";
|
||||||
import { LoadingActionsCardContent } from "./thread-view-loading";
|
import { LoadingActionsCardContent } from "./thread-view-loading";
|
||||||
import { Interrupt } from "../thread/messages/interrupt";
|
import { Interrupt } from "../thread/messages/interrupt";
|
||||||
|
import { AlertCircle } from "lucide-react";
|
||||||
|
import { Alert, AlertTitle, AlertDescription } from "../ui/alert";
|
||||||
|
|
||||||
interface AcceptedPlanEventData {
|
interface AcceptedPlanEventData {
|
||||||
planTitle: string;
|
planTitle: string;
|
||||||
|
|
@ -116,6 +118,7 @@ export function ActionsRenderer<State extends PlannerGraphState | GraphState>({
|
||||||
);
|
);
|
||||||
const joinedRunId = useRef<string | undefined>(undefined);
|
const joinedRunId = useRef<string | undefined>(undefined);
|
||||||
const [streamLoading, setStreamLoading] = useState(false);
|
const [streamLoading, setStreamLoading] = useState(false);
|
||||||
|
const [errorMessage, setErrorMessage] = useState("");
|
||||||
|
|
||||||
const stream = useStream<State>({
|
const stream = useStream<State>({
|
||||||
apiUrl: process.env.NEXT_PUBLIC_API_URL,
|
apiUrl: process.env.NEXT_PUBLIC_API_URL,
|
||||||
|
|
@ -130,6 +133,18 @@ export function ActionsRenderer<State extends PlannerGraphState | GraphState>({
|
||||||
fetchStateHistory: false,
|
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<State>({
|
const { cancelRun } = useCancelStream<State>({
|
||||||
stream,
|
stream,
|
||||||
threadId,
|
threadId,
|
||||||
|
|
@ -304,6 +319,13 @@ export function ActionsRenderer<State extends PlannerGraphState | GraphState>({
|
||||||
thread={stream as UseStream<Record<string, unknown>>}
|
thread={stream as UseStream<Record<string, unknown>>}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
{errorMessage ? (
|
||||||
|
<Alert variant="destructive">
|
||||||
|
<AlertCircle className="size-4" />
|
||||||
|
<AlertTitle>An error occurred:</AlertTitle>
|
||||||
|
<AlertDescription>{errorMessage}</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,21 @@ import { useState } from "react";
|
||||||
import { StickToBottom } from "use-stick-to-bottom";
|
import { StickToBottom } from "use-stick-to-bottom";
|
||||||
import { TooltipIconButton } from "../ui/tooltip-icon-button";
|
import { TooltipIconButton } from "../ui/tooltip-icon-button";
|
||||||
import { AnimatePresence, motion } from "framer-motion";
|
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 { Textarea } from "../ui/textarea";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { isAIMessageSDK } from "@/lib/langchain-messages";
|
import { isAIMessageSDK } from "@/lib/langchain-messages";
|
||||||
import { BasicMarkdownText } from "../thread/markdown-text";
|
import { BasicMarkdownText } from "../thread/markdown-text";
|
||||||
|
import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
|
||||||
|
|
||||||
function MessageCopyButton({ content }: { content: string }) {
|
function MessageCopyButton({ content }: { content: string }) {
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
@ -66,6 +75,7 @@ interface ManagerChatProps {
|
||||||
handleSendMessage: () => void;
|
handleSendMessage: () => void;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
cancelRun: () => void;
|
cancelRun: () => void;
|
||||||
|
errorMessage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractResponseFromMessage(message: Message): string {
|
function extractResponseFromMessage(message: Message): string {
|
||||||
|
|
@ -88,6 +98,7 @@ export function ManagerChat({
|
||||||
handleSendMessage,
|
handleSendMessage,
|
||||||
isLoading,
|
isLoading,
|
||||||
cancelRun,
|
cancelRun,
|
||||||
|
errorMessage,
|
||||||
}: ManagerChatProps) {
|
}: ManagerChatProps) {
|
||||||
return (
|
return (
|
||||||
<div className="border-border bg-muted/30 flex h-full w-1/3 flex-col border-r dark:bg-gray-950">
|
<div className="border-border bg-muted/30 flex h-full w-1/3 flex-col border-r dark:bg-gray-950">
|
||||||
|
|
@ -136,6 +147,13 @@ export function ManagerChat({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{errorMessage ? (
|
||||||
|
<Alert variant="destructive">
|
||||||
|
<AlertCircle className="size-4" />
|
||||||
|
<AlertTitle>An error occurred:</AlertTitle>
|
||||||
|
<AlertDescription>{errorMessage}</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
) : null}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
footer={
|
footer={
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ export function ThreadView({
|
||||||
useState<ManagerGraphState["plannerSession"]>();
|
useState<ManagerGraphState["plannerSession"]>();
|
||||||
const [programmerSession, setProgrammerSession] =
|
const [programmerSession, setProgrammerSession] =
|
||||||
useState<ManagerGraphState["programmerSession"]>();
|
useState<ManagerGraphState["programmerSession"]>();
|
||||||
|
const [errorMessage, setErrorMessage] = useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
|
|
@ -69,6 +70,18 @@ export function ThreadView({
|
||||||
}
|
}
|
||||||
}, [stream?.values]);
|
}, [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 { status: realTimeStatus } = useThreadStatus(displayThread.id);
|
||||||
|
|
||||||
const getStatusDotColor = (status: string) => {
|
const getStatusDotColor = (status: string) => {
|
||||||
|
|
@ -170,6 +183,7 @@ export function ThreadView({
|
||||||
handleSendMessage={handleSendMessage}
|
handleSendMessage={handleSendMessage}
|
||||||
isLoading={stream.isLoading}
|
isLoading={stream.isLoading}
|
||||||
cancelRun={cancelRun}
|
cancelRun={cancelRun}
|
||||||
|
errorMessage={errorMessage}
|
||||||
/>
|
/>
|
||||||
{/* Right Side - Actions & Plan */}
|
{/* Right Side - Actions & Plan */}
|
||||||
<div className="flex h-full flex-1 flex-col">
|
<div className="flex h-full flex-1 flex-col">
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue