@langchain/langgraph-sdk 0.1.4 → 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.
- package/CHANGELOG.md +14 -0
- package/dist/react/index.cjs +3 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +1 -0
- package/dist/react/manager.cjs +12 -5
- package/dist/react/manager.d.ts +1 -0
- package/dist/react/manager.js +12 -5
- package/dist/react/stream.cjs +12 -460
- package/dist/react/stream.custom.cjs +148 -0
- package/dist/react/stream.custom.d.ts +41 -0
- package/dist/react/stream.custom.js +142 -0
- package/dist/react/stream.d.ts +61 -1
- package/dist/react/stream.js +13 -460
- package/dist/react/stream.lgp.cjs +485 -0
- package/dist/react/stream.lgp.d.ts +7 -0
- package/dist/react/stream.lgp.js +481 -0
- package/dist/react/thread.cjs +19 -0
- package/dist/react/thread.d.ts +4 -0
- package/dist/react/thread.js +15 -0
- package/dist/react/types.d.ts +24 -0
- package/package.json +1 -1
|
@@ -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
|
+
}
|
package/dist/react/stream.d.ts
CHANGED
|
@@ -1,4 +1,64 @@
|
|
|
1
|
-
import
|
|
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;
|