@langchain/langgraph-sdk 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ /* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */
3
+ "use client";
4
+ /* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FetchStreamTransport = void 0;
7
+ exports.useStreamCustom = useStreamCustom;
8
+ const react_1 = require("react");
9
+ const manager_js_1 = require("./manager.cjs");
10
+ const messages_js_1 = require("./messages.cjs");
11
+ const sse_js_1 = require("../utils/sse.cjs");
12
+ const stream_js_1 = require("../utils/stream.cjs");
13
+ const thread_js_1 = require("./thread.cjs");
14
+ class FetchStreamTransport {
15
+ constructor(options) {
16
+ Object.defineProperty(this, "options", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: options
21
+ });
22
+ }
23
+ async stream(payload) {
24
+ const { signal, ...body } = payload;
25
+ let requestInit = {
26
+ method: "POST",
27
+ headers: {
28
+ "Content-Type": "application/json",
29
+ ...this.options.defaultHeaders,
30
+ },
31
+ body: JSON.stringify(body),
32
+ signal,
33
+ };
34
+ if (this.options.onRequest) {
35
+ requestInit = await this.options.onRequest(this.options.apiUrl, requestInit);
36
+ }
37
+ const fetchFn = this.options.fetch ?? fetch;
38
+ const response = await fetchFn(this.options.apiUrl, requestInit);
39
+ if (!response.ok) {
40
+ throw new Error(`Failed to stream: ${response.statusText}`);
41
+ }
42
+ const stream = (response.body || new ReadableStream({ start: (ctrl) => ctrl.close() }))
43
+ .pipeThrough((0, sse_js_1.BytesLineDecoder)())
44
+ .pipeThrough((0, sse_js_1.SSEDecoder)());
45
+ return stream_js_1.IterableReadableStream.fromReadableStream(stream);
46
+ }
47
+ }
48
+ exports.FetchStreamTransport = FetchStreamTransport;
49
+ function useStreamCustom(options) {
50
+ const [messageManager] = (0, react_1.useState)(() => new messages_js_1.MessageTupleManager());
51
+ const [stream] = (0, react_1.useState)(() => new manager_js_1.StreamManager(messageManager));
52
+ (0, react_1.useSyncExternalStore)(stream.subscribe, stream.getSnapshot, stream.getSnapshot);
53
+ const [threadId, onThreadId] = (0, thread_js_1.useControllableThreadId)(options);
54
+ const threadIdRef = (0, react_1.useRef)(threadId);
55
+ // Cancel the stream if thread ID has changed
56
+ (0, react_1.useEffect)(() => {
57
+ if (threadIdRef.current !== threadId) {
58
+ threadIdRef.current = threadId;
59
+ stream.clear();
60
+ }
61
+ }, [threadId, stream]);
62
+ const getMessages = (value) => {
63
+ const messagesKey = options.messagesKey ?? "messages";
64
+ return Array.isArray(value[messagesKey]) ? value[messagesKey] : [];
65
+ };
66
+ const setMessages = (current, messages) => {
67
+ const messagesKey = options.messagesKey ?? "messages";
68
+ return { ...current, [messagesKey]: messages };
69
+ };
70
+ const historyValues = options.initialValues ?? {};
71
+ const stop = () => stream.stop(historyValues, { onStop: options.onStop });
72
+ const submit = async (values, submitOptions) => {
73
+ let callbackMeta;
74
+ let usableThreadId = threadId;
75
+ stream.setStreamValues(() => {
76
+ if (submitOptions?.optimisticValues != null) {
77
+ return {
78
+ ...historyValues,
79
+ ...(typeof submitOptions.optimisticValues === "function"
80
+ ? submitOptions.optimisticValues(historyValues)
81
+ : submitOptions.optimisticValues),
82
+ };
83
+ }
84
+ return { ...historyValues };
85
+ });
86
+ await stream.start(async (signal) => {
87
+ if (!usableThreadId) {
88
+ // generate random thread id
89
+ usableThreadId = crypto.randomUUID();
90
+ threadIdRef.current = usableThreadId;
91
+ onThreadId(usableThreadId);
92
+ }
93
+ if (!usableThreadId) {
94
+ throw new Error("Failed to obtain valid thread ID.");
95
+ }
96
+ return options.transport.stream({
97
+ input: values,
98
+ context: submitOptions?.context,
99
+ command: submitOptions?.command,
100
+ signal,
101
+ config: {
102
+ ...submitOptions?.config,
103
+ configurable: {
104
+ thread_id: usableThreadId,
105
+ ...submitOptions?.config?.configurable,
106
+ },
107
+ },
108
+ });
109
+ }, {
110
+ getMessages,
111
+ setMessages,
112
+ initialValues: {},
113
+ callbacks: options,
114
+ onSuccess: () => undefined,
115
+ onError(error) {
116
+ options.onError?.(error, callbackMeta);
117
+ },
118
+ });
119
+ };
120
+ return {
121
+ get values() {
122
+ return stream.values ?? {};
123
+ },
124
+ error: stream.error,
125
+ isLoading: stream.isLoading,
126
+ stop,
127
+ submit,
128
+ get interrupt() {
129
+ if (stream.values != null &&
130
+ "__interrupt__" in stream.values &&
131
+ Array.isArray(stream.values.__interrupt__)) {
132
+ const valueInterrupts = stream.values.__interrupt__;
133
+ if (valueInterrupts.length === 0)
134
+ return { when: "breakpoint" };
135
+ if (valueInterrupts.length === 1)
136
+ return valueInterrupts[0];
137
+ // TODO: fix the typing of interrupts if multiple interrupts are returned
138
+ return valueInterrupts;
139
+ }
140
+ return undefined;
141
+ },
142
+ get messages() {
143
+ if (!stream.values)
144
+ return [];
145
+ return getMessages(stream.values);
146
+ },
147
+ };
148
+ }
@@ -0,0 +1,41 @@
1
+ import type { BagTemplate, GetUpdateType, GetConfigurableType, UseStreamCustomOptions, UseStreamCustom, UseStreamTransport } from "./types.js";
2
+ import { Command } from "../types.js";
3
+ interface FetchStreamTransportOptions {
4
+ /**
5
+ * The URL of the API to use.
6
+ */
7
+ apiUrl: string;
8
+ /**
9
+ * Default headers to send with requests.
10
+ */
11
+ defaultHeaders?: HeadersInit;
12
+ /**
13
+ * Specify a custom fetch implementation.
14
+ */
15
+ fetch?: typeof fetch | ((...args: any[]) => any);
16
+ /**
17
+ * Callback that is called before the request is made.
18
+ */
19
+ onRequest?: (url: string, init: RequestInit) => Promise<RequestInit> | RequestInit;
20
+ }
21
+ export declare class FetchStreamTransport<StateType extends Record<string, unknown> = Record<string, unknown>, Bag extends BagTemplate = BagTemplate> implements UseStreamTransport<StateType, Bag> {
22
+ private readonly options;
23
+ constructor(options: FetchStreamTransportOptions);
24
+ stream(payload: {
25
+ input: GetUpdateType<Bag, StateType> | null | undefined;
26
+ context: GetConfigurableType<Bag> | undefined;
27
+ command: Command | undefined;
28
+ signal: AbortSignal;
29
+ }): Promise<AsyncGenerator<{
30
+ id?: string;
31
+ event: string;
32
+ data: unknown;
33
+ }>>;
34
+ }
35
+ export declare function useStreamCustom<StateType extends Record<string, unknown> = Record<string, unknown>, Bag extends {
36
+ ConfigurableType?: Record<string, unknown>;
37
+ InterruptType?: unknown;
38
+ CustomEventType?: unknown;
39
+ UpdateType?: unknown;
40
+ } = BagTemplate>(options: UseStreamCustomOptions<StateType, Bag>): UseStreamCustom<StateType, Bag>;
41
+ export {};
@@ -0,0 +1,142 @@
1
+ /* __LC_ALLOW_ENTRYPOINT_SIDE_EFFECTS__ */
2
+ "use client";
3
+ import { useEffect, useRef, useState, useSyncExternalStore } from "react";
4
+ import { StreamManager } from "./manager.js";
5
+ import { MessageTupleManager } from "./messages.js";
6
+ import { BytesLineDecoder, SSEDecoder } from "../utils/sse.js";
7
+ import { IterableReadableStream } from "../utils/stream.js";
8
+ import { useControllableThreadId } from "./thread.js";
9
+ export class FetchStreamTransport {
10
+ constructor(options) {
11
+ Object.defineProperty(this, "options", {
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ value: options
16
+ });
17
+ }
18
+ async stream(payload) {
19
+ const { signal, ...body } = payload;
20
+ let requestInit = {
21
+ method: "POST",
22
+ headers: {
23
+ "Content-Type": "application/json",
24
+ ...this.options.defaultHeaders,
25
+ },
26
+ body: JSON.stringify(body),
27
+ signal,
28
+ };
29
+ if (this.options.onRequest) {
30
+ requestInit = await this.options.onRequest(this.options.apiUrl, requestInit);
31
+ }
32
+ const fetchFn = this.options.fetch ?? fetch;
33
+ const response = await fetchFn(this.options.apiUrl, requestInit);
34
+ if (!response.ok) {
35
+ throw new Error(`Failed to stream: ${response.statusText}`);
36
+ }
37
+ const stream = (response.body || new ReadableStream({ start: (ctrl) => ctrl.close() }))
38
+ .pipeThrough(BytesLineDecoder())
39
+ .pipeThrough(SSEDecoder());
40
+ return IterableReadableStream.fromReadableStream(stream);
41
+ }
42
+ }
43
+ export function useStreamCustom(options) {
44
+ const [messageManager] = useState(() => new MessageTupleManager());
45
+ const [stream] = useState(() => new StreamManager(messageManager));
46
+ useSyncExternalStore(stream.subscribe, stream.getSnapshot, stream.getSnapshot);
47
+ const [threadId, onThreadId] = useControllableThreadId(options);
48
+ const threadIdRef = useRef(threadId);
49
+ // Cancel the stream if thread ID has changed
50
+ useEffect(() => {
51
+ if (threadIdRef.current !== threadId) {
52
+ threadIdRef.current = threadId;
53
+ stream.clear();
54
+ }
55
+ }, [threadId, stream]);
56
+ const getMessages = (value) => {
57
+ const messagesKey = options.messagesKey ?? "messages";
58
+ return Array.isArray(value[messagesKey]) ? value[messagesKey] : [];
59
+ };
60
+ const setMessages = (current, messages) => {
61
+ const messagesKey = options.messagesKey ?? "messages";
62
+ return { ...current, [messagesKey]: messages };
63
+ };
64
+ const historyValues = options.initialValues ?? {};
65
+ const stop = () => stream.stop(historyValues, { onStop: options.onStop });
66
+ const submit = async (values, submitOptions) => {
67
+ let callbackMeta;
68
+ let usableThreadId = threadId;
69
+ stream.setStreamValues(() => {
70
+ if (submitOptions?.optimisticValues != null) {
71
+ return {
72
+ ...historyValues,
73
+ ...(typeof submitOptions.optimisticValues === "function"
74
+ ? submitOptions.optimisticValues(historyValues)
75
+ : submitOptions.optimisticValues),
76
+ };
77
+ }
78
+ return { ...historyValues };
79
+ });
80
+ await stream.start(async (signal) => {
81
+ if (!usableThreadId) {
82
+ // generate random thread id
83
+ usableThreadId = crypto.randomUUID();
84
+ threadIdRef.current = usableThreadId;
85
+ onThreadId(usableThreadId);
86
+ }
87
+ if (!usableThreadId) {
88
+ throw new Error("Failed to obtain valid thread ID.");
89
+ }
90
+ return options.transport.stream({
91
+ input: values,
92
+ context: submitOptions?.context,
93
+ command: submitOptions?.command,
94
+ signal,
95
+ config: {
96
+ ...submitOptions?.config,
97
+ configurable: {
98
+ thread_id: usableThreadId,
99
+ ...submitOptions?.config?.configurable,
100
+ },
101
+ },
102
+ });
103
+ }, {
104
+ getMessages,
105
+ setMessages,
106
+ initialValues: {},
107
+ callbacks: options,
108
+ onSuccess: () => undefined,
109
+ onError(error) {
110
+ options.onError?.(error, callbackMeta);
111
+ },
112
+ });
113
+ };
114
+ return {
115
+ get values() {
116
+ return stream.values ?? {};
117
+ },
118
+ error: stream.error,
119
+ isLoading: stream.isLoading,
120
+ stop,
121
+ submit,
122
+ get interrupt() {
123
+ if (stream.values != null &&
124
+ "__interrupt__" in stream.values &&
125
+ Array.isArray(stream.values.__interrupt__)) {
126
+ const valueInterrupts = stream.values.__interrupt__;
127
+ if (valueInterrupts.length === 0)
128
+ return { when: "breakpoint" };
129
+ if (valueInterrupts.length === 1)
130
+ return valueInterrupts[0];
131
+ // TODO: fix the typing of interrupts if multiple interrupts are returned
132
+ return valueInterrupts;
133
+ }
134
+ return undefined;
135
+ },
136
+ get messages() {
137
+ if (!stream.values)
138
+ return [];
139
+ return getMessages(stream.values);
140
+ },
141
+ };
142
+ }
@@ -1,4 +1,64 @@
1
- import type { BagTemplate, UseStreamOptions, UseStream } from "./types.js";
1
+ import { BagTemplate, UseStream, UseStreamCustom, UseStreamCustomOptions, UseStreamOptions } from "./types.js";
2
+ /**
3
+ * A React hook that provides seamless integration with LangGraph streaming capabilities.
4
+ *
5
+ * The `useStream` hook handles all the complexities of streaming, state management, and branching logic,
6
+ * letting you focus on building great chat experiences. It provides automatic state management for
7
+ * messages, interrupts, loading states, and errors.
8
+ *
9
+ * @template StateType The type of the thread state (default: `Record<string, unknown>`)
10
+ * @template Bag Type configuration bag containing:
11
+ * - `ConfigurableType`: Type for the `config.configurable` property
12
+ * - `InterruptType`: Type for interrupt values
13
+ * - `CustomEventType`: Type for custom events
14
+ * - `UpdateType`: Type for the submit function updates
15
+ *
16
+ * @see {@link https://docs.langchain.com/langgraph-platform/use-stream-react | LangGraph React Integration Guide}
17
+ */
18
+ export declare function useStream<StateType extends Record<string, unknown> = Record<string, unknown>, Bag extends {
19
+ ConfigurableType?: Record<string, unknown>;
20
+ InterruptType?: unknown;
21
+ CustomEventType?: unknown;
22
+ UpdateType?: unknown;
23
+ } = BagTemplate>(options: UseStreamOptions<StateType, Bag>): UseStream<StateType, Bag>;
24
+ /**
25
+ * A React hook that provides seamless integration with LangGraph streaming capabilities.
26
+ *
27
+ * The `useStream` hook handles all the complexities of streaming, state management, and branching logic,
28
+ * letting you focus on building great chat experiences. It provides automatic state management for
29
+ * messages, interrupts, loading states, and errors.
30
+ *
31
+ * @template StateType The type of the thread state (default: `Record<string, unknown>`)
32
+ * @template Bag Type configuration bag containing:
33
+ * - `ConfigurableType`: Type for the `config.configurable` property
34
+ * - `InterruptType`: Type for interrupt values
35
+ * - `CustomEventType`: Type for custom events
36
+ * - `UpdateType`: Type for the submit function updates
37
+ *
38
+ * @see {@link https://docs.langchain.com/langgraph-platform/use-stream-react | LangGraph React Integration Guide}
39
+ */
40
+ export declare function useStream<StateType extends Record<string, unknown> = Record<string, unknown>, Bag extends {
41
+ ConfigurableType?: Record<string, unknown>;
42
+ InterruptType?: unknown;
43
+ CustomEventType?: unknown;
44
+ UpdateType?: unknown;
45
+ } = BagTemplate>(options: UseStreamCustomOptions<StateType, Bag>): UseStreamCustom<StateType, Bag>;
46
+ /**
47
+ * A React hook that provides seamless integration with LangGraph streaming capabilities.
48
+ *
49
+ * The `useStream` hook handles all the complexities of streaming, state management, and branching logic,
50
+ * letting you focus on building great chat experiences. It provides automatic state management for
51
+ * messages, interrupts, loading states, and errors.
52
+ *
53
+ * @template StateType The type of the thread state (default: `Record<string, unknown>`)
54
+ * @template Bag Type configuration bag containing:
55
+ * - `ConfigurableType`: Type for the `config.configurable` property
56
+ * - `InterruptType`: Type for interrupt values
57
+ * - `CustomEventType`: Type for custom events
58
+ * - `UpdateType`: Type for the submit function updates
59
+ *
60
+ * @see {@link https://docs.langchain.com/langgraph-platform/use-stream-react | LangGraph React Integration Guide}
61
+ */
2
62
  export declare function useStream<StateType extends Record<string, unknown> = Record<string, unknown>, Bag extends {
3
63
  ConfigurableType?: Record<string, unknown>;
4
64
  InterruptType?: unknown;