From 59336a570f5fd20d1b978baa11a804320b5c097f Mon Sep 17 00:00:00 2001 From: suyao Date: Tue, 8 Jul 2025 04:35:40 +0800 Subject: [PATCH] fix(ai): Unexpected reasoning-start event in extract reasoning middleware --- .changeset/spotty-apples-call.md | 5 + .../extract-reasoning-middleware.test.ts | 100 +++++++++++++++--- .../extract-reasoning-middleware.ts | 6 +- 3 files changed, 95 insertions(+), 16 deletions(-) create mode 100644 .changeset/spotty-apples-call.md diff --git a/.changeset/spotty-apples-call.md b/.changeset/spotty-apples-call.md new file mode 100644 index 000000000000..911896c0d95c --- /dev/null +++ b/.changeset/spotty-apples-call.md @@ -0,0 +1,5 @@ +--- +'ai': patch +--- + +fix(ai): Unexpected reasoning-start event in extract reasoning middleware diff --git a/packages/ai/core/middleware/extract-reasoning-middleware.test.ts b/packages/ai/core/middleware/extract-reasoning-middleware.test.ts index 3874ba9f9f6a..7829260ba570 100644 --- a/packages/ai/core/middleware/extract-reasoning-middleware.test.ts +++ b/packages/ai/core/middleware/extract-reasoning-middleware.test.ts @@ -683,20 +683,12 @@ describe('extractReasoningMiddleware', () => { "id": "1", "type": "text-start", }, - { - "id": "reasoning-0", - "type": "reasoning-start", - }, { "id": "1", "providerMetadata": undefined, "text": "ana", "type": "text", }, - { - "id": "reasoning-0", - "type": "reasoning-start", - }, { "id": "1", "providerMetadata": undefined, @@ -705,18 +697,100 @@ describe('extractReasoningMiddleware', () => { "type": "text", }, { - "id": "reasoning-0", - "type": "reasoning-start", + "id": "1", + "providerMetadata": undefined, + "text": "", + "type": "text", }, { "id": "1", "providerMetadata": undefined, - "text": "", + "text": "this is the response", "type": "text", }, { - "id": "reasoning-0", - "type": "reasoning-start", + "id": "1", + "type": "text-end", + }, + { + "finishReason": "stop", + "providerMetadata": undefined, + "response": { + "headers": undefined, + "id": "id-0", + "modelId": "mock-model-id", + "timestamp": 1970-01-01T00:00:00.000Z, + }, + "type": "finish-step", + "usage": { + "cachedInputTokens": undefined, + "inputTokens": 5, + "outputTokens": 10, + "reasoningTokens": 3, + "totalTokens": 18, + }, + }, + { + "finishReason": "stop", + "totalUsage": { + "cachedInputTokens": undefined, + "inputTokens": 5, + "outputTokens": 10, + "reasoningTokens": 3, + "totalTokens": 18, + }, + "type": "finish", + }, + ] + `); + }); + + it('should keep original text when tag is not present', async () => { + const mockModel = new MockLanguageModelV2({ + async doStream() { + return { + stream: convertArrayToReadableStream([ + { + type: 'response-metadata', + id: 'id-0', + modelId: 'mock-model-id', + timestamp: new Date(0), + }, + { type: 'text-start', id: '1' }, + { type: 'text-delta', id: '1', delta: 'this is the response' }, + { type: 'text-end', id: '1' }, + { + type: 'finish', + finishReason: 'stop', + usage: testUsage, + }, + ]), + }; + }, + }); + + const result = streamText({ + model: wrapLanguageModel({ + model: mockModel, + middleware: extractReasoningMiddleware({ tagName: 'think' }), + }), + prompt: 'Hello, how can I help?', + }); + + expect(await convertAsyncIterableToArray(result.fullStream)) + .toMatchInlineSnapshot(` + [ + { + "type": "start", + }, + { + "request": {}, + "type": "start-step", + "warnings": [], + }, + { + "id": "1", + "type": "text-start", }, { "id": "1", diff --git a/packages/ai/core/middleware/extract-reasoning-middleware.ts b/packages/ai/core/middleware/extract-reasoning-middleware.ts index 6e4eaf8c87e1..37a272887ee9 100644 --- a/packages/ai/core/middleware/extract-reasoning-middleware.ts +++ b/packages/ai/core/middleware/extract-reasoning-middleware.ts @@ -133,9 +133,9 @@ export function extractReasoningMiddleware({ : ''; if ( - (activeExtraction.afterSwitch && - activeExtraction.isReasoning) || - activeExtraction.isFirstReasoning + activeExtraction.isReasoning && + (activeExtraction.afterSwitch || + activeExtraction.isFirstReasoning) ) { controller.enqueue({ type: 'reasoning-start',