ai 7.0.122 → 7.0.124
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 +7598 -7268
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19555 -22545
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +2235 -2004
- package/dist/internal/index.d.ts.map +1 -0
- package/dist/internal/index.js +3261 -3921
- package/dist/internal/index.js.map +1 -1
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/test/index.d.ts +294 -273
- package/dist/test/index.d.ts.map +1 -0
- package/dist/test/index.js +325 -469
- package/dist/test/index.js.map +1 -1
- package/docs/02-foundations/03-prompts.mdx +3 -1
- package/docs/02-foundations/06-provider-options.mdx +1 -1
- package/docs/02-getting-started/06-nodejs.mdx +4 -4
- package/docs/02-getting-started/08-tanstack-start.mdx +1 -1
- package/docs/03-agents/05-configuring-call-options.mdx +1 -1
- package/docs/03-ai-sdk-core/10-generating-structured-data.mdx +1 -1
- package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +3 -0
- package/docs/03-ai-sdk-core/17-mcp-apps.mdx +5 -5
- package/docs/03-ai-sdk-core/36-transcription.mdx +36 -0
- package/docs/03-ai-sdk-core/37-speech.mdx +22 -1
- package/docs/03-ai-sdk-core/42-batch.mdx +1 -1
- package/docs/03-ai-sdk-core/60-telemetry.mdx +51 -3
- package/docs/03-ai-sdk-harnesses/02-harness-agent.mdx +24 -0
- package/docs/03-ai-sdk-harnesses/05-harness-adapters.mdx +12 -12
- package/docs/04-ai-sdk-ui/03-chatbot-message-persistence.mdx +9 -7
- package/docs/04-ai-sdk-ui/03-chatbot-resume-streams.mdx +2 -2
- package/docs/04-ai-sdk-ui/03-chatbot-tool-usage.mdx +7 -9
- package/docs/04-ai-sdk-ui/05-completion.mdx +1 -1
- package/docs/04-ai-sdk-ui/50-stream-protocol.mdx +2 -2
- package/docs/05-ai-sdk-rsc/03-generative-ui-state.mdx +8 -7
- package/docs/05-ai-sdk-rsc/06-loading-state.mdx +19 -10
- package/docs/05-ai-sdk-rsc/08-error-handling.mdx +2 -0
- package/docs/05-ai-sdk-rsc/10-migrating-to-ui.mdx +4 -3
- package/docs/06-advanced/02-stopping-streams.mdx +2 -2
- package/docs/06-advanced/04-caching.mdx +9 -5
- package/docs/06-advanced/05-multiple-streamables.mdx +1 -1
- package/docs/06-advanced/07-rendering-ui-with-language-models.mdx +16 -16
- package/docs/06-advanced/09-multistep-interfaces.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +4 -4
- package/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx +4 -4
- package/docs/07-reference/01-ai-sdk-core/11-stream-transcribe.mdx +7 -0
- package/docs/07-reference/01-ai-sdk-core/11-transcribe.mdx +7 -0
- package/docs/07-reference/01-ai-sdk-core/12-generate-speech.mdx +7 -0
- package/docs/07-reference/01-ai-sdk-core/14-upload-file.mdx +4 -4
- package/docs/07-reference/01-ai-sdk-core/14-upload-skill.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/15-agent.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/18-create-agent-ui-stream-response.mdx +1 -4
- package/docs/07-reference/01-ai-sdk-core/22-dynamic-tool.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/23-create-mcp-client.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/30-model-message.mdx +2 -2
- package/docs/07-reference/01-ai-sdk-core/69-add-tool-input-examples-middleware.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/80-smooth-stream.mdx +1 -1
- package/docs/07-reference/02-ai-sdk-ui/31-convert-to-model-messages.mdx +5 -4
- package/docs/07-reference/02-ai-sdk-ui/40-create-ui-message-stream.mdx +3 -2
- package/docs/07-reference/02-ai-sdk-ui/41-create-ui-message-stream-response.mdx +7 -0
- package/docs/07-reference/02-ai-sdk-ui/42-pipe-ui-message-stream-to-response.mdx +7 -0
- package/docs/07-reference/02-ai-sdk-ui/50-direct-chat-transport.mdx +2 -2
- package/docs/07-reference/03-ai-sdk-rsc/01-stream-ui.mdx +2 -2
- package/docs/07-reference/04-ai-sdk-workflow/01-workflow-agent.mdx +1 -1
- package/docs/08-migration-guides/25-migration-guide-5-0-data.mdx +4 -5
- package/docs/08-migration-guides/26-migration-guide-5-0.mdx +2 -1
- package/docs/08-migration-guides/29-migration-guide-4-0.mdx +4 -4
- package/docs/08-migration-guides/38-migration-guide-3-2.mdx +1 -1
- package/docs/08-migration-guides/39-migration-guide-3-1.mdx +1 -1
- package/docs/09-troubleshooting/12-use-chat-an-error-occurred.mdx +8 -6
- package/docs/09-troubleshooting/50-react-maximum-update-depth-exceeded.mdx +6 -6
- package/docs/09-troubleshooting/70-high-memory-usage-with-images.mdx +1 -1
- package/package.json +17 -17
- package/src/agent/create-agent-ui-stream-response.ts +3 -0
- package/src/agent/pipe-agent-ui-stream-to-response.ts +3 -0
- package/src/generate-speech/generate-speech.ts +163 -38
- package/src/generate-speech/index.ts +4 -0
- package/src/generate-speech/speech-events.ts +91 -0
- package/src/generate-text/prune-messages.ts +1 -1
- package/src/middleware/extract-reasoning-middleware.ts +27 -11
- package/src/telemetry/create-telemetry-dispatcher.ts +18 -5
- package/src/telemetry/telemetry.ts +38 -6
- package/src/telemetry/tracing-channel.ts +3 -0
- package/src/transcribe/index.ts +6 -0
- package/src/transcribe/stream-transcribe.ts +182 -31
- package/src/transcribe/transcribe.ts +186 -33
- package/src/transcribe/transcription-events.ts +111 -0
- package/src/ui/convert-to-model-messages.ts +18 -6
- package/src/ui-message-stream/create-sse-stream-with-keep-alive.ts +89 -0
- package/src/ui-message-stream/create-ui-message-stream-response.ts +7 -1
- package/src/ui-message-stream/pipe-ui-message-stream-to-response.ts +7 -1
- package/src/ui-message-stream/ui-message-stream-response-init.ts +7 -0
|
@@ -61,14 +61,14 @@ export default function Chat() {
|
|
|
61
61
|
type: 'UIMessage[]',
|
|
62
62
|
isOptional: true,
|
|
63
63
|
description:
|
|
64
|
-
'The original messages. If provided, persistence mode is assumed, and a message ID is provided for the response message.',
|
|
64
|
+
'The original messages. If provided, persistence mode is assumed, and a message ID is provided for the response message (generated with `generateMessageId`, or reused from the last message when it is an assistant message).',
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
name: 'generateMessageId',
|
|
68
68
|
type: 'IdGenerator',
|
|
69
69
|
isOptional: true,
|
|
70
70
|
description:
|
|
71
|
-
'Generate a message ID for the response message. If not provided, no message ID will be set for the response message.',
|
|
71
|
+
'Generate a message ID for the response message. If not provided, no message ID will be set for the response message (unless the original messages are provided and the last message is an assistant message).',
|
|
72
72
|
},
|
|
73
73
|
{
|
|
74
74
|
name: 'messageMetadata',
|
|
@@ -208,7 +208,7 @@ To see `streamUI` in action, check out [these examples](#examples).
|
|
|
208
208
|
{
|
|
209
209
|
name: 'role',
|
|
210
210
|
type: "'tool'",
|
|
211
|
-
description: 'The role for the
|
|
211
|
+
description: 'The role for the tool message.',
|
|
212
212
|
},
|
|
213
213
|
{
|
|
214
214
|
name: 'content',
|
|
@@ -364,7 +364,7 @@ To see `streamUI` in action, check out [these examples](#examples).
|
|
|
364
364
|
{
|
|
365
365
|
name: 'generate',
|
|
366
366
|
isOptional: true,
|
|
367
|
-
type: '(
|
|
367
|
+
type: '(parameters) => ReactNode | Promise<ReactNode> | Generator<ReactNode, ReactNode, void> | AsyncGenerator<ReactNode, ReactNode, void>',
|
|
368
368
|
description:
|
|
369
369
|
'A function or a generator function that is called with the arguments from the tool call and yields React nodes as the UI.',
|
|
370
370
|
},
|
|
@@ -127,6 +127,7 @@ type V4FilePart = Extract<V4Part, { type: 'file' }>;
|
|
|
127
127
|
// Type guards
|
|
128
128
|
function isV4Message(msg: V4Message | MyUIMessage): msg is V4Message {
|
|
129
129
|
return (
|
|
130
|
+
msg?.parts == null ||
|
|
130
131
|
'toolInvocations' in msg ||
|
|
131
132
|
(msg?.parts?.some(p => p.type === 'tool-invocation') ?? false) ||
|
|
132
133
|
msg?.role === 'data' ||
|
|
@@ -465,9 +466,8 @@ export async function POST(req: Request) {
|
|
|
465
466
|
message: convertV5MessageToV4(message), // convert to v4
|
|
466
467
|
});
|
|
467
468
|
|
|
468
|
-
// Load
|
|
469
|
-
const
|
|
470
|
-
const messages = [...previousMessages, message];
|
|
469
|
+
// Load all messages, including the one just saved (already in v5 format)
|
|
470
|
+
const messages = await loadChat(chatId);
|
|
471
471
|
|
|
472
472
|
const result = streamText({
|
|
473
473
|
model: __MODEL__,
|
|
@@ -827,8 +827,7 @@ export async function POST(req: Request) {
|
|
|
827
827
|
message,
|
|
828
828
|
});
|
|
829
829
|
|
|
830
|
-
const
|
|
831
|
-
const messages = [...previousMessages, message];
|
|
830
|
+
const messages = await loadChat(chatId);
|
|
832
831
|
|
|
833
832
|
const result = streamText({
|
|
834
833
|
model: __MODEL__,
|
|
@@ -723,7 +723,8 @@ const runtimeTool = dynamicTool({
|
|
|
723
723
|
inputSchema: z.object({}),
|
|
724
724
|
execute: async input => {
|
|
725
725
|
// Input and output are typed as 'unknown'
|
|
726
|
-
|
|
726
|
+
const { query } = input as { query: string };
|
|
727
|
+
return { result: `Processed: ${query}` };
|
|
727
728
|
},
|
|
728
729
|
});
|
|
729
730
|
```
|
|
@@ -440,7 +440,7 @@ const result = streamText({
|
|
|
440
440
|
|
|
441
441
|
result.toDataStream();
|
|
442
442
|
result.pipeDataStreamToResponse(response);
|
|
443
|
-
result.
|
|
443
|
+
result.toDataStreamResponse();
|
|
444
444
|
```
|
|
445
445
|
|
|
446
446
|
### Renamed "formatStreamPart" to "formatDataStreamPart"
|
|
@@ -573,7 +573,7 @@ const result = await streamText({
|
|
|
573
573
|
// ...
|
|
574
574
|
});
|
|
575
575
|
|
|
576
|
-
result.
|
|
576
|
+
result.pipeDataStreamToResponse(response, {
|
|
577
577
|
init: {
|
|
578
578
|
headers: {
|
|
579
579
|
'X-Custom-Header': 'value',
|
|
@@ -588,7 +588,7 @@ const result = streamText({
|
|
|
588
588
|
// ...
|
|
589
589
|
});
|
|
590
590
|
|
|
591
|
-
result.
|
|
591
|
+
result.pipeDataStreamToResponse(response, {
|
|
592
592
|
headers: {
|
|
593
593
|
'X-Custom-Header': 'value',
|
|
594
594
|
},
|
|
@@ -710,7 +710,7 @@ const warnings = await result.warnings;
|
|
|
710
710
|
code manually.
|
|
711
711
|
</Note>
|
|
712
712
|
|
|
713
|
-
The `simulateReadableStream` function from `ai/test` has been renamed to `chunks`.
|
|
713
|
+
The `values` option of the `simulateReadableStream` function from `ai/test` has been renamed to `chunks`.
|
|
714
714
|
|
|
715
715
|
```ts filename="AI SDK 3.4"
|
|
716
716
|
import { simulateReadableStream } from 'ai/test';
|
|
@@ -23,7 +23,7 @@ This guide will help you upgrade to AI SDK 3.2:
|
|
|
23
23
|
|
|
24
24
|
To update to AI SDK version 3.2, run the following command using your preferred package manager:
|
|
25
25
|
|
|
26
|
-
<Snippet text="pnpm add ai@
|
|
26
|
+
<Snippet text="pnpm add ai@3.2" />
|
|
27
27
|
|
|
28
28
|
## Removed Functionality
|
|
29
29
|
|
|
@@ -49,14 +49,16 @@ return createUIMessageStreamResponse({
|
|
|
49
49
|
});
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
In case you are using `
|
|
52
|
+
In case you are using `createUIMessageStream`, you can pass the `onError` function to it:
|
|
53
53
|
|
|
54
54
|
```tsx
|
|
55
|
-
const response =
|
|
56
|
-
|
|
57
|
-
async execute(dataStream) {
|
|
55
|
+
const response = createUIMessageStreamResponse({
|
|
56
|
+
stream: createUIMessageStream({
|
|
58
57
|
// ...
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
async execute({ writer }) {
|
|
59
|
+
// ...
|
|
60
|
+
},
|
|
61
|
+
onError: errorHandler,
|
|
62
|
+
}),
|
|
61
63
|
});
|
|
62
64
|
```
|
|
@@ -23,17 +23,17 @@ Use the `throttle` option to throttle the UI updates:
|
|
|
23
23
|
### `useChat`
|
|
24
24
|
|
|
25
25
|
```tsx filename="page.tsx" highlight="2-3"
|
|
26
|
-
const { messages
|
|
26
|
+
const { messages } = useChat({
|
|
27
27
|
// Throttle the messages and data updates to 50ms:
|
|
28
|
-
throttle: 50
|
|
29
|
-
})
|
|
28
|
+
throttle: 50,
|
|
29
|
+
});
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
### `useCompletion`
|
|
33
33
|
|
|
34
34
|
```tsx filename="page.tsx" highlight="2-3"
|
|
35
|
-
const { completion
|
|
35
|
+
const { completion } = useCompletion({
|
|
36
36
|
// Throttle the completion and data updates to 50ms:
|
|
37
|
-
throttle: 50
|
|
38
|
-
})
|
|
37
|
+
throttle: 50,
|
|
38
|
+
});
|
|
39
39
|
```
|
|
@@ -17,7 +17,7 @@ This is especially noticeable when using `experimental_download` to process imag
|
|
|
17
17
|
|
|
18
18
|
## Background
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
When enabled with the `include` option, the AI SDK includes the full request messages plus request and response bodies in the step results. When processing images, the request messages and request body can contain base64-encoded image data, which can be very large (a single image can be 1MB+ when base64 encoded). If you process many images and keep references to the results, this data accumulates in memory.
|
|
21
21
|
|
|
22
22
|
For example, processing 100 images of 500KB each would include ~50MB+ of request body data in memory.
|
|
23
23
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.124",
|
|
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.
|
|
46
|
-
"@ai-sdk/provider": "4.0.
|
|
47
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
45
|
+
"@ai-sdk/gateway": "4.0.102",
|
|
46
|
+
"@ai-sdk/provider": "4.0.21",
|
|
47
|
+
"@ai-sdk/provider-utils": "5.0.53"
|
|
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.
|
|
57
|
-
"@ai-sdk/test-server": "2.0.
|
|
58
|
-
"@ai-sdk/xai": "5.0.
|
|
50
|
+
"@ai-sdk/amazon-bedrock": "5.0.103",
|
|
51
|
+
"@ai-sdk/deepseek": "3.0.58",
|
|
52
|
+
"@ai-sdk/google": "4.0.87",
|
|
53
|
+
"@ai-sdk/groq": "4.0.54",
|
|
54
|
+
"@ai-sdk/huggingface": "2.0.61",
|
|
55
|
+
"@ai-sdk/moonshotai": "3.0.62",
|
|
56
|
+
"@ai-sdk/openai": "4.0.83",
|
|
57
|
+
"@ai-sdk/test-server": "2.0.3",
|
|
58
|
+
"@ai-sdk/xai": "5.0.14",
|
|
59
59
|
"@edge-runtime/vm": "^5.0.0",
|
|
60
60
|
"@smithy/eventstream-codec": "^4.3.3",
|
|
61
61
|
"@smithy/util-utf8": "^4.3.3",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"@types/node": "22.19.19",
|
|
64
64
|
"@vercel/ai-tsconfig": "0.0.0",
|
|
65
65
|
"esbuild": "^0.28.1",
|
|
66
|
-
"
|
|
66
|
+
"tsdown": "^0.23.0",
|
|
67
67
|
"tsx": "4.23.12",
|
|
68
68
|
"typescript": "5.8.3",
|
|
69
69
|
"zod": "3.25.76"
|
|
@@ -113,9 +113,9 @@
|
|
|
113
113
|
"grok"
|
|
114
114
|
],
|
|
115
115
|
"scripts": {
|
|
116
|
-
"build": "pnpm clean &&
|
|
117
|
-
"build:watch": "pnpm clean &&
|
|
118
|
-
"clean": "del-cli
|
|
116
|
+
"build": "pnpm clean && tsdown",
|
|
117
|
+
"build:watch": "pnpm clean && tsdown --watch",
|
|
118
|
+
"clean": "del-cli docs *.tsbuildinfo",
|
|
119
119
|
"type-check": "tsc --build",
|
|
120
120
|
"test": "pnpm test:node && pnpm test:edge",
|
|
121
121
|
"test:update": "pnpm test:node -u",
|
|
@@ -33,6 +33,7 @@ import { createAgentUIStream } from './create-agent-ui-stream';
|
|
|
33
33
|
* @param headers - Additional headers for the response. Optional.
|
|
34
34
|
* @param status - The status code for the response. Optional.
|
|
35
35
|
* @param statusText - The status text for the response. Optional.
|
|
36
|
+
* @param keepAliveMs - Optional interval for sending SSE keep-alive comments.
|
|
36
37
|
* @param consumeSseStream - Whether to consume the SSE stream. Optional.
|
|
37
38
|
*
|
|
38
39
|
* @returns The response object.
|
|
@@ -47,6 +48,7 @@ export async function createAgentUIStreamResponse<
|
|
|
47
48
|
headers,
|
|
48
49
|
status,
|
|
49
50
|
statusText,
|
|
51
|
+
keepAliveMs,
|
|
50
52
|
consumeSseStream,
|
|
51
53
|
...options
|
|
52
54
|
}: {
|
|
@@ -68,6 +70,7 @@ export async function createAgentUIStreamResponse<
|
|
|
68
70
|
headers,
|
|
69
71
|
status,
|
|
70
72
|
statusText,
|
|
73
|
+
keepAliveMs,
|
|
71
74
|
consumeSseStream,
|
|
72
75
|
stream: await createAgentUIStream(options),
|
|
73
76
|
});
|
|
@@ -35,6 +35,7 @@ import { createAgentUIStream } from './create-agent-ui-stream';
|
|
|
35
35
|
* @param headers - Additional headers for the response. Optional.
|
|
36
36
|
* @param status - The status code for the response. Optional.
|
|
37
37
|
* @param statusText - The status text for the response. Optional.
|
|
38
|
+
* @param keepAliveMs - Optional interval for sending SSE keep-alive comments.
|
|
38
39
|
* @param consumeSseStream - Whether to consume the SSE stream. Optional.
|
|
39
40
|
*/
|
|
40
41
|
export async function pipeAgentUIStreamToResponse<
|
|
@@ -48,6 +49,7 @@ export async function pipeAgentUIStreamToResponse<
|
|
|
48
49
|
headers,
|
|
49
50
|
status,
|
|
50
51
|
statusText,
|
|
52
|
+
keepAliveMs,
|
|
51
53
|
consumeSseStream,
|
|
52
54
|
...options
|
|
53
55
|
}: {
|
|
@@ -71,6 +73,7 @@ export async function pipeAgentUIStreamToResponse<
|
|
|
71
73
|
headers,
|
|
72
74
|
status,
|
|
73
75
|
statusText,
|
|
76
|
+
keepAliveMs,
|
|
74
77
|
consumeSseStream,
|
|
75
78
|
stream: await createAgentUIStream(options),
|
|
76
79
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { JSONObject } from '@ai-sdk/provider';
|
|
2
2
|
import {
|
|
3
|
+
createIdGenerator,
|
|
3
4
|
detectMediaType,
|
|
4
5
|
withUserAgentSuffix,
|
|
5
6
|
type ProviderOptions,
|
|
@@ -7,16 +8,28 @@ import {
|
|
|
7
8
|
import { NoSpeechGeneratedError } from '../error/no-speech-generated-error';
|
|
8
9
|
import { logWarnings } from '../logger/log-warnings';
|
|
9
10
|
import { resolveSpeechModel } from '../model/resolve-model';
|
|
11
|
+
import { createTelemetryDispatcher } from '../telemetry/create-telemetry-dispatcher';
|
|
12
|
+
import type { TelemetryOptions } from '../telemetry/telemetry-options';
|
|
10
13
|
import type { SpeechModel } from '../types/speech-model';
|
|
11
14
|
import type { SpeechModelResponseMetadata } from '../types/speech-model-response-metadata';
|
|
12
15
|
import type { Warning } from '../types/warning';
|
|
13
16
|
import { prepareRetries } from '../util/prepare-retries';
|
|
17
|
+
import { notify } from '../util/notify';
|
|
14
18
|
import { VERSION } from '../version';
|
|
15
19
|
import type { SpeechResult } from './generate-speech-result';
|
|
16
20
|
import {
|
|
17
21
|
DefaultGeneratedAudioFile,
|
|
18
22
|
type GeneratedAudioFile,
|
|
19
23
|
} from './generated-audio-file';
|
|
24
|
+
import type {
|
|
25
|
+
GenerateSpeechEndEvent,
|
|
26
|
+
GenerateSpeechStartEvent,
|
|
27
|
+
} from './speech-events';
|
|
28
|
+
|
|
29
|
+
const originalGenerateCallId = createIdGenerator({
|
|
30
|
+
prefix: 'call',
|
|
31
|
+
size: 24,
|
|
32
|
+
});
|
|
20
33
|
/**
|
|
21
34
|
* Generates speech audio using a speech model.
|
|
22
35
|
*
|
|
@@ -32,6 +45,7 @@ import {
|
|
|
32
45
|
* @param maxRetries - Maximum number of retries. Set to 0 to disable retries. Default: 2.
|
|
33
46
|
* @param abortSignal - An optional abort signal that can be used to cancel the call.
|
|
34
47
|
* @param headers - Additional HTTP headers to be sent with the request. Only applicable for HTTP-based providers.
|
|
48
|
+
* @param telemetry - Optional telemetry configuration.
|
|
35
49
|
*
|
|
36
50
|
* @returns A result object that contains the generated audio data.
|
|
37
51
|
*/
|
|
@@ -47,6 +61,8 @@ export async function generateSpeech({
|
|
|
47
61
|
maxRetries: maxRetriesArg,
|
|
48
62
|
abortSignal,
|
|
49
63
|
headers,
|
|
64
|
+
telemetry,
|
|
65
|
+
_internal: { generateCallId = originalGenerateCallId } = {},
|
|
50
66
|
}: {
|
|
51
67
|
/**
|
|
52
68
|
* The speech model to use.
|
|
@@ -115,6 +131,18 @@ export async function generateSpeech({
|
|
|
115
131
|
* Only applicable for HTTP-based providers.
|
|
116
132
|
*/
|
|
117
133
|
headers?: Record<string, string>;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Optional telemetry configuration.
|
|
137
|
+
*/
|
|
138
|
+
telemetry?: TelemetryOptions;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Internal. For test use only. May change without notice.
|
|
142
|
+
*/
|
|
143
|
+
_internal?: {
|
|
144
|
+
generateCallId?: () => string;
|
|
145
|
+
};
|
|
118
146
|
}): Promise<SpeechResult> {
|
|
119
147
|
const resolvedModel = resolveSpeechModel(model);
|
|
120
148
|
if (!resolvedModel) {
|
|
@@ -126,55 +154,152 @@ export async function generateSpeech({
|
|
|
126
154
|
`ai/${VERSION}`,
|
|
127
155
|
);
|
|
128
156
|
|
|
129
|
-
const { retry } = prepareRetries({
|
|
157
|
+
const { maxRetries, retry } = prepareRetries({
|
|
130
158
|
maxRetries: maxRetriesArg,
|
|
131
159
|
abortSignal,
|
|
132
160
|
});
|
|
133
161
|
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
speed,
|
|
141
|
-
language,
|
|
142
|
-
abortSignal,
|
|
143
|
-
headers: headersWithUserAgent,
|
|
144
|
-
providerOptions,
|
|
145
|
-
}),
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
if (!result.audio || result.audio.length === 0) {
|
|
149
|
-
throw new NoSpeechGeneratedError({ responses: [result.response] });
|
|
150
|
-
}
|
|
162
|
+
const callId = generateCallId();
|
|
163
|
+
const telemetryDispatcher = createTelemetryDispatcher({ telemetry });
|
|
164
|
+
const runInTracingChannelSpan =
|
|
165
|
+
telemetryDispatcher.runInTracingChannelSpan ??
|
|
166
|
+
(async <T>({ execute }: { execute: () => PromiseLike<T> }) =>
|
|
167
|
+
await execute());
|
|
151
168
|
|
|
152
|
-
|
|
153
|
-
|
|
169
|
+
const startEvent: GenerateSpeechStartEvent = {
|
|
170
|
+
callId,
|
|
171
|
+
operationId: 'ai.generateSpeech',
|
|
154
172
|
provider: resolvedModel.provider,
|
|
155
|
-
|
|
156
|
-
|
|
173
|
+
modelId: resolvedModel.modelId,
|
|
174
|
+
text,
|
|
175
|
+
voice,
|
|
176
|
+
outputFormat,
|
|
177
|
+
instructions,
|
|
178
|
+
speed,
|
|
179
|
+
language,
|
|
180
|
+
maxRetries,
|
|
181
|
+
headers,
|
|
182
|
+
providerOptions,
|
|
183
|
+
};
|
|
157
184
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
185
|
+
return await runInTracingChannelSpan({
|
|
186
|
+
type: 'generateSpeech',
|
|
187
|
+
event: startEvent,
|
|
188
|
+
execute: async () => {
|
|
189
|
+
await notify({
|
|
190
|
+
event: startEvent,
|
|
191
|
+
callbacks: [telemetryDispatcher.onStart],
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const result = await retry(() =>
|
|
196
|
+
resolvedModel.doGenerate({
|
|
197
|
+
text,
|
|
198
|
+
voice,
|
|
199
|
+
outputFormat,
|
|
200
|
+
instructions,
|
|
201
|
+
speed,
|
|
202
|
+
language,
|
|
203
|
+
abortSignal,
|
|
204
|
+
headers: headersWithUserAgent,
|
|
205
|
+
providerOptions,
|
|
206
|
+
}),
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
if (!result.audio || result.audio.length === 0) {
|
|
210
|
+
throw new NoSpeechGeneratedError({ responses: [result.response] });
|
|
211
|
+
}
|
|
162
212
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
213
|
+
logWarnings({
|
|
214
|
+
warnings: result.warnings,
|
|
215
|
+
provider: resolvedModel.provider,
|
|
216
|
+
model: resolvedModel.modelId,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const detectedMediaType = detectMediaType({
|
|
220
|
+
data: result.audio,
|
|
221
|
+
topLevelType: 'audio',
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const audio = new DefaultGeneratedAudioFile({
|
|
225
|
+
data: result.audio,
|
|
226
|
+
mediaType:
|
|
227
|
+
detectedMediaType ??
|
|
228
|
+
getResponseAudioMediaType(result.response.headers) ??
|
|
229
|
+
getOutputFormatMediaType(outputFormat) ??
|
|
230
|
+
'audio/mp3',
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
if (telemetryDispatcher.onEnd != null) {
|
|
234
|
+
const endEvent: GenerateSpeechEndEvent = {
|
|
235
|
+
callId,
|
|
236
|
+
operationId: 'ai.generateSpeech',
|
|
237
|
+
provider: resolvedModel.provider,
|
|
238
|
+
modelId: resolvedModel.modelId,
|
|
239
|
+
text,
|
|
240
|
+
audio: {
|
|
241
|
+
byteLength: getBase64OrBinaryByteLength(result.audio),
|
|
242
|
+
mediaType: audio.mediaType,
|
|
243
|
+
format: audio.format,
|
|
244
|
+
},
|
|
245
|
+
usage: result.usage,
|
|
246
|
+
warnings: result.warnings,
|
|
247
|
+
providerMetadata: result.providerMetadata,
|
|
248
|
+
response: getTelemetryResponseMetadata(result.response),
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
await notify({
|
|
252
|
+
event: endEvent,
|
|
253
|
+
callbacks: [telemetryDispatcher.onEnd],
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return new DefaultSpeechResult({
|
|
258
|
+
audio,
|
|
259
|
+
warnings: result.warnings,
|
|
260
|
+
responses: [result.response],
|
|
261
|
+
providerMetadata: result.providerMetadata,
|
|
262
|
+
});
|
|
263
|
+
} catch (error) {
|
|
264
|
+
await telemetryDispatcher.onError?.({ callId, error });
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
},
|
|
175
268
|
});
|
|
176
269
|
}
|
|
177
270
|
|
|
271
|
+
function getBase64OrBinaryByteLength(audio: string | Uint8Array): number {
|
|
272
|
+
if (audio instanceof Uint8Array) {
|
|
273
|
+
return audio.byteLength;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
let characterCount = 0;
|
|
277
|
+
let lastCharacter = '';
|
|
278
|
+
let secondLastCharacter = '';
|
|
279
|
+
for (const character of audio) {
|
|
280
|
+
if (/\s/.test(character)) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
characterCount++;
|
|
284
|
+
secondLastCharacter = lastCharacter;
|
|
285
|
+
lastCharacter = character;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const padding =
|
|
289
|
+
lastCharacter === '=' ? (secondLastCharacter === '=' ? 2 : 1) : 0;
|
|
290
|
+
return Math.floor((characterCount * 3) / 4) - padding;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function getTelemetryResponseMetadata(
|
|
294
|
+
response: SpeechModelResponseMetadata,
|
|
295
|
+
): SpeechModelResponseMetadata {
|
|
296
|
+
return {
|
|
297
|
+
timestamp: response.timestamp,
|
|
298
|
+
modelId: response.modelId,
|
|
299
|
+
headers: response.headers,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
178
303
|
function getResponseAudioMediaType(
|
|
179
304
|
headers: Record<string, string> | undefined,
|
|
180
305
|
): string | undefined {
|
|
@@ -4,6 +4,10 @@ import { generateSpeech } from './generate-speech';
|
|
|
4
4
|
export { generateSpeech } from './generate-speech';
|
|
5
5
|
export type { SpeechResult } from './generate-speech-result';
|
|
6
6
|
export type { GeneratedAudioFile } from './generated-audio-file';
|
|
7
|
+
export type {
|
|
8
|
+
GenerateSpeechEndEvent,
|
|
9
|
+
GenerateSpeechStartEvent,
|
|
10
|
+
} from './speech-events';
|
|
7
11
|
|
|
8
12
|
// deprecated exports
|
|
9
13
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { JSONObject } from '@ai-sdk/provider';
|
|
2
|
+
import type { ProviderOptions } from '@ai-sdk/provider-utils';
|
|
3
|
+
import type { ProviderMetadata } from '../types';
|
|
4
|
+
import type { SpeechModelResponseMetadata } from '../types/speech-model-response-metadata';
|
|
5
|
+
import type { Warning } from '../types/warning';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Event passed to telemetry integrations when speech generation begins.
|
|
9
|
+
*/
|
|
10
|
+
export type GenerateSpeechStartEvent = {
|
|
11
|
+
/** Unique identifier for this speech generation call. */
|
|
12
|
+
readonly callId: string;
|
|
13
|
+
|
|
14
|
+
/** Identifies the operation type (`ai.generateSpeech`). */
|
|
15
|
+
readonly operationId: 'ai.generateSpeech';
|
|
16
|
+
|
|
17
|
+
/** The provider identifier. */
|
|
18
|
+
readonly provider: string;
|
|
19
|
+
|
|
20
|
+
/** The specific model identifier. */
|
|
21
|
+
readonly modelId: string;
|
|
22
|
+
|
|
23
|
+
/** The text to convert to speech. */
|
|
24
|
+
readonly text: string;
|
|
25
|
+
|
|
26
|
+
/** The requested voice. */
|
|
27
|
+
readonly voice: string | undefined;
|
|
28
|
+
|
|
29
|
+
/** The requested output format. */
|
|
30
|
+
readonly outputFormat: string | undefined;
|
|
31
|
+
|
|
32
|
+
/** Additional speech instructions. */
|
|
33
|
+
readonly instructions: string | undefined;
|
|
34
|
+
|
|
35
|
+
/** The requested speech speed. */
|
|
36
|
+
readonly speed: number | undefined;
|
|
37
|
+
|
|
38
|
+
/** The requested language. */
|
|
39
|
+
readonly language: string | undefined;
|
|
40
|
+
|
|
41
|
+
/** Maximum number of retries for failed requests. */
|
|
42
|
+
readonly maxRetries: number;
|
|
43
|
+
|
|
44
|
+
/** Additional HTTP headers sent with the request. */
|
|
45
|
+
readonly headers: Record<string, string> | undefined;
|
|
46
|
+
|
|
47
|
+
/** Additional provider-specific options. */
|
|
48
|
+
readonly providerOptions: ProviderOptions;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Event passed to telemetry integrations when speech generation completes.
|
|
53
|
+
*
|
|
54
|
+
* Raw audio is intentionally not included. Integrations receive only its size,
|
|
55
|
+
* media type, and format.
|
|
56
|
+
*/
|
|
57
|
+
export type GenerateSpeechEndEvent = {
|
|
58
|
+
/** Unique identifier for this speech generation call. */
|
|
59
|
+
readonly callId: string;
|
|
60
|
+
|
|
61
|
+
/** Identifies the operation type (`ai.generateSpeech`). */
|
|
62
|
+
readonly operationId: 'ai.generateSpeech';
|
|
63
|
+
|
|
64
|
+
/** The provider identifier. */
|
|
65
|
+
readonly provider: string;
|
|
66
|
+
|
|
67
|
+
/** The specific model identifier. */
|
|
68
|
+
readonly modelId: string;
|
|
69
|
+
|
|
70
|
+
/** The text that was converted to speech. */
|
|
71
|
+
readonly text: string;
|
|
72
|
+
|
|
73
|
+
/** Metadata about the generated audio. */
|
|
74
|
+
readonly audio: {
|
|
75
|
+
readonly byteLength: number;
|
|
76
|
+
readonly mediaType: string;
|
|
77
|
+
readonly format: string;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** Usage reported by the provider, when available. */
|
|
81
|
+
readonly usage: JSONObject | undefined;
|
|
82
|
+
|
|
83
|
+
/** Warnings from the speech model. */
|
|
84
|
+
readonly warnings: Array<Warning>;
|
|
85
|
+
|
|
86
|
+
/** Optional provider-specific metadata. */
|
|
87
|
+
readonly providerMetadata: ProviderMetadata | undefined;
|
|
88
|
+
|
|
89
|
+
/** Response metadata from the provider. */
|
|
90
|
+
readonly response: SpeechModelResponseMetadata;
|
|
91
|
+
};
|