@langchain/langgraph-sdk 0.0.112 → 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 +12 -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 +167 -435
- package/dist/react/stream.d.ts +1 -323
- package/dist/react/stream.js +166 -434
- 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/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,7 +19,7 @@ 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) {
|
|
@@ -261,39 +83,7 @@ const useControllableThreadId = (options) => {
|
|
|
261
83
|
}
|
|
262
84
|
return [options.threadId ?? null, onThreadId];
|
|
263
85
|
};
|
|
264
|
-
function useStreamValuesState() {
|
|
265
|
-
const [values, setValues] = (0, react_1.useState)(null);
|
|
266
|
-
const setStreamValues = (0, react_1.useCallback)((values, kind = "stream") => {
|
|
267
|
-
if (typeof values === "function") {
|
|
268
|
-
setValues((prevTuple) => {
|
|
269
|
-
const [prevValues, prevKind] = prevTuple ?? [null, "stream"];
|
|
270
|
-
const next = values(prevValues, prevKind);
|
|
271
|
-
if (next == null)
|
|
272
|
-
return null;
|
|
273
|
-
return [next, kind];
|
|
274
|
-
});
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
if (values == null)
|
|
278
|
-
setValues(null);
|
|
279
|
-
setValues([values, kind]);
|
|
280
|
-
}, []);
|
|
281
|
-
const mutate = (0, react_1.useCallback)((kind, serverValues) => (update) => {
|
|
282
|
-
setStreamValues((clientValues) => {
|
|
283
|
-
const prev = { ...serverValues, ...clientValues };
|
|
284
|
-
const next = typeof update === "function" ? update(prev) : update;
|
|
285
|
-
return { ...prev, ...next };
|
|
286
|
-
}, kind);
|
|
287
|
-
}, [setStreamValues]);
|
|
288
|
-
return [values?.[0] ?? null, setStreamValues, mutate];
|
|
289
|
-
}
|
|
290
86
|
function useStream(options) {
|
|
291
|
-
const matchEventType = (expected, actual, _data) => {
|
|
292
|
-
return expected === actual || actual.startsWith(`${expected}|`);
|
|
293
|
-
};
|
|
294
|
-
let { messagesKey } = options;
|
|
295
|
-
const { assistantId, fetchStateHistory } = options;
|
|
296
|
-
const { onCreated, onError, onFinish } = options;
|
|
297
87
|
const reconnectOnMountRef = (0, react_1.useRef)(options.reconnectOnMount);
|
|
298
88
|
const runMetadataStorage = (0, react_1.useMemo)(() => {
|
|
299
89
|
if (typeof window === "undefined")
|
|
@@ -305,7 +95,6 @@ function useStream(options) {
|
|
|
305
95
|
return storage();
|
|
306
96
|
return null;
|
|
307
97
|
}, []);
|
|
308
|
-
messagesKey ??= "messages";
|
|
309
98
|
const client = (0, react_1.useMemo)(() => options.client ??
|
|
310
99
|
new client_js_1.Client({
|
|
311
100
|
apiUrl: options.apiUrl,
|
|
@@ -319,20 +108,16 @@ function useStream(options) {
|
|
|
319
108
|
options.callerOptions,
|
|
320
109
|
options.defaultHeaders,
|
|
321
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);
|
|
322
114
|
const [threadId, onThreadId] = useControllableThreadId(options);
|
|
323
|
-
const [branch, setBranch] = (0, react_1.useState)("");
|
|
324
|
-
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
|
|
325
|
-
const [streamError, setStreamError] = (0, react_1.useState)(undefined);
|
|
326
|
-
const [streamValues, setStreamValues, getMutateFn] = useStreamValuesState();
|
|
327
|
-
const messageManagerRef = (0, react_1.useRef)(new MessageTupleManager());
|
|
328
|
-
const submittingRef = (0, react_1.useRef)(false);
|
|
329
|
-
const abortRef = (0, react_1.useRef)(null);
|
|
330
115
|
const trackStreamModeRef = (0, react_1.useRef)([]);
|
|
331
116
|
const trackStreamMode = (0, react_1.useCallback)((...mode) => {
|
|
117
|
+
const ref = trackStreamModeRef.current;
|
|
332
118
|
for (const m of mode) {
|
|
333
|
-
if (!
|
|
334
|
-
|
|
335
|
-
}
|
|
119
|
+
if (!ref.includes(m))
|
|
120
|
+
ref.push(m);
|
|
336
121
|
}
|
|
337
122
|
}, []);
|
|
338
123
|
const hasUpdateListener = options.onUpdateEvent != null;
|
|
@@ -365,35 +150,37 @@ function useStream(options) {
|
|
|
365
150
|
hasTaskListener,
|
|
366
151
|
]);
|
|
367
152
|
const clearCallbackRef = (0, react_1.useRef)(null);
|
|
368
|
-
clearCallbackRef.current =
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
messageManagerRef.current.clear();
|
|
372
|
-
};
|
|
153
|
+
clearCallbackRef.current = stream.clear;
|
|
154
|
+
const submittingRef = (0, react_1.useRef)(false);
|
|
155
|
+
submittingRef.current = stream.isLoading;
|
|
373
156
|
const onErrorRef = (0, react_1.useRef)(undefined);
|
|
374
157
|
onErrorRef.current = options.onError;
|
|
375
|
-
const historyLimit = typeof fetchStateHistory === "object" &&
|
|
376
|
-
|
|
377
|
-
|
|
158
|
+
const historyLimit = typeof options.fetchStateHistory === "object" &&
|
|
159
|
+
options.fetchStateHistory != null
|
|
160
|
+
? options.fetchStateHistory.limit ?? false
|
|
161
|
+
: options.fetchStateHistory ?? false;
|
|
378
162
|
const history = useThreadHistory(threadId, client, historyLimit, clearCallbackRef, submittingRef, onErrorRef);
|
|
379
|
-
const getMessages = (
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const
|
|
388
|
-
const
|
|
389
|
-
|
|
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;
|
|
390
178
|
if (error == null)
|
|
391
179
|
return undefined;
|
|
392
180
|
try {
|
|
393
181
|
const parsed = JSON.parse(error);
|
|
394
|
-
if (StreamError.isStructuredError(parsed))
|
|
395
|
-
return new StreamError(parsed);
|
|
396
|
-
}
|
|
182
|
+
if (errors_js_1.StreamError.isStructuredError(parsed))
|
|
183
|
+
return new errors_js_1.StreamError(parsed);
|
|
397
184
|
return parsed;
|
|
398
185
|
}
|
|
399
186
|
catch {
|
|
@@ -405,16 +192,13 @@ function useStream(options) {
|
|
|
405
192
|
const alreadyShown = new Set();
|
|
406
193
|
return getMessages(historyValues).map((message, idx) => {
|
|
407
194
|
const messageId = message.id ?? idx;
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
: undefined;
|
|
411
|
-
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)
|
|
412
197
|
.map((m, idx) => m.id ?? idx)
|
|
413
198
|
.includes(messageId));
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
? branchByCheckpoint[checkpointId]
|
|
199
|
+
const checkpointId = firstSeenState?.checkpoint?.checkpoint_id;
|
|
200
|
+
let branch = checkpointId != null
|
|
201
|
+
? branchContext.branchByCheckpoint[checkpointId]
|
|
418
202
|
: undefined;
|
|
419
203
|
if (!branch?.branch?.length)
|
|
420
204
|
branch = undefined;
|
|
@@ -427,158 +211,38 @@ function useStream(options) {
|
|
|
427
211
|
}
|
|
428
212
|
return {
|
|
429
213
|
messageId: messageId.toString(),
|
|
430
|
-
firstSeenState
|
|
214
|
+
firstSeenState,
|
|
431
215
|
branch: branch?.branch,
|
|
432
216
|
branchOptions: branch?.branchOptions,
|
|
433
|
-
streamMetadata,
|
|
434
217
|
};
|
|
435
218
|
});
|
|
436
219
|
})();
|
|
437
|
-
const stop = () => {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
setStreamError(undefined);
|
|
454
|
-
submittingRef.current = true;
|
|
455
|
-
abortRef.current = new AbortController();
|
|
456
|
-
const run = await action(abortRef.current.signal);
|
|
457
|
-
getCallbackMeta = run.getCallbackMeta;
|
|
458
|
-
let streamError;
|
|
459
|
-
for await (const { event, data } of run.stream) {
|
|
460
|
-
if (event === "error") {
|
|
461
|
-
streamError = new StreamError(data);
|
|
462
|
-
break;
|
|
463
|
-
}
|
|
464
|
-
const namespace = event.includes("|")
|
|
465
|
-
? event.split("|").slice(1)
|
|
466
|
-
: undefined;
|
|
467
|
-
const mutate = getMutateFn("stream", historyValues);
|
|
468
|
-
if (event === "metadata")
|
|
469
|
-
options.onMetadataEvent?.(data);
|
|
470
|
-
if (event === "events")
|
|
471
|
-
options.onLangChainEvent?.(data);
|
|
472
|
-
if (matchEventType("updates", event, data)) {
|
|
473
|
-
options.onUpdateEvent?.(data, { namespace, mutate });
|
|
474
|
-
}
|
|
475
|
-
if (matchEventType("custom", event, data)) {
|
|
476
|
-
options.onCustomEvent?.(data, { namespace, mutate });
|
|
477
|
-
}
|
|
478
|
-
if (matchEventType("checkpoints", event, data)) {
|
|
479
|
-
options.onCheckpointEvent?.(data, { namespace });
|
|
480
|
-
}
|
|
481
|
-
if (matchEventType("tasks", event, data)) {
|
|
482
|
-
options.onTaskEvent?.(data, { namespace });
|
|
483
|
-
}
|
|
484
|
-
if (matchEventType("debug", event, data)) {
|
|
485
|
-
options.onDebugEvent?.(data, { namespace });
|
|
486
|
-
}
|
|
487
|
-
if (event === "values") {
|
|
488
|
-
// don't update values on interrupt values event
|
|
489
|
-
if ("__interrupt__" in data)
|
|
490
|
-
continue;
|
|
491
|
-
setStreamValues(data);
|
|
492
|
-
}
|
|
493
|
-
// Consume subgraph messages as well
|
|
494
|
-
if (matchEventType("messages", event, data)) {
|
|
495
|
-
const [serialized, metadata] = data;
|
|
496
|
-
const messageId = messageManagerRef.current.add(serialized, metadata);
|
|
497
|
-
if (!messageId) {
|
|
498
|
-
console.warn("Failed to add message to manager, no message ID found");
|
|
499
|
-
continue;
|
|
500
|
-
}
|
|
501
|
-
setStreamValues((streamValues) => {
|
|
502
|
-
const values = { ...historyValues, ...streamValues };
|
|
503
|
-
// Assumption: we're concatenating the message
|
|
504
|
-
const messages = getMessages(values).slice();
|
|
505
|
-
const { chunk, index } = messageManagerRef.current.get(messageId, messages.length) ?? {};
|
|
506
|
-
if (!chunk || index == null)
|
|
507
|
-
return values;
|
|
508
|
-
messages[index] = toMessageDict(chunk);
|
|
509
|
-
return { ...values, [messagesKey]: messages };
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
// TODO: stream created checkpoints to avoid an unnecessary network request
|
|
514
|
-
const result = await run.onSuccess();
|
|
515
|
-
setStreamValues((values, kind) => {
|
|
516
|
-
// Do not clear out the user values set on `stop`.
|
|
517
|
-
if (kind === "stop")
|
|
518
|
-
return values;
|
|
519
|
-
return null;
|
|
520
|
-
});
|
|
521
|
-
if (streamError != null)
|
|
522
|
-
throw streamError;
|
|
523
|
-
const lastHead = result.at(0);
|
|
524
|
-
if (lastHead)
|
|
525
|
-
onFinish?.(lastHead, getCallbackMeta?.());
|
|
526
|
-
}
|
|
527
|
-
catch (error) {
|
|
528
|
-
if (!(error instanceof Error && // eslint-disable-line no-instanceof/no-instanceof
|
|
529
|
-
(error.name === "AbortError" || error.name === "TimeoutError"))) {
|
|
530
|
-
console.error(error);
|
|
531
|
-
setStreamError(error);
|
|
532
|
-
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
|
+
};
|
|
533
236
|
}
|
|
534
|
-
|
|
535
|
-
finally {
|
|
536
|
-
setIsLoading(false);
|
|
537
|
-
submittingRef.current = false;
|
|
538
|
-
abortRef.current = null;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
const joinStream = async (runId, lastEventId, options) => {
|
|
542
|
-
// eslint-disable-next-line no-param-reassign
|
|
543
|
-
lastEventId ??= "-1";
|
|
544
|
-
if (!threadId)
|
|
545
|
-
return;
|
|
546
|
-
await consumeStream(async (signal) => {
|
|
547
|
-
const stream = client.runs.joinStream(threadId, runId, {
|
|
548
|
-
signal,
|
|
549
|
-
lastEventId,
|
|
550
|
-
streamMode: options?.streamMode,
|
|
551
|
-
});
|
|
552
|
-
return {
|
|
553
|
-
onSuccess: () => {
|
|
554
|
-
runMetadataStorage?.removeItem(`lg:stream:${threadId}`);
|
|
555
|
-
return history.mutate(threadId);
|
|
556
|
-
},
|
|
557
|
-
stream,
|
|
558
|
-
getCallbackMeta: () => ({ thread_id: threadId, run_id: runId }),
|
|
559
|
-
};
|
|
237
|
+
return { ...historyValues };
|
|
560
238
|
});
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
if (newPath != null)
|
|
569
|
-
setBranch(newPath ?? "");
|
|
570
|
-
setStreamValues(() => {
|
|
571
|
-
if (submitOptions?.optimisticValues != null) {
|
|
572
|
-
return {
|
|
573
|
-
...historyValues,
|
|
574
|
-
...(typeof submitOptions.optimisticValues === "function"
|
|
575
|
-
? submitOptions.optimisticValues(historyValues)
|
|
576
|
-
: submitOptions.optimisticValues),
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
return { ...historyValues };
|
|
580
|
-
});
|
|
581
|
-
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) => {
|
|
582
246
|
if (!usableThreadId) {
|
|
583
247
|
const thread = await client.threads.create({
|
|
584
248
|
threadId: submitOptions?.threadId,
|
|
@@ -587,26 +251,28 @@ function useStream(options) {
|
|
|
587
251
|
onThreadId(thread.thread_id);
|
|
588
252
|
usableThreadId = thread.thread_id;
|
|
589
253
|
}
|
|
590
|
-
if (!usableThreadId)
|
|
254
|
+
if (!usableThreadId) {
|
|
591
255
|
throw new Error("Failed to obtain valid thread ID.");
|
|
592
|
-
|
|
256
|
+
}
|
|
257
|
+
const streamMode = (0, utils_js_1.unique)([
|
|
593
258
|
...(submitOptions?.streamMode ?? []),
|
|
594
259
|
...trackStreamModeRef.current,
|
|
595
260
|
...callbackStreamMode,
|
|
596
261
|
]);
|
|
597
|
-
let checkpoint = submitOptions?.checkpoint ??
|
|
262
|
+
let checkpoint = submitOptions?.checkpoint ??
|
|
263
|
+
(includeImplicitBranch
|
|
264
|
+
? branchContext.threadHead?.checkpoint
|
|
265
|
+
: undefined) ??
|
|
266
|
+
undefined;
|
|
598
267
|
// Avoid specifying a checkpoint if user explicitly set it to null
|
|
599
|
-
if (submitOptions?.checkpoint === null)
|
|
268
|
+
if (submitOptions?.checkpoint === null)
|
|
600
269
|
checkpoint = undefined;
|
|
601
|
-
}
|
|
602
270
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
603
271
|
// @ts-expect-error
|
|
604
272
|
if (checkpoint != null)
|
|
605
273
|
delete checkpoint.thread_id;
|
|
606
|
-
let rejoinKey;
|
|
607
|
-
let callbackMeta;
|
|
608
274
|
const streamResumable = submitOptions?.streamResumable ?? !!runMetadataStorage;
|
|
609
|
-
|
|
275
|
+
return client.runs.stream(usableThreadId, options.assistantId, {
|
|
610
276
|
input: values,
|
|
611
277
|
config: submitOptions?.config,
|
|
612
278
|
context: submitOptions?.context,
|
|
@@ -630,25 +296,77 @@ function useStream(options) {
|
|
|
630
296
|
thread_id: params.thread_id ?? usableThreadId,
|
|
631
297
|
};
|
|
632
298
|
if (runMetadataStorage) {
|
|
633
|
-
rejoinKey = `lg:stream:${
|
|
299
|
+
rejoinKey = `lg:stream:${usableThreadId}`;
|
|
634
300
|
runMetadataStorage.setItem(rejoinKey, callbackMeta.run_id);
|
|
635
301
|
}
|
|
636
|
-
onCreated?.(callbackMeta);
|
|
302
|
+
options.onCreated?.(callbackMeta);
|
|
637
303
|
},
|
|
638
304
|
});
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
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
|
+
},
|
|
648
366
|
});
|
|
649
367
|
};
|
|
650
368
|
const reconnectKey = (0, react_1.useMemo)(() => {
|
|
651
|
-
if (!runMetadataStorage || isLoading)
|
|
369
|
+
if (!runMetadataStorage || stream.isLoading)
|
|
652
370
|
return undefined;
|
|
653
371
|
if (typeof window === "undefined")
|
|
654
372
|
return undefined;
|
|
@@ -656,7 +374,7 @@ function useStream(options) {
|
|
|
656
374
|
if (!runId)
|
|
657
375
|
return undefined;
|
|
658
376
|
return { runId, threadId };
|
|
659
|
-
}, [runMetadataStorage, isLoading, threadId]);
|
|
377
|
+
}, [runMetadataStorage, stream.isLoading, threadId]);
|
|
660
378
|
const shouldReconnect = !!runMetadataStorage;
|
|
661
379
|
const reconnectRef = (0, react_1.useRef)({ threadId, shouldReconnect });
|
|
662
380
|
const joinStreamRef = (0, react_1.useRef)(joinStream);
|
|
@@ -673,38 +391,44 @@ function useStream(options) {
|
|
|
673
391
|
void joinStreamRef.current?.(reconnectKey.runId);
|
|
674
392
|
}
|
|
675
393
|
}, [reconnectKey]);
|
|
676
|
-
|
|
677
|
-
const
|
|
394
|
+
// --- END TRANSPORT ---
|
|
395
|
+
const error = stream.error ?? historyError ?? history.error;
|
|
396
|
+
const values = stream.values ?? historyValues;
|
|
678
397
|
return {
|
|
679
398
|
get values() {
|
|
680
399
|
trackStreamMode("values");
|
|
681
400
|
return values;
|
|
682
401
|
},
|
|
683
402
|
client,
|
|
684
|
-
assistantId,
|
|
403
|
+
assistantId: options.assistantId,
|
|
685
404
|
error,
|
|
686
|
-
isLoading,
|
|
405
|
+
isLoading: stream.isLoading,
|
|
687
406
|
stop,
|
|
688
|
-
submit,
|
|
407
|
+
submit,
|
|
689
408
|
joinStream,
|
|
690
409
|
branch,
|
|
691
410
|
setBranch,
|
|
692
|
-
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
|
+
},
|
|
693
417
|
isThreadLoading: history.isLoading && history.data == null,
|
|
694
418
|
get experimental_branchTree() {
|
|
695
419
|
if (historyLimit === false) {
|
|
696
|
-
throw new Error("`
|
|
420
|
+
throw new Error("`fetchStateHistory` must be set to `true` to use `experimental_branchTree`");
|
|
697
421
|
}
|
|
698
|
-
return
|
|
422
|
+
return branchContext.branchTree;
|
|
699
423
|
},
|
|
700
424
|
get interrupt() {
|
|
701
425
|
// Don't show the interrupt if the stream is loading
|
|
702
|
-
if (isLoading)
|
|
426
|
+
if (stream.isLoading)
|
|
703
427
|
return undefined;
|
|
704
|
-
const interrupts = threadHead?.tasks?.at(-1)?.interrupts;
|
|
428
|
+
const interrupts = branchContext.threadHead?.tasks?.at(-1)?.interrupts;
|
|
705
429
|
if (interrupts == null || interrupts.length === 0) {
|
|
706
430
|
// check if there's a next task present
|
|
707
|
-
const next = threadHead?.next ?? [];
|
|
431
|
+
const next = branchContext.threadHead?.next ?? [];
|
|
708
432
|
if (!next.length || error != null)
|
|
709
433
|
return undefined;
|
|
710
434
|
return { when: "breakpoint" };
|
|
@@ -718,7 +442,15 @@ function useStream(options) {
|
|
|
718
442
|
},
|
|
719
443
|
getMessagesMetadata(message, index) {
|
|
720
444
|
trackStreamMode("values");
|
|
721
|
-
|
|
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;
|
|
722
454
|
},
|
|
723
455
|
};
|
|
724
456
|
}
|