Skip to content

Commit

Permalink
fix (ai/core): correct whitespace in streamText continueSteps (#3123)
Browse files Browse the repository at this point in the history
  • Loading branch information
lgrammel authored Sep 26, 2024
1 parent 4db074b commit 1297e1b
Show file tree
Hide file tree
Showing 13 changed files with 504 additions and 196 deletions.
5 changes: 5 additions & 0 deletions .changeset/heavy-plants-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'ai': patch
---

fix (ai/core): correct whitespace in streamText continueSteps
6 changes: 6 additions & 0 deletions content/docs/03-ai-sdk-core/05-generating-text.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,12 @@ const {
});
```

<Note>
When `experimental_continueSteps` is enabled, only full words are streamed in
`streamText`, and both `generateText` and `streamText` might drop the trailing
tokens of some calls to prevent whitespace issues.
</Note>

## Examples

You can see `generateText` and `streamText` in action using various frameworks in the following examples:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > onStepFinish
},
"stepType": "initial",
"text": "part 1
to-be-discarded",
",
"toolCalls": [],
"toolResults": [],
"usage": {
Expand Down Expand Up @@ -234,7 +234,7 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > onStepFinish
"timestamp": 1970-01-01T00:00:20.000Z,
},
"stepType": "continue",
"text": " final value keep all whitespace
"text": "final value keep all whitespace
end",
"toolCalls": [],
"toolResults": [],
Expand Down Expand Up @@ -262,7 +262,7 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > result.steps
},
"stepType": "initial",
"text": "part 1
to-be-discarded",
",
"toolCalls": [],
"toolResults": [],
"usage": {
Expand Down Expand Up @@ -306,7 +306,7 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > result.steps
"timestamp": 1970-01-01T00:00:20.000Z,
},
"stepType": "continue",
"text": " final value keep all whitespace
"text": "final value keep all whitespace
end",
"toolCalls": [],
"toolResults": [],
Expand Down
157 changes: 41 additions & 116 deletions packages/ai/core/generate-text/__snapshots__/stream-text.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -603,15 +603,17 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > callbacks > o
{
"content": [
{
"text": "part-1",
"text": "part 1
",
"type": "text",
},
{
"text": "part-2",
"text": "no-whitespace",
"type": "text",
},
{
"text": "part-3",
"text": "final value keep all whitespace
end",
"type": "text",
},
],
Expand All @@ -630,7 +632,8 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > callbacks > o
"timestamp": 1970-01-01T00:00:00.000Z,
},
"stepType": "initial",
"text": "part-1",
"text": "part 1
",
"toolCalls": [],
"toolResults": [],
"usage": {
Expand All @@ -651,7 +654,7 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > callbacks > o
"timestamp": 1970-01-01T00:00:01.000Z,
},
"stepType": "continue",
"text": "part-2",
"text": "no-whitespace",
"toolCalls": [],
"toolResults": [],
"usage": {
Expand All @@ -676,7 +679,8 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > callbacks > o
"timestamp": 1970-01-01T00:00:01.000Z,
},
"stepType": "continue",
"text": "part-3",
"text": "final value keep all whitespace
end",
"toolCalls": [],
"toolResults": [],
"usage": {
Expand All @@ -687,7 +691,9 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > callbacks > o
"warnings": undefined,
},
],
"text": "part-1part-2part-3",
"text": "part 1
no-whitespacefinal value keep all whitespace
end",
"toolCalls": [],
"toolResults": [],
"usage": {
Expand All @@ -712,7 +718,8 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > callbacks > o
"timestamp": 1970-01-01T00:00:00.000Z,
},
"stepType": "initial",
"text": "part-1",
"text": "part 1
",
"toolCalls": [],
"toolResults": [],
"usage": {
Expand All @@ -733,7 +740,7 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > callbacks > o
"timestamp": 1970-01-01T00:00:01.000Z,
},
"stepType": "continue",
"text": "part-2",
"text": "no-whitespace",
"toolCalls": [],
"toolResults": [],
"usage": {
Expand All @@ -758,7 +765,8 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > callbacks > o
"timestamp": 1970-01-01T00:00:01.000Z,
},
"stepType": "continue",
"text": "part-3",
"text": "final value keep all whitespace
end",
"toolCalls": [],
"toolResults": [],
"usage": {
Expand All @@ -771,99 +779,6 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > callbacks > o
]
`;

