@assistant-ui/react-langchain 0.0.31 → 0.0.33
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/attachSubagentTranscripts.d.ts +19 -12
- package/dist/attachSubagentTranscripts.d.ts.map +1 -1
- package/dist/attachSubagentTranscripts.js +8 -4
- package/dist/attachSubagentTranscripts.js.map +1 -1
- package/dist/convertMessages.d.ts +8 -11
- package/dist/convertMessages.d.ts.map +1 -1
- package/dist/convertMessages.js +53 -15
- package/dist/convertMessages.js.map +1 -1
- package/dist/converter.d.ts +137 -138
- package/dist/converter.d.ts.map +1 -1
- package/dist/converter.js +2 -1
- package/dist/converter.js.map +1 -1
- package/dist/findForkCheckpointInHistory.d.ts +17 -19
- package/dist/findForkCheckpointInHistory.d.ts.map +1 -1
- package/dist/hooks.d.ts +19 -22
- package/dist/hooks.d.ts.map +1 -1
- package/dist/index.d.ts +7 -8
- package/dist/index.d.ts.map +1 -0
- package/dist/resolveForkCheckpoint.d.ts +5 -7
- package/dist/resolveForkCheckpoint.d.ts.map +1 -1
- package/dist/runtimeExtras.d.ts +2 -5
- package/dist/runtimeExtras.d.ts.map +1 -1
- package/dist/sdkIdentity.d.ts +2 -5
- package/dist/sdkIdentity.d.ts.map +1 -1
- package/dist/sdkIdentity.js +1 -1
- package/dist/streamingTiming.d.ts +4 -7
- package/dist/streamingTiming.d.ts.map +1 -1
- package/dist/subagentMessagesProjection.d.ts +17 -0
- package/dist/subagentMessagesProjection.d.ts.map +1 -0
- package/dist/subagentMessagesProjection.js +40 -0
- package/dist/subagentMessagesProjection.js.map +1 -0
- package/dist/types.d.ts +106 -109
- package/dist/types.d.ts.map +1 -1
- package/dist/uiMessages.d.ts +37 -10
- package/dist/uiMessages.d.ts.map +1 -1
- package/dist/uiMessages.js +55 -5
- package/dist/uiMessages.js.map +1 -1
- package/dist/useStreamRuntime.d.ts +9 -12
- package/dist/useStreamRuntime.d.ts.map +1 -1
- package/dist/useStreamRuntime.js +102 -28
- package/dist/useStreamRuntime.js.map +1 -1
- package/dist/useSubagentTranscripts.d.ts +5 -8
- package/dist/useSubagentTranscripts.d.ts.map +1 -1
- package/dist/useSubagentTranscripts.js +129 -34
- package/dist/useSubagentTranscripts.js.map +1 -1
- package/package.json +16 -11
- package/src/attachSubagentTranscripts.test.ts +13 -5
- package/src/attachSubagentTranscripts.ts +26 -7
- package/src/convertMessages.test.ts +69 -0
- package/src/convertMessages.ts +46 -8
- package/src/converter.ts +6 -0
- package/src/subagentMessagesProjection.test.ts +175 -0
- package/src/subagentMessagesProjection.ts +50 -0
- package/src/uiMessages.test.ts +149 -0
- package/src/uiMessages.ts +79 -4
- package/src/useStreamRuntime.test.tsx +212 -0
- package/src/useStreamRuntime.ts +202 -50
- package/src/useStreamRuntime.voice.test.tsx +712 -0
- package/src/useSubagentTranscripts.test.tsx +795 -52
- package/src/useSubagentTranscripts.ts +269 -50
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useEffect, useRef, useSyncExternalStore } from "react";
|
|
4
|
-
import
|
|
4
|
+
import {
|
|
5
|
+
stepStreamingTiming,
|
|
6
|
+
type MessageTiming,
|
|
7
|
+
type StreamingTimingState,
|
|
8
|
+
type ThreadMessage,
|
|
9
|
+
type ToolCallTiming,
|
|
10
|
+
} from "@assistant-ui/core";
|
|
5
11
|
import {
|
|
6
12
|
convertExternalMessages,
|
|
7
13
|
createExternalMessageConversionCache,
|
|
@@ -10,51 +16,91 @@ import {
|
|
|
10
16
|
} from "@assistant-ui/core/react";
|
|
11
17
|
import { STREAM_CONTROLLER, type AnyStream } from "@langchain/react";
|
|
12
18
|
import type { BaseMessage } from "@langchain/core/messages";
|
|
13
|
-
import {
|
|
19
|
+
import { channelProjection, type Event } from "@langchain/langgraph-sdk/stream";
|
|
14
20
|
import type { SubagentDiscoverySnapshot } from "@langchain/react";
|
|
15
21
|
import {
|
|
16
22
|
attachSubagentTranscripts,
|
|
17
23
|
type AttachMemo,
|
|
18
24
|
createAttachMemo,
|
|
25
|
+
type SubagentTranscript,
|
|
19
26
|
} from "./attachSubagentTranscripts";
|
|
20
27
|
import { convertLangChainBaseMessage } from "./convertMessages";
|
|
28
|
+
import { groupUIMessagesByParent } from "./converter";
|
|
29
|
+
import { langChainStreamingTimingAccessors } from "./streamingTiming";
|
|
30
|
+
import { subagentMessagesProjection } from "./subagentMessagesProjection";
|
|
21
31
|
import type { LangChainBaseMessage, UIMessage } from "./types";
|
|
32
|
+
import {
|
|
33
|
+
createUIFoldMemo,
|
|
34
|
+
foldUIUpdates,
|
|
35
|
+
mergeUIMessages,
|
|
36
|
+
UI_CUSTOM_CHANNELS,
|
|
37
|
+
type UIFoldMemo,
|
|
38
|
+
} from "./uiMessages";
|
|
22
39
|
|
|
23
40
|
export const MAX_SUBAGENT_DEPTH = 16;
|
|
24
41
|
|
|
42
|
+
const ROOT_UI_CHANNEL_DEPTH = 1;
|
|
43
|
+
|
|
25
44
|
const TRANSCRIPT_METADATA = {};
|
|
26
45
|
|
|
27
|
-
|
|
28
|
-
|
|
46
|
+
const NO_UI_MESSAGES: readonly UIMessage[] = [];
|
|
47
|
+
|
|
48
|
+
const NO_MESSAGE_TIMING: Record<string, MessageTiming> = {};
|
|
49
|
+
|
|
50
|
+
type ProjectionStore<T> = {
|
|
51
|
+
getSnapshot(): T;
|
|
29
52
|
subscribe(listener: () => void): () => void;
|
|
30
53
|
};
|
|
31
54
|
|
|
32
55
|
type ProjectionResource = {
|
|
33
56
|
snapshot: SubagentDiscoverySnapshot;
|
|
34
57
|
namespace: readonly string[];
|
|
35
|
-
store: ProjectionStore
|
|
36
|
-
|
|
37
|
-
|
|
58
|
+
store: ProjectionStore<BaseMessage[]>;
|
|
59
|
+
uiStore: ProjectionStore<readonly Event[]> | undefined;
|
|
60
|
+
dispose: () => void;
|
|
38
61
|
storeSnapshot: BaseMessage[] | undefined;
|
|
39
62
|
status: SubagentDiscoverySnapshot["status"] | undefined;
|
|
63
|
+
uiFoldMemo: UIFoldMemo;
|
|
64
|
+
localUiMessages: readonly UIMessage[];
|
|
65
|
+
rootUiMessagesByParent: Map<string, UIMessage[]>;
|
|
66
|
+
uiMessagesByParent: Map<string, UIMessage[]>;
|
|
67
|
+
timingState: StreamingTimingState | null;
|
|
68
|
+
messageTiming: Record<string, MessageTiming>;
|
|
69
|
+
convertedMessageTiming: Record<string, MessageTiming> | undefined;
|
|
70
|
+
convert: useExternalMessageConverter.Callback<LangChainBaseMessage>;
|
|
40
71
|
uiMessages: readonly UIMessage[];
|
|
41
72
|
converted: readonly ThreadMessage[] | undefined;
|
|
42
|
-
childTranscripts: ReadonlyMap<string,
|
|
73
|
+
childTranscripts: ReadonlyMap<string, SubagentTranscript> | undefined;
|
|
43
74
|
transcript: readonly ThreadMessage[] | undefined;
|
|
75
|
+
timing: ToolCallTiming | undefined;
|
|
76
|
+
entry: SubagentTranscript | undefined;
|
|
44
77
|
memo: AttachMemo;
|
|
45
78
|
cache: ExternalMessageConversionCache;
|
|
46
79
|
};
|
|
47
80
|
|
|
81
|
+
type MeasuredTiming = {
|
|
82
|
+
timing: ToolCallTiming | undefined;
|
|
83
|
+
timingState: StreamingTimingState | null;
|
|
84
|
+
messageTiming: Record<string, MessageTiming>;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
type NamespaceRequest = {
|
|
88
|
+
id: string;
|
|
89
|
+
attempts: number;
|
|
90
|
+
pending: boolean;
|
|
91
|
+
retryQueued: boolean;
|
|
92
|
+
status: SubagentDiscoverySnapshot["status"];
|
|
93
|
+
};
|
|
94
|
+
|
|
48
95
|
type SubagentTranscriptSource = {
|
|
49
96
|
resources: Map<string, ProjectionResource>;
|
|
50
|
-
|
|
51
|
-
snapshot: ReadonlyMap<string,
|
|
97
|
+
namespaceRequests: Map<string, NamespaceRequest>;
|
|
98
|
+
snapshot: ReadonlyMap<string, SubagentTranscript>;
|
|
52
99
|
listeners: Set<() => void>;
|
|
53
100
|
controller: AnyStream[typeof STREAM_CONTROLLER] | undefined;
|
|
54
101
|
uiMessagesByParent: Map<string, UIMessage[]>;
|
|
55
|
-
convert: useExternalMessageConverter.Callback<LangChainBaseMessage>;
|
|
56
102
|
subscribe(listener: () => void): () => void;
|
|
57
|
-
getSnapshot(): ReadonlyMap<string,
|
|
103
|
+
getSnapshot(): ReadonlyMap<string, SubagentTranscript>;
|
|
58
104
|
reconcile(
|
|
59
105
|
controller: AnyStream[typeof STREAM_CONTROLLER],
|
|
60
106
|
subagents: AnyStream["subagents"],
|
|
@@ -66,9 +112,47 @@ type SubagentTranscriptSource = {
|
|
|
66
112
|
const sameNamespace = (a: readonly string[], b: readonly string[]) =>
|
|
67
113
|
a.length === b.length && a.every((segment, index) => segment === b[index]);
|
|
68
114
|
|
|
115
|
+
const needsNamespaceResolution = (snapshot: SubagentDiscoverySnapshot) =>
|
|
116
|
+
snapshot.namespace.length === 1 &&
|
|
117
|
+
snapshot.namespace[0] === `tools:${snapshot.id}`;
|
|
118
|
+
|
|
119
|
+
const requestSubagentNamespace = (
|
|
120
|
+
source: SubagentTranscriptSource,
|
|
121
|
+
controller: AnyStream[typeof STREAM_CONTROLLER],
|
|
122
|
+
request: NamespaceRequest,
|
|
123
|
+
) => {
|
|
124
|
+
request.attempts += 1;
|
|
125
|
+
request.pending = true;
|
|
126
|
+
request.retryQueued = false;
|
|
127
|
+
void controller
|
|
128
|
+
.resolveSubagentNamespace(request.id)
|
|
129
|
+
.catch(() => {})
|
|
130
|
+
.finally(() => {
|
|
131
|
+
if (
|
|
132
|
+
source.controller !== controller ||
|
|
133
|
+
source.namespaceRequests.get(request.id) !== request
|
|
134
|
+
)
|
|
135
|
+
return;
|
|
136
|
+
|
|
137
|
+
request.pending = false;
|
|
138
|
+
if (
|
|
139
|
+
!request.retryQueued &&
|
|
140
|
+
request.attempts === 1 &&
|
|
141
|
+
request.status !== "running"
|
|
142
|
+
) {
|
|
143
|
+
request.retryQueued = true;
|
|
144
|
+
}
|
|
145
|
+
if (!request.retryQueued || request.attempts >= 2) {
|
|
146
|
+
request.retryQueued = false;
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
requestSubagentNamespace(source, controller, request);
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
|
|
69
153
|
const sameTranscriptEntries = (
|
|
70
|
-
a: ReadonlyMap<string,
|
|
71
|
-
b: ReadonlyMap<string,
|
|
154
|
+
a: ReadonlyMap<string, SubagentTranscript> | undefined,
|
|
155
|
+
b: ReadonlyMap<string, SubagentTranscript>,
|
|
72
156
|
) =>
|
|
73
157
|
a?.size === b.size &&
|
|
74
158
|
[...b].every(([id, transcript]) => a.get(id) === transcript);
|
|
@@ -89,23 +173,61 @@ const collectUIMessages = (
|
|
|
89
173
|
const sameUIMessages = (a: readonly UIMessage[], b: readonly UIMessage[]) =>
|
|
90
174
|
a.length === b.length && a.every((ui, index) => ui === b[index]);
|
|
91
175
|
|
|
92
|
-
const
|
|
176
|
+
const foldLocalUIMessages = (resource: ProjectionResource) => {
|
|
177
|
+
const events = resource.uiStore?.getSnapshot();
|
|
178
|
+
if (events === undefined) return NO_UI_MESSAGES;
|
|
179
|
+
return foldUIUpdates(events, resource.uiFoldMemo);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const mergeLocalUIMessages = (
|
|
183
|
+
uiMessagesByParent: Map<string, UIMessage[]>,
|
|
184
|
+
localUiMessages: readonly UIMessage[],
|
|
185
|
+
) => {
|
|
186
|
+
const merged = new Map(uiMessagesByParent);
|
|
187
|
+
for (const [parentId, messages] of groupUIMessagesByParent<UIMessage>(
|
|
188
|
+
localUiMessages,
|
|
189
|
+
)) {
|
|
190
|
+
merged.set(parentId, mergeUIMessages(messages, merged.get(parentId)));
|
|
191
|
+
}
|
|
192
|
+
return merged;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const createConverter =
|
|
93
196
|
(
|
|
94
197
|
uiMessagesByParent: Map<string, UIMessage[]>,
|
|
198
|
+
messageTiming: Record<string, MessageTiming>,
|
|
95
199
|
): useExternalMessageConverter.Callback<LangChainBaseMessage> =>
|
|
96
200
|
(message, metadata) =>
|
|
97
|
-
convertLangChainBaseMessage(message, {
|
|
201
|
+
convertLangChainBaseMessage(message, {
|
|
202
|
+
...metadata,
|
|
203
|
+
uiMessagesByParent,
|
|
204
|
+
messageTiming,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const updateTiming = (resource: ProjectionResource) => {
|
|
208
|
+
if (resource.timing === undefined) return;
|
|
209
|
+
const startedAt = resource.snapshot.startedAt.getTime();
|
|
210
|
+
const completedAt = resource.snapshot.completedAt?.getTime();
|
|
211
|
+
if (
|
|
212
|
+
resource.timing.startedAt === startedAt &&
|
|
213
|
+
resource.timing.completedAt === completedAt
|
|
214
|
+
)
|
|
215
|
+
return;
|
|
216
|
+
resource.timing = {
|
|
217
|
+
startedAt,
|
|
218
|
+
...(completedAt !== undefined && { completedAt }),
|
|
219
|
+
};
|
|
220
|
+
};
|
|
98
221
|
|
|
99
222
|
const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
|
|
100
223
|
const uiMessagesByParent = new Map<string, UIMessage[]>();
|
|
101
224
|
const source: SubagentTranscriptSource = {
|
|
102
225
|
resources: new Map(),
|
|
103
|
-
|
|
226
|
+
namespaceRequests: new Map(),
|
|
104
227
|
snapshot: new Map(),
|
|
105
228
|
listeners: new Set(),
|
|
106
229
|
controller: undefined,
|
|
107
230
|
uiMessagesByParent,
|
|
108
|
-
convert: convertWithUIMessages(uiMessagesByParent),
|
|
109
231
|
subscribe(listener) {
|
|
110
232
|
source.listeners.add(listener);
|
|
111
233
|
return () => source.listeners.delete(listener);
|
|
@@ -114,18 +236,19 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
|
|
|
114
236
|
return source.snapshot;
|
|
115
237
|
},
|
|
116
238
|
reconcile(controller, subagents, uiMessagesByParent) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
239
|
+
source.uiMessagesByParent = uiMessagesByParent;
|
|
240
|
+
// A resource is rebuilt from scratch when its namespace resolves, which
|
|
241
|
+
// routinely happens after the subagent finished. Its timing was measured
|
|
242
|
+
// while the client watched the task run and cannot be measured again.
|
|
243
|
+
const rebound = new Map<string, MeasuredTiming>();
|
|
121
244
|
|
|
122
245
|
if (source.controller !== controller) {
|
|
123
246
|
source.dispose();
|
|
124
247
|
source.controller = controller;
|
|
125
248
|
}
|
|
126
249
|
|
|
127
|
-
for (const id of source.
|
|
128
|
-
if (!subagents.has(id)) source.
|
|
250
|
+
for (const id of source.namespaceRequests.keys()) {
|
|
251
|
+
if (!subagents.has(id)) source.namespaceRequests.delete(id);
|
|
129
252
|
}
|
|
130
253
|
|
|
131
254
|
for (const [id, resource] of source.resources) {
|
|
@@ -135,8 +258,13 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
|
|
|
135
258
|
snapshot.depth > MAX_SUBAGENT_DEPTH ||
|
|
136
259
|
!sameNamespace(resource.namespace, snapshot.namespace)
|
|
137
260
|
) {
|
|
138
|
-
|
|
139
|
-
|
|
261
|
+
if (snapshot && snapshot.depth <= MAX_SUBAGENT_DEPTH)
|
|
262
|
+
rebound.set(id, {
|
|
263
|
+
timing: resource.timing,
|
|
264
|
+
timingState: resource.timingState,
|
|
265
|
+
messageTiming: resource.messageTiming,
|
|
266
|
+
});
|
|
267
|
+
resource.dispose();
|
|
140
268
|
source.resources.delete(id);
|
|
141
269
|
continue;
|
|
142
270
|
}
|
|
@@ -145,52 +273,96 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
|
|
|
145
273
|
|
|
146
274
|
for (const snapshot of subagents.values()) {
|
|
147
275
|
if (snapshot.depth > MAX_SUBAGENT_DEPTH) continue;
|
|
148
|
-
if (!
|
|
149
|
-
source.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
276
|
+
if (!needsNamespaceResolution(snapshot)) {
|
|
277
|
+
source.namespaceRequests.delete(snapshot.id);
|
|
278
|
+
} else {
|
|
279
|
+
const request = source.namespaceRequests.get(snapshot.id);
|
|
280
|
+
if (!request) {
|
|
281
|
+
const nextRequest: NamespaceRequest = {
|
|
282
|
+
id: snapshot.id,
|
|
283
|
+
attempts: 0,
|
|
284
|
+
pending: false,
|
|
285
|
+
retryQueued: false,
|
|
286
|
+
status: snapshot.status,
|
|
287
|
+
};
|
|
288
|
+
source.namespaceRequests.set(snapshot.id, nextRequest);
|
|
289
|
+
requestSubagentNamespace(source, controller, nextRequest);
|
|
290
|
+
} else if (request.status !== snapshot.status) {
|
|
291
|
+
request.status = snapshot.status;
|
|
292
|
+
if (request.pending) {
|
|
293
|
+
request.retryQueued = request.attempts < 2;
|
|
294
|
+
} else if (request.attempts < 2) {
|
|
295
|
+
requestSubagentNamespace(source, controller, request);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
154
298
|
}
|
|
155
299
|
if (source.resources.has(snapshot.id)) continue;
|
|
156
300
|
const acquired = controller.registry.acquire(
|
|
157
|
-
|
|
301
|
+
subagentMessagesProjection(snapshot.namespace),
|
|
158
302
|
);
|
|
303
|
+
const acquiredUI =
|
|
304
|
+
snapshot.namespace.length > ROOT_UI_CHANNEL_DEPTH
|
|
305
|
+
? controller.registry.acquire(
|
|
306
|
+
channelProjection(UI_CUSTOM_CHANNELS, snapshot.namespace),
|
|
307
|
+
)
|
|
308
|
+
: undefined;
|
|
159
309
|
const resource: ProjectionResource = {
|
|
160
310
|
snapshot,
|
|
161
311
|
namespace: snapshot.namespace,
|
|
162
312
|
store: acquired.store,
|
|
163
|
-
|
|
164
|
-
|
|
313
|
+
uiStore: acquiredUI?.store,
|
|
314
|
+
dispose: () => {},
|
|
165
315
|
storeSnapshot: undefined,
|
|
166
316
|
status: undefined,
|
|
317
|
+
uiFoldMemo: createUIFoldMemo(),
|
|
318
|
+
localUiMessages: NO_UI_MESSAGES,
|
|
319
|
+
rootUiMessagesByParent: source.uiMessagesByParent,
|
|
320
|
+
uiMessagesByParent: source.uiMessagesByParent,
|
|
321
|
+
timingState: rebound.get(snapshot.id)?.timingState ?? null,
|
|
322
|
+
messageTiming:
|
|
323
|
+
rebound.get(snapshot.id)?.messageTiming ?? NO_MESSAGE_TIMING,
|
|
324
|
+
convertedMessageTiming: undefined,
|
|
325
|
+
convert: createConverter(
|
|
326
|
+
source.uiMessagesByParent,
|
|
327
|
+
NO_MESSAGE_TIMING,
|
|
328
|
+
),
|
|
167
329
|
uiMessages: [],
|
|
168
330
|
converted: undefined,
|
|
169
331
|
childTranscripts: undefined,
|
|
170
332
|
transcript: undefined,
|
|
333
|
+
timing: rebound.has(snapshot.id)
|
|
334
|
+
? rebound.get(snapshot.id)!.timing
|
|
335
|
+
: snapshot.status === "running"
|
|
336
|
+
? { startedAt: snapshot.startedAt.getTime() }
|
|
337
|
+
: undefined,
|
|
338
|
+
entry: undefined,
|
|
171
339
|
memo: createAttachMemo(),
|
|
172
340
|
cache: createExternalMessageConversionCache(),
|
|
173
341
|
};
|
|
174
|
-
|
|
342
|
+
const unsubscribe = resource.store.subscribe(() => rebuild());
|
|
343
|
+
const unsubscribeUI = resource.uiStore?.subscribe(() => rebuild());
|
|
344
|
+
resource.dispose = () => {
|
|
345
|
+
unsubscribe();
|
|
346
|
+
unsubscribeUI?.();
|
|
347
|
+
acquired.release();
|
|
348
|
+
acquiredUI?.release();
|
|
349
|
+
};
|
|
175
350
|
source.resources.set(snapshot.id, resource);
|
|
176
351
|
}
|
|
177
352
|
|
|
178
353
|
rebuild();
|
|
179
354
|
},
|
|
180
355
|
dispose() {
|
|
181
|
-
for (const resource of source.resources.values())
|
|
182
|
-
resource.unsubscribe();
|
|
183
|
-
resource.release();
|
|
184
|
-
}
|
|
356
|
+
for (const resource of source.resources.values()) resource.dispose();
|
|
185
357
|
source.resources.clear();
|
|
186
|
-
source.
|
|
358
|
+
source.namespaceRequests.clear();
|
|
187
359
|
source.snapshot = new Map();
|
|
188
360
|
for (const listener of source.listeners) listener();
|
|
189
361
|
},
|
|
190
362
|
};
|
|
191
363
|
|
|
192
364
|
const rebuild = () => {
|
|
193
|
-
const {
|
|
365
|
+
const { uiMessagesByParent } = source;
|
|
194
366
|
const resources = [...source.resources.values()];
|
|
195
367
|
const childrenByParent = new Map<string, ProjectionResource[]>();
|
|
196
368
|
|
|
@@ -202,7 +374,7 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
|
|
|
202
374
|
else childrenByParent.set(parentId, [resource]);
|
|
203
375
|
}
|
|
204
376
|
|
|
205
|
-
const transcripts = new Map<string,
|
|
377
|
+
const transcripts = new Map<string, SubagentTranscript>();
|
|
206
378
|
let changed = source.snapshot.size !== resources.length;
|
|
207
379
|
const built = new Set<string>();
|
|
208
380
|
|
|
@@ -216,24 +388,57 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
|
|
|
216
388
|
for (const child of children) build(child, depth + 1);
|
|
217
389
|
const childTranscripts = new Map(
|
|
218
390
|
children.flatMap((child) =>
|
|
219
|
-
child.
|
|
220
|
-
? [[child.snapshot.id, child.transcript] as const]
|
|
221
|
-
: [],
|
|
391
|
+
child.entry ? [[child.snapshot.id, child.entry] as const] : [],
|
|
222
392
|
),
|
|
223
393
|
);
|
|
224
394
|
const storeSnapshot = resource.store.getSnapshot();
|
|
225
395
|
const status = resource.snapshot.status;
|
|
226
|
-
const
|
|
396
|
+
const stepped = stepStreamingTiming(
|
|
397
|
+
resource.timingState,
|
|
398
|
+
storeSnapshot as LangChainBaseMessage[],
|
|
399
|
+
status === "running",
|
|
400
|
+
langChainStreamingTimingAccessors,
|
|
401
|
+
undefined,
|
|
402
|
+
);
|
|
403
|
+
resource.timingState = stepped.state;
|
|
404
|
+
if (Object.keys(stepped.timings).length > 0) {
|
|
405
|
+
resource.messageTiming = {
|
|
406
|
+
...resource.messageTiming,
|
|
407
|
+
...stepped.timings,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
const localUiMessages = foldLocalUIMessages(resource);
|
|
411
|
+
if (
|
|
412
|
+
resource.localUiMessages !== localUiMessages ||
|
|
413
|
+
resource.rootUiMessagesByParent !== uiMessagesByParent ||
|
|
414
|
+
resource.convertedMessageTiming !== resource.messageTiming
|
|
415
|
+
) {
|
|
416
|
+
resource.localUiMessages = localUiMessages;
|
|
417
|
+
resource.rootUiMessagesByParent = uiMessagesByParent;
|
|
418
|
+
resource.uiMessagesByParent =
|
|
419
|
+
localUiMessages.length === 0
|
|
420
|
+
? uiMessagesByParent
|
|
421
|
+
: mergeLocalUIMessages(uiMessagesByParent, localUiMessages);
|
|
422
|
+
resource.convert = createConverter(
|
|
423
|
+
resource.uiMessagesByParent,
|
|
424
|
+
resource.messageTiming,
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
const uiMessages = collectUIMessages(
|
|
428
|
+
storeSnapshot,
|
|
429
|
+
resource.uiMessagesByParent,
|
|
430
|
+
);
|
|
227
431
|
|
|
228
432
|
const conversionChanged =
|
|
229
433
|
resource.converted === undefined ||
|
|
230
434
|
resource.storeSnapshot !== storeSnapshot ||
|
|
231
435
|
resource.status !== status ||
|
|
436
|
+
resource.convertedMessageTiming !== resource.messageTiming ||
|
|
232
437
|
!sameUIMessages(resource.uiMessages, uiMessages);
|
|
233
438
|
if (conversionChanged) {
|
|
234
439
|
resource.converted = convertExternalMessages(
|
|
235
440
|
storeSnapshot as LangChainBaseMessage[],
|
|
236
|
-
convert,
|
|
441
|
+
resource.convert,
|
|
237
442
|
status === "running",
|
|
238
443
|
TRANSCRIPT_METADATA,
|
|
239
444
|
resource.cache,
|
|
@@ -241,6 +446,7 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
|
|
|
241
446
|
resource.storeSnapshot = storeSnapshot;
|
|
242
447
|
resource.status = status;
|
|
243
448
|
resource.uiMessages = uiMessages;
|
|
449
|
+
resource.convertedMessageTiming = resource.messageTiming;
|
|
244
450
|
}
|
|
245
451
|
|
|
246
452
|
if (
|
|
@@ -258,8 +464,21 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
|
|
|
258
464
|
resource.childTranscripts = childTranscripts;
|
|
259
465
|
}
|
|
260
466
|
|
|
467
|
+
updateTiming(resource);
|
|
468
|
+
if (
|
|
469
|
+
resource.entry === undefined ||
|
|
470
|
+
resource.entry.messages !== resource.transcript ||
|
|
471
|
+
resource.entry.timing !== resource.timing
|
|
472
|
+
) {
|
|
473
|
+
resource.entry = {
|
|
474
|
+
messages: resource.transcript!,
|
|
475
|
+
...(resource.timing && { timing: resource.timing }),
|
|
476
|
+
};
|
|
477
|
+
changed = true;
|
|
478
|
+
}
|
|
479
|
+
|
|
261
480
|
if (!source.snapshot.has(resource.snapshot.id)) changed = true;
|
|
262
|
-
transcripts.set(resource.snapshot.id, resource.
|
|
481
|
+
transcripts.set(resource.snapshot.id, resource.entry);
|
|
263
482
|
};
|
|
264
483
|
|
|
265
484
|
for (const resource of resources) {
|
|
@@ -282,7 +501,7 @@ const createSubagentTranscriptSource = (): SubagentTranscriptSource => {
|
|
|
282
501
|
export const useSubagentTranscripts = (
|
|
283
502
|
stream: AnyStream,
|
|
284
503
|
uiMessagesByParent: Map<string, UIMessage[]>,
|
|
285
|
-
): ReadonlyMap<string,
|
|
504
|
+
): ReadonlyMap<string, SubagentTranscript> => {
|
|
286
505
|
const sourceRef = useRef<SubagentTranscriptSource | undefined>(undefined);
|
|
287
506
|
if (!sourceRef.current) {
|
|
288
507
|
sourceRef.current = createSubagentTranscriptSource();
|