ai 7.0.105 → 7.0.107
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/CHANGELOG.md +42 -0
- package/dist/index.js +278 -88
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +15 -3
- package/dist/internal/index.js +153 -46
- package/dist/internal/index.js.map +1 -1
- package/docs/03-ai-sdk-core/19-tool-search.mdx +4 -2
- package/docs/07-reference/01-ai-sdk-core/23-tool-search.mdx +3 -2
- package/package.json +11 -11
- package/src/batch/batch.ts +1 -0
- package/src/generate-object/generate-object.ts +1 -0
- package/src/generate-object/stream-object.ts +1 -0
- package/src/generate-speech/generate-speech.ts +44 -4
- package/src/generate-text/generate-text.ts +28 -15
- package/src/generate-text/invoke-tool-callbacks-from-stream.ts +68 -16
- package/src/generate-text/prune-messages.ts +13 -0
- package/src/generate-text/stream-language-model-call.ts +1 -0
- package/src/generate-text/stream-text.ts +29 -12
- package/src/generate-video/generate-video.ts +61 -10
- package/src/middleware/extract-json-middleware.ts +35 -9
- package/src/middleware/simulate-streaming-middleware.ts +7 -1
- package/src/middleware/wrap-provider.ts +2 -0
- package/src/prompt/convert-to-language-model-prompt.ts +4 -2
- package/src/prompt/create-tool-model-output.ts +20 -11
- package/src/test/mock-server-response.ts +19 -3
- package/src/text-stream/pipe-text-stream-to-response.ts +3 -5
- package/src/ui/http-chat-transport.ts +12 -2
- package/src/ui/last-assistant-message-is-complete-with-approval-responses.ts +1 -1
- package/src/ui/last-assistant-message-is-complete-with-tool-calls.ts +2 -1
- package/src/ui-message-stream/pipe-ui-message-stream-to-response.ts +1 -3
- package/src/ui-message-stream/read-ui-message-stream.ts +13 -4
- package/src/util/data-url.ts +13 -1
- package/src/util/download/download-function.ts +5 -2
- package/src/util/write-to-server-response.ts +7 -3
|
@@ -10,8 +10,10 @@ definition into its initial context. Register tools with `deferLoading: true`;
|
|
|
10
10
|
search matches their names and descriptions and makes them available on the
|
|
11
11
|
**next model step**.
|
|
12
12
|
|
|
13
|
-
Use it with `generateText`, `streamText`,
|
|
14
|
-
arguments; the model supplies a search
|
|
13
|
+
Use it with `generateText`, `streamText`, `ToolLoopAgent`, or `WorkflowAgent` from
|
|
14
|
+
`@ai-sdk/workflow`. The factory takes no arguments; the model supplies a search
|
|
15
|
+
query. `WorkflowAgent` supports direct tool calling; the other APIs also support
|
|
16
|
+
cache-preserving code mode.
|
|
15
17
|
|
|
16
18
|
## Direct Tool Calling
|
|
17
19
|
|
|
@@ -7,8 +7,9 @@ description: Search deferred tools and load their definitions on demand for dire
|
|
|
7
7
|
|
|
8
8
|
Creates a tool that searches the surrounding generation's deferred tools by name
|
|
9
9
|
and description. The factory takes no arguments. Use it with `generateText`,
|
|
10
|
-
`streamText`,
|
|
11
|
-
|
|
10
|
+
`streamText`, `ToolLoopAgent`, or `WorkflowAgent` from `@ai-sdk/workflow`.
|
|
11
|
+
`WorkflowAgent` supports direct tool calling. The other APIs also support code
|
|
12
|
+
mode configured with `toolDiscovery: 'conversation'`.
|
|
12
13
|
|
|
13
14
|
```ts
|
|
14
15
|
import { toolSearch } from 'ai';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.107",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -42,20 +42,20 @@
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@ai-sdk/gateway": "4.0.
|
|
45
|
+
"@ai-sdk/gateway": "4.0.87",
|
|
46
46
|
"@ai-sdk/provider": "4.0.17",
|
|
47
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
47
|
+
"@ai-sdk/provider-utils": "5.0.45"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@ai-sdk/amazon-bedrock": "5.0.
|
|
51
|
-
"@ai-sdk/deepseek": "3.0.
|
|
52
|
-
"@ai-sdk/google": "4.0.
|
|
53
|
-
"@ai-sdk/groq": "4.0.
|
|
54
|
-
"@ai-sdk/huggingface": "2.0.
|
|
55
|
-
"@ai-sdk/moonshotai": "3.0.
|
|
56
|
-
"@ai-sdk/openai": "4.0.
|
|
50
|
+
"@ai-sdk/amazon-bedrock": "5.0.88",
|
|
51
|
+
"@ai-sdk/deepseek": "3.0.49",
|
|
52
|
+
"@ai-sdk/google": "4.0.76",
|
|
53
|
+
"@ai-sdk/groq": "4.0.46",
|
|
54
|
+
"@ai-sdk/huggingface": "2.0.53",
|
|
55
|
+
"@ai-sdk/moonshotai": "3.0.54",
|
|
56
|
+
"@ai-sdk/openai": "4.0.71",
|
|
57
57
|
"@ai-sdk/test-server": "2.0.1",
|
|
58
|
-
"@ai-sdk/xai": "5.0.
|
|
58
|
+
"@ai-sdk/xai": "5.0.4",
|
|
59
59
|
"@edge-runtime/vm": "^5.0.0",
|
|
60
60
|
"@smithy/eventstream-codec": "^4.3.3",
|
|
61
61
|
"@smithy/util-utf8": "^4.3.3",
|
package/src/batch/batch.ts
CHANGED
|
@@ -155,14 +155,19 @@ export async function generateSpeech({
|
|
|
155
155
|
model: resolvedModel.modelId,
|
|
156
156
|
});
|
|
157
157
|
|
|
158
|
+
const detectedMediaType = detectMediaType({
|
|
159
|
+
data: result.audio,
|
|
160
|
+
topLevelType: 'audio',
|
|
161
|
+
});
|
|
162
|
+
|
|
158
163
|
return new DefaultSpeechResult({
|
|
159
164
|
audio: new DefaultGeneratedAudioFile({
|
|
160
165
|
data: result.audio,
|
|
161
166
|
mediaType:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
167
|
+
detectedMediaType ??
|
|
168
|
+
getResponseAudioMediaType(result.response.headers) ??
|
|
169
|
+
getOutputFormatMediaType(outputFormat) ??
|
|
170
|
+
'audio/mp3',
|
|
166
171
|
}),
|
|
167
172
|
warnings: result.warnings,
|
|
168
173
|
responses: [result.response],
|
|
@@ -170,6 +175,41 @@ export async function generateSpeech({
|
|
|
170
175
|
});
|
|
171
176
|
}
|
|
172
177
|
|
|
178
|
+
function getResponseAudioMediaType(
|
|
179
|
+
headers: Record<string, string> | undefined,
|
|
180
|
+
): string | undefined {
|
|
181
|
+
const mediaType = Object.entries(headers ?? {}).find(
|
|
182
|
+
([name]) => name.toLowerCase() === 'content-type',
|
|
183
|
+
)?.[1];
|
|
184
|
+
|
|
185
|
+
if (mediaType == null) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const normalizedMediaType = mediaType.split(';', 1)[0].trim().toLowerCase();
|
|
190
|
+
|
|
191
|
+
if (normalizedMediaType.length === 0) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return normalizedMediaType.startsWith('audio/')
|
|
196
|
+
? normalizedMediaType
|
|
197
|
+
: undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getOutputFormatMediaType(outputFormat: string | undefined) {
|
|
201
|
+
if (outputFormat == null) {
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const normalizedOutputFormat = outputFormat.trim().toLowerCase();
|
|
206
|
+
|
|
207
|
+
return normalizedOutputFormat === 'pcm' ||
|
|
208
|
+
normalizedOutputFormat === 'audio/pcm'
|
|
209
|
+
? 'audio/pcm'
|
|
210
|
+
: undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
173
213
|
class DefaultSpeechResult implements SpeechResult {
|
|
174
214
|
readonly audio: GeneratedAudioFile;
|
|
175
215
|
readonly warnings: Array<Warning>;
|
|
@@ -118,6 +118,7 @@ import type {
|
|
|
118
118
|
OnToolExecutionEndCallback,
|
|
119
119
|
OnToolExecutionStartCallback,
|
|
120
120
|
} from './tool-execution-events';
|
|
121
|
+
import { validateToolContext } from './validate-tool-context';
|
|
121
122
|
import type { ToolInputRefinement } from './tool-input-refinement';
|
|
122
123
|
import type { ToolOrder } from './tool-order';
|
|
123
124
|
import type { ToolOutput } from './tool-output';
|
|
@@ -964,6 +965,7 @@ export async function generateText<
|
|
|
964
965
|
},
|
|
965
966
|
supportedUrls: await stepModel.supportedUrls,
|
|
966
967
|
download,
|
|
968
|
+
abortSignal: mergedAbortSignal,
|
|
967
969
|
provider: stepModel.provider.split('.')[0],
|
|
968
970
|
});
|
|
969
971
|
|
|
@@ -1182,23 +1184,34 @@ export async function generateText<
|
|
|
1182
1184
|
continue;
|
|
1183
1185
|
}
|
|
1184
1186
|
|
|
1185
|
-
if (
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1187
|
+
if (
|
|
1188
|
+
tool.onInputStart != null ||
|
|
1189
|
+
tool.onInputAvailable != null
|
|
1190
|
+
) {
|
|
1191
|
+
const context = await validateToolContext({
|
|
1192
|
+
toolName: toolCall.toolName,
|
|
1193
|
+
context: getOwn(toolsContext, toolCall.toolName),
|
|
1194
|
+
contextSchema: tool.contextSchema,
|
|
1191
1195
|
});
|
|
1192
|
-
}
|
|
1193
1196
|
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
}
|
|
1197
|
+
if (tool.onInputStart != null) {
|
|
1198
|
+
await tool.onInputStart({
|
|
1199
|
+
toolCallId: toolCall.toolCallId,
|
|
1200
|
+
messages: stepMessages,
|
|
1201
|
+
abortSignal: mergedAbortSignal,
|
|
1202
|
+
context,
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
if (tool.onInputAvailable != null) {
|
|
1207
|
+
await tool.onInputAvailable({
|
|
1208
|
+
input: toolCall.input,
|
|
1209
|
+
toolCallId: toolCall.toolCallId,
|
|
1210
|
+
messages: stepMessages,
|
|
1211
|
+
abortSignal: mergedAbortSignal,
|
|
1212
|
+
context,
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1202
1215
|
}
|
|
1203
1216
|
|
|
1204
1217
|
const toolApprovalStatus = await resolveToolApproval({
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
InferToolSetContext,
|
|
3
|
+
ModelMessage,
|
|
4
|
+
ToolSet,
|
|
5
|
+
} from '@ai-sdk/provider-utils';
|
|
2
6
|
import { createIdMap } from '../util/create-id-map';
|
|
3
7
|
import { getOwn } from '../util/get-own';
|
|
4
8
|
import type { LanguageModelStreamPart } from './stream-language-model-call';
|
|
@@ -6,30 +10,61 @@ import {
|
|
|
6
10
|
isStreamRetryAttemptBoundaryPart,
|
|
7
11
|
type StreamRetryAttemptBoundaryPart,
|
|
8
12
|
} from './stream-retry-attempt-boundary';
|
|
13
|
+
import { validateToolContext } from './validate-tool-context';
|
|
9
14
|
|
|
10
15
|
type ToolCallbackStreamPart<TOOLS extends ToolSet> =
|
|
11
16
|
| LanguageModelStreamPart<TOOLS>
|
|
12
17
|
| StreamRetryAttemptBoundaryPart;
|
|
13
18
|
|
|
14
|
-
export function invokeToolCallbacksFromStream<
|
|
15
|
-
TOOLS extends ToolSet,
|
|
16
|
-
RUNTIME_CONTEXT extends Context,
|
|
17
|
-
>({
|
|
19
|
+
export function invokeToolCallbacksFromStream<TOOLS extends ToolSet>({
|
|
18
20
|
stream,
|
|
19
21
|
tools,
|
|
20
22
|
stepInputMessages,
|
|
21
23
|
abortSignal,
|
|
22
|
-
|
|
24
|
+
toolsContext,
|
|
23
25
|
}: {
|
|
24
26
|
stream: ReadableStream<ToolCallbackStreamPart<TOOLS>>;
|
|
25
27
|
tools: TOOLS | undefined;
|
|
26
28
|
stepInputMessages: Array<ModelMessage>;
|
|
27
29
|
abortSignal: AbortSignal | undefined;
|
|
28
|
-
|
|
30
|
+
toolsContext: InferToolSetContext<TOOLS>;
|
|
29
31
|
}): ReadableStream<ToolCallbackStreamPart<TOOLS>> {
|
|
30
32
|
if (tools == null) return stream;
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
let ongoingToolCalls: Record<
|
|
35
|
+
string,
|
|
36
|
+
{
|
|
37
|
+
toolName: string;
|
|
38
|
+
validatedContext: Promise<unknown> | undefined;
|
|
39
|
+
}
|
|
40
|
+
> = createIdMap();
|
|
41
|
+
|
|
42
|
+
const getValidatedContext = ({
|
|
43
|
+
toolCallId,
|
|
44
|
+
toolName,
|
|
45
|
+
}: {
|
|
46
|
+
toolCallId: string;
|
|
47
|
+
toolName: string;
|
|
48
|
+
}): Promise<unknown> => {
|
|
49
|
+
const ongoingToolCall = ongoingToolCalls[toolCallId];
|
|
50
|
+
|
|
51
|
+
if (ongoingToolCall?.validatedContext != null) {
|
|
52
|
+
return ongoingToolCall.validatedContext;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const tool = getOwn(tools, toolName);
|
|
56
|
+
const validatedContext = validateToolContext({
|
|
57
|
+
toolName,
|
|
58
|
+
context: getOwn(toolsContext, toolName),
|
|
59
|
+
contextSchema: tool?.contextSchema,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (ongoingToolCall != null) {
|
|
63
|
+
ongoingToolCall.validatedContext = validatedContext;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return validatedContext;
|
|
67
|
+
};
|
|
33
68
|
|
|
34
69
|
return stream.pipeThrough(
|
|
35
70
|
new TransformStream({
|
|
@@ -37,12 +72,16 @@ export function invokeToolCallbacksFromStream<
|
|
|
37
72
|
controller.enqueue(chunk);
|
|
38
73
|
|
|
39
74
|
if (isStreamRetryAttemptBoundaryPart(chunk)) {
|
|
75
|
+
ongoingToolCalls = createIdMap();
|
|
40
76
|
return;
|
|
41
77
|
}
|
|
42
78
|
|
|
43
79
|
switch (chunk.type) {
|
|
44
80
|
case 'tool-input-start': {
|
|
45
|
-
|
|
81
|
+
ongoingToolCalls[chunk.id] = {
|
|
82
|
+
toolName: chunk.toolName,
|
|
83
|
+
validatedContext: undefined,
|
|
84
|
+
};
|
|
46
85
|
|
|
47
86
|
const tool = getOwn(tools, chunk.toolName);
|
|
48
87
|
if (tool?.onInputStart != null) {
|
|
@@ -50,7 +89,10 @@ export function invokeToolCallbacksFromStream<
|
|
|
50
89
|
toolCallId: chunk.id,
|
|
51
90
|
messages: stepInputMessages,
|
|
52
91
|
abortSignal,
|
|
53
|
-
context:
|
|
92
|
+
context: await getValidatedContext({
|
|
93
|
+
toolCallId: chunk.id,
|
|
94
|
+
toolName: chunk.toolName,
|
|
95
|
+
}),
|
|
54
96
|
});
|
|
55
97
|
}
|
|
56
98
|
|
|
@@ -58,7 +100,7 @@ export function invokeToolCallbacksFromStream<
|
|
|
58
100
|
}
|
|
59
101
|
|
|
60
102
|
case 'tool-input-delta': {
|
|
61
|
-
const toolName =
|
|
103
|
+
const toolName = ongoingToolCalls[chunk.id]?.toolName;
|
|
62
104
|
const tool = getOwn(tools, toolName);
|
|
63
105
|
|
|
64
106
|
if (tool?.onInputDelta != null) {
|
|
@@ -67,7 +109,10 @@ export function invokeToolCallbacksFromStream<
|
|
|
67
109
|
toolCallId: chunk.id,
|
|
68
110
|
messages: stepInputMessages,
|
|
69
111
|
abortSignal,
|
|
70
|
-
context:
|
|
112
|
+
context: await getValidatedContext({
|
|
113
|
+
toolCallId: chunk.id,
|
|
114
|
+
toolName,
|
|
115
|
+
}),
|
|
71
116
|
});
|
|
72
117
|
}
|
|
73
118
|
|
|
@@ -75,19 +120,26 @@ export function invokeToolCallbacksFromStream<
|
|
|
75
120
|
}
|
|
76
121
|
|
|
77
122
|
case 'tool-call': {
|
|
78
|
-
const toolName =
|
|
123
|
+
const toolName = ongoingToolCalls[chunk.toolCallId]?.toolName;
|
|
79
124
|
const tool = getOwn(tools, toolName);
|
|
80
125
|
|
|
81
|
-
|
|
126
|
+
if (!chunk.invalid && tool?.onInputAvailable != null) {
|
|
127
|
+
const validatedContext = getValidatedContext({
|
|
128
|
+
toolCallId: chunk.toolCallId,
|
|
129
|
+
toolName,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
delete ongoingToolCalls[chunk.toolCallId];
|
|
82
133
|
|
|
83
|
-
if (tool?.onInputAvailable != null) {
|
|
84
134
|
await tool.onInputAvailable({
|
|
85
135
|
input: chunk.input,
|
|
86
136
|
toolCallId: chunk.toolCallId,
|
|
87
137
|
messages: stepInputMessages,
|
|
88
138
|
abortSignal,
|
|
89
|
-
context:
|
|
139
|
+
context: await validatedContext,
|
|
90
140
|
});
|
|
141
|
+
} else {
|
|
142
|
+
delete ongoingToolCalls[chunk.toolCallId];
|
|
91
143
|
}
|
|
92
144
|
}
|
|
93
145
|
}
|
|
@@ -121,6 +121,7 @@ export function pruneMessages({
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
const approvalIdToToolCallId = new Map<string, string>();
|
|
124
125
|
const approvalIdToToolName = new Map<string, string>();
|
|
125
126
|
for (const message of messages) {
|
|
126
127
|
if (
|
|
@@ -129,6 +130,8 @@ export function pruneMessages({
|
|
|
129
130
|
) {
|
|
130
131
|
for (const part of message.content) {
|
|
131
132
|
if (part.type === 'tool-approval-request') {
|
|
133
|
+
approvalIdToToolCallId.set(part.approvalId, part.toolCallId);
|
|
134
|
+
|
|
132
135
|
const toolName = toolCallIdToToolName.get(part.toolCallId);
|
|
133
136
|
if (toolName != null) {
|
|
134
137
|
approvalIdToToolName.set(part.approvalId, toolName);
|
|
@@ -138,6 +141,16 @@ export function pruneMessages({
|
|
|
138
141
|
}
|
|
139
142
|
}
|
|
140
143
|
|
|
144
|
+
// Approval requests depend on their originating tool call. When an
|
|
145
|
+
// approval response is in the retained messages, trace through its request
|
|
146
|
+
// so pruning does not leave a pending approval without the call to execute.
|
|
147
|
+
for (const approvalId of keptApprovalIds) {
|
|
148
|
+
const toolCallId = approvalIdToToolCallId.get(approvalId);
|
|
149
|
+
if (toolCallId != null) {
|
|
150
|
+
keptToolCallIds.add(toolCallId);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
141
154
|
messages = messages.map((message, messageIndex) => {
|
|
142
155
|
if (
|
|
143
156
|
(message.role !== 'assistant' && message.role !== 'tool') ||
|
|
@@ -968,14 +968,16 @@ function createOutputTransformStream<
|
|
|
968
968
|
let text = '';
|
|
969
969
|
let textChunk = '';
|
|
970
970
|
let textProviderMetadata: ProviderMetadata | undefined = undefined;
|
|
971
|
-
let lastPublishedValue =
|
|
971
|
+
let lastPublishedValue: string | undefined = undefined;
|
|
972
|
+
let hasPublishedValue = false;
|
|
972
973
|
|
|
973
|
-
function
|
|
974
|
+
function resetOutputState() {
|
|
974
975
|
firstTextChunkId = undefined;
|
|
975
976
|
text = '';
|
|
976
977
|
textChunk = '';
|
|
977
978
|
textProviderMetadata = undefined;
|
|
978
|
-
lastPublishedValue =
|
|
979
|
+
lastPublishedValue = undefined;
|
|
980
|
+
hasPublishedValue = false;
|
|
979
981
|
}
|
|
980
982
|
|
|
981
983
|
function enqueueChunk({
|
|
@@ -1020,11 +1022,15 @@ function createOutputTransformStream<
|
|
|
1020
1022
|
>({
|
|
1021
1023
|
async transform(chunk, controller) {
|
|
1022
1024
|
if (isStreamRetryBoundaryPart(chunk)) {
|
|
1023
|
-
|
|
1025
|
+
resetOutputState();
|
|
1024
1026
|
controller.enqueue(chunk);
|
|
1025
1027
|
return;
|
|
1026
1028
|
}
|
|
1027
1029
|
|
|
1030
|
+
if (chunk.type === 'start-step') {
|
|
1031
|
+
resetOutputState();
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1028
1034
|
// ensure that we publish the last text chunk before the step finish:
|
|
1029
1035
|
if (chunk.type === 'finish-step' && textChunk.length > 0) {
|
|
1030
1036
|
publishTextChunk({ controller });
|
|
@@ -1096,9 +1102,10 @@ function createOutputTransformStream<
|
|
|
1096
1102
|
typeof result.partial === 'string'
|
|
1097
1103
|
? result.partial
|
|
1098
1104
|
: JSON.stringify(result.partial);
|
|
1099
|
-
if (currentValue !== lastPublishedValue) {
|
|
1105
|
+
if (!hasPublishedValue || currentValue !== lastPublishedValue) {
|
|
1100
1106
|
publishTextChunk({ controller, partialOutput: result.partial });
|
|
1101
1107
|
lastPublishedValue = currentValue;
|
|
1108
|
+
hasPublishedValue = true;
|
|
1102
1109
|
}
|
|
1103
1110
|
}
|
|
1104
1111
|
},
|
|
@@ -1414,6 +1421,8 @@ class DefaultStreamTextResult<
|
|
|
1414
1421
|
const initialResponseMessages: Array<ResponseMessage> = [];
|
|
1415
1422
|
let stepMessagesForNextStep: Array<ModelMessage> | undefined;
|
|
1416
1423
|
let currentStepMessages: Array<ModelMessage> = [];
|
|
1424
|
+
let isAborted = false;
|
|
1425
|
+
let currentStepModel = model;
|
|
1417
1426
|
|
|
1418
1427
|
// provider-assigned text/reasoning part IDs are only unique within a
|
|
1419
1428
|
// single model call (e.g. Anthropic uses the content block index, which
|
|
@@ -1661,8 +1670,8 @@ class DefaultStreamTextResult<
|
|
|
1661
1670
|
new DefaultStepResult({
|
|
1662
1671
|
callId,
|
|
1663
1672
|
stepNumber: recordedSteps.length,
|
|
1664
|
-
provider:
|
|
1665
|
-
modelId:
|
|
1673
|
+
provider: currentStepModel.provider,
|
|
1674
|
+
modelId: currentStepModel.modelId,
|
|
1666
1675
|
runtimeContext,
|
|
1667
1676
|
toolsContext,
|
|
1668
1677
|
content: recordedContent,
|
|
@@ -1691,8 +1700,8 @@ class DefaultStreamTextResult<
|
|
|
1691
1700
|
|
|
1692
1701
|
logWarnings({
|
|
1693
1702
|
warnings: recordedWarnings,
|
|
1694
|
-
provider:
|
|
1695
|
-
model:
|
|
1703
|
+
provider: currentStepModel.provider,
|
|
1704
|
+
model: currentStepModel.modelId,
|
|
1696
1705
|
});
|
|
1697
1706
|
|
|
1698
1707
|
recordedSteps.push(currentStepResult);
|
|
@@ -1743,6 +1752,10 @@ class DefaultStreamTextResult<
|
|
|
1743
1752
|
// aggregate results:
|
|
1744
1753
|
self._steps.resolve(recordedSteps);
|
|
1745
1754
|
|
|
1755
|
+
if (isAborted) {
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1746
1759
|
// call onEnd callback:
|
|
1747
1760
|
const finalStep = recordedSteps[recordedSteps.length - 1];
|
|
1748
1761
|
const content = recordedSteps.flatMap(step => step.content);
|
|
@@ -1845,6 +1858,8 @@ class DefaultStreamTextResult<
|
|
|
1845
1858
|
async pull(controller) {
|
|
1846
1859
|
// abort handling:
|
|
1847
1860
|
async function abort() {
|
|
1861
|
+
isAborted = true;
|
|
1862
|
+
|
|
1848
1863
|
await notify({
|
|
1849
1864
|
event: {
|
|
1850
1865
|
callId,
|
|
@@ -1885,6 +1900,7 @@ class DefaultStreamTextResult<
|
|
|
1885
1900
|
if (isAbortError(error) && abortSignal?.aborted) {
|
|
1886
1901
|
await abort();
|
|
1887
1902
|
} else {
|
|
1903
|
+
await telemetryDispatcher.onError?.({ callId, error });
|
|
1888
1904
|
controller.error(error);
|
|
1889
1905
|
}
|
|
1890
1906
|
}
|
|
@@ -2328,6 +2344,7 @@ class DefaultStreamTextResult<
|
|
|
2328
2344
|
const stepModel = resolveLanguageModel(
|
|
2329
2345
|
prepareStepResult?.model ?? model,
|
|
2330
2346
|
);
|
|
2347
|
+
currentStepModel = stepModel;
|
|
2331
2348
|
|
|
2332
2349
|
const stepActiveTools = filterActiveTools({
|
|
2333
2350
|
tools,
|
|
@@ -2658,7 +2675,7 @@ class DefaultStreamTextResult<
|
|
|
2658
2675
|
tools: stepExecutionTools as TOOLS,
|
|
2659
2676
|
stepInputMessages: stepMessages,
|
|
2660
2677
|
abortSignal,
|
|
2661
|
-
|
|
2678
|
+
toolsContext,
|
|
2662
2679
|
});
|
|
2663
2680
|
|
|
2664
2681
|
// Create child spans under the current step context.
|
|
@@ -2752,7 +2769,7 @@ class DefaultStreamTextResult<
|
|
|
2752
2769
|
const createStepResponse = () => ({
|
|
2753
2770
|
id: generateId(),
|
|
2754
2771
|
timestamp: new Date(),
|
|
2755
|
-
modelId:
|
|
2772
|
+
modelId: stepModel.modelId,
|
|
2756
2773
|
});
|
|
2757
2774
|
let stepResponse: {
|
|
2758
2775
|
id: string;
|
|
@@ -3375,7 +3392,7 @@ class DefaultStreamTextResult<
|
|
|
3375
3392
|
InferPartialOutput<OUTPUT>
|
|
3376
3393
|
>({
|
|
3377
3394
|
transform({ partialOutput }, controller) {
|
|
3378
|
-
if (partialOutput
|
|
3395
|
+
if (partialOutput !== undefined) {
|
|
3379
3396
|
controller.enqueue(partialOutput);
|
|
3380
3397
|
}
|
|
3381
3398
|
},
|