From 4d6468c8d7ae3314ae6a1e500c6239ee60db7440 Mon Sep 17 00:00:00 2001 From: Nick Oates <58091943+n1ckoates@users.noreply.github.com> Date: Thu, 13 Aug 2026 13:33:23 -0700 Subject: [PATCH 1/3] changeset --- .changeset/tidy-reasoning-items-close.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tidy-reasoning-items-close.md diff --git a/.changeset/tidy-reasoning-items-close.md b/.changeset/tidy-reasoning-items-close.md new file mode 100644 index 000000000000..a16433d8e92d --- /dev/null +++ b/.changeset/tidy-reasoning-items-close.md @@ -0,0 +1,5 @@ +--- +'@ai-sdk/open-responses': patch +--- + +fix(open-responses): close unfinished reasoning stream parts with their original IDs From 02a384890a7877229f329c464c49d6a3cabf59ae Mon Sep 17 00:00:00 2001 From: Nick Oates <58091943+n1ckoates@users.noreply.github.com> Date: Thu, 13 Aug 2026 13:33:26 -0700 Subject: [PATCH 2/3] failing test --- .../open-responses-language-model.test.ts | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/packages/open-responses/src/responses/open-responses-language-model.test.ts b/packages/open-responses/src/responses/open-responses-language-model.test.ts index 960e83c839f1..84b66226ca48 100644 --- a/packages/open-responses/src/responses/open-responses-language-model.test.ts +++ b/packages/open-responses/src/responses/open-responses-language-model.test.ts @@ -734,6 +734,62 @@ describe('OpenResponsesLanguageModel', () => { }); }); + it('should close unfinished reasoning items with their original ids', async () => { + server.urls[URL].response = { + type: 'stream-chunks', + chunks: [ + `data: ${JSON.stringify({ + type: 'response.output_item.added', + sequence_number: 0, + output_index: 0, + item: { + id: 'rs_reasoning_item', + type: 'reasoning', + summary: [], + }, + })}\n\n`, + `data: ${JSON.stringify({ + type: 'response.incomplete', + sequence_number: 1, + response: { + status: 'incomplete', + incomplete_details: { reason: 'max_output_tokens' }, + usage: { + input_tokens: 1, + input_tokens_details: { cached_tokens: 0 }, + output_tokens: 1, + output_tokens_details: { reasoning_tokens: 1 }, + total_tokens: 2, + }, + }, + })}\n\n`, + 'data: [DONE]\n\n', + ], + }; + + const result = await createModel().doStream({ + prompt: TEST_PROMPT, + }); + + const parts = await convertReadableStreamToArray(result.stream); + + expect(parts).toContainEqual({ + type: 'reasoning-start', + id: 'rs_reasoning_item', + }); + expect(parts).toContainEqual({ + type: 'reasoning-end', + id: 'rs_reasoning_item', + }); + expect(parts.at(-1)).toMatchObject({ + type: 'finish', + finishReason: { + unified: 'length', + raw: 'max_output_tokens', + }, + }); + }); + it('should not pollute Object.prototype from tool call item ids', async () => { const originalArgumentsDescriptor = Object.getOwnPropertyDescriptor( Object.prototype, From ee9b2831619c7b11575f6ba9cc942f891cf757a8 Mon Sep 17 00:00:00 2001 From: Nick Oates <58091943+n1ckoates@users.noreply.github.com> Date: Thu, 13 Aug 2026 13:38:57 -0700 Subject: [PATCH 3/3] fix --- .../responses/open-responses-language-model.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/open-responses/src/responses/open-responses-language-model.ts b/packages/open-responses/src/responses/open-responses-language-model.ts index a241b2c9999f..436f3e760ad7 100644 --- a/packages/open-responses/src/responses/open-responses-language-model.ts +++ b/packages/open-responses/src/responses/open-responses-language-model.ts @@ -401,7 +401,7 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 { usage.raw = responseUsage; }; - let isActiveReasoning = false; + let activeReasoningId: string | undefined; let hasToolCalls = false; let finishReason: LanguageModelV4FinishReason = { unified: 'other', @@ -503,7 +503,7 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 { type: 'reasoning-start', id: chunk.item.id, }); - isActiveReasoning = true; + activeReasoningId = chunk.item.id; } else if ( (chunk as { type: string }).type === 'response.reasoning_text.delta' @@ -522,7 +522,9 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 { chunk.item.type === 'reasoning' ) { controller.enqueue({ type: 'reasoning-end', id: chunk.item.id }); - isActiveReasoning = false; + if (activeReasoningId === chunk.item.id) { + activeReasoningId = undefined; + } } // Text events @@ -565,8 +567,11 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 { }, flush(controller) { - if (isActiveReasoning) { - controller.enqueue({ type: 'reasoning-end', id: 'reasoning-0' }); + if (activeReasoningId != null) { + controller.enqueue({ + type: 'reasoning-end', + id: activeReasoningId, + }); } controller.enqueue({