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