From fa9c26aa31d6461c6f8459c91b5a79438c95692a Mon Sep 17 00:00:00 2001 From: bracesproul Date: Tue, 11 Mar 2025 10:21:21 -0700 Subject: [PATCH] feat: Error if graph can not connect --- README.md | 2 + .../components/inbox-item-input.tsx | 2 +- .../components/thread-actions-view.tsx | 2 +- .../hooks/use-interrupted-actions.tsx | 12 ++--- src/providers/Stream.tsx | 47 ++++++++++++++++++- 5 files changed, 56 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 604699b..aa78848 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,9 @@ First, clone the repository, or run the [`npx` command](https://www.npmjs.com/pa ```bash npx create-agent-chat-app ``` + or + ```bash git clone https://github.com/langchain-ai/agent-chat-ui.git diff --git a/src/components/thread/agent-inbox/components/inbox-item-input.tsx b/src/components/thread/agent-inbox/components/inbox-item-input.tsx index 087d8df..7042933 100644 --- a/src/components/thread/agent-inbox/components/inbox-item-input.tsx +++ b/src/components/thread/agent-inbox/components/inbox-item-input.tsx @@ -337,7 +337,7 @@ export function InboxItemInput({ (Array.isArray(change) && !Array.isArray(key)) || (!Array.isArray(change) && Array.isArray(key)) ) { - toast("Error", { + toast.error("Error", { description: "Something went wrong", richColors: true, closeButton: true, diff --git a/src/components/thread/agent-inbox/components/thread-actions-view.tsx b/src/components/thread/agent-inbox/components/thread-actions-view.tsx index 187773b..d79486c 100644 --- a/src/components/thread/agent-inbox/components/thread-actions-view.tsx +++ b/src/components/thread/agent-inbox/components/thread-actions-view.tsx @@ -85,7 +85,7 @@ export function ThreadActionsView({ const handleOpenInStudio = () => { if (!apiUrl) { - toast("Error", { + toast.error("Error", { description: "Please set the LangGraph deployment URL in settings.", duration: 5000, richColors: true, diff --git a/src/components/thread/agent-inbox/hooks/use-interrupted-actions.tsx b/src/components/thread/agent-inbox/hooks/use-interrupted-actions.tsx index 8555bd6..c6ce64c 100644 --- a/src/components/thread/agent-inbox/hooks/use-interrupted-actions.tsx +++ b/src/components/thread/agent-inbox/hooks/use-interrupted-actions.tsx @@ -110,7 +110,7 @@ export default function useInterruptedActions({ ) => { e.preventDefault(); if (!humanResponse) { - toast("Error", { + toast.error("Error", { description: "Please enter a response.", duration: 5000, richColors: true, @@ -159,7 +159,7 @@ export default function useInterruptedActions({ (r) => r.type === selectedSubmitType, ); if (!input) { - toast("Error", { + toast.error("Error", { description: "No response found.", richColors: true, closeButton: true, @@ -189,7 +189,7 @@ export default function useInterruptedActions({ console.error("Error sending human response", e); if ("message" in e && e.message.includes("Invalid assistant ID")) { - toast("Error: Invalid assistant ID", { + toast.error("Error: Invalid assistant ID", { description: "The provided assistant ID was not found in this graph. Please update the assistant ID in the settings and try again.", richColors: true, @@ -197,7 +197,7 @@ export default function useInterruptedActions({ duration: 5000, }); } else { - toast("Error", { + toast.error("Error", { description: "Failed to submit response.", richColors: true, closeButton: true, @@ -234,7 +234,7 @@ export default function useInterruptedActions({ const ignoreResponse = humanResponse.find((r) => r.type === "ignore"); if (!ignoreResponse) { - toast("Error", { + toast.error("Error", { description: "The selected thread does not support ignoring.", duration: 5000, }); @@ -284,7 +284,7 @@ export default function useInterruptedActions({ }); } catch (e) { console.error("Error marking thread as resolved", e); - toast("Error", { + toast.error("Error", { description: "Failed to mark thread as resolved.", richColors: true, closeButton: true, diff --git a/src/providers/Stream.tsx b/src/providers/Stream.tsx index 258dc8a..ce475a6 100644 --- a/src/providers/Stream.tsx +++ b/src/providers/Stream.tsx @@ -1,4 +1,10 @@ -import React, { createContext, useContext, ReactNode, useState } from "react"; +import React, { + createContext, + useContext, + ReactNode, + useState, + useEffect, +} from "react"; import { useStream } from "@langchain/langgraph-sdk/react"; import { type Message } from "@langchain/langgraph-sdk"; import { @@ -15,6 +21,7 @@ import { ArrowRight } from "lucide-react"; import { PasswordInput } from "@/components/ui/password-input"; import { getApiKey } from "@/lib/api-key"; import { useThreads } from "./Thread"; +import { toast } from "sonner"; export type StateType = { messages: Message[]; ui?: UIMessage[] }; @@ -36,6 +43,26 @@ async function sleep(ms = 4000) { return new Promise((resolve) => setTimeout(resolve, ms)); } +async function checkGraphStatus( + apiUrl: string, + apiKey: string | null, +): Promise { + try { + const res = await fetch(`${apiUrl}/ok`, { + ...(apiKey && { + headers: { + "X-Api-Key": apiKey, + }, + }), + }); + + return res.ok; + } catch (e) { + console.error(e); + return false; + } +} + const StreamSession = ({ children, apiKey, @@ -68,6 +95,24 @@ const StreamSession = ({ }, }); + useEffect(() => { + checkGraphStatus(apiUrl, apiKey).then((ok) => { + if (!ok) { + toast.error("Failed to connect to LangGraph server", { + description: () => ( +

+ Please ensure your graph is running at {apiUrl} and + your API key is correctly set (if connecting to a deployed graph). +

+ ), + duration: 10000, + richColors: true, + closeButton: true, + }); + } + }); + }, [apiKey, apiUrl]); + return ( {children}