veryfront 0.1.322 → 0.1.324
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/esm/deno.js +1 -1
- package/esm/src/agent/chat-ui-message-stream.d.ts +45 -0
- package/esm/src/agent/chat-ui-message-stream.d.ts.map +1 -0
- package/esm/src/agent/chat-ui-message-stream.js +442 -0
- package/esm/src/agent/index.d.ts +1 -0
- package/esm/src/agent/index.d.ts.map +1 -1
- package/esm/src/agent/index.js +1 -0
- package/esm/src/tool/host-tools.d.ts +15 -0
- package/esm/src/tool/host-tools.d.ts.map +1 -0
- package/esm/src/tool/host-tools.js +60 -0
- package/esm/src/tool/index.d.ts +2 -0
- package/esm/src/tool/index.d.ts.map +1 -1
- package/esm/src/tool/index.js +1 -0
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/src/agent/chat-ui-message-stream.ts +596 -0
- package/src/src/agent/index.ts +6 -0
- package/src/src/tool/host-tools.ts +92 -0
- package/src/src/tool/index.ts +2 -0
- package/src/src/utils/version-constant.ts +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.324";
|
|
2
2
|
//# sourceMappingURL=version-constant.d.ts.map
|
package/package.json
CHANGED
package/src/deno.js
CHANGED
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import type { ChatFinishReason, ChatStreamEvent } from "../chat/protocol.js";
|
|
3
|
+
import type {
|
|
4
|
+
ChatDynamicToolUiPart,
|
|
5
|
+
ChatUiMessage,
|
|
6
|
+
ChatUiMessageChunk,
|
|
7
|
+
MessageMetadata,
|
|
8
|
+
} from "../chat/types.js";
|
|
9
|
+
import { createAgUiRuntimeChatStreamEncoder } from "./ag-ui-runtime-chat-stream-encoder.js";
|
|
10
|
+
import {
|
|
11
|
+
mergeToolInputDelta,
|
|
12
|
+
parseToolInputObject,
|
|
13
|
+
streamDataStreamEvents,
|
|
14
|
+
stripLeadingEmptyObjectPlaceholder,
|
|
15
|
+
} from "./data-stream.js";
|
|
16
|
+
import {
|
|
17
|
+
normalizeChatMessageMetadata,
|
|
18
|
+
normalizeChatUiMessageStream,
|
|
19
|
+
} from "../chat/chat-ui-message-helpers.js";
|
|
20
|
+
|
|
21
|
+
export type ChatUiMessageStreamFinishPart = {
|
|
22
|
+
type: "finish";
|
|
23
|
+
finishReason: ChatFinishReason;
|
|
24
|
+
rawFinishReason: ChatFinishReason;
|
|
25
|
+
totalUsage: {
|
|
26
|
+
inputTokens: number;
|
|
27
|
+
outputTokens: number;
|
|
28
|
+
totalTokens: number;
|
|
29
|
+
inputTokenDetails: {
|
|
30
|
+
noCacheTokens?: number;
|
|
31
|
+
cacheReadTokens?: number;
|
|
32
|
+
cacheWriteTokens?: number;
|
|
33
|
+
};
|
|
34
|
+
outputTokenDetails: {
|
|
35
|
+
textTokens?: number;
|
|
36
|
+
reasoningTokens?: number;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type ChatUiMessageStreamFinish<TMessageMetadata = MessageMetadata> = {
|
|
42
|
+
messages: Array<ChatUiMessage<TMessageMetadata>>;
|
|
43
|
+
isContinuation: false;
|
|
44
|
+
responseMessage: ChatUiMessage<TMessageMetadata>;
|
|
45
|
+
isAborted: false;
|
|
46
|
+
finishReason: ChatFinishReason;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type ChatUiMessageStreamOptions<TMessageMetadata = MessageMetadata> = {
|
|
50
|
+
generateMessageId?: () => string;
|
|
51
|
+
sendReasoning?: boolean;
|
|
52
|
+
onError?: (error: unknown) => string;
|
|
53
|
+
messageMetadata?: (
|
|
54
|
+
input: { part: ChatUiMessageStreamFinishPart },
|
|
55
|
+
) => TMessageMetadata | undefined;
|
|
56
|
+
onFinish?: (finish: ChatUiMessageStreamFinish<TMessageMetadata>) => void | Promise<void>;
|
|
57
|
+
onOrphanedToolInput?: (input: { toolCallId: string; inputText: string }) => void;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
type OrderedTextBlock = {
|
|
61
|
+
id: string;
|
|
62
|
+
order: number;
|
|
63
|
+
text: string;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
type ToolPart = {
|
|
67
|
+
toolCallId: string;
|
|
68
|
+
toolName: string;
|
|
69
|
+
order: number;
|
|
70
|
+
inputText: string;
|
|
71
|
+
input: Record<string, unknown>;
|
|
72
|
+
state: "input-available" | "output-available" | "output-error";
|
|
73
|
+
output?: unknown;
|
|
74
|
+
errorText?: string;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
type DataPart = {
|
|
78
|
+
name: string;
|
|
79
|
+
order: number;
|
|
80
|
+
value: unknown;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
type PendingToolDelta = {
|
|
84
|
+
inputText: string;
|
|
85
|
+
chunks: string[];
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
type FrameworkUiMessageState = {
|
|
89
|
+
textBlocks: Map<string, OrderedTextBlock>;
|
|
90
|
+
reasoningBlocks: Map<string, OrderedTextBlock>;
|
|
91
|
+
toolParts: Map<string, ToolPart>;
|
|
92
|
+
dataParts: DataPart[];
|
|
93
|
+
pendingToolDeltas: Map<string, PendingToolDelta>;
|
|
94
|
+
nextOrder: number;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
function createFrameworkUiMessageState(): FrameworkUiMessageState {
|
|
98
|
+
return {
|
|
99
|
+
textBlocks: new Map(),
|
|
100
|
+
reasoningBlocks: new Map(),
|
|
101
|
+
toolParts: new Map(),
|
|
102
|
+
dataParts: [],
|
|
103
|
+
pendingToolDeltas: new Map(),
|
|
104
|
+
nextOrder: 0,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
109
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function getStringField(event: ChatStreamEvent, key: string): string | undefined {
|
|
113
|
+
const value = event[key as keyof ChatStreamEvent];
|
|
114
|
+
return typeof value === "string" ? value : undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function appendPendingToolDelta(
|
|
118
|
+
state: FrameworkUiMessageState,
|
|
119
|
+
toolCallId: string,
|
|
120
|
+
inputTextDelta: string,
|
|
121
|
+
): void {
|
|
122
|
+
const existing = state.pendingToolDeltas.get(toolCallId);
|
|
123
|
+
if (existing) {
|
|
124
|
+
existing.inputText = mergeToolInputDelta(existing.inputText, inputTextDelta);
|
|
125
|
+
existing.chunks.push(inputTextDelta);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
state.pendingToolDeltas.set(toolCallId, {
|
|
130
|
+
inputText: inputTextDelta,
|
|
131
|
+
chunks: [inputTextDelta],
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function trackPendingFrameworkToolInput(input: {
|
|
136
|
+
state: FrameworkUiMessageState;
|
|
137
|
+
materializedToolCallIds: Set<string>;
|
|
138
|
+
event: Record<string, unknown> & { type: string };
|
|
139
|
+
}): void {
|
|
140
|
+
const { state, materializedToolCallIds, event } = input;
|
|
141
|
+
|
|
142
|
+
if (event.type === "tool-input-delta") {
|
|
143
|
+
const toolCallId = typeof event.toolCallId === "string" ? event.toolCallId : null;
|
|
144
|
+
const inputTextDelta = typeof event.inputTextDelta === "string"
|
|
145
|
+
? event.inputTextDelta
|
|
146
|
+
: typeof event.delta === "string"
|
|
147
|
+
? event.delta
|
|
148
|
+
: "";
|
|
149
|
+
if (toolCallId && inputTextDelta.length > 0 && !materializedToolCallIds.has(toolCallId)) {
|
|
150
|
+
appendPendingToolDelta(state, toolCallId, inputTextDelta);
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (event.type === "tool-input-start" || event.type === "tool-input-available") {
|
|
156
|
+
if (typeof event.toolCallId === "string") {
|
|
157
|
+
materializedToolCallIds.add(event.toolCallId);
|
|
158
|
+
state.pendingToolDeltas.delete(event.toolCallId);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function getParsedStreamedToolInput(inputText: string): Record<string, unknown> | null {
|
|
164
|
+
const normalizedInputText = stripLeadingEmptyObjectPlaceholder(inputText).trim();
|
|
165
|
+
if (normalizedInputText.length === 0) {
|
|
166
|
+
return {};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
const parsed = JSON.parse(normalizedInputText);
|
|
171
|
+
return isRecord(parsed) ? Object.fromEntries(Object.entries(parsed)) : {};
|
|
172
|
+
} catch {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function observeChatStreamEvent(input: {
|
|
178
|
+
event: ChatStreamEvent;
|
|
179
|
+
responseMessageId: string;
|
|
180
|
+
state: FrameworkUiMessageState;
|
|
181
|
+
}): void {
|
|
182
|
+
const { event, responseMessageId, state } = input;
|
|
183
|
+
|
|
184
|
+
switch (event.type) {
|
|
185
|
+
case "text-start": {
|
|
186
|
+
const id = event.id || responseMessageId;
|
|
187
|
+
if (!state.textBlocks.has(id)) {
|
|
188
|
+
state.textBlocks.set(id, { id, order: state.nextOrder, text: "" });
|
|
189
|
+
state.nextOrder += 1;
|
|
190
|
+
}
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
case "text-delta": {
|
|
194
|
+
const id = event.id || responseMessageId;
|
|
195
|
+
const existingBlock = state.textBlocks.get(id);
|
|
196
|
+
if (existingBlock) {
|
|
197
|
+
existingBlock.text += event.delta;
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
state.textBlocks.set(id, { id, order: state.nextOrder, text: event.delta });
|
|
201
|
+
state.nextOrder += 1;
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
case "reasoning-start": {
|
|
205
|
+
if (!state.reasoningBlocks.has(event.id)) {
|
|
206
|
+
state.reasoningBlocks.set(event.id, { id: event.id, order: state.nextOrder, text: "" });
|
|
207
|
+
state.nextOrder += 1;
|
|
208
|
+
}
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
case "reasoning-delta": {
|
|
212
|
+
const existingBlock = state.reasoningBlocks.get(event.id);
|
|
213
|
+
if (existingBlock) {
|
|
214
|
+
existingBlock.text += event.delta;
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
state.reasoningBlocks.set(event.id, {
|
|
218
|
+
id: event.id,
|
|
219
|
+
order: state.nextOrder,
|
|
220
|
+
text: event.delta,
|
|
221
|
+
});
|
|
222
|
+
state.nextOrder += 1;
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
case "tool-input-start": {
|
|
226
|
+
if (!state.toolParts.has(event.toolCallId)) {
|
|
227
|
+
state.toolParts.set(event.toolCallId, {
|
|
228
|
+
toolCallId: event.toolCallId,
|
|
229
|
+
toolName: event.toolName,
|
|
230
|
+
order: state.nextOrder,
|
|
231
|
+
inputText: "",
|
|
232
|
+
input: {},
|
|
233
|
+
state: "input-available",
|
|
234
|
+
});
|
|
235
|
+
state.nextOrder += 1;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const pendingToolDelta = state.pendingToolDeltas.get(event.toolCallId);
|
|
239
|
+
if (!pendingToolDelta) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const toolPart = state.toolParts.get(event.toolCallId);
|
|
244
|
+
if (!toolPart) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
toolPart.inputText = pendingToolDelta.inputText;
|
|
249
|
+
const parsedInput = getParsedStreamedToolInput(toolPart.inputText);
|
|
250
|
+
if (parsedInput) {
|
|
251
|
+
toolPart.input = parsedInput;
|
|
252
|
+
}
|
|
253
|
+
state.pendingToolDeltas.delete(event.toolCallId);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
case "tool-input-delta": {
|
|
257
|
+
const toolPart = state.toolParts.get(event.toolCallId);
|
|
258
|
+
if (!toolPart) {
|
|
259
|
+
appendPendingToolDelta(state, event.toolCallId, event.inputTextDelta);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
toolPart.inputText = mergeToolInputDelta(toolPart.inputText, event.inputTextDelta);
|
|
263
|
+
const parsedInput = getParsedStreamedToolInput(toolPart.inputText);
|
|
264
|
+
if (parsedInput) {
|
|
265
|
+
toolPart.input = parsedInput;
|
|
266
|
+
}
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
case "tool-input-available": {
|
|
270
|
+
const toolPart = state.toolParts.get(event.toolCallId);
|
|
271
|
+
const input = parseToolInputObject(event.input);
|
|
272
|
+
if (toolPart) {
|
|
273
|
+
toolPart.toolName = event.toolName;
|
|
274
|
+
toolPart.input = input;
|
|
275
|
+
toolPart.state = "input-available";
|
|
276
|
+
} else {
|
|
277
|
+
state.toolParts.set(event.toolCallId, {
|
|
278
|
+
toolCallId: event.toolCallId,
|
|
279
|
+
toolName: event.toolName,
|
|
280
|
+
order: state.nextOrder,
|
|
281
|
+
inputText: "",
|
|
282
|
+
input,
|
|
283
|
+
state: "input-available",
|
|
284
|
+
});
|
|
285
|
+
state.nextOrder += 1;
|
|
286
|
+
}
|
|
287
|
+
state.pendingToolDeltas.delete(event.toolCallId);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
case "tool-output-available": {
|
|
291
|
+
const toolPart = state.toolParts.get(event.toolCallId);
|
|
292
|
+
if (!toolPart) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
toolPart.state = "output-available";
|
|
296
|
+
toolPart.output = event.output;
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
case "tool-output-error":
|
|
300
|
+
case "tool-input-error": {
|
|
301
|
+
const toolPart = state.toolParts.get(event.toolCallId);
|
|
302
|
+
if (toolPart) {
|
|
303
|
+
toolPart.state = "output-error";
|
|
304
|
+
toolPart.errorText = event.errorText;
|
|
305
|
+
if ("input" in event && event.input !== undefined) {
|
|
306
|
+
toolPart.input = parseToolInputObject(event.input);
|
|
307
|
+
}
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
state.toolParts.set(event.toolCallId, {
|
|
311
|
+
toolCallId: event.toolCallId,
|
|
312
|
+
toolName: getStringField(event, "toolName") ?? "unknown",
|
|
313
|
+
order: state.nextOrder,
|
|
314
|
+
inputText: "",
|
|
315
|
+
input: "input" in event && event.input !== undefined
|
|
316
|
+
? parseToolInputObject(event.input)
|
|
317
|
+
: {},
|
|
318
|
+
state: "output-error",
|
|
319
|
+
errorText: event.errorText,
|
|
320
|
+
});
|
|
321
|
+
state.nextOrder += 1;
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
default: {
|
|
325
|
+
if (!event.type.startsWith("data-")) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (!("data" in event)) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
state.dataParts.push({
|
|
332
|
+
name: event.type.slice("data-".length),
|
|
333
|
+
order: state.nextOrder,
|
|
334
|
+
value: event.data,
|
|
335
|
+
});
|
|
336
|
+
state.nextOrder += 1;
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function getOrphanedToolInput(
|
|
343
|
+
state: FrameworkUiMessageState,
|
|
344
|
+
toolCallId: string,
|
|
345
|
+
): Record<string, unknown> {
|
|
346
|
+
const pending = state.pendingToolDeltas.get(toolCallId);
|
|
347
|
+
if (!pending) {
|
|
348
|
+
return {};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const parsedInput = getParsedStreamedToolInput(pending.inputText);
|
|
352
|
+
if (parsedInput) {
|
|
353
|
+
return parsedInput;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
__rawInputText: pending.inputText,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function buildOrphanedToolInputErrorText(inputText: string): string {
|
|
362
|
+
const normalizedInputText = inputText.trim();
|
|
363
|
+
const preview = normalizedInputText.length > 160
|
|
364
|
+
? `${normalizedInputText.slice(0, 160)}...`
|
|
365
|
+
: normalizedInputText;
|
|
366
|
+
return preview.length > 0
|
|
367
|
+
? `Tool input started streaming before the tool lifecycle was established and never materialized into an executable tool call. Buffered args: ${preview}`
|
|
368
|
+
: "Tool input started streaming before the tool lifecycle was established and never materialized into an executable tool call.";
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function buildResponseMessageParts(state: FrameworkUiMessageState): ChatUiMessage["parts"] {
|
|
372
|
+
const orderedParts: Array<{ order: number; part: ChatUiMessage["parts"][number] }> = [];
|
|
373
|
+
|
|
374
|
+
for (const textBlock of state.textBlocks.values()) {
|
|
375
|
+
if (textBlock.text.length === 0) {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
orderedParts.push({
|
|
380
|
+
order: textBlock.order,
|
|
381
|
+
part: {
|
|
382
|
+
type: "text",
|
|
383
|
+
text: textBlock.text,
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
for (const reasoningBlock of state.reasoningBlocks.values()) {
|
|
389
|
+
if (reasoningBlock.text.length === 0) {
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
orderedParts.push({
|
|
394
|
+
order: reasoningBlock.order,
|
|
395
|
+
part: {
|
|
396
|
+
type: "reasoning",
|
|
397
|
+
text: reasoningBlock.text,
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
for (const toolPart of state.toolParts.values()) {
|
|
403
|
+
const basePart: Pick<
|
|
404
|
+
ChatDynamicToolUiPart,
|
|
405
|
+
"type" | "toolName" | "toolCallId" | "input"
|
|
406
|
+
> = {
|
|
407
|
+
type: "dynamic-tool",
|
|
408
|
+
toolName: toolPart.toolName,
|
|
409
|
+
toolCallId: toolPart.toolCallId,
|
|
410
|
+
input: toolPart.input,
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const part: ChatUiMessage["parts"][number] = toolPart.state === "output-available"
|
|
414
|
+
? {
|
|
415
|
+
...basePart,
|
|
416
|
+
state: "output-available",
|
|
417
|
+
output: toolPart.output,
|
|
418
|
+
}
|
|
419
|
+
: toolPart.state === "output-error"
|
|
420
|
+
? {
|
|
421
|
+
...basePart,
|
|
422
|
+
state: "output-error",
|
|
423
|
+
errorText: toolPart.errorText ?? "Tool execution failed",
|
|
424
|
+
}
|
|
425
|
+
: {
|
|
426
|
+
...basePart,
|
|
427
|
+
state: "input-available",
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
orderedParts.push({
|
|
431
|
+
order: toolPart.order,
|
|
432
|
+
part,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
for (const dataPart of state.dataParts) {
|
|
437
|
+
orderedParts.push({
|
|
438
|
+
order: dataPart.order,
|
|
439
|
+
part: {
|
|
440
|
+
type: `data-${dataPart.name}`,
|
|
441
|
+
data: dataPart.value,
|
|
442
|
+
},
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return orderedParts.sort((left, right) => left.order - right.order).map((entry) => entry.part);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function buildFinishPart(finishReason: ChatFinishReason): ChatUiMessageStreamFinishPart {
|
|
450
|
+
return {
|
|
451
|
+
type: "finish",
|
|
452
|
+
finishReason,
|
|
453
|
+
rawFinishReason: finishReason,
|
|
454
|
+
totalUsage: {
|
|
455
|
+
inputTokens: 0,
|
|
456
|
+
outputTokens: 0,
|
|
457
|
+
totalTokens: 0,
|
|
458
|
+
inputTokenDetails: {
|
|
459
|
+
noCacheTokens: undefined,
|
|
460
|
+
cacheReadTokens: undefined,
|
|
461
|
+
cacheWriteTokens: undefined,
|
|
462
|
+
},
|
|
463
|
+
outputTokenDetails: {
|
|
464
|
+
textTokens: undefined,
|
|
465
|
+
reasoningTokens: undefined,
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function toUiChunk(event: ChatStreamEvent): ChatUiMessageChunk<MessageMetadata> | null {
|
|
472
|
+
switch (event.type) {
|
|
473
|
+
case "start":
|
|
474
|
+
return {
|
|
475
|
+
type: "start",
|
|
476
|
+
...(event.messageId ? { messageId: event.messageId } : {}),
|
|
477
|
+
...(event.messageMetadata !== undefined
|
|
478
|
+
? { messageMetadata: normalizeChatMessageMetadata(event.messageMetadata) }
|
|
479
|
+
: {}),
|
|
480
|
+
};
|
|
481
|
+
case "finish":
|
|
482
|
+
return {
|
|
483
|
+
type: "finish",
|
|
484
|
+
...(event.finishReason ? { finishReason: event.finishReason } : {}),
|
|
485
|
+
};
|
|
486
|
+
case "message-metadata":
|
|
487
|
+
return {
|
|
488
|
+
type: "message-metadata",
|
|
489
|
+
messageMetadata: normalizeChatMessageMetadata(event.messageMetadata),
|
|
490
|
+
};
|
|
491
|
+
default:
|
|
492
|
+
return event;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
export function createChatUiMessageStreamFromDataStream<TMessageMetadata = MessageMetadata>(
|
|
497
|
+
input: { stream: ReadableStream<Uint8Array> },
|
|
498
|
+
options: ChatUiMessageStreamOptions<TMessageMetadata> = {},
|
|
499
|
+
): AsyncIterable<ChatUiMessageChunk<MessageMetadata>> {
|
|
500
|
+
const responseMessageId = options.generateMessageId?.() ?? dntShim.crypto.randomUUID();
|
|
501
|
+
const state = createFrameworkUiMessageState();
|
|
502
|
+
const chatEventEncoder = createAgUiRuntimeChatStreamEncoder({
|
|
503
|
+
responseMessageId,
|
|
504
|
+
sendReasoning: options.sendReasoning,
|
|
505
|
+
onError: options.onError,
|
|
506
|
+
});
|
|
507
|
+
const materializedToolCallIds = new Set<string>();
|
|
508
|
+
let finishReason: ChatFinishReason = "stop";
|
|
509
|
+
|
|
510
|
+
return normalizeChatUiMessageStream(
|
|
511
|
+
(async function* () {
|
|
512
|
+
const ensureStepStarted = function* (shouldStartStep: boolean) {
|
|
513
|
+
if (shouldStartStep) {
|
|
514
|
+
yield { type: "start-step" as const };
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
yield {
|
|
519
|
+
type: "start",
|
|
520
|
+
messageId: responseMessageId,
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
for await (const event of streamDataStreamEvents(input.stream)) {
|
|
524
|
+
trackPendingFrameworkToolInput({
|
|
525
|
+
state,
|
|
526
|
+
materializedToolCallIds,
|
|
527
|
+
event,
|
|
528
|
+
});
|
|
529
|
+
const chatEvents = chatEventEncoder.encode(event);
|
|
530
|
+
finishReason = chatEventEncoder.state.finishReason;
|
|
531
|
+
for (const chatEvent of chatEvents) {
|
|
532
|
+
observeChatStreamEvent({
|
|
533
|
+
event: chatEvent,
|
|
534
|
+
responseMessageId,
|
|
535
|
+
state,
|
|
536
|
+
});
|
|
537
|
+
const chunk = toUiChunk(chatEvent);
|
|
538
|
+
if (chunk) {
|
|
539
|
+
yield chunk;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
for (const [toolCallId, pendingToolDelta] of state.pendingToolDeltas.entries()) {
|
|
545
|
+
yield* ensureStepStarted(!chatEventEncoder.state.isStepOpen);
|
|
546
|
+
chatEventEncoder.state.isStepOpen = true;
|
|
547
|
+
const toolInput = getOrphanedToolInput(state, toolCallId);
|
|
548
|
+
const errorText = buildOrphanedToolInputErrorText(pendingToolDelta.inputText);
|
|
549
|
+
|
|
550
|
+
options.onOrphanedToolInput?.({ toolCallId, inputText: pendingToolDelta.inputText });
|
|
551
|
+
|
|
552
|
+
state.toolParts.set(toolCallId, {
|
|
553
|
+
toolCallId,
|
|
554
|
+
toolName: "unknown",
|
|
555
|
+
order: state.nextOrder,
|
|
556
|
+
inputText: pendingToolDelta.inputText,
|
|
557
|
+
input: toolInput,
|
|
558
|
+
state: "output-error",
|
|
559
|
+
errorText,
|
|
560
|
+
});
|
|
561
|
+
state.nextOrder += 1;
|
|
562
|
+
|
|
563
|
+
yield {
|
|
564
|
+
type: "tool-input-error",
|
|
565
|
+
toolCallId,
|
|
566
|
+
toolName: "unknown",
|
|
567
|
+
input: toolInput,
|
|
568
|
+
errorText,
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
state.pendingToolDeltas.clear();
|
|
572
|
+
|
|
573
|
+
const finishPart = buildFinishPart(finishReason);
|
|
574
|
+
const messageMetadata = options.messageMetadata?.({ part: finishPart });
|
|
575
|
+
const responseMessage: ChatUiMessage<TMessageMetadata> = {
|
|
576
|
+
id: responseMessageId,
|
|
577
|
+
role: "assistant",
|
|
578
|
+
parts: buildResponseMessageParts(state),
|
|
579
|
+
...(messageMetadata ? { metadata: messageMetadata } : {}),
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
await options.onFinish?.({
|
|
583
|
+
messages: [responseMessage],
|
|
584
|
+
isContinuation: false,
|
|
585
|
+
responseMessage,
|
|
586
|
+
isAborted: false,
|
|
587
|
+
finishReason: finishPart.finishReason,
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
yield {
|
|
591
|
+
type: "finish",
|
|
592
|
+
finishReason,
|
|
593
|
+
};
|
|
594
|
+
})(),
|
|
595
|
+
);
|
|
596
|
+
}
|
package/src/src/agent/index.ts
CHANGED
|
@@ -231,6 +231,12 @@ export {
|
|
|
231
231
|
createAgUiRuntimeBrowserResponse,
|
|
232
232
|
type CreateAgUiRuntimeBrowserResponseInput,
|
|
233
233
|
} from "./ag-ui-runtime-browser-response.js";
|
|
234
|
+
export {
|
|
235
|
+
type ChatUiMessageStreamFinish,
|
|
236
|
+
type ChatUiMessageStreamFinishPart,
|
|
237
|
+
type ChatUiMessageStreamOptions,
|
|
238
|
+
createChatUiMessageStreamFromDataStream,
|
|
239
|
+
} from "./chat-ui-message-stream.js";
|
|
234
240
|
export {
|
|
235
241
|
createAgUiTrackedBrowserResponse,
|
|
236
242
|
type CreateAgUiTrackedBrowserResponseInput,
|