exports[`options.maxSteps > 3 steps: initial, continue, continue > should contain text deltas from all steps 1`] = `
[
{
"textDelta": "part-",
"type": "text-delta",
},
{
"textDelta": "1",
"type": "text-delta",
},
{
"experimental_providerMetadata": undefined,
"finishReason": "length",
"logprobs": undefined,
"response": {
"id": "id-0",
"modelId": "mock-model-id",
"timestamp": 1970-01-01T00:00:00.000Z,
},
"type": "step-finish",
"usage": {
"completionTokens": 20,
"promptTokens": 10,
"totalTokens": 30,
},
},
{
"textDelta": "part-",
"type": "text-delta",
},
{
"textDelta": "2",
"type": "text-delta",
},
{
"experimental_providerMetadata": undefined,
"finishReason": "length",
"logprobs": undefined,
"response": {
"id": "id-1",
"modelId": "mock-model-id",
"timestamp": 1970-01-01T00:00:01.000Z,
},
"type": "step-finish",
"usage": {
"completionTokens": 5,
"promptTokens": 30,
"totalTokens": 35,
},
},
{
"textDelta": "part-",
"type": "text-delta",
},
{
"textDelta": "3",
"type": "text-delta",
},
{
"experimental_providerMetadata": undefined,
"finishReason": "stop",
"logprobs": undefined,
"response": {
"id": "id-1",
"modelId": "mock-model-id",
"timestamp": 1970-01-01T00:00:01.000Z,
},
"type": "step-finish",
"usage": {
"completionTokens": 2,
"promptTokens": 3,
"totalTokens": 5,
},
},
{
"experimental_providerMetadata": undefined,
"finishReason": "stop",
"logprobs": undefined,
"response": {
"id": "id-1",
"modelId": "mock-model-id",
"timestamp": 1970-01-01T00:00:01.000Z,
},
"type": "finish",
"usage": {
"completionTokens": 27,
"promptTokens": 43,
"totalTokens": 70,
},
},
]
`;

