import React, { createContext, useContext, ReactNode, useState } from "react"; import { useStream } from "@langchain/langgraph-sdk/react"; import { type Message } from "@langchain/langgraph-sdk"; import { uiMessageReducer, isUIMessage, isRemoveUIMessage, type UIMessage, type RemoveUIMessage, } from "@langchain/langgraph-sdk/react-ui"; import { useQueryState } from "nuqs"; import { LangGraphLogoSVG } from "@/components/icons/langgraph"; import { useThreads } from "./Thread"; import { TooltipIconButton } from "@/components/thread/tooltip-icon-button"; import { Copy, CopyCheck } from "lucide-react"; import { motion } from "framer-motion"; type TargetRepository = { owner: string; repo: string }; export type StateType = { messages: Message[]; ui?: UIMessage[]; targetRepository?: TargetRepository; }; const useTypedStream = useStream< StateType, { UpdateType: { messages?: Message[] | Message | string; ui?: (UIMessage | RemoveUIMessage)[] | UIMessage | RemoveUIMessage; context?: Record; targetRepository?: TargetRepository; }; 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, apiUrl, assistantId, }: { children: ReactNode; apiUrl: string; assistantId: string; }) => { const [threadId, setThreadId] = useQueryState("threadId"); const { getThreads, setThreads } = useThreads(); const streamValue = useTypedStream({ apiUrl, assistantId, threadId: threadId ?? null, onCustomEvent: (event, options) => { if (isUIMessage(event) || isRemoveUIMessage(event)) { 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 baseCopyTooltipText = "Copy environment variables"; const [copyTooltipText, setCopyTooltipText] = useState(baseCopyTooltipText); const apiUrl: string | undefined = process.env.NEXT_PUBLIC_API_URL ?? ""; const assistantId: string | undefined = process.env.NEXT_PUBLIC_ASSISTANT_ID ?? ""; if (!apiUrl || !assistantId) { return (

Environment Variables Missing

Whoops, looks like you don't have an API URL or assistant ID set in your environment variables. Please make sure you have both of these set before continuing.

{ const textToCopy = `NEXT_PUBLIC_API_URL=${apiUrl}\nNEXT_PUBLIC_ASSISTANT_ID=${assistantId}`; navigator.clipboard.writeText(textToCopy).then(() => { setCopyTooltipText("Copied!"); setTimeout( () => setCopyTooltipText(baseCopyTooltipText), 2000, ); }); }} className="absolute top-2 right-2 cursor-pointer" tooltip={copyTooltipText} > {copyTooltipText === baseCopyTooltipText ? ( ) : ( )} NEXT_PUBLIC_API_URL={apiUrl} NEXT_PUBLIC_ASSISTANT_ID={assistantId}
); } 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;