Skip to content

Commit cc16a83

Browse files
authored
feat (ai/core): add experimental transform option to streamText (#4074)
1 parent 3ce210f commit cc16a83

16 files changed

Lines changed: 391 additions & 14 deletions

File tree

.changeset/giant-ducks-live.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'ai': patch
3+
---
4+
5+
feat (ai/core): add smoothStream helper

.changeset/kind-cougars-check.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'ai': patch
3+
---
4+
5+
feat (ai/core): add experimental transform option to streamText

content/docs/03-ai-sdk-core/05-generating-text.mdx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,24 @@ for await (const part of result.fullStream) {
200200
}
201201
```
202202

203+
### Stream transformation
204+
205+
You can use the `experimental_transform` option to transform the stream.
206+
This is useful for e.g. filtering, changing, or smoothing the text stream.
207+
208+
The AI SDK Core provides a [`smoothStream` function](/docs/reference/ai-sdk-core/smooth-stream) that
209+
can be used to smooth out text streaming.
210+
211+
```tsx highlight="6"
212+
import { smoothStream, streamText } from 'ai';
213+
214+
const result = streamText({
215+
model,
216+
prompt,
217+
experimental_transform: smoothStream(),
218+
});
219+
```
220+
203221
## Generating Long Text
204222

205223
Most language models have an output limit that is much shorter than their context window.

content/docs/07-reference/01-ai-sdk-core/02-stream-text.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,12 @@ To see `streamText` in action, check out [these examples](#examples).
469469
description:
470470
'Enable streaming of tool call deltas as they are generated. Disabled by default.',
471471
},
472+
{
473+
name: 'experimental_transform',
474+
type: 'TransformStream<TextStreamPart<TOOLS>, TextStreamPart<TOOLS>>',
475+
isOptional: true,
476+
description: 'Optional transformation that is applied to the stream.',
477+
},
472478
{
473479
name: 'experimental_providerMetadata',
474480
type: 'Record<string,Record<string,JSONValue>> | undefined',
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
title: smoothStream
3+
description: Helper function for smoothing text streaming output
4+
---
5+
6+
# `smoothStream()`
7+
8+
`smoothStream` is a utility function that creates a TransformStream
9+
for the `streamText` `transform` option
10+
to smooth out text streaming by buffering and releasing complete words with configurable delays.
11+
This creates a more natural reading experience when streaming text responses.
12+
13+
```ts highlight={"6-8"}
14+
import { smoothStream, streamText } from 'ai';
15+
16+
const result = streamText({
17+
model,
18+
prompt,
19+
experimental_transform: smoothStream({
20+
delayInMs: 40, // optional: defaults to 40ms
21+
}),
22+
});
23+
```
24+
25+
## Import
26+
27+
<Snippet text={`import { smoothStream } from "ai"`} prompt={false} />
28+
29+
## API Signature
30+
31+
### Parameters
32+
33+
<PropertiesTable
34+
content={[
35+
{
36+
name: 'delayInMs',
37+
type: 'number',
38+
isOptional: true,
39+
description:
40+
'The delay in milliseconds between outputting each word. Defaults to 40ms. Set to 0 to disable delays.',
41+
},
42+
]}
43+
/>
44+
45+
### Returns
46+
47+
Returns a `TransformStream` that:
48+
49+
- Buffers incoming text chunks
50+
- Releases complete words when whitespace is encountered
51+
- Adds configurable delays between words for smooth output
52+
- Passes through non-text chunks (like step-finish events) immediately

content/docs/07-reference/01-ai-sdk-core/index.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,10 @@ It also contains the following helper functions:
8181
'Calculates the cosine similarity between two vectors, e.g. embeddings.',
8282
href: '/docs/reference/ai-sdk-core/cosine-similarity',
8383
},
84+
{
85+
title: 'smoothStream()',
86+
description: 'Smooths text streaming output.',
87+
href: '/docs/reference/ai-sdk-core/smooth-stream',
88+
},
8489
]}
8590
/>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { anthropic } from '@ai-sdk/anthropic';
2+
import { smoothStream, streamText } from 'ai';
3+
import 'dotenv/config';
4+
5+
async function main() {
6+
const result = streamText({
7+
model: anthropic('claude-3-5-sonnet-20240620'),
8+
prompt: 'Invent a new holiday and describe its traditions.',
9+
experimental_transform: smoothStream(),
10+
});
11+
12+
for await (const textPart of result.textStream) {
13+
process.stdout.write(textPart);
14+
}
15+
16+
console.log();
17+
console.log('Token usage:', await result.usage);
18+
console.log('Finish reason:', await result.finishReason);
19+
}
20+
21+
main().catch(console.error);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { azure } from '@ai-sdk/azure';
2+
import { smoothStream, streamText } from 'ai';
3+
import 'dotenv/config';
4+
5+
async function main() {
6+
const result = streamText({
7+
model: azure('gpt-4o'), // use your own deployment
8+
prompt: 'Invent a new holiday and describe its traditions.',
9+
experimental_transform: smoothStream(),
10+
});
11+
12+
for await (const textPart of result.textStream) {
13+
process.stdout.write(textPart);
14+
}
15+
16+
console.log();
17+
console.log('Token usage:', await result.usage);
18+
console.log('Finish reason:', await result.finishReason);
19+
}
20+
21+
main().catch(console.error);

packages/ai/core/data-stream/create-data-stream.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { convertReadableStreamToArray } from '@ai-sdk/provider-utils/test';
22
import { formatDataStreamPart } from '@ai-sdk/ui-utils';
33
import { expect, it } from 'vitest';
4-
import { createDataStream } from './create-data-stream';
5-
import { DataStreamWriter } from './data-stream-writer';
64
import { delay } from '../../util/delay';
7-
import { createResolvablePromise } from '../../util/create-resolvable-promise';
85
import { DelayedPromise } from '../../util/delayed-promise';
6+
import { createDataStream } from './create-data-stream';
7+
import { DataStreamWriter } from './data-stream-writer';
98

109
describe('createDataStream', () => {
1110
it('should send single data json and close the stream', async () => {

packages/ai/core/generate-text/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export type { StepResult } from './step-result';
55
export { streamText } from './stream-text';
66
export type { StreamTextResult, TextStreamPart } from './stream-text-result';
77
export type { ToolCallRepairFunction } from './tool-call-repair';
8+
export { smoothStream } from './smooth-stream';
89

910
// TODO 4.1: rename to ToolCall and ToolResult, deprecate old names
1011
export type {

0 commit comments

Comments
 (0)