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