@langgraph-js/sdk
Version:
The UI SDK for LangGraph - seamlessly integrate your AI agents with frontend interfaces
107 lines (93 loc) • 3.45 kB
text/typescript
import { createElement, createContext, useContext, useMemo, ReactNode, useEffect, useRef } from "react";
import { createChatStore, UnionStore, useUnionStore } from "../ui-store/index.js";
import { useStore } from "@nanostores/react";
import { ILangGraphClient } from "@langgraph-js/pure-graph/dist/types.js";
const ChatContext = createContext<UnionStore<ReturnType<typeof createChatStore>> | undefined>(undefined);
export const useChat = () => {
const context = useContext(ChatContext);
if (!context) {
throw new Error("useChat must be used within a ChatProvider");
}
return context;
};
interface ChatProviderProps {
children: ReactNode;
defaultAgent?: string;
apiUrl?: string;
defaultHeaders?: Record<string, string>;
withCredentials?: boolean;
fetch?: typeof fetch;
showHistory?: boolean;
showGraph?: boolean;
fallbackToAvailableAssistants?: boolean;
/** 初始化时是否自动激活最近的历史会话(默认 false,创建新会话) */
autoRestoreLastSession?: boolean;
onInitError?: (error: any, currentAgent: string) => void;
client?: ILangGraphClient;
legacyMode?: boolean;
}
export const ChatProvider: React.FC<ChatProviderProps> = ({
children,
defaultAgent = "",
apiUrl = "http://localhost:8123",
defaultHeaders,
withCredentials = false,
fetch,
showHistory = false,
showGraph = false,
fallbackToAvailableAssistants = false,
autoRestoreLastSession = false,
onInitError,
client,
legacyMode = false,
}) => {
// 使用 useMemo 稳定 defaultHeaders 的引用
const stableHeaders = useMemo(() => defaultHeaders || {}, [defaultHeaders]);
// 使用 useRef 保存 onInitError 的最新引用
const onInitErrorRef = useRef(onInitError);
useEffect(() => {
onInitErrorRef.current = onInitError;
}, [onInitError]);
const store = useMemo(() => {
const baseFetch = fetch || globalThis.fetch;
const F = withCredentials
? (url: string, options: RequestInit) => {
options.credentials = "include";
return baseFetch(url, options);
}
: baseFetch;
const config = {
apiUrl,
defaultHeaders: stableHeaders,
callerOptions: {
fetch: F,
maxRetries: 1,
},
legacyMode,
};
/** @ts-ignore */
if (client) config.client = client;
return createChatStore(defaultAgent, config, {
showHistory,
showGraph,
fallbackToAvailableAssistants,
autoRestoreLastSession,
});
}, [defaultAgent, apiUrl, stableHeaders, withCredentials, fetch, showHistory, showGraph, fallbackToAvailableAssistants, autoRestoreLastSession]);
const unionStore = useUnionStore(store, useStore);
// 使用 ref 标记是否已初始化
const initializedRef = useRef(false);
useEffect(() => {
if (initializedRef.current) {
return;
}
initializedRef.current = true;
unionStore.initClient().catch((err) => {
console.error(err);
if (onInitErrorRef.current) {
onInitErrorRef.current(err, unionStore.currentAgent);
}
});
}, [unionStore]);
return createElement(ChatContext.Provider, { value: unionStore }, children);
};