ai 7.0.52 → 7.0.55
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 +29 -0
- package/dist/index.d.ts +134 -4
- package/dist/index.js +315 -36
- package/dist/index.js.map +1 -1
- package/dist/internal/index.js +9 -2
- package/dist/internal/index.js.map +1 -1
- package/docs/02-foundations/02-providers-and-models.mdx +1 -0
- package/docs/03-ai-sdk-core/36-transcription.mdx +1 -0
- package/docs/03-ai-sdk-core/37-speech.mdx +3 -0
- package/docs/03-ai-sdk-core/40-middleware.mdx +28 -0
- package/docs/03-ai-sdk-core/60-telemetry.mdx +3 -2
- package/docs/07-reference/01-ai-sdk-core/68-default-instructions-middleware.mdx +106 -0
- package/docs/07-reference/01-ai-sdk-core/68-default-settings-middleware.mdx +3 -0
- package/docs/07-reference/01-ai-sdk-core/index.mdx +5 -0
- package/package.json +4 -4
- package/src/agent/tool-loop-agent-settings.ts +2 -0
- package/src/batch/batch-types.ts +134 -0
- package/src/batch/batch.ts +336 -0
- package/src/batch/index.ts +19 -0
- package/src/index.ts +1 -0
- package/src/middleware/default-instructions-middleware.ts +44 -0
- package/src/middleware/index.ts +1 -0
- package/src/ui/chat.ts +4 -1
- package/src/ui/convert-to-model-messages.ts +4 -2
- package/src/util/async-iterable-stream.ts +9 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UnsupportedFunctionalityError,
|
|
3
|
+
type Experimental_BatchLanguageModelV4 as BatchLanguageModelV4,
|
|
4
|
+
type Experimental_BatchV4ItemResult as BatchV4ItemResult,
|
|
5
|
+
type LanguageModelV4,
|
|
6
|
+
type LanguageModelV4GenerateResult,
|
|
7
|
+
} from '@ai-sdk/provider';
|
|
8
|
+
import { withUserAgentSuffix } from '@ai-sdk/provider-utils';
|
|
9
|
+
import { InvalidArgumentError } from '../error/invalid-argument-error';
|
|
10
|
+
import { logWarnings } from '../logger/log-warnings';
|
|
11
|
+
import { resolveLanguageModel } from '../model/resolve-model';
|
|
12
|
+
import { convertToLanguageModelPrompt } from '../prompt/convert-to-language-model-prompt';
|
|
13
|
+
import { prepareLanguageModelCallOptions } from '../prompt/prepare-language-model-call-options';
|
|
14
|
+
import { getTotalTimeoutMs } from '../prompt/request-options';
|
|
15
|
+
import { standardizePrompt } from '../prompt/standardize-prompt';
|
|
16
|
+
import { wrapGatewayError } from '../prompt/wrap-gateway-error';
|
|
17
|
+
import { asLanguageModelUsage } from '../types/usage';
|
|
18
|
+
import { asAsyncIterableStream } from '../util/async-iterable-stream';
|
|
19
|
+
import { mergeAbortSignals } from '../util/merge-abort-signals';
|
|
20
|
+
import { prepareRetries } from '../util/prepare-retries';
|
|
21
|
+
import { VERSION } from '../version';
|
|
22
|
+
import type {
|
|
23
|
+
BatchOperationOptions,
|
|
24
|
+
BatchReference,
|
|
25
|
+
BatchStatus,
|
|
26
|
+
StartTextBatchOptions,
|
|
27
|
+
StartTextBatchResult,
|
|
28
|
+
TextBatchGenerationResult,
|
|
29
|
+
TextBatchItemResult,
|
|
30
|
+
TextBatchRequest,
|
|
31
|
+
} from './batch-types';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Starts a durable text-generation batch.
|
|
35
|
+
*/
|
|
36
|
+
export async function startTextBatch({
|
|
37
|
+
model: modelArg,
|
|
38
|
+
requests,
|
|
39
|
+
providerOptions,
|
|
40
|
+
abortSignal,
|
|
41
|
+
headers,
|
|
42
|
+
timeout,
|
|
43
|
+
}: StartTextBatchOptions): Promise<StartTextBatchResult> {
|
|
44
|
+
validateRequests(requests);
|
|
45
|
+
|
|
46
|
+
const model = resolveBatchLanguageModel(modelArg);
|
|
47
|
+
const operationAbortSignal = mergeAbortSignals(
|
|
48
|
+
abortSignal,
|
|
49
|
+
getTotalTimeoutMs(timeout),
|
|
50
|
+
);
|
|
51
|
+
const supportedUrls = await model.supportedUrls;
|
|
52
|
+
operationAbortSignal?.throwIfAborted();
|
|
53
|
+
const normalizedRequests = [];
|
|
54
|
+
|
|
55
|
+
for (const request of requests) {
|
|
56
|
+
const standardizedPrompt = await standardizePrompt(request);
|
|
57
|
+
|
|
58
|
+
normalizedRequests.push({
|
|
59
|
+
id: request.id,
|
|
60
|
+
options: {
|
|
61
|
+
...prepareLanguageModelCallOptions(request),
|
|
62
|
+
prompt: await convertToLanguageModelPrompt({
|
|
63
|
+
prompt: standardizedPrompt,
|
|
64
|
+
supportedUrls,
|
|
65
|
+
download: undefined,
|
|
66
|
+
provider: model.provider.split('.')[0],
|
|
67
|
+
}),
|
|
68
|
+
providerOptions: request.providerOptions,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
operationAbortSignal?.throwIfAborted();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const headersWithUserAgent = withUserAgentSuffix(
|
|
75
|
+
headers ?? {},
|
|
76
|
+
`ai/${VERSION}`,
|
|
77
|
+
);
|
|
78
|
+
try {
|
|
79
|
+
const result = await model.experimental_doStartBatch({
|
|
80
|
+
requests: normalizedRequests,
|
|
81
|
+
providerOptions,
|
|
82
|
+
abortSignal: operationAbortSignal,
|
|
83
|
+
headers: headersWithUserAgent,
|
|
84
|
+
});
|
|
85
|
+
const { batchId, warnings, ...status } = result;
|
|
86
|
+
|
|
87
|
+
logWarnings({
|
|
88
|
+
warnings: warnings.map(({ warning }) => warning),
|
|
89
|
+
provider: model.provider,
|
|
90
|
+
model: model.modelId,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
version: 1,
|
|
95
|
+
type: 'text',
|
|
96
|
+
id: batchId,
|
|
97
|
+
provider: model.provider,
|
|
98
|
+
modelId: model.modelId,
|
|
99
|
+
...status,
|
|
100
|
+
warnings,
|
|
101
|
+
};
|
|
102
|
+
} catch (error) {
|
|
103
|
+
throw wrapGatewayError(error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Retrieves the latest normalized status for a durable batch.
|
|
109
|
+
*/
|
|
110
|
+
export async function getBatchStatus({
|
|
111
|
+
model: modelArg,
|
|
112
|
+
batch,
|
|
113
|
+
providerOptions,
|
|
114
|
+
maxRetries,
|
|
115
|
+
abortSignal,
|
|
116
|
+
headers,
|
|
117
|
+
timeout,
|
|
118
|
+
}: BatchOperationOptions): Promise<BatchStatus> {
|
|
119
|
+
const model = resolveBatchLanguageModel(modelArg);
|
|
120
|
+
validateBatchReference({ model, batch });
|
|
121
|
+
|
|
122
|
+
const operationAbortSignal = mergeAbortSignals(
|
|
123
|
+
abortSignal,
|
|
124
|
+
getTotalTimeoutMs(timeout),
|
|
125
|
+
);
|
|
126
|
+
const { retry } = prepareRetries({
|
|
127
|
+
maxRetries,
|
|
128
|
+
abortSignal: operationAbortSignal,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const status = await retry(() =>
|
|
133
|
+
model.experimental_doGetBatchStatus({
|
|
134
|
+
batchId: batch.id,
|
|
135
|
+
providerOptions,
|
|
136
|
+
abortSignal: operationAbortSignal,
|
|
137
|
+
headers: withUserAgentSuffix(headers ?? {}, `ai/${VERSION}`),
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
return status;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
throw wrapGatewayError(error);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Streams complete terminal results for the requests in a durable batch.
|
|
149
|
+
*/
|
|
150
|
+
export function getBatchResults({
|
|
151
|
+
model: modelArg,
|
|
152
|
+
batch,
|
|
153
|
+
providerOptions,
|
|
154
|
+
maxRetries,
|
|
155
|
+
abortSignal,
|
|
156
|
+
headers,
|
|
157
|
+
timeout,
|
|
158
|
+
}: BatchOperationOptions) {
|
|
159
|
+
const model = resolveBatchLanguageModel(modelArg);
|
|
160
|
+
validateBatchReference({ model, batch });
|
|
161
|
+
|
|
162
|
+
const streamAbortController = new AbortController();
|
|
163
|
+
const operationAbortSignal = mergeAbortSignals(
|
|
164
|
+
abortSignal,
|
|
165
|
+
getTotalTimeoutMs(timeout),
|
|
166
|
+
streamAbortController.signal,
|
|
167
|
+
);
|
|
168
|
+
const { retry } = prepareRetries({
|
|
169
|
+
maxRetries,
|
|
170
|
+
abortSignal: operationAbortSignal,
|
|
171
|
+
});
|
|
172
|
+
const transformer: Transformer<
|
|
173
|
+
BatchV4ItemResult<LanguageModelV4GenerateResult>,
|
|
174
|
+
TextBatchItemResult
|
|
175
|
+
> & { cancel?: (reason?: unknown) => void } = {
|
|
176
|
+
transform(item, controller) {
|
|
177
|
+
controller.enqueue(convertBatchItemResult(item));
|
|
178
|
+
},
|
|
179
|
+
|
|
180
|
+
cancel(reason) {
|
|
181
|
+
streamAbortController.abort(
|
|
182
|
+
reason ?? new Error('Batch results stream was cancelled.'),
|
|
183
|
+
);
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
const transform = new TransformStream<
|
|
187
|
+
BatchV4ItemResult<LanguageModelV4GenerateResult>,
|
|
188
|
+
TextBatchItemResult
|
|
189
|
+
>(transformer);
|
|
190
|
+
|
|
191
|
+
void (async () => {
|
|
192
|
+
try {
|
|
193
|
+
const stream = await retry(() =>
|
|
194
|
+
model.experimental_doGetBatchResults({
|
|
195
|
+
batchId: batch.id,
|
|
196
|
+
providerOptions,
|
|
197
|
+
abortSignal: operationAbortSignal,
|
|
198
|
+
headers: withUserAgentSuffix(headers ?? {}, `ai/${VERSION}`),
|
|
199
|
+
}),
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
await stream.pipeTo(transform.writable, {
|
|
203
|
+
signal: operationAbortSignal,
|
|
204
|
+
});
|
|
205
|
+
} catch (error) {
|
|
206
|
+
await transform.writable.abort(wrapGatewayError(error)).catch(() => {});
|
|
207
|
+
}
|
|
208
|
+
})();
|
|
209
|
+
|
|
210
|
+
return asAsyncIterableStream(transform.readable);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function resolveBatchLanguageModel(
|
|
214
|
+
modelArg: StartTextBatchOptions['model'],
|
|
215
|
+
): BatchLanguageModelV4 {
|
|
216
|
+
const model = resolveLanguageModel(modelArg);
|
|
217
|
+
|
|
218
|
+
if (!isBatchLanguageModel(model)) {
|
|
219
|
+
throw new UnsupportedFunctionalityError({
|
|
220
|
+
functionality: 'batch processing',
|
|
221
|
+
message: `The ${model.provider} model "${model.modelId}" does not support batch processing.`,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return model;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function isBatchLanguageModel(
|
|
229
|
+
model: LanguageModelV4,
|
|
230
|
+
): model is BatchLanguageModelV4 {
|
|
231
|
+
const candidate = model as Partial<BatchLanguageModelV4>;
|
|
232
|
+
return (
|
|
233
|
+
typeof candidate.experimental_doStartBatch === 'function' &&
|
|
234
|
+
typeof candidate.experimental_doGetBatchStatus === 'function' &&
|
|
235
|
+
typeof candidate.experimental_doGetBatchResults === 'function'
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function validateRequests(requests: ReadonlyArray<TextBatchRequest>) {
|
|
240
|
+
if (requests.length === 0) {
|
|
241
|
+
throw new InvalidArgumentError({
|
|
242
|
+
parameter: 'requests',
|
|
243
|
+
value: requests,
|
|
244
|
+
message: 'requests must not be empty',
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const ids = new Set<string>();
|
|
249
|
+
|
|
250
|
+
for (const request of requests) {
|
|
251
|
+
if (request.id.trim().length === 0) {
|
|
252
|
+
throw new InvalidArgumentError({
|
|
253
|
+
parameter: 'requests',
|
|
254
|
+
value: requests,
|
|
255
|
+
message: 'request IDs must not be empty',
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (ids.has(request.id)) {
|
|
260
|
+
throw new InvalidArgumentError({
|
|
261
|
+
parameter: 'requests',
|
|
262
|
+
value: requests,
|
|
263
|
+
message: `request IDs must be unique; duplicate ID "${request.id}"`,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
ids.add(request.id);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function validateBatchReference({
|
|
272
|
+
model,
|
|
273
|
+
batch,
|
|
274
|
+
}: {
|
|
275
|
+
model: BatchLanguageModelV4;
|
|
276
|
+
batch: BatchReference;
|
|
277
|
+
}) {
|
|
278
|
+
if (batch.version !== 1 || batch.type !== 'text') {
|
|
279
|
+
throw new InvalidArgumentError({
|
|
280
|
+
parameter: 'batch',
|
|
281
|
+
value: batch,
|
|
282
|
+
message: 'batch must be a supported text batch reference',
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (batch.provider !== model.provider || batch.modelId !== model.modelId) {
|
|
287
|
+
throw new InvalidArgumentError({
|
|
288
|
+
parameter: 'model',
|
|
289
|
+
value: model,
|
|
290
|
+
message:
|
|
291
|
+
`model ${model.provider}:${model.modelId} is not compatible with ` +
|
|
292
|
+
`batch ${batch.provider}:${batch.modelId}`,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function convertBatchItemResult(
|
|
298
|
+
item: BatchV4ItemResult<LanguageModelV4GenerateResult>,
|
|
299
|
+
): TextBatchItemResult {
|
|
300
|
+
if (item.status !== 'succeeded') {
|
|
301
|
+
return item;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
id: item.id,
|
|
306
|
+
status: 'succeeded',
|
|
307
|
+
...convertGenerateResult(item.result),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function convertGenerateResult(
|
|
312
|
+
result: LanguageModelV4GenerateResult,
|
|
313
|
+
): TextBatchGenerationResult {
|
|
314
|
+
return {
|
|
315
|
+
text: result.content
|
|
316
|
+
.filter(
|
|
317
|
+
(part): part is Extract<typeof part, { type: 'text' }> =>
|
|
318
|
+
part.type === 'text',
|
|
319
|
+
)
|
|
320
|
+
.map(part => part.text)
|
|
321
|
+
.join(''),
|
|
322
|
+
finishReason: result.finishReason.unified,
|
|
323
|
+
rawFinishReason: result.finishReason.raw,
|
|
324
|
+
usage: asLanguageModelUsage(result.usage),
|
|
325
|
+
...(result.response != null
|
|
326
|
+
? {
|
|
327
|
+
response: {
|
|
328
|
+
id: result.response.id,
|
|
329
|
+
timestamp: result.response.timestamp?.toISOString(),
|
|
330
|
+
modelId: result.response.modelId,
|
|
331
|
+
},
|
|
332
|
+
}
|
|
333
|
+
: {}),
|
|
334
|
+
providerMetadata: result.providerMetadata,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export {
|
|
2
|
+
startTextBatch as experimental_startTextBatch,
|
|
3
|
+
getBatchResults as experimental_getBatchResults,
|
|
4
|
+
getBatchStatus as experimental_getBatchStatus,
|
|
5
|
+
} from './batch';
|
|
6
|
+
export type {
|
|
7
|
+
BatchError as Experimental_BatchError,
|
|
8
|
+
BatchLanguageModel as Experimental_BatchLanguageModel,
|
|
9
|
+
BatchOperationOptions as Experimental_BatchOperationOptions,
|
|
10
|
+
BatchReference as Experimental_BatchReference,
|
|
11
|
+
BatchStatus as Experimental_BatchStatus,
|
|
12
|
+
StartTextBatchOptions as Experimental_StartTextBatchOptions,
|
|
13
|
+
StartTextBatchResult as Experimental_StartTextBatchResult,
|
|
14
|
+
TextBatch as Experimental_TextBatch,
|
|
15
|
+
TextBatchGenerationResult as Experimental_TextBatchGenerationResult,
|
|
16
|
+
TextBatchItemResult as Experimental_TextBatchItemResult,
|
|
17
|
+
TextBatchReference as Experimental_TextBatchReference,
|
|
18
|
+
TextBatchRequest as Experimental_TextBatchRequest,
|
|
19
|
+
} from './batch-types';
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { LanguageModelV4Prompt } from '@ai-sdk/provider';
|
|
2
|
+
import { asArray } from '@ai-sdk/provider-utils';
|
|
3
|
+
import type { Instructions } from '../prompt/prompt';
|
|
4
|
+
import type { LanguageModelMiddleware } from '../types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Applies default instructions to a language model when a call does not
|
|
8
|
+
* already contain a system message.
|
|
9
|
+
*/
|
|
10
|
+
export function defaultInstructionsMiddleware({
|
|
11
|
+
instructions,
|
|
12
|
+
}: {
|
|
13
|
+
/**
|
|
14
|
+
* Default instructions to prepend to calls that do not contain a system
|
|
15
|
+
* message.
|
|
16
|
+
*/
|
|
17
|
+
instructions: Instructions;
|
|
18
|
+
}): LanguageModelMiddleware {
|
|
19
|
+
const defaultSystemMessages: LanguageModelV4Prompt =
|
|
20
|
+
typeof instructions === 'string'
|
|
21
|
+
? [{ role: 'system', content: instructions }]
|
|
22
|
+
: asArray(instructions).map(message => ({
|
|
23
|
+
role: 'system',
|
|
24
|
+
content: message.content,
|
|
25
|
+
providerOptions: message.providerOptions,
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
specificationVersion: 'v4',
|
|
30
|
+
transformParams: async ({ params }) => {
|
|
31
|
+
if (
|
|
32
|
+
defaultSystemMessages.length === 0 ||
|
|
33
|
+
params.prompt.some(message => message.role === 'system')
|
|
34
|
+
) {
|
|
35
|
+
return params;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
...params,
|
|
40
|
+
prompt: [...defaultSystemMessages, ...params.prompt],
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
package/src/middleware/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { defaultEmbeddingSettingsMiddleware } from './default-embedding-settings-middleware';
|
|
2
|
+
export { defaultInstructionsMiddleware } from './default-instructions-middleware';
|
|
2
3
|
export { defaultSettingsMiddleware } from './default-settings-middleware';
|
|
3
4
|
export { extractJsonMiddleware } from './extract-json-middleware';
|
|
4
5
|
export { extractReasoningMiddleware } from './extract-reasoning-middleware';
|
package/src/ui/chat.ts
CHANGED
|
@@ -655,7 +655,10 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
|
|
|
655
655
|
try {
|
|
656
656
|
const response = {
|
|
657
657
|
state: createStreamingUIMessageState({
|
|
658
|
-
lastMessage:
|
|
658
|
+
lastMessage:
|
|
659
|
+
trigger === 'regenerate-message'
|
|
660
|
+
? undefined
|
|
661
|
+
: this.state.snapshot(lastMessage),
|
|
659
662
|
messageId: this.generateId(),
|
|
660
663
|
}),
|
|
661
664
|
abortController: new AbortController(),
|
|
@@ -62,8 +62,10 @@ export async function convertToModelMessages<UI_MESSAGE extends UIMessage>(
|
|
|
62
62
|
parts: message.parts.filter(
|
|
63
63
|
part =>
|
|
64
64
|
!isToolUIPart(part) ||
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
part.state === 'approval-responded' ||
|
|
66
|
+
part.state === 'output-available' ||
|
|
67
|
+
part.state === 'output-error' ||
|
|
68
|
+
part.state === 'output-denied',
|
|
67
69
|
),
|
|
68
70
|
}));
|
|
69
71
|
}
|
|
@@ -78,7 +78,15 @@ export function asAsyncIterableStream<T>(
|
|
|
78
78
|
return { done: true, value: undefined };
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
let result: ReadableStreamReadResult<T>;
|
|
82
|
+
try {
|
|
83
|
+
result = await reader.read();
|
|
84
|
+
} catch (error) {
|
|
85
|
+
await cleanup(false);
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const { done, value } = result;
|
|
82
90
|
|
|
83
91
|
if (done) {
|
|
84
92
|
await cleanup(true);
|