UNPKG

@langchain/langgraph-sdk

Version:

Client library for interacting with the LangGraph API

448 lines (445 loc) • 15.6 kB
"use client"; const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs'); const require_client = require('../client.cjs'); const require_utils = require('../ui/utils.cjs'); const require_errors = require('../ui/errors.cjs'); const require_branching = require('../ui/branching.cjs'); const require_messages = require('../ui/messages.cjs'); const require_manager = require('../ui/manager.cjs'); const require_tools = require('../utils/tools.cjs'); const require_thread = require('./thread.cjs'); let react = require("react"); //#region src/react/stream.lgp.tsx function getFetchHistoryKey(client, threadId, limit) { return [ require_client.getClientConfigHash(client), threadId, limit ].join(":"); } function fetchHistory(client, threadId, options) { if (options?.limit === false) return client.threads.getState(threadId).then((state) => { if (state.checkpoint == null) return []; return [state]; }); const limit = typeof options?.limit === "number" ? options.limit : 10; return client.threads.getHistory(threadId, { limit }); } function useThreadHistory(client, threadId, limit, options) { const key = getFetchHistoryKey(client, threadId, limit); const [state, setState] = (0, react.useState)(() => ({ key: void 0, data: void 0, error: void 0, isLoading: threadId != null })); const clientRef = (0, react.useRef)(client); clientRef.current = client; const onErrorRef = (0, react.useRef)(options?.onError); onErrorRef.current = options?.onError; const fetcher = (0, react.useCallback)((threadId$1, limit$1) => { if (options.passthrough) return Promise.resolve([]); const client$1 = clientRef.current; const key$1 = getFetchHistoryKey(client$1, threadId$1, limit$1); if (threadId$1 != null) { setState((state$1) => { if (state$1.key === key$1) return { ...state$1, isLoading: true }; return { key: key$1, data: void 0, error: void 0, isLoading: true }; }); return fetchHistory(client$1, threadId$1, { limit: limit$1 }).then((data) => { setState((state$1) => { if (state$1.key !== key$1) return state$1; return { key: key$1, data, error: void 0, isLoading: false }; }); return data; }, (error) => { setState((state$1) => { if (state$1.key !== key$1) return state$1; return { key: key$1, data: state$1.data, error, isLoading: false }; }); onErrorRef.current?.(error); return Promise.reject(error); }); } setState({ key: key$1, data: void 0, error: void 0, isLoading: false }); return Promise.resolve([]); }, [options.passthrough]); (0, react.useEffect)(() => { if (options.submittingRef.current != null && options.submittingRef.current === threadId) return; fetcher(threadId, limit); }, [fetcher, key]); return { data: state.data, error: state.error, isLoading: state.isLoading, mutate: (mutateId) => fetcher(mutateId ?? threadId, limit) }; } function useStreamLGP(options) { const reconnectOnMountRef = (0, react.useRef)(options.reconnectOnMount); const runMetadataStorage = (0, react.useMemo)(() => { if (typeof window === "undefined") return null; const storage = reconnectOnMountRef.current; if (storage === true) return window.sessionStorage; if (typeof storage === "function") return storage(); return null; }, []); const client = (0, react.useMemo)(() => options.client ?? new require_client.Client({ apiUrl: options.apiUrl, apiKey: options.apiKey, callerOptions: options.callerOptions, defaultHeaders: options.defaultHeaders }), [ options.client, options.apiKey, options.apiUrl, options.callerOptions, options.defaultHeaders ]); const [messageManager] = (0, react.useState)(() => new require_messages.MessageTupleManager()); const [stream] = (0, react.useState)(() => new require_manager.StreamManager(messageManager, { throttle: options.throttle ?? false })); (0, react.useSyncExternalStore)(stream.subscribe, stream.getSnapshot, stream.getSnapshot); const [threadId, onThreadId] = require_thread.useControllableThreadId(options); const trackStreamModeRef = (0, react.useRef)([]); const trackStreamMode = (0, react.useCallback)((...mode) => { const ref = trackStreamModeRef.current; for (const m of mode) if (!ref.includes(m)) ref.push(m); }, []); const hasUpdateListener = options.onUpdateEvent != null; const hasCustomListener = options.onCustomEvent != null; const hasLangChainListener = options.onLangChainEvent != null; const hasDebugListener = options.onDebugEvent != null; const hasCheckpointListener = options.onCheckpointEvent != null; const hasTaskListener = options.onTaskEvent != null; const callbackStreamMode = (0, react.useMemo)(() => { const modes = []; if (hasUpdateListener) modes.push("updates"); if (hasCustomListener) modes.push("custom"); if (hasLangChainListener) modes.push("events"); if (hasDebugListener) modes.push("debug"); if (hasCheckpointListener) modes.push("checkpoints"); if (hasTaskListener) modes.push("tasks"); return modes; }, [ hasUpdateListener, hasCustomListener, hasLangChainListener, hasDebugListener, hasCheckpointListener, hasTaskListener ]); const threadIdRef = (0, react.useRef)(threadId); const threadIdStreamingRef = (0, react.useRef)(null); (0, react.useEffect)(() => { if (threadIdRef.current !== threadId) { threadIdRef.current = threadId; stream.clear(); } }, [threadId, stream]); const historyLimit = typeof options.fetchStateHistory === "object" && options.fetchStateHistory != null ? options.fetchStateHistory.limit ?? false : options.fetchStateHistory ?? false; const builtInHistory = useThreadHistory(client, threadId, historyLimit, { passthrough: options.thread != null, submittingRef: threadIdStreamingRef, onError: options.onError }); const history = options.thread ?? builtInHistory; const getMessages = (value) => { const messagesKey = options.messagesKey ?? "messages"; return Array.isArray(value[messagesKey]) ? value[messagesKey] : []; }; const setMessages = (current, messages) => { const messagesKey = options.messagesKey ?? "messages"; return { ...current, [messagesKey]: messages }; }; const [branch, setBranch] = (0, react.useState)(""); const branchContext = require_branching.getBranchContext(branch, history.data ?? void 0); const historyValues = branchContext.threadHead?.values ?? options.initialValues ?? {}; const historyError = (() => { const error$1 = branchContext.threadHead?.tasks?.at(-1)?.error; if (error$1 == null) return void 0; try { const parsed = JSON.parse(error$1); if (require_errors.StreamError.isStructuredError(parsed)) return new require_errors.StreamError(parsed); return parsed; } catch {} return error$1; })(); const messageMetadata = (() => { const alreadyShown = /* @__PURE__ */ new Set(); return getMessages(historyValues).map((message, idx) => { const messageId = message.id ?? idx; const firstSeenState = require_utils.findLast(history.data ?? [], (state) => getMessages(state.values).map((m, idx$1) => m.id ?? idx$1).includes(messageId)); const checkpointId = firstSeenState?.checkpoint?.checkpoint_id; let branch$1 = checkpointId != null ? branchContext.branchByCheckpoint[checkpointId] : void 0; if (!branch$1?.branch?.length) branch$1 = void 0; const optionsShown = branch$1?.branchOptions?.flat(2).join(","); if (optionsShown) { if (alreadyShown.has(optionsShown)) branch$1 = void 0; alreadyShown.add(optionsShown); } return { messageId: messageId.toString(), firstSeenState, branch: branch$1?.branch, branchOptions: branch$1?.branchOptions }; }); })(); const stop = () => stream.stop(historyValues, { onStop: (args) => { if (runMetadataStorage && threadId) { const runId = runMetadataStorage.getItem(`lg:stream:${threadId}`); if (runId) client.runs.cancel(threadId, runId); runMetadataStorage.removeItem(`lg:stream:${threadId}`); } options.onStop?.(args); } }); const submit = async (values$1, submitOptions) => { const checkpointId = submitOptions?.checkpoint?.checkpoint_id; setBranch(checkpointId != null ? branchContext.branchByCheckpoint[checkpointId]?.branch ?? "" : ""); const includeImplicitBranch = historyLimit === true || typeof historyLimit === "number"; const shouldRefetch = options.onFinish != null || includeImplicitBranch; let callbackMeta; let rejoinKey; let usableThreadId = threadId; await stream.start(async (signal) => { stream.setStreamValues((values$2) => { const prev = { ...historyValues, ...values$2 ?? {} }; if (submitOptions?.optimisticValues != null) return { ...prev, ...typeof submitOptions.optimisticValues === "function" ? submitOptions.optimisticValues(prev) : submitOptions.optimisticValues }; return { ...prev }; }); if (!usableThreadId) { usableThreadId = (await client.threads.create({ threadId: submitOptions?.threadId, metadata: submitOptions?.metadata, signal })).thread_id; threadIdRef.current = usableThreadId; threadIdStreamingRef.current = usableThreadId; onThreadId(usableThreadId); } if (!usableThreadId) throw new Error("Failed to obtain valid thread ID."); threadIdStreamingRef.current = usableThreadId; const streamMode = require_utils.unique([ ...submitOptions?.streamMode ?? [], ...trackStreamModeRef.current, ...callbackStreamMode ]); let checkpoint = submitOptions?.checkpoint ?? (includeImplicitBranch ? branchContext.threadHead?.checkpoint : void 0) ?? void 0; if (submitOptions?.checkpoint === null) checkpoint = void 0; if (checkpoint != null) delete checkpoint.thread_id; const streamResumable = submitOptions?.streamResumable ?? !!runMetadataStorage; return client.runs.stream(usableThreadId, options.assistantId, { input: values$1, config: submitOptions?.config, context: submitOptions?.context, command: submitOptions?.command, interruptBefore: submitOptions?.interruptBefore, interruptAfter: submitOptions?.interruptAfter, metadata: submitOptions?.metadata, multitaskStrategy: submitOptions?.multitaskStrategy, onCompletion: submitOptions?.onCompletion, onDisconnect: submitOptions?.onDisconnect ?? (streamResumable ? "continue" : "cancel"), signal, checkpoint, streamMode, streamSubgraphs: submitOptions?.streamSubgraphs, streamResumable, durability: submitOptions?.durability, onRunCreated(params) { callbackMeta = { run_id: params.run_id, thread_id: params.thread_id ?? usableThreadId }; if (runMetadataStorage) { rejoinKey = `lg:stream:${usableThreadId}`; runMetadataStorage.setItem(rejoinKey, callbackMeta.run_id); } options.onCreated?.(callbackMeta); } }); }, { getMessages, setMessages, initialValues: historyValues, callbacks: options, async onSuccess() { if (rejoinKey) runMetadataStorage?.removeItem(rejoinKey); if (shouldRefetch) { const lastHead = (await history.mutate(usableThreadId))?.at(0); if (lastHead) { options.onFinish?.(lastHead, callbackMeta); return null; } } }, onError(error$1) { options.onError?.(error$1, callbackMeta); }, onFinish() { threadIdStreamingRef.current = null; } }); }; const joinStream = async (runId, lastEventId, joinOptions) => { lastEventId ??= "-1"; if (!threadId) return; const callbackMeta = { thread_id: threadId, run_id: runId }; await stream.start(async (signal) => { threadIdStreamingRef.current = threadId; return client.runs.joinStream(threadId, runId, { signal, lastEventId, streamMode: joinOptions?.streamMode }); }, { getMessages, setMessages, initialValues: historyValues, callbacks: options, async onSuccess() { runMetadataStorage?.removeItem(`lg:stream:${threadId}`); const lastHead = (await history.mutate(threadId))?.at(0); if (lastHead) options.onFinish?.(lastHead, callbackMeta); }, onError(error$1) { options.onError?.(error$1, callbackMeta); }, onFinish() { threadIdStreamingRef.current = null; } }); }; const reconnectKey = (0, react.useMemo)(() => { if (!runMetadataStorage || stream.isLoading) return void 0; if (typeof window === "undefined") return void 0; const runId = runMetadataStorage?.getItem(`lg:stream:${threadId}`); if (!runId) return void 0; return { runId, threadId }; }, [ runMetadataStorage, stream.isLoading, threadId ]); const shouldReconnect = !!runMetadataStorage; const reconnectRef = (0, react.useRef)({ threadId, shouldReconnect }); const joinStreamRef = (0, react.useRef)(joinStream); joinStreamRef.current = joinStream; (0, react.useEffect)(() => { if (reconnectRef.current.threadId !== threadId) reconnectRef.current = { threadId, shouldReconnect }; }, [threadId, shouldReconnect]); (0, react.useEffect)(() => { if (reconnectKey && reconnectRef.current.shouldReconnect) { reconnectRef.current.shouldReconnect = false; joinStreamRef.current?.(reconnectKey.runId); } }, [reconnectKey]); const error = stream.error ?? historyError ?? history.error; const values = stream.values ?? historyValues; return { get values() { trackStreamMode("values"); return values; }, client, assistantId: options.assistantId, error, isLoading: stream.isLoading, stop, submit, joinStream, branch, setBranch, get history() { if (historyLimit === false) throw new Error("`fetchStateHistory` must be set to `true` to use `history`"); return branchContext.flatHistory; }, isThreadLoading: history.isLoading && history.data == null, get experimental_branchTree() { if (historyLimit === false) throw new Error("`fetchStateHistory` must be set to `true` to use `experimental_branchTree`"); return branchContext.branchTree; }, get interrupt() { if (values != null && "__interrupt__" in values && Array.isArray(values.__interrupt__)) { const valueInterrupts = values.__interrupt__; if (valueInterrupts.length === 0) return { when: "breakpoint" }; if (valueInterrupts.length === 1) return valueInterrupts[0]; return valueInterrupts; } if (stream.isLoading) return void 0; const interrupts = branchContext.threadHead?.tasks?.at(-1)?.interrupts; if (interrupts == null || interrupts.length === 0) { if (!(branchContext.threadHead?.next ?? []).length || error != null) return void 0; return { when: "breakpoint" }; } return interrupts.at(-1); }, get messages() { trackStreamMode("messages-tuple", "values"); return getMessages(values); }, get toolCalls() { trackStreamMode("messages-tuple", "values"); return require_tools.getToolCallsWithResults(getMessages(values)); }, getToolCalls(message) { trackStreamMode("messages-tuple", "values"); return require_tools.getToolCallsWithResults(getMessages(values)).filter((tc) => tc.aiMessage.id === message.id); }, getMessagesMetadata(message, index) { trackStreamMode("values"); const streamMetadata = messageManager.get(message.id)?.metadata; const historyMetadata = messageMetadata?.find((m) => m.messageId === (message.id ?? index)); if (streamMetadata != null || historyMetadata != null) return { ...historyMetadata, streamMetadata }; } }; } //#endregion exports.useStreamLGP = useStreamLGP; //# sourceMappingURL=stream.lgp.cjs.map