exports[`options.maxSteps > 3 steps: initial, continue, continue > should record telemetry data for each step 1`] = `
[
{
Expand All @@ -874,8 +789,12 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > should record
"ai.operationId": "ai.streamText",
"ai.prompt": "{"prompt":"test-input"}",
"ai.response.finishReason": "stop",
"ai.response.text": "part-1part-2part-3",
"ai.result.text": "part-1part-2part-3",
"ai.response.text": "part 1
no-whitespacefinal value keep all whitespace
end",
"ai.result.text": "part 1
no-whitespacefinal value keep all whitespace
end",
"ai.settings.maxSteps": 5,
"ai.usage.completionTokens": 27,
"ai.usage.promptTokens": 43,
Expand All @@ -898,9 +817,11 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > should record
"ai.response.model": "mock-model-id",
"ai.response.msToFinish": 500,
"ai.response.msToFirstChunk": 100,
"ai.response.text": "part-1",
"ai.response.text": "part 1
",
"ai.response.timestamp": "1970-01-01T00:00:00.000Z",
"ai.result.text": "part-1",
"ai.result.text": "part 1
",
"ai.stream.msToFirstChunk": 100,
"ai.usage.completionTokens": 20,
"ai.usage.promptTokens": 10,
Expand Down Expand Up @@ -937,16 +858,16 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > should record
"ai.model.provider": "mock-provider",
"ai.operationId": "ai.streamText.doStream",
"ai.prompt.format": "messages",
"ai.prompt.messages": "[{"role":"user","content":[{"type":"text","text":"test-input"}]},{"role":"assistant","content":[{"type":"text","text":"part-1"}]}]",
"ai.prompt.messages": "[{"role":"user","content":[{"type":"text","text":"test-input"}]},{"role":"assistant","content":[{"type":"text","text":"part 1 \\n "}]}]",
"ai.response.avgCompletionTokensPerSecond": 12.5,
"ai.response.finishReason": "length",
"ai.response.id": "id-1",
"ai.response.model": "mock-model-id",
"ai.response.msToFinish": 400,
"ai.response.msToFirstChunk": 400,
"ai.response.text": "part-2",
"ai.response.text": "no-whitespace",
"ai.response.timestamp": "1970-01-01T00:00:01.000Z",
"ai.result.text": "part-2",
"ai.result.text": "no-whitespace",
"ai.stream.msToFirstChunk": 400,
"ai.usage.completionTokens": 5,
"ai.usage.promptTokens": 30,
Expand Down Expand Up @@ -983,16 +904,18 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > should record
"ai.model.provider": "mock-provider",
"ai.operationId": "ai.streamText.doStream",
"ai.prompt.format": "messages",
"ai.prompt.messages": "[{"role":"user","content":[{"type":"text","text":"test-input"}]},{"role":"assistant","content":[{"type":"text","text":"part-1"},{"text":"part-2","type":"text"}]}]",
"ai.prompt.messages": "[{"role":"user","content":[{"type":"text","text":"test-input"}]},{"role":"assistant","content":[{"type":"text","text":"part 1 \\n "},{"text":"no-whitespace","type":"text"}]}]",
"ai.response.avgCompletionTokensPerSecond": Infinity,
"ai.response.finishReason": "stop",
"ai.response.id": "id-1",
"ai.response.model": "mock-model-id",
"ai.response.msToFinish": 0,
"ai.response.msToFirstChunk": 0,
"ai.response.text": "part-3",
"ai.response.text": "final value keep all whitespace
end",
"ai.response.timestamp": "1970-01-01T00:00:01.000Z",
"ai.result.text": "part-3",
"ai.result.text": "final value keep all whitespace
end",
"ai.stream.msToFirstChunk": 0,
"ai.usage.completionTokens": 2,
"ai.usage.promptTokens": 3,
Expand Down Expand Up @@ -1038,7 +961,8 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > value promise
"timestamp": 1970-01-01T00:00:00.000Z,
},
"stepType": "initial",
"text": "part-1",
"text": "part 1
",
"toolCalls": [],
"toolResults": [],
"usage": {
Expand All @@ -1059,7 +983,7 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > value promise
"timestamp": 1970-01-01T00:00:01.000Z,
},
"stepType": "continue",
"text": "part-2",
"text": "no-whitespace",
"toolCalls": [],
"toolResults": [],
"usage": {
Expand All @@ -1084,7 +1008,8 @@ exports[`options.maxSteps > 3 steps: initial, continue, continue > value promise
"timestamp": 1970-01-01T00:00:01.000Z,
},
"stepType": "continue",
"text": "part-3",
"text": "final value keep all whitespace
end",
"toolCalls": [],
"toolResults": [],
"usage": {
Expand Down
6 changes: 3 additions & 3 deletions packages/ai/core/generate-text/generate-text.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ describe('options.maxSteps', () => {

return {
...dummyResponseValues,
text: ' final value keep all whitespace\n end',
text: 'final value keep all whitespace\n end',
finishReason: 'stop',
response: {
id: 'test-id-3-from-model',
Expand All @@ -596,7 +596,7 @@ describe('options.maxSteps', () => {

it('result.text should return text from both steps separated by space', async () => {
expect(result.text).toStrictEqual(
'part 1 \n no-whitespace final value keep all whitespace\n end',
'part 1 \n no-whitespacefinal value keep all whitespace\n end',
);
});

Expand All @@ -613,7 +613,7 @@ describe('options.maxSteps', () => {
type: 'text',
},
{
text: ' final value keep all whitespace\n end',
text: 'final value keep all whitespace\n end',
type: 'text',
},
],
Expand Down
Loading

0 comments on commit 1297e1b

Please sign in to comment.