@assistant-ui/react-langchain 0.0.34 → 0.0.36
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/dist/LangChainThreadController.d.ts +9 -0
- package/dist/LangChainThreadController.d.ts.map +1 -0
- package/dist/LangChainThreadController.js +21 -0
- package/dist/LangChainThreadController.js.map +1 -0
- package/dist/convertMessages.d.ts +2 -1
- package/dist/convertMessages.d.ts.map +1 -1
- package/dist/convertMessages.js +12 -12
- package/dist/convertMessages.js.map +1 -1
- package/dist/converter.d.ts +24 -1
- package/dist/converter.d.ts.map +1 -1
- package/dist/converter.js +84 -18
- package/dist/converter.js.map +1 -1
- package/dist/langChainThreadState.d.ts +54 -0
- package/dist/langChainThreadState.d.ts.map +1 -0
- package/dist/langChainThreadState.js +135 -0
- package/dist/langChainThreadState.js.map +1 -0
- package/dist/sdkIdentity.js +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/useStreamRuntime.d.ts +3 -2
- package/dist/useStreamRuntime.d.ts.map +1 -1
- package/dist/useStreamRuntime.js +159 -144
- package/dist/useStreamRuntime.js.map +1 -1
- package/dist/useSubagentTranscripts.js +1 -1
- package/package.json +9 -8
- package/src/LangChainThreadController.ts +24 -0
- package/src/convertMessages.test.ts +23 -0
- package/src/convertMessages.ts +43 -17
- package/src/converter.test.ts +322 -0
- package/src/converter.ts +138 -27
- package/src/getPendingToolCalls.test.ts +35 -0
- package/src/langChainThreadState.test.ts +203 -0
- package/src/langChainThreadState.ts +239 -0
- package/src/types.ts +9 -0
- package/src/useStreamRuntime.cloud-options.test.tsx +61 -0
- package/src/useStreamRuntime.test.tsx +296 -8
- package/src/useStreamRuntime.ts +221 -249
package/src/useStreamRuntime.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
useMemo,
|
|
9
9
|
useRef,
|
|
10
10
|
useState,
|
|
11
|
+
useSyncExternalStore,
|
|
11
12
|
} from "react";
|
|
12
13
|
import type { AppendMessage, ToolExecutionStatus } from "@assistant-ui/core";
|
|
13
14
|
import {
|
|
@@ -39,9 +40,11 @@ import type {
|
|
|
39
40
|
import { getMessageModality, groupUIMessagesByParent } from "./converter";
|
|
40
41
|
export { groupUIMessagesByParent } from "./converter";
|
|
41
42
|
import {
|
|
43
|
+
createLangChainMetadataKey,
|
|
42
44
|
convertLangChainBaseMessage,
|
|
43
45
|
getMessageContent,
|
|
44
46
|
getMessageType,
|
|
47
|
+
type LangChainMessageConverterMetadata,
|
|
45
48
|
} from "./convertMessages";
|
|
46
49
|
import {
|
|
47
50
|
attachSubagentTranscripts,
|
|
@@ -60,6 +63,8 @@ import { langChainExtras } from "./runtimeExtras";
|
|
|
60
63
|
import { resolveForkCheckpoint } from "./resolveForkCheckpoint";
|
|
61
64
|
import { useLangChainStreamingTiming } from "./streamingTiming";
|
|
62
65
|
import { LANGCHAIN_SDK } from "./sdkIdentity";
|
|
66
|
+
import { LangChainThreadController } from "./LangChainThreadController";
|
|
67
|
+
import type { LangChainThreadAction } from "./langChainThreadState";
|
|
63
68
|
|
|
64
69
|
export const runConfigToSubmitOptions = (
|
|
65
70
|
runConfig: AppendMessage["runConfig"],
|
|
@@ -72,14 +77,20 @@ type NormalizedRunConfigOptions = NonNullable<
|
|
|
72
77
|
ReturnType<typeof runConfigToSubmitOptions>
|
|
73
78
|
>;
|
|
74
79
|
|
|
75
|
-
const getPendingToolCalls = (
|
|
80
|
+
export const getPendingToolCalls = (
|
|
76
81
|
messages: readonly LangChainBaseMessage[],
|
|
77
82
|
): LangChainToolCall[] =>
|
|
78
83
|
scanPendingToolCalls(
|
|
79
84
|
messages,
|
|
80
85
|
(message) => {
|
|
81
86
|
const type = getMessageType(message);
|
|
82
|
-
if (type === "ai")
|
|
87
|
+
if (type === "ai") {
|
|
88
|
+
return {
|
|
89
|
+
toolCalls: (message.tool_calls ?? []).filter(
|
|
90
|
+
(toolCall) => typeof toolCall === "object" && toolCall !== null,
|
|
91
|
+
),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
83
94
|
if (type === "tool" && message.tool_call_id) {
|
|
84
95
|
return { toolCallId: message.tool_call_id };
|
|
85
96
|
}
|
|
@@ -106,26 +117,6 @@ const toStagedMessageInput = (message: LangChainBaseMessage) => ({
|
|
|
106
117
|
}),
|
|
107
118
|
});
|
|
108
119
|
|
|
109
|
-
const humanContentText = (content: LangChainBaseMessage["content"]) => {
|
|
110
|
-
if (typeof content === "string") return content;
|
|
111
|
-
if (!Array.isArray(content)) return "";
|
|
112
|
-
return content
|
|
113
|
-
.filter(
|
|
114
|
-
(part): part is { type: "text"; text: string } =>
|
|
115
|
-
typeof part === "object" &&
|
|
116
|
-
part !== null &&
|
|
117
|
-
part.type === "text" &&
|
|
118
|
-
typeof part.text === "string",
|
|
119
|
-
)
|
|
120
|
-
.map((part) => part.text)
|
|
121
|
-
.join("");
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const hasSameMessageContent = (
|
|
125
|
-
a: LangChainBaseMessage,
|
|
126
|
-
b: LangChainBaseMessage,
|
|
127
|
-
) => humanContentText(a.content) === humanContentText(b.content);
|
|
128
|
-
|
|
129
120
|
const truncateLangChainBaseMessages = (
|
|
130
121
|
threadMessages: readonly ThreadMessage[],
|
|
131
122
|
parentId: string | null,
|
|
@@ -149,7 +140,7 @@ type DistributiveOmit<T, K extends keyof any> = T extends unknown
|
|
|
149
140
|
const useStreamThreadRuntime = (
|
|
150
141
|
options: DistributiveOmit<
|
|
151
142
|
UseStreamRuntimeOptions,
|
|
152
|
-
"cloud" | "unstable_threadListAdapter" | "create" | "delete"
|
|
143
|
+
"cloud" | "scopeId" | "unstable_threadListAdapter" | "create" | "delete"
|
|
153
144
|
>,
|
|
154
145
|
) => {
|
|
155
146
|
const { adapters, autoCancelPendingToolCalls, unstable_allowCancellation } =
|
|
@@ -166,9 +157,19 @@ const useStreamThreadRuntime = (
|
|
|
166
157
|
const stream = useStream(
|
|
167
158
|
Object.assign({}, options, { threadId: externalId }),
|
|
168
159
|
);
|
|
169
|
-
const [
|
|
170
|
-
|
|
171
|
-
|
|
160
|
+
const [threadController] = useState(() => new LangChainThreadController());
|
|
161
|
+
const getVisibleStagedMessages = () =>
|
|
162
|
+
threadController.getState().visibleStagedMessages;
|
|
163
|
+
const visibleStagedMessages = useSyncExternalStore(
|
|
164
|
+
threadController.subscribe,
|
|
165
|
+
getVisibleStagedMessages,
|
|
166
|
+
getVisibleStagedMessages,
|
|
167
|
+
);
|
|
168
|
+
const forkPending = useSyncExternalStore(
|
|
169
|
+
threadController.subscribe,
|
|
170
|
+
() => threadController.getState().forkPending,
|
|
171
|
+
() => threadController.getState().forkPending,
|
|
172
|
+
);
|
|
172
173
|
|
|
173
174
|
const [toolStatuses, setToolStatuses] = useState<
|
|
174
175
|
Record<string, ToolExecutionStatus>
|
|
@@ -202,7 +203,7 @@ const useStreamThreadRuntime = (
|
|
|
202
203
|
);
|
|
203
204
|
|
|
204
205
|
const visibleMessages =
|
|
205
|
-
|
|
206
|
+
visibleStagedMessages ?? (stream.messages as LangChainBaseMessage[]);
|
|
206
207
|
|
|
207
208
|
const messageTiming = useLangChainStreamingTiming(
|
|
208
209
|
visibleMessages,
|
|
@@ -214,22 +215,21 @@ const useStreamThreadRuntime = (
|
|
|
214
215
|
uiMessagesByParent,
|
|
215
216
|
);
|
|
216
217
|
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
uiMessagesByParent,
|
|
224
|
-
messageTiming,
|
|
225
|
-
}),
|
|
218
|
+
const [getConverterMetadataKey] = useState(createLangChainMetadataKey);
|
|
219
|
+
const converterMetadata = useMemo<LangChainMessageConverterMetadata>(
|
|
220
|
+
() => ({
|
|
221
|
+
uiMessagesByParent: uiMessagesByParent,
|
|
222
|
+
messageTiming,
|
|
223
|
+
}),
|
|
226
224
|
[uiMessagesByParent, messageTiming],
|
|
227
225
|
);
|
|
228
226
|
|
|
229
227
|
const threadMessages = useExternalMessageConverter({
|
|
230
|
-
callback:
|
|
228
|
+
callback: convertLangChainBaseMessage,
|
|
231
229
|
messages: visibleMessages,
|
|
232
230
|
isRunning: effectiveIsRunning,
|
|
231
|
+
metadata: converterMetadata,
|
|
232
|
+
getMetadataKey: getConverterMetadataKey,
|
|
233
233
|
});
|
|
234
234
|
const [memo] = useState(createAttachMemo);
|
|
235
235
|
const messagesWithTranscripts = useMemo(
|
|
@@ -308,85 +308,42 @@ const useStreamThreadRuntime = (
|
|
|
308
308
|
visibleMessagesRef.current = visibleMessages;
|
|
309
309
|
}, [visibleMessages]);
|
|
310
310
|
|
|
311
|
+
const dispatchStaging = useCallback(
|
|
312
|
+
(action: LangChainThreadAction, fallback = visibleMessagesRef.current) => {
|
|
313
|
+
threadController.dispatch(action);
|
|
314
|
+
visibleMessagesRef.current =
|
|
315
|
+
threadController.getState().visibleStagedMessages ?? fallback;
|
|
316
|
+
},
|
|
317
|
+
[threadController],
|
|
318
|
+
);
|
|
319
|
+
|
|
311
320
|
const threadMessagesRef = useRef(messagesWithTranscripts);
|
|
312
321
|
useInsertionEffect(() => {
|
|
313
322
|
threadMessagesRef.current = messagesWithTranscripts;
|
|
314
323
|
}, [messagesWithTranscripts]);
|
|
315
324
|
|
|
316
|
-
const stagedMessagesRef = useRef(
|
|
317
|
-
new Map<
|
|
318
|
-
string,
|
|
319
|
-
{
|
|
320
|
-
message: LangChainBaseMessage & { id: string };
|
|
321
|
-
runConfig: AppendMessage["runConfig"];
|
|
322
|
-
reconcileOnEcho: boolean;
|
|
323
|
-
baseMessageCount: number;
|
|
324
|
-
transcriptStatus?: "unsent" | "sent";
|
|
325
|
-
}
|
|
326
|
-
>(),
|
|
327
|
-
);
|
|
328
|
-
const stagedBaseMessagesRef = useRef<LangChainBaseMessage[] | null>(null);
|
|
329
325
|
useEffect(() => {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
326
|
+
const state = threadController.getState();
|
|
327
|
+
if (state.stagedEntries.size === 0) return;
|
|
328
|
+
const messages = stream.messages as LangChainBaseMessage[];
|
|
329
|
+
dispatchStaging(
|
|
330
|
+
{
|
|
331
|
+
type: "reconcile",
|
|
332
|
+
messages,
|
|
333
|
+
visibleMessages: visibleMessagesRef.current,
|
|
334
|
+
},
|
|
335
|
+
state.stagedBaseMessages ?? messages,
|
|
340
336
|
);
|
|
341
|
-
|
|
342
|
-
if (!visibleStagedIds.has(id)) continue;
|
|
343
|
-
const echoed = baseMessages.some((message, index) => {
|
|
344
|
-
if (matchedBaseMessageIndexes.has(index)) return false;
|
|
345
|
-
if (message.id === id) {
|
|
346
|
-
matchedBaseMessageIndexes.add(index);
|
|
347
|
-
return true;
|
|
348
|
-
}
|
|
349
|
-
if (
|
|
350
|
-
!staged.reconcileOnEcho ||
|
|
351
|
-
index < staged.baseMessageCount ||
|
|
352
|
-
getMessageType(message) !== "human" ||
|
|
353
|
-
!hasSameMessageContent(message, staged.message)
|
|
354
|
-
) {
|
|
355
|
-
return false;
|
|
356
|
-
}
|
|
357
|
-
matchedBaseMessageIndexes.add(index);
|
|
358
|
-
return true;
|
|
359
|
-
});
|
|
360
|
-
if (echoed) stagedMessagesRef.current.delete(id);
|
|
361
|
-
else remainingStagedMessages.push(staged.message);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
if (remainingStagedMessages.length === 0) {
|
|
365
|
-
stagedBaseMessagesRef.current = null;
|
|
366
|
-
visibleMessagesRef.current = baseMessages;
|
|
367
|
-
// Reconciling against the upstream stream mutates the staged refs above,
|
|
368
|
-
// which cannot happen during render.
|
|
369
|
-
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
370
|
-
setStagedMessages(null);
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
const nextMessages = [...baseMessages, ...remainingStagedMessages];
|
|
375
|
-
visibleMessagesRef.current = nextMessages;
|
|
376
|
-
setStagedMessages(nextMessages);
|
|
377
|
-
}, [stream.messages]);
|
|
337
|
+
}, [stream.messages, threadController, dispatchStaging]);
|
|
378
338
|
|
|
379
339
|
const getStagedRun = (parentId: string | null) => {
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
: undefined;
|
|
340
|
+
const stagedEntries = threadController.getState().stagedEntries;
|
|
341
|
+
const parent = parentId ? stagedEntries.get(parentId) : undefined;
|
|
383
342
|
if (!parent || parent.transcriptStatus === "sent") return null;
|
|
384
343
|
|
|
385
344
|
const staged: LangChainBaseMessage[] = [];
|
|
386
345
|
for (const message of visibleMessagesRef.current) {
|
|
387
|
-
const entry = message.id
|
|
388
|
-
? stagedMessagesRef.current.get(message.id)
|
|
389
|
-
: undefined;
|
|
346
|
+
const entry = message.id ? stagedEntries.get(message.id) : undefined;
|
|
390
347
|
if (entry && entry.transcriptStatus !== "sent") {
|
|
391
348
|
staged.push(entry.message);
|
|
392
349
|
}
|
|
@@ -408,36 +365,33 @@ const useStreamThreadRuntime = (
|
|
|
408
365
|
additional_kwargs: { modality: message.metadata.modality },
|
|
409
366
|
}),
|
|
410
367
|
};
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
368
|
+
dispatchStaging({
|
|
369
|
+
type: "stage",
|
|
370
|
+
entry: {
|
|
371
|
+
message: transcript,
|
|
372
|
+
runConfig: undefined,
|
|
373
|
+
reconcileOnEcho: false,
|
|
374
|
+
baseMessageCount: streamRef.current.messages.length,
|
|
375
|
+
transcriptStatus: "unsent",
|
|
376
|
+
},
|
|
377
|
+
visibleMessages: visibleMessagesRef.current,
|
|
417
378
|
});
|
|
418
|
-
const nextMessages = [...visibleMessagesRef.current, transcript];
|
|
419
|
-
visibleMessagesRef.current = nextMessages;
|
|
420
|
-
setStagedMessages(nextMessages);
|
|
421
379
|
};
|
|
422
380
|
|
|
423
|
-
const getUnsentTranscripts = () =>
|
|
424
|
-
|
|
381
|
+
const getUnsentTranscripts = () => {
|
|
382
|
+
const stagedEntries = threadController.getState().stagedEntries;
|
|
383
|
+
return visibleMessagesRef.current.filter(
|
|
425
384
|
(message) =>
|
|
426
385
|
message.id !== undefined &&
|
|
427
|
-
|
|
428
|
-
"unsent",
|
|
386
|
+
stagedEntries.get(message.id)?.transcriptStatus === "unsent",
|
|
429
387
|
);
|
|
388
|
+
};
|
|
430
389
|
|
|
431
390
|
const setTranscriptStatus = (
|
|
432
391
|
messages: readonly LangChainBaseMessage[],
|
|
433
392
|
status: "unsent" | "sent",
|
|
434
393
|
) => {
|
|
435
|
-
|
|
436
|
-
const staged = message.id
|
|
437
|
-
? stagedMessagesRef.current.get(message.id)
|
|
438
|
-
: undefined;
|
|
439
|
-
if (staged?.transcriptStatus) staged.transcriptStatus = status;
|
|
440
|
-
}
|
|
394
|
+
dispatchStaging({ type: "markTranscript", messages, status });
|
|
441
395
|
};
|
|
442
396
|
|
|
443
397
|
// Reserving before the submit keeps an overlapping submit from carrying the
|
|
@@ -459,7 +413,8 @@ const useStreamThreadRuntime = (
|
|
|
459
413
|
for (const message of messages) {
|
|
460
414
|
if (
|
|
461
415
|
message.id &&
|
|
462
|
-
|
|
416
|
+
threadController.getState().stagedEntries.get(message.id)
|
|
417
|
+
?.transcriptStatus
|
|
463
418
|
)
|
|
464
419
|
removeStagedMessage(message.id);
|
|
465
420
|
}
|
|
@@ -467,7 +422,7 @@ const useStreamThreadRuntime = (
|
|
|
467
422
|
|
|
468
423
|
const isTranscriptMessage = (message: LangChainBaseMessage) => {
|
|
469
424
|
const staged = message.id
|
|
470
|
-
?
|
|
425
|
+
? threadController.getState().stagedEntries.get(message.id)
|
|
471
426
|
: undefined;
|
|
472
427
|
if (staged?.transcriptStatus === "unsent") return true;
|
|
473
428
|
const type = getMessageType(message);
|
|
@@ -497,30 +452,32 @@ const useStreamThreadRuntime = (
|
|
|
497
452
|
|
|
498
453
|
const stageUserMessage = (msg: AppendMessage, reconcileOnEcho = false) => {
|
|
499
454
|
const stagedMessage = toStagedHumanMessage(msg);
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
455
|
+
dispatchStaging({
|
|
456
|
+
type: "stage",
|
|
457
|
+
entry: {
|
|
458
|
+
message: stagedMessage,
|
|
459
|
+
runConfig: msg.runConfig,
|
|
460
|
+
reconcileOnEcho,
|
|
461
|
+
baseMessageCount: streamRef.current.messages.length,
|
|
462
|
+
},
|
|
463
|
+
visibleMessages: visibleMessagesRef.current,
|
|
505
464
|
});
|
|
506
|
-
const nextMessages = [...visibleMessagesRef.current, stagedMessage];
|
|
507
|
-
visibleMessagesRef.current = nextMessages;
|
|
508
|
-
setStagedMessages(nextMessages);
|
|
509
465
|
return stagedMessage;
|
|
510
466
|
};
|
|
511
467
|
|
|
512
468
|
const removeStagedMessage = (id: string) => {
|
|
513
|
-
if (!
|
|
514
|
-
const
|
|
515
|
-
|
|
469
|
+
if (!threadController.getState().stagedEntries.has(id)) return;
|
|
470
|
+
const visible = visibleMessagesRef.current;
|
|
471
|
+
const nextMessages = visible.filter((message) => message.id !== id);
|
|
472
|
+
dispatchStaging(
|
|
473
|
+
{ type: "remove", id, visibleMessages: visible },
|
|
474
|
+
nextMessages,
|
|
516
475
|
);
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
setStagedMessages(nextMessages);
|
|
523
|
-
}
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
const supersedeFork = () => {
|
|
479
|
+
threadController.dispatch({ type: "supersedeFork" });
|
|
480
|
+
return threadController.getState().forkGeneration;
|
|
524
481
|
};
|
|
525
482
|
|
|
526
483
|
const extras = useMemo(
|
|
@@ -552,7 +509,7 @@ const useStreamThreadRuntime = (
|
|
|
552
509
|
|
|
553
510
|
const runtime = useExternalStoreRuntime({
|
|
554
511
|
...pickExternalStoreSharedOptions(options),
|
|
555
|
-
isRunning: stream.isLoading,
|
|
512
|
+
isRunning: stream.isLoading || forkPending,
|
|
556
513
|
isLoading: stream.isThreadLoading,
|
|
557
514
|
messages: messagesWithTranscripts,
|
|
558
515
|
adapters,
|
|
@@ -638,129 +595,142 @@ const useStreamThreadRuntime = (
|
|
|
638
595
|
);
|
|
639
596
|
},
|
|
640
597
|
onReload: async (parentId, config) => {
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
)
|
|
647
|
-
|
|
648
|
-
const promotedIds = new Set<string>();
|
|
649
|
-
for (const message of stagedRun.messages) {
|
|
598
|
+
const forkGeneration = supersedeFork();
|
|
599
|
+
try {
|
|
600
|
+
await streamRef.current.stop();
|
|
601
|
+
if (threadController.getState().forkGeneration !== forkGeneration)
|
|
602
|
+
return;
|
|
603
|
+
const stagedRun = getStagedRun(parentId);
|
|
604
|
+
if (stagedRun) {
|
|
650
605
|
if (
|
|
651
|
-
|
|
652
|
-
|
|
606
|
+
config.sourceId &&
|
|
607
|
+
threadController.getState().stagedEntries.get(config.sourceId)
|
|
608
|
+
?.transcriptStatus
|
|
653
609
|
)
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const
|
|
661
|
-
|
|
610
|
+
removeStagedMessage(config.sourceId);
|
|
611
|
+
dispatchStaging({
|
|
612
|
+
type: "promote",
|
|
613
|
+
messages: stagedRun.messages,
|
|
614
|
+
visibleMessages: visibleMessagesRef.current,
|
|
615
|
+
});
|
|
616
|
+
const runConfig = config.runConfig ?? stagedRun.runConfig;
|
|
617
|
+
setActiveRunConfig(runConfig);
|
|
618
|
+
await submitCarryingTranscripts(stagedRun.messages, () =>
|
|
619
|
+
stream.submit(
|
|
620
|
+
{
|
|
621
|
+
[messagesKey]: stagedRun.messages.map(toStagedMessageInput),
|
|
622
|
+
},
|
|
623
|
+
runConfigToSubmitOptions(runConfig),
|
|
624
|
+
),
|
|
662
625
|
);
|
|
663
|
-
|
|
664
|
-
setStagedMessages(nextMessages);
|
|
665
|
-
} else {
|
|
666
|
-
setStagedMessages(null);
|
|
626
|
+
return;
|
|
667
627
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
628
|
+
|
|
629
|
+
const threadId = externalId;
|
|
630
|
+
if (!threadId || parentId == null) return;
|
|
631
|
+
const s = streamRef.current;
|
|
632
|
+
const fork = planForkTranscripts(parentId);
|
|
633
|
+
const checkpointId = await resolveForkCheckpoint(
|
|
634
|
+
s.client,
|
|
635
|
+
threadId,
|
|
636
|
+
s.messages as readonly LangChainBaseMessage[],
|
|
637
|
+
fork.forkParentId,
|
|
638
|
+
config.sourceId,
|
|
639
|
+
s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(),
|
|
640
|
+
messagesKey,
|
|
641
|
+
);
|
|
642
|
+
if (threadController.getState().forkGeneration !== forkGeneration)
|
|
643
|
+
return;
|
|
644
|
+
if (!checkpointId) return;
|
|
645
|
+
dropTranscripts(fork.truncated);
|
|
646
|
+
setActiveRunConfig(config.runConfig);
|
|
647
|
+
await submitCarryingTranscripts(fork.transcripts, () =>
|
|
648
|
+
s.submit(
|
|
649
|
+
fork.transcripts.length > 0
|
|
650
|
+
? { [messagesKey]: fork.transcripts.map(toStagedMessageInput) }
|
|
651
|
+
: null,
|
|
672
652
|
{
|
|
673
|
-
|
|
653
|
+
forkFrom: checkpointId,
|
|
654
|
+
...runConfigToSubmitOptions(config.runConfig),
|
|
674
655
|
},
|
|
675
|
-
runConfigToSubmitOptions(runConfig),
|
|
676
656
|
),
|
|
677
657
|
);
|
|
678
|
-
|
|
658
|
+
} finally {
|
|
659
|
+
threadController.dispatch({
|
|
660
|
+
type: "finishFork",
|
|
661
|
+
generation: forkGeneration,
|
|
662
|
+
});
|
|
679
663
|
}
|
|
680
|
-
|
|
681
|
-
const threadId = externalId;
|
|
682
|
-
if (!threadId || parentId == null) return;
|
|
683
|
-
const s = streamRef.current;
|
|
684
|
-
const fork = planForkTranscripts(parentId);
|
|
685
|
-
const checkpointId = await resolveForkCheckpoint(
|
|
686
|
-
s.client,
|
|
687
|
-
threadId,
|
|
688
|
-
s.messages as readonly LangChainBaseMessage[],
|
|
689
|
-
fork.forkParentId,
|
|
690
|
-
config.sourceId,
|
|
691
|
-
s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(),
|
|
692
|
-
messagesKey,
|
|
693
|
-
);
|
|
694
|
-
if (!checkpointId) return;
|
|
695
|
-
dropTranscripts(fork.truncated);
|
|
696
|
-
setActiveRunConfig(config.runConfig);
|
|
697
|
-
await submitCarryingTranscripts(fork.transcripts, () =>
|
|
698
|
-
s.submit(
|
|
699
|
-
fork.transcripts.length > 0
|
|
700
|
-
? { [messagesKey]: fork.transcripts.map(toStagedMessageInput) }
|
|
701
|
-
: null,
|
|
702
|
-
{
|
|
703
|
-
forkFrom: checkpointId,
|
|
704
|
-
...runConfigToSubmitOptions(config.runConfig),
|
|
705
|
-
},
|
|
706
|
-
),
|
|
707
|
-
);
|
|
708
664
|
},
|
|
709
665
|
onEdit: async (message) => {
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
666
|
+
const forkGeneration = supersedeFork();
|
|
667
|
+
try {
|
|
668
|
+
await streamRef.current.stop();
|
|
669
|
+
if (threadController.getState().forkGeneration !== forkGeneration)
|
|
670
|
+
return;
|
|
671
|
+
if (!(message.startRun ?? message.role === "user")) {
|
|
672
|
+
const truncated = truncateLangChainBaseMessages(
|
|
673
|
+
threadMessagesRef.current,
|
|
674
|
+
message.parentId,
|
|
675
|
+
);
|
|
676
|
+
const stagedMessage = toStagedHumanMessage(message);
|
|
677
|
+
dispatchStaging({
|
|
678
|
+
type: "stageEdit",
|
|
679
|
+
entry: {
|
|
680
|
+
message: stagedMessage,
|
|
681
|
+
runConfig: message.runConfig,
|
|
682
|
+
reconcileOnEcho: false,
|
|
683
|
+
baseMessageCount: 0,
|
|
684
|
+
},
|
|
685
|
+
baseMessages: truncated,
|
|
686
|
+
});
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
const threadId = externalId;
|
|
691
|
+
if (!threadId) return;
|
|
692
|
+
const s = streamRef.current;
|
|
693
|
+
const fork = planForkTranscripts(message.parentId);
|
|
694
|
+
const checkpointId = await resolveForkCheckpoint(
|
|
695
|
+
s.client,
|
|
696
|
+
threadId,
|
|
697
|
+
s.messages as readonly LangChainBaseMessage[],
|
|
698
|
+
fork.forkParentId,
|
|
699
|
+
message.sourceId,
|
|
700
|
+
s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(),
|
|
701
|
+
messagesKey,
|
|
702
|
+
);
|
|
703
|
+
if (threadController.getState().forkGeneration !== forkGeneration)
|
|
704
|
+
return;
|
|
705
|
+
if (!checkpointId) return;
|
|
706
|
+
dropTranscripts(fork.truncated);
|
|
707
|
+
const content = getMessageContent(message);
|
|
708
|
+
setActiveRunConfig(message.runConfig);
|
|
709
|
+
await submitCarryingTranscripts(fork.transcripts, () =>
|
|
710
|
+
s.submit(
|
|
711
|
+
{
|
|
712
|
+
[messagesKey]: [
|
|
713
|
+
...fork.transcripts.map(toStagedMessageInput),
|
|
714
|
+
{ type: "human", content },
|
|
715
|
+
],
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
forkFrom: checkpointId,
|
|
719
|
+
...runConfigToSubmitOptions(message.runConfig),
|
|
720
|
+
},
|
|
721
|
+
),
|
|
714
722
|
);
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
reconcileOnEcho: false,
|
|
720
|
-
baseMessageCount: 0,
|
|
723
|
+
} finally {
|
|
724
|
+
threadController.dispatch({
|
|
725
|
+
type: "finishFork",
|
|
726
|
+
generation: forkGeneration,
|
|
721
727
|
});
|
|
722
|
-
stagedBaseMessagesRef.current = truncated;
|
|
723
|
-
const nextMessages = [...truncated, stagedMessage];
|
|
724
|
-
visibleMessagesRef.current = nextMessages;
|
|
725
|
-
setStagedMessages(nextMessages);
|
|
726
|
-
return;
|
|
727
728
|
}
|
|
728
|
-
|
|
729
|
-
const threadId = externalId;
|
|
730
|
-
if (!threadId) return;
|
|
731
|
-
const s = streamRef.current;
|
|
732
|
-
const fork = planForkTranscripts(message.parentId);
|
|
733
|
-
const checkpointId = await resolveForkCheckpoint(
|
|
734
|
-
s.client,
|
|
735
|
-
threadId,
|
|
736
|
-
s.messages as readonly LangChainBaseMessage[],
|
|
737
|
-
fork.forkParentId,
|
|
738
|
-
message.sourceId,
|
|
739
|
-
s[STREAM_CONTROLLER]?.messageMetadataStore?.getSnapshot?.(),
|
|
740
|
-
messagesKey,
|
|
741
|
-
);
|
|
742
|
-
if (!checkpointId) return;
|
|
743
|
-
dropTranscripts(fork.truncated);
|
|
744
|
-
const content = getMessageContent(message);
|
|
745
|
-
setActiveRunConfig(message.runConfig);
|
|
746
|
-
await submitCarryingTranscripts(fork.transcripts, () =>
|
|
747
|
-
s.submit(
|
|
748
|
-
{
|
|
749
|
-
[messagesKey]: [
|
|
750
|
-
...fork.transcripts.map(toStagedMessageInput),
|
|
751
|
-
{ type: "human", content },
|
|
752
|
-
],
|
|
753
|
-
},
|
|
754
|
-
{
|
|
755
|
-
forkFrom: checkpointId,
|
|
756
|
-
...runConfigToSubmitOptions(message.runConfig),
|
|
757
|
-
},
|
|
758
|
-
),
|
|
759
|
-
);
|
|
760
729
|
},
|
|
761
730
|
onCancel:
|
|
762
731
|
unstable_allowCancellation !== false
|
|
763
732
|
? async () => {
|
|
733
|
+
threadController.dispatch({ type: "cancelFork" });
|
|
764
734
|
activeRunConfigRef.current = undefined;
|
|
765
735
|
await stream.stop();
|
|
766
736
|
}
|
|
@@ -773,7 +743,7 @@ const useStreamThreadRuntime = (
|
|
|
773
743
|
/**
|
|
774
744
|
* Creates an assistant-ui runtime backed by LangChain's `useStream` hook.
|
|
775
745
|
* Accepts the same options as `useStream` from `@langchain/react`, plus
|
|
776
|
-
* `cloud` and `adapters`.
|
|
746
|
+
* `cloud`, `scopeId`, and `adapters`.
|
|
777
747
|
*
|
|
778
748
|
* @example
|
|
779
749
|
* ```tsx
|
|
@@ -797,6 +767,7 @@ const useStreamThreadRuntime = (
|
|
|
797
767
|
export const useStreamRuntime = (rawOptions: UseStreamRuntimeOptions) => {
|
|
798
768
|
const {
|
|
799
769
|
cloud,
|
|
770
|
+
scopeId,
|
|
800
771
|
unstable_threadListAdapter,
|
|
801
772
|
create,
|
|
802
773
|
delete: deleteFn,
|
|
@@ -808,6 +779,7 @@ export const useStreamRuntime = (rawOptions: UseStreamRuntimeOptions) => {
|
|
|
808
779
|
const cloudAdapter = useCloudThreadListAdapter({
|
|
809
780
|
sdk: LANGCHAIN_SDK,
|
|
810
781
|
cloud,
|
|
782
|
+
scopeId,
|
|
811
783
|
create: createCloudThreadListAdapterCreateFallback(
|
|
812
784
|
create,
|
|
813
785
|
aui.threadListItem,
|