import React, { createContext, useContext, ReactNode, useState } from "react"; import { useStream } from "@langchain/langgraph-sdk/react"; import { type Message } from "@langchain/langgraph-sdk"; import { uiMessageReducer, type UIMessage, type RemoveUIMessage, } from "@langchain/langgraph-sdk/react-ui"; import { useQueryParam, StringParam } from "use-query-params"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { LangGraphLogoSVG } from "@/components/icons/langgraph"; import { Label } from "@/components/ui/label"; import { ArrowRight } from "lucide-react"; import { PasswordInput } from "@/components/ui/password-input"; import { getApiKey } from "@/lib/api-key"; import { useThreads } from "./Thread"; export type StateType = { messages: Message[]; ui?: UIMessage[] }; const useTypedStream = useStream< StateType, { UpdateType: { messages?: Message[] | Message | string; ui?: (UIMessage | RemoveUIMessage)[] | UIMessage | RemoveUIMessage; }; CustomEventType: UIMessage | RemoveUIMessage; } >; type StreamContextType = ReturnType; const StreamContext = createContext(undefined); async function sleep(ms = 4000) { return new Promise((resolve) => setTimeout(resolve, ms)); } const StreamSession = ({ children, apiKey, apiUrl, assistantId, }: { children: ReactNode; apiKey: string | null; apiUrl: string; assistantId: string; }) => { const [threadId, setThreadId] = useQueryParam("threadId", StringParam); const { getThreads, setThreads } = useThreads(); const streamValue = useTypedStream({ apiUrl, apiKey: apiKey ?? undefined, assistantId, threadId: threadId ?? null, onCustomEvent: (event, options) => { options.mutate((prev) => { const ui = uiMessageReducer(prev.ui ?? [], event); return { ...prev, ui }; }); }, onThreadId: (id) => { setThreadId(id); // Refetch threads list when thread ID changes. // Wait for some seconds before fetching so we're able to get the new thread that was created. sleep().then(() => getThreads().then(setThreads).catch(console.error)); }, }); return ( {children} ); }; export const StreamProvider: React.FC<{ children: ReactNode }> = ({ children, }) => { const [apiUrl, setApiUrl] = useQueryParam("apiUrl", StringParam); const [apiKey, _setApiKey] = useState(() => { return getApiKey(); }); const setApiKey = (key: string) => { window.localStorage.setItem("lg:chat:apiKey", key); _setApiKey(key); }; const [assistantId, setAssistantId] = useQueryParam( "assistantId", StringParam, ); if (!apiUrl || !assistantId) { return (

Chat LangGraph

Welcome to Chat LangGraph! Before you get started, you need to enter the URL of the deployment and the assistant / graph ID.

{ e.preventDefault(); const form = e.target as HTMLFormElement; const formData = new FormData(form); const apiUrl = formData.get("apiUrl") as string; const assistantId = formData.get("assistantId") as string; const apiKey = formData.get("apiKey") as string; setApiUrl(apiUrl); setApiKey(apiKey); setAssistantId(assistantId); form.reset(); }} className="flex flex-col gap-6 p-6 bg-muted/50" >

This is the URL of your LangGraph deployment. Can be a local, or production deployment.

This is the ID of the graph (can be the graph name), or assistant to fetch threads from, and invoke when actions are taken.

This is NOT required if using a local LangGraph server. This value is stored in your browser's local storage and is only used to authenticate requests sent to your LangGraph server.

); } return ( {children} ); }; // Create a custom hook to use the context export const useStreamContext = (): StreamContextType => { const context = useContext(StreamContext); if (context === undefined) { throw new Error("useStreamContext must be used within a StreamProvider"); } return context; }; export default StreamContext;