@langchain/langgraph-sdk 0.0.111 → 0.1.0
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 +20 -0
- package/dist/client.cjs +23 -0
- package/dist/client.d.ts +9 -1
- package/dist/client.js +23 -0
- package/dist/react/branching.cjs +149 -0
- package/dist/react/branching.d.ts +35 -0
- package/dist/react/branching.js +144 -0
- package/dist/react/errors.cjs +13 -0
- package/dist/react/errors.d.ts +12 -0
- package/dist/react/errors.js +9 -0
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +1 -1
- package/dist/react/manager.cjs +219 -0
- package/dist/react/manager.d.ts +86 -0
- package/dist/react/manager.js +215 -0
- package/dist/react/messages.cjs +69 -0
- package/dist/react/messages.d.ts +18 -0
- package/dist/react/messages.js +64 -0
- package/dist/react/stream.cjs +174 -437
- package/dist/react/stream.d.ts +1 -315
- package/dist/react/stream.js +173 -436
- package/dist/react/types.cjs +2 -0
- package/dist/react/types.d.ts +316 -0
- package/dist/react/types.js +1 -0
- package/dist/react/utils.cjs +14 -0
- package/dist/react/utils.d.ts +2 -0
- package/dist/react/utils.js +10 -0
- package/dist/react-ui/client.d.ts +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.stream.d.ts +1 -0
- package/package.json +1 -1
- package/dist/react/debug.cjs +0 -31
- package/dist/react/debug.d.ts +0 -23
- package/dist/react/debug.js +0 -28
package/dist/react/stream.cjs
CHANGED
|
@@ -5,190 +5,12 @@
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.useStream = useStream;
|
|
7
7
|
const react_1 = require("react");
|
|
8
|
-
const
|
|
8
|
+
const utils_js_1 = require("./utils.cjs");
|
|
9
|
+
const errors_js_1 = require("./errors.cjs");
|
|
10
|
+
const branching_js_1 = require("./branching.cjs");
|
|
11
|
+
const manager_js_1 = require("./manager.cjs");
|
|
9
12
|
const client_js_1 = require("../client.cjs");
|
|
10
|
-
|
|
11
|
-
constructor(data) {
|
|
12
|
-
super(data.message);
|
|
13
|
-
this.name = data.name ?? data.error ?? "StreamError";
|
|
14
|
-
}
|
|
15
|
-
static isStructuredError(error) {
|
|
16
|
-
return typeof error === "object" && error != null && "message" in error;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function tryConvertToChunk(message) {
|
|
20
|
-
try {
|
|
21
|
-
return (0, messages_1.convertToChunk)(message);
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
class MessageTupleManager {
|
|
28
|
-
constructor() {
|
|
29
|
-
Object.defineProperty(this, "chunks", {
|
|
30
|
-
enumerable: true,
|
|
31
|
-
configurable: true,
|
|
32
|
-
writable: true,
|
|
33
|
-
value: {}
|
|
34
|
-
});
|
|
35
|
-
this.chunks = {};
|
|
36
|
-
}
|
|
37
|
-
add(serialized, metadata) {
|
|
38
|
-
// TODO: this is sometimes sent from the API
|
|
39
|
-
// figure out how to prevent this or move this to LC.js
|
|
40
|
-
if (serialized.type.endsWith("MessageChunk")) {
|
|
41
|
-
// eslint-disable-next-line no-param-reassign
|
|
42
|
-
serialized.type = serialized.type
|
|
43
|
-
.slice(0, -"MessageChunk".length)
|
|
44
|
-
.toLowerCase();
|
|
45
|
-
}
|
|
46
|
-
const message = (0, messages_1.coerceMessageLikeToMessage)(serialized);
|
|
47
|
-
const chunk = tryConvertToChunk(message);
|
|
48
|
-
const { id } = chunk ?? message;
|
|
49
|
-
if (!id) {
|
|
50
|
-
console.warn("No message ID found for chunk, ignoring in state", serialized);
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
this.chunks[id] ??= {};
|
|
54
|
-
this.chunks[id].metadata = metadata ?? this.chunks[id].metadata;
|
|
55
|
-
if (chunk) {
|
|
56
|
-
const prev = this.chunks[id].chunk;
|
|
57
|
-
this.chunks[id].chunk =
|
|
58
|
-
((0, messages_1.isBaseMessageChunk)(prev) ? prev : null)?.concat(chunk) ?? chunk;
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
this.chunks[id].chunk = message;
|
|
62
|
-
}
|
|
63
|
-
return id;
|
|
64
|
-
}
|
|
65
|
-
clear() {
|
|
66
|
-
this.chunks = {};
|
|
67
|
-
}
|
|
68
|
-
get(id, defaultIndex) {
|
|
69
|
-
if (this.chunks[id] == null)
|
|
70
|
-
return null;
|
|
71
|
-
if (defaultIndex != null)
|
|
72
|
-
this.chunks[id].index ??= defaultIndex;
|
|
73
|
-
return this.chunks[id];
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
const toMessageDict = (chunk) => {
|
|
77
|
-
const { type, data } = chunk.toDict();
|
|
78
|
-
return { ...data, type };
|
|
79
|
-
};
|
|
80
|
-
function unique(array) {
|
|
81
|
-
return [...new Set(array)];
|
|
82
|
-
}
|
|
83
|
-
function findLastIndex(array, predicate) {
|
|
84
|
-
for (let i = array.length - 1; i >= 0; i -= 1) {
|
|
85
|
-
if (predicate(array[i]))
|
|
86
|
-
return i;
|
|
87
|
-
}
|
|
88
|
-
return -1;
|
|
89
|
-
}
|
|
90
|
-
function getBranchSequence(history) {
|
|
91
|
-
const childrenMap = {};
|
|
92
|
-
// Short circuit if there's only a singular one state
|
|
93
|
-
// TODO: I think we can make this more generalizable for all `fetchStateHistory` values.
|
|
94
|
-
if (history.length <= 1) {
|
|
95
|
-
return {
|
|
96
|
-
rootSequence: {
|
|
97
|
-
type: "sequence",
|
|
98
|
-
items: history.map((value) => ({ type: "node", value, path: [] })),
|
|
99
|
-
},
|
|
100
|
-
paths: [],
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
// First pass - collect nodes for each checkpoint
|
|
104
|
-
history.forEach((state) => {
|
|
105
|
-
const checkpointId = state.parent_checkpoint?.checkpoint_id ?? "$";
|
|
106
|
-
childrenMap[checkpointId] ??= [];
|
|
107
|
-
childrenMap[checkpointId].push(state);
|
|
108
|
-
});
|
|
109
|
-
const rootSequence = { type: "sequence", items: [] };
|
|
110
|
-
const queue = [{ id: "$", sequence: rootSequence, path: [] }];
|
|
111
|
-
const paths = [];
|
|
112
|
-
const visited = new Set();
|
|
113
|
-
while (queue.length > 0) {
|
|
114
|
-
const task = queue.shift();
|
|
115
|
-
if (visited.has(task.id))
|
|
116
|
-
continue;
|
|
117
|
-
visited.add(task.id);
|
|
118
|
-
const children = childrenMap[task.id];
|
|
119
|
-
if (children == null || children.length === 0)
|
|
120
|
-
continue;
|
|
121
|
-
// If we've encountered a fork (2+ children), push the fork
|
|
122
|
-
// to the sequence and add a new sequence for each child
|
|
123
|
-
let fork;
|
|
124
|
-
if (children.length > 1) {
|
|
125
|
-
fork = { type: "fork", items: [] };
|
|
126
|
-
task.sequence.items.push(fork);
|
|
127
|
-
}
|
|
128
|
-
for (const value of children) {
|
|
129
|
-
const id = value.checkpoint?.checkpoint_id;
|
|
130
|
-
if (id == null)
|
|
131
|
-
continue;
|
|
132
|
-
let { sequence } = task;
|
|
133
|
-
let { path } = task;
|
|
134
|
-
if (fork != null) {
|
|
135
|
-
sequence = { type: "sequence", items: [] };
|
|
136
|
-
fork.items.unshift(sequence);
|
|
137
|
-
path = path.slice();
|
|
138
|
-
path.push(id);
|
|
139
|
-
paths.push(path);
|
|
140
|
-
}
|
|
141
|
-
sequence.items.push({ type: "node", value, path });
|
|
142
|
-
queue.push({ id, sequence, path });
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return { rootSequence, paths };
|
|
146
|
-
}
|
|
147
|
-
const PATH_SEP = ">";
|
|
148
|
-
const ROOT_ID = "$";
|
|
149
|
-
// Get flat view
|
|
150
|
-
function getBranchView(sequence, paths, branch) {
|
|
151
|
-
const path = branch.split(PATH_SEP);
|
|
152
|
-
const pathMap = {};
|
|
153
|
-
for (const path of paths) {
|
|
154
|
-
const parent = path.at(-2) ?? ROOT_ID;
|
|
155
|
-
pathMap[parent] ??= [];
|
|
156
|
-
pathMap[parent].unshift(path);
|
|
157
|
-
}
|
|
158
|
-
const history = [];
|
|
159
|
-
const branchByCheckpoint = {};
|
|
160
|
-
const forkStack = path.slice();
|
|
161
|
-
const queue = [...sequence.items];
|
|
162
|
-
while (queue.length > 0) {
|
|
163
|
-
const item = queue.shift();
|
|
164
|
-
if (item.type === "node") {
|
|
165
|
-
history.push(item.value);
|
|
166
|
-
const checkpointId = item.value.checkpoint?.checkpoint_id;
|
|
167
|
-
if (checkpointId == null)
|
|
168
|
-
continue;
|
|
169
|
-
branchByCheckpoint[checkpointId] = {
|
|
170
|
-
branch: item.path.join(PATH_SEP),
|
|
171
|
-
branchOptions: (item.path.length > 0
|
|
172
|
-
? pathMap[item.path.at(-2) ?? ROOT_ID] ?? []
|
|
173
|
-
: []).map((p) => p.join(PATH_SEP)),
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
if (item.type === "fork") {
|
|
177
|
-
const forkId = forkStack.shift();
|
|
178
|
-
const index = forkId != null
|
|
179
|
-
? item.items.findIndex((value) => {
|
|
180
|
-
const firstItem = value.items.at(0);
|
|
181
|
-
if (!firstItem || firstItem.type !== "node")
|
|
182
|
-
return false;
|
|
183
|
-
return firstItem.value.checkpoint?.checkpoint_id === forkId;
|
|
184
|
-
})
|
|
185
|
-
: -1;
|
|
186
|
-
const nextItems = item.items.at(index)?.items ?? [];
|
|
187
|
-
queue.push(...nextItems);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return { history, branchByCheckpoint };
|
|
191
|
-
}
|
|
13
|
+
const messages_js_1 = require("./messages.cjs");
|
|
192
14
|
function fetchHistory(client, threadId, options) {
|
|
193
15
|
if (options?.limit === false) {
|
|
194
16
|
return client.threads.getState(threadId).then((state) => {
|
|
@@ -197,12 +19,16 @@ function fetchHistory(client, threadId, options) {
|
|
|
197
19
|
return [state];
|
|
198
20
|
});
|
|
199
21
|
}
|
|
200
|
-
const limit = typeof options?.limit === "number" ? options.limit :
|
|
22
|
+
const limit = typeof options?.limit === "number" ? options.limit : 10;
|
|
201
23
|
return client.threads.getHistory(threadId, { limit });
|
|
202
24
|
}
|
|
203
25
|
function useThreadHistory(threadId, client, limit, clearCallbackRef, submittingRef, onErrorRef) {
|
|
204
26
|
const [history, setHistory] = (0, react_1.useState)(undefined);
|
|
205
|
-
const [isLoading, setIsLoading] = (0, react_1.useState)(
|
|
27
|
+
const [isLoading, setIsLoading] = (0, react_1.useState)(() => {
|
|
28
|
+
if (threadId == null)
|
|
29
|
+
return false;
|
|
30
|
+
return true;
|
|
31
|
+
});
|
|
206
32
|
const [error, setError] = (0, react_1.useState)(undefined);
|
|
207
33
|
const clientHash = (0, client_js_1.getClientConfigHash)(client);
|
|
208
34
|
const clientRef = (0, react_1.useRef)(client);
|
|
@@ -236,7 +62,7 @@ function useThreadHistory(threadId, client, limit, clearCallbackRef, submittingR
|
|
|
236
62
|
if (submittingRef.current)
|
|
237
63
|
return;
|
|
238
64
|
void fetcher(threadId);
|
|
239
|
-
}, [fetcher, clientHash, limit,
|
|
65
|
+
}, [fetcher, submittingRef, clientHash, limit, threadId]);
|
|
240
66
|
return {
|
|
241
67
|
data: history,
|
|
242
68
|
isLoading,
|
|
@@ -257,39 +83,7 @@ const useControllableThreadId = (options) => {
|
|
|
257
83
|
}
|
|
258
84
|
return [options.threadId ?? null, onThreadId];
|
|
259
85
|
};
|
|
260
|
-
function useStreamValuesState() {
|
|
261
|
-
const [values, setValues] = (0, react_1.useState)(null);
|
|
262
|
-
const setStreamValues = (0, react_1.useCallback)((values, kind = "stream") => {
|
|
263
|
-
if (typeof values === "function") {
|
|
264
|
-
setValues((prevTuple) => {
|
|
265
|
-
const [prevValues, prevKind] = prevTuple ?? [null, "stream"];
|
|
266
|
-
const next = values(prevValues, prevKind);
|
|
267
|
-
if (next == null)
|
|
268
|
-
return null;
|
|
269
|
-
return [next, kind];
|
|
270
|
-
});
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
if (values == null)
|
|
274
|
-
setValues(null);
|
|
275
|
-
setValues([values, kind]);
|
|
276
|
-
}, []);
|
|
277
|
-
const mutate = (0, react_1.useCallback)((kind, serverValues) => (update) => {
|
|
278
|
-
setStreamValues((clientValues) => {
|
|
279
|
-
const prev = { ...serverValues, ...clientValues };
|
|
280
|
-
const next = typeof update === "function" ? update(prev) : update;
|
|
281
|
-
return { ...prev, ...next };
|
|
282
|
-
}, kind);
|
|
283
|
-
}, [setStreamValues]);
|
|
284
|
-
return [values?.[0] ?? null, setStreamValues, mutate];
|
|
285
|
-
}
|
|
286
86
|
function useStream(options) {
|
|
287
|
-
const matchEventType = (expected, actual, _data) => {
|
|
288
|
-
return expected === actual || actual.startsWith(`${expected}|`);
|
|
289
|
-
};
|
|
290
|
-
let { messagesKey } = options;
|
|
291
|
-
const { assistantId, fetchStateHistory } = options;
|
|
292
|
-
const { onCreated, onError, onFinish } = options;
|
|
293
87
|
const reconnectOnMountRef = (0, react_1.useRef)(options.reconnectOnMount);
|
|
294
88
|
const runMetadataStorage = (0, react_1.useMemo)(() => {
|
|
295
89
|
if (typeof window === "undefined")
|
|
@@ -301,7 +95,6 @@ function useStream(options) {
|
|
|
301
95
|
return storage();
|
|
302
96
|
return null;
|
|
303
97
|
}, []);
|
|
304
|
-
messagesKey ??= "messages";
|
|
305
98
|
const client = (0, react_1.useMemo)(() => options.client ??
|
|
306
99
|
new client_js_1.Client({
|
|
307
100
|
apiUrl: options.apiUrl,
|
|
@@ -315,20 +108,16 @@ function useStream(options) {
|
|
|
315
108
|
options.callerOptions,
|
|
316
109
|
options.defaultHeaders,
|
|
317
110
|
]);
|
|
111
|
+
const [messageManager] = (0, react_1.useState)(() => new messages_js_1.MessageTupleManager());
|
|
112
|
+
const [stream] = (0, react_1.useState)(() => new manager_js_1.StreamManager(messageManager));
|
|
113
|
+
(0, react_1.useSyncExternalStore)(stream.subscribe, stream.getSnapshot, stream.getSnapshot);
|
|
318
114
|
const [threadId, onThreadId] = useControllableThreadId(options);
|
|
319
|
-
const [branch, setBranch] = (0, react_1.useState)("");
|
|
320
|
-
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
|
|
321
|
-
const [streamError, setStreamError] = (0, react_1.useState)(undefined);
|
|
322
|
-
const [streamValues, setStreamValues, getMutateFn] = useStreamValuesState();
|
|
323
|
-
const messageManagerRef = (0, react_1.useRef)(new MessageTupleManager());
|
|
324
|
-
const submittingRef = (0, react_1.useRef)(false);
|
|
325
|
-
const abortRef = (0, react_1.useRef)(null);
|
|
326
115
|
const trackStreamModeRef = (0, react_1.useRef)([]);
|
|
327
116
|
const trackStreamMode = (0, react_1.useCallback)((...mode) => {
|
|
117
|
+
const ref = trackStreamModeRef.current;
|
|
328
118
|
for (const m of mode) {
|
|
329
|
-
if (!
|
|
330
|
-
|
|
331
|
-
}
|
|
119
|
+
if (!ref.includes(m))
|
|
120
|
+
ref.push(m);
|
|
332
121
|
}
|
|
333
122
|
}, []);
|
|
334
123
|
const hasUpdateListener = options.onUpdateEvent != null;
|
|
@@ -361,35 +150,37 @@ function useStream(options) {
|
|
|
361
150
|
hasTaskListener,
|
|
362
151
|
]);
|
|
363
152
|
const clearCallbackRef = (0, react_1.useRef)(null);
|
|
364
|
-
clearCallbackRef.current =
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
messageManagerRef.current.clear();
|
|
368
|
-
};
|
|
153
|
+
clearCallbackRef.current = stream.clear;
|
|
154
|
+
const submittingRef = (0, react_1.useRef)(false);
|
|
155
|
+
submittingRef.current = stream.isLoading;
|
|
369
156
|
const onErrorRef = (0, react_1.useRef)(undefined);
|
|
370
157
|
onErrorRef.current = options.onError;
|
|
371
|
-
const historyLimit = typeof fetchStateHistory === "object" &&
|
|
372
|
-
|
|
373
|
-
|
|
158
|
+
const historyLimit = typeof options.fetchStateHistory === "object" &&
|
|
159
|
+
options.fetchStateHistory != null
|
|
160
|
+
? options.fetchStateHistory.limit ?? false
|
|
161
|
+
: options.fetchStateHistory ?? false;
|
|
374
162
|
const history = useThreadHistory(threadId, client, historyLimit, clearCallbackRef, submittingRef, onErrorRef);
|
|
375
|
-
const getMessages = (
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
const
|
|
384
|
-
const
|
|
385
|
-
|
|
163
|
+
const getMessages = (value) => {
|
|
164
|
+
const messagesKey = options.messagesKey ?? "messages";
|
|
165
|
+
return Array.isArray(value[messagesKey]) ? value[messagesKey] : [];
|
|
166
|
+
};
|
|
167
|
+
const setMessages = (current, messages) => {
|
|
168
|
+
const messagesKey = options.messagesKey ?? "messages";
|
|
169
|
+
return { ...current, [messagesKey]: messages };
|
|
170
|
+
};
|
|
171
|
+
const [branch, setBranch] = (0, react_1.useState)("");
|
|
172
|
+
const branchContext = (0, branching_js_1.getBranchContext)(branch, history.data);
|
|
173
|
+
const historyValues = branchContext.threadHead?.values ??
|
|
174
|
+
options.initialValues ??
|
|
175
|
+
{};
|
|
176
|
+
const historyError = (() => {
|
|
177
|
+
const error = branchContext.threadHead?.tasks?.at(-1)?.error;
|
|
386
178
|
if (error == null)
|
|
387
179
|
return undefined;
|
|
388
180
|
try {
|
|
389
181
|
const parsed = JSON.parse(error);
|
|
390
|
-
if (StreamError.isStructuredError(parsed))
|
|
391
|
-
return new StreamError(parsed);
|
|
392
|
-
}
|
|
182
|
+
if (errors_js_1.StreamError.isStructuredError(parsed))
|
|
183
|
+
return new errors_js_1.StreamError(parsed);
|
|
393
184
|
return parsed;
|
|
394
185
|
}
|
|
395
186
|
catch {
|
|
@@ -401,16 +192,13 @@ function useStream(options) {
|
|
|
401
192
|
const alreadyShown = new Set();
|
|
402
193
|
return getMessages(historyValues).map((message, idx) => {
|
|
403
194
|
const messageId = message.id ?? idx;
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
: undefined;
|
|
407
|
-
const firstSeenIdx = findLastIndex(history.data ?? [], (state) => getMessages(state.values)
|
|
195
|
+
// Find the first checkpoint where the message was seen
|
|
196
|
+
const firstSeenState = (0, utils_js_1.findLast)(history.data ?? [], (state) => getMessages(state.values)
|
|
408
197
|
.map((m, idx) => m.id ?? idx)
|
|
409
198
|
.includes(messageId));
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
? branchByCheckpoint[checkpointId]
|
|
199
|
+
const checkpointId = firstSeenState?.checkpoint?.checkpoint_id;
|
|
200
|
+
let branch = checkpointId != null
|
|
201
|
+
? branchContext.branchByCheckpoint[checkpointId]
|
|
414
202
|
: undefined;
|
|
415
203
|
if (!branch?.branch?.length)
|
|
416
204
|
branch = undefined;
|
|
@@ -423,158 +211,38 @@ function useStream(options) {
|
|
|
423
211
|
}
|
|
424
212
|
return {
|
|
425
213
|
messageId: messageId.toString(),
|
|
426
|
-
firstSeenState
|
|
214
|
+
firstSeenState,
|
|
427
215
|
branch: branch?.branch,
|
|
428
216
|
branchOptions: branch?.branchOptions,
|
|
429
|
-
streamMetadata,
|
|
430
217
|
};
|
|
431
218
|
});
|
|
432
219
|
})();
|
|
433
|
-
const stop = () => {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
setStreamError(undefined);
|
|
450
|
-
submittingRef.current = true;
|
|
451
|
-
abortRef.current = new AbortController();
|
|
452
|
-
const run = await action(abortRef.current.signal);
|
|
453
|
-
getCallbackMeta = run.getCallbackMeta;
|
|
454
|
-
let streamError;
|
|
455
|
-
for await (const { event, data } of run.stream) {
|
|
456
|
-
if (event === "error") {
|
|
457
|
-
streamError = new StreamError(data);
|
|
458
|
-
break;
|
|
459
|
-
}
|
|
460
|
-
const namespace = event.includes("|")
|
|
461
|
-
? event.split("|").slice(1)
|
|
462
|
-
: undefined;
|
|
463
|
-
const mutate = getMutateFn("stream", historyValues);
|
|
464
|
-
if (event === "metadata")
|
|
465
|
-
options.onMetadataEvent?.(data);
|
|
466
|
-
if (event === "events")
|
|
467
|
-
options.onLangChainEvent?.(data);
|
|
468
|
-
if (matchEventType("updates", event, data)) {
|
|
469
|
-
options.onUpdateEvent?.(data, { namespace, mutate });
|
|
470
|
-
}
|
|
471
|
-
if (matchEventType("custom", event, data)) {
|
|
472
|
-
options.onCustomEvent?.(data, { namespace, mutate });
|
|
473
|
-
}
|
|
474
|
-
if (matchEventType("checkpoints", event, data)) {
|
|
475
|
-
options.onCheckpointEvent?.(data, { namespace });
|
|
476
|
-
}
|
|
477
|
-
if (matchEventType("tasks", event, data)) {
|
|
478
|
-
options.onTaskEvent?.(data, { namespace });
|
|
479
|
-
}
|
|
480
|
-
if (matchEventType("debug", event, data)) {
|
|
481
|
-
options.onDebugEvent?.(data, { namespace });
|
|
482
|
-
}
|
|
483
|
-
if (event === "values") {
|
|
484
|
-
// don't update values on interrupt values event
|
|
485
|
-
if ("__interrupt__" in data)
|
|
486
|
-
continue;
|
|
487
|
-
setStreamValues(data);
|
|
488
|
-
}
|
|
489
|
-
// Consume subgraph messages as well
|
|
490
|
-
if (matchEventType("messages", event, data)) {
|
|
491
|
-
const [serialized, metadata] = data;
|
|
492
|
-
const messageId = messageManagerRef.current.add(serialized, metadata);
|
|
493
|
-
if (!messageId) {
|
|
494
|
-
console.warn("Failed to add message to manager, no message ID found");
|
|
495
|
-
continue;
|
|
496
|
-
}
|
|
497
|
-
setStreamValues((streamValues) => {
|
|
498
|
-
const values = { ...historyValues, ...streamValues };
|
|
499
|
-
// Assumption: we're concatenating the message
|
|
500
|
-
const messages = getMessages(values).slice();
|
|
501
|
-
const { chunk, index } = messageManagerRef.current.get(messageId, messages.length) ?? {};
|
|
502
|
-
if (!chunk || index == null)
|
|
503
|
-
return values;
|
|
504
|
-
messages[index] = toMessageDict(chunk);
|
|
505
|
-
return { ...values, [messagesKey]: messages };
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
// TODO: stream created checkpoints to avoid an unnecessary network request
|
|
510
|
-
const result = await run.onSuccess();
|
|
511
|
-
setStreamValues((values, kind) => {
|
|
512
|
-
// Do not clear out the user values set on `stop`.
|
|
513
|
-
if (kind === "stop")
|
|
514
|
-
return values;
|
|
515
|
-
return null;
|
|
516
|
-
});
|
|
517
|
-
if (streamError != null)
|
|
518
|
-
throw streamError;
|
|
519
|
-
const lastHead = result.at(0);
|
|
520
|
-
if (lastHead)
|
|
521
|
-
onFinish?.(lastHead, getCallbackMeta?.());
|
|
522
|
-
}
|
|
523
|
-
catch (error) {
|
|
524
|
-
if (!(error instanceof Error && // eslint-disable-line no-instanceof/no-instanceof
|
|
525
|
-
(error.name === "AbortError" || error.name === "TimeoutError"))) {
|
|
526
|
-
console.error(error);
|
|
527
|
-
setStreamError(error);
|
|
528
|
-
onError?.(error, getCallbackMeta?.());
|
|
220
|
+
const stop = () => stream.stop(historyValues, { onStop: options.onStop });
|
|
221
|
+
// --- TRANSPORT ---
|
|
222
|
+
const submit = async (values, submitOptions) => {
|
|
223
|
+
// Unbranch things
|
|
224
|
+
const checkpointId = submitOptions?.checkpoint?.checkpoint_id;
|
|
225
|
+
setBranch(checkpointId != null
|
|
226
|
+
? branchContext.branchByCheckpoint[checkpointId]?.branch ?? ""
|
|
227
|
+
: "");
|
|
228
|
+
stream.setStreamValues(() => {
|
|
229
|
+
if (submitOptions?.optimisticValues != null) {
|
|
230
|
+
return {
|
|
231
|
+
...historyValues,
|
|
232
|
+
...(typeof submitOptions.optimisticValues === "function"
|
|
233
|
+
? submitOptions.optimisticValues(historyValues)
|
|
234
|
+
: submitOptions.optimisticValues),
|
|
235
|
+
};
|
|
529
236
|
}
|
|
530
|
-
|
|
531
|
-
finally {
|
|
532
|
-
setIsLoading(false);
|
|
533
|
-
submittingRef.current = false;
|
|
534
|
-
abortRef.current = null;
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
const joinStream = async (runId, lastEventId, options) => {
|
|
538
|
-
// eslint-disable-next-line no-param-reassign
|
|
539
|
-
lastEventId ??= "-1";
|
|
540
|
-
if (!threadId)
|
|
541
|
-
return;
|
|
542
|
-
await consumeStream(async (signal) => {
|
|
543
|
-
const stream = client.runs.joinStream(threadId, runId, {
|
|
544
|
-
signal,
|
|
545
|
-
lastEventId,
|
|
546
|
-
streamMode: options?.streamMode,
|
|
547
|
-
});
|
|
548
|
-
return {
|
|
549
|
-
onSuccess: () => {
|
|
550
|
-
runMetadataStorage?.removeItem(`lg:stream:${threadId}`);
|
|
551
|
-
return history.mutate(threadId);
|
|
552
|
-
},
|
|
553
|
-
stream,
|
|
554
|
-
getCallbackMeta: () => ({ thread_id: threadId, run_id: runId }),
|
|
555
|
-
};
|
|
237
|
+
return { ...historyValues };
|
|
556
238
|
});
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
if (newPath != null)
|
|
565
|
-
setBranch(newPath ?? "");
|
|
566
|
-
setStreamValues(() => {
|
|
567
|
-
if (submitOptions?.optimisticValues != null) {
|
|
568
|
-
return {
|
|
569
|
-
...historyValues,
|
|
570
|
-
...(typeof submitOptions.optimisticValues === "function"
|
|
571
|
-
? submitOptions.optimisticValues(historyValues)
|
|
572
|
-
: submitOptions.optimisticValues),
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
return { ...historyValues };
|
|
576
|
-
});
|
|
577
|
-
let usableThreadId = threadId;
|
|
239
|
+
// When `fetchStateHistory` is requested, thus we assume that branching
|
|
240
|
+
// is enabled. We then need to include the implicit branch.
|
|
241
|
+
const includeImplicitBranch = historyLimit === true || typeof historyLimit === "number";
|
|
242
|
+
let callbackMeta;
|
|
243
|
+
let rejoinKey;
|
|
244
|
+
let usableThreadId = threadId;
|
|
245
|
+
await stream.start(async (signal) => {
|
|
578
246
|
if (!usableThreadId) {
|
|
579
247
|
const thread = await client.threads.create({
|
|
580
248
|
threadId: submitOptions?.threadId,
|
|
@@ -583,26 +251,28 @@ function useStream(options) {
|
|
|
583
251
|
onThreadId(thread.thread_id);
|
|
584
252
|
usableThreadId = thread.thread_id;
|
|
585
253
|
}
|
|
586
|
-
if (!usableThreadId)
|
|
254
|
+
if (!usableThreadId) {
|
|
587
255
|
throw new Error("Failed to obtain valid thread ID.");
|
|
588
|
-
|
|
256
|
+
}
|
|
257
|
+
const streamMode = (0, utils_js_1.unique)([
|
|
589
258
|
...(submitOptions?.streamMode ?? []),
|
|
590
259
|
...trackStreamModeRef.current,
|
|
591
260
|
...callbackStreamMode,
|
|
592
261
|
]);
|
|
593
|
-
let checkpoint = submitOptions?.checkpoint ??
|
|
262
|
+
let checkpoint = submitOptions?.checkpoint ??
|
|
263
|
+
(includeImplicitBranch
|
|
264
|
+
? branchContext.threadHead?.checkpoint
|
|
265
|
+
: undefined) ??
|
|
266
|
+
undefined;
|
|
594
267
|
// Avoid specifying a checkpoint if user explicitly set it to null
|
|
595
|
-
if (submitOptions?.checkpoint === null)
|
|
268
|
+
if (submitOptions?.checkpoint === null)
|
|
596
269
|
checkpoint = undefined;
|
|
597
|
-
}
|
|
598
270
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
599
271
|
// @ts-expect-error
|
|
600
272
|
if (checkpoint != null)
|
|
601
273
|
delete checkpoint.thread_id;
|
|
602
|
-
let rejoinKey;
|
|
603
|
-
let callbackMeta;
|
|
604
274
|
const streamResumable = submitOptions?.streamResumable ?? !!runMetadataStorage;
|
|
605
|
-
|
|
275
|
+
return client.runs.stream(usableThreadId, options.assistantId, {
|
|
606
276
|
input: values,
|
|
607
277
|
config: submitOptions?.config,
|
|
608
278
|
context: submitOptions?.context,
|
|
@@ -619,31 +289,84 @@ function useStream(options) {
|
|
|
619
289
|
streamMode,
|
|
620
290
|
streamSubgraphs: submitOptions?.streamSubgraphs,
|
|
621
291
|
streamResumable,
|
|
292
|
+
durability: submitOptions?.durability,
|
|
622
293
|
onRunCreated(params) {
|
|
623
294
|
callbackMeta = {
|
|
624
295
|
run_id: params.run_id,
|
|
625
296
|
thread_id: params.thread_id ?? usableThreadId,
|
|
626
297
|
};
|
|
627
298
|
if (runMetadataStorage) {
|
|
628
|
-
rejoinKey = `lg:stream:${
|
|
299
|
+
rejoinKey = `lg:stream:${usableThreadId}`;
|
|
629
300
|
runMetadataStorage.setItem(rejoinKey, callbackMeta.run_id);
|
|
630
301
|
}
|
|
631
|
-
onCreated?.(callbackMeta);
|
|
302
|
+
options.onCreated?.(callbackMeta);
|
|
632
303
|
},
|
|
633
304
|
});
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
305
|
+
}, {
|
|
306
|
+
getMessages,
|
|
307
|
+
setMessages,
|
|
308
|
+
initialValues: historyValues,
|
|
309
|
+
callbacks: options,
|
|
310
|
+
async onSuccess() {
|
|
311
|
+
if (rejoinKey)
|
|
312
|
+
runMetadataStorage?.removeItem(rejoinKey);
|
|
313
|
+
const shouldRefetch =
|
|
314
|
+
// We're expecting the whole thread state in onFinish
|
|
315
|
+
options.onFinish != null ||
|
|
316
|
+
// We're fetching history, thus we need the latest checkpoint
|
|
317
|
+
// to ensure we're not accidentally submitting to a wrong branch
|
|
318
|
+
includeImplicitBranch;
|
|
319
|
+
if (shouldRefetch) {
|
|
320
|
+
const newHistory = await history.mutate(usableThreadId);
|
|
321
|
+
const lastHead = newHistory.at(0);
|
|
322
|
+
if (lastHead) {
|
|
323
|
+
// We now have the latest update from /history
|
|
324
|
+
// Thus we can clear the local stream state
|
|
325
|
+
options.onFinish?.(lastHead, callbackMeta);
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return undefined;
|
|
330
|
+
},
|
|
331
|
+
onError(error) {
|
|
332
|
+
options.onError?.(error, callbackMeta);
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
};
|
|
336
|
+
const joinStream = async (runId, lastEventId, joinOptions) => {
|
|
337
|
+
// eslint-disable-next-line no-param-reassign
|
|
338
|
+
lastEventId ??= "-1";
|
|
339
|
+
if (!threadId)
|
|
340
|
+
return;
|
|
341
|
+
const callbackMeta = {
|
|
342
|
+
thread_id: threadId,
|
|
343
|
+
run_id: runId,
|
|
344
|
+
};
|
|
345
|
+
await stream.start(async (signal) => {
|
|
346
|
+
return client.runs.joinStream(threadId, runId, {
|
|
347
|
+
signal,
|
|
348
|
+
lastEventId,
|
|
349
|
+
streamMode: joinOptions?.streamMode,
|
|
350
|
+
});
|
|
351
|
+
}, {
|
|
352
|
+
getMessages,
|
|
353
|
+
setMessages,
|
|
354
|
+
initialValues: historyValues,
|
|
355
|
+
callbacks: options,
|
|
356
|
+
async onSuccess() {
|
|
357
|
+
runMetadataStorage?.removeItem(`lg:stream:${threadId}`);
|
|
358
|
+
const newHistory = await history.mutate(threadId);
|
|
359
|
+
const lastHead = newHistory.at(0);
|
|
360
|
+
if (lastHead)
|
|
361
|
+
options.onFinish?.(lastHead, callbackMeta);
|
|
362
|
+
},
|
|
363
|
+
onError(error) {
|
|
364
|
+
options.onError?.(error, callbackMeta);
|
|
365
|
+
},
|
|
643
366
|
});
|
|
644
367
|
};
|
|
645
368
|
const reconnectKey = (0, react_1.useMemo)(() => {
|
|
646
|
-
if (!runMetadataStorage || isLoading)
|
|
369
|
+
if (!runMetadataStorage || stream.isLoading)
|
|
647
370
|
return undefined;
|
|
648
371
|
if (typeof window === "undefined")
|
|
649
372
|
return undefined;
|
|
@@ -651,7 +374,7 @@ function useStream(options) {
|
|
|
651
374
|
if (!runId)
|
|
652
375
|
return undefined;
|
|
653
376
|
return { runId, threadId };
|
|
654
|
-
}, [runMetadataStorage, isLoading, threadId]);
|
|
377
|
+
}, [runMetadataStorage, stream.isLoading, threadId]);
|
|
655
378
|
const shouldReconnect = !!runMetadataStorage;
|
|
656
379
|
const reconnectRef = (0, react_1.useRef)({ threadId, shouldReconnect });
|
|
657
380
|
const joinStreamRef = (0, react_1.useRef)(joinStream);
|
|
@@ -668,38 +391,44 @@ function useStream(options) {
|
|
|
668
391
|
void joinStreamRef.current?.(reconnectKey.runId);
|
|
669
392
|
}
|
|
670
393
|
}, [reconnectKey]);
|
|
671
|
-
|
|
672
|
-
const
|
|
394
|
+
// --- END TRANSPORT ---
|
|
395
|
+
const error = stream.error ?? historyError ?? history.error;
|
|
396
|
+
const values = stream.values ?? historyValues;
|
|
673
397
|
return {
|
|
674
398
|
get values() {
|
|
675
399
|
trackStreamMode("values");
|
|
676
400
|
return values;
|
|
677
401
|
},
|
|
678
402
|
client,
|
|
679
|
-
assistantId,
|
|
403
|
+
assistantId: options.assistantId,
|
|
680
404
|
error,
|
|
681
|
-
isLoading,
|
|
405
|
+
isLoading: stream.isLoading,
|
|
682
406
|
stop,
|
|
683
|
-
submit,
|
|
407
|
+
submit,
|
|
684
408
|
joinStream,
|
|
685
409
|
branch,
|
|
686
410
|
setBranch,
|
|
687
|
-
history
|
|
411
|
+
get history() {
|
|
412
|
+
if (historyLimit === false) {
|
|
413
|
+
throw new Error("`fetchStateHistory` must be set to `true` to use `history`");
|
|
414
|
+
}
|
|
415
|
+
return branchContext.flatHistory;
|
|
416
|
+
},
|
|
688
417
|
isThreadLoading: history.isLoading && history.data == null,
|
|
689
418
|
get experimental_branchTree() {
|
|
690
419
|
if (historyLimit === false) {
|
|
691
|
-
throw new Error("`
|
|
420
|
+
throw new Error("`fetchStateHistory` must be set to `true` to use `experimental_branchTree`");
|
|
692
421
|
}
|
|
693
|
-
return
|
|
422
|
+
return branchContext.branchTree;
|
|
694
423
|
},
|
|
695
424
|
get interrupt() {
|
|
696
425
|
// Don't show the interrupt if the stream is loading
|
|
697
|
-
if (isLoading)
|
|
426
|
+
if (stream.isLoading)
|
|
698
427
|
return undefined;
|
|
699
|
-
const interrupts = threadHead?.tasks?.at(-1)?.interrupts;
|
|
428
|
+
const interrupts = branchContext.threadHead?.tasks?.at(-1)?.interrupts;
|
|
700
429
|
if (interrupts == null || interrupts.length === 0) {
|
|
701
430
|
// check if there's a next task present
|
|
702
|
-
const next = threadHead?.next ?? [];
|
|
431
|
+
const next = branchContext.threadHead?.next ?? [];
|
|
703
432
|
if (!next.length || error != null)
|
|
704
433
|
return undefined;
|
|
705
434
|
return { when: "breakpoint" };
|
|
@@ -713,7 +442,15 @@ function useStream(options) {
|
|
|
713
442
|
},
|
|
714
443
|
getMessagesMetadata(message, index) {
|
|
715
444
|
trackStreamMode("values");
|
|
716
|
-
|
|
445
|
+
const streamMetadata = messageManager.get(message.id)?.metadata;
|
|
446
|
+
const historyMetadata = messageMetadata?.find((m) => m.messageId === (message.id ?? index));
|
|
447
|
+
if (streamMetadata != null || historyMetadata != null) {
|
|
448
|
+
return {
|
|
449
|
+
...historyMetadata,
|
|
450
|
+
streamMetadata,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
return undefined;
|
|
717
454
|
},
|
|
718
455
|
};
|
|
719
456
|
}
|