From 6d064045d6ed19b28966b2a9120511ca0459234b Mon Sep 17 00:00:00 2001 From: ai-sdk-factory <308175966+ai-sdk-factory@users.noreply.github.com> Date: Thu, 13 Aug 2026 19:22:02 +0000 Subject: [PATCH 1/3] reproduction (reproduced) --- .ai-sdk-factory/reproduction-replay.json | 12 ++ ...ssue-18871-open-responses-dropped-tools.ts | 119 ++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 .ai-sdk-factory/reproduction-replay.json create mode 100644 examples/ai-functions/src/reproduction/issue-18871-open-responses-dropped-tools.ts diff --git a/.ai-sdk-factory/reproduction-replay.json b/.ai-sdk-factory/reproduction-replay.json new file mode 100644 index 000000000000..f9814a189065 --- /dev/null +++ b/.ai-sdk-factory/reproduction-replay.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "issueId": 18871, + "command": "pnpm -C examples/ai-functions exec tsx src/reproduction/issue-18871-open-responses-dropped-tools.ts", + "artifactPaths": [ + "examples/ai-functions/src/reproduction/issue-18871-open-responses-dropped-tools.ts" + ], + "expectedFailure": { + "exitCode": 1, + "signal": "ISSUE #18871 REPRODUCED: provider-defined tools were dropped without unsupported warnings" + } +} diff --git a/examples/ai-functions/src/reproduction/issue-18871-open-responses-dropped-tools.ts b/examples/ai-functions/src/reproduction/issue-18871-open-responses-dropped-tools.ts new file mode 100644 index 000000000000..c25de127bf7f --- /dev/null +++ b/examples/ai-functions/src/reproduction/issue-18871-open-responses-dropped-tools.ts @@ -0,0 +1,119 @@ +import { createOpenResponses } from '@ai-sdk/open-responses'; +import { openai } from '@ai-sdk/openai'; +import { generateText, tool } from 'ai'; +import { z } from 'zod'; + +const FAILURE_SIGNAL = + 'ISSUE #18871 REPRODUCED: provider-defined tools were dropped without unsupported warnings'; + +async function main() { + let requestBody: Record | undefined; + + const model = createOpenResponses({ + name: 'openai', + url: 'https://example.test/v1/responses', + fetch: async (_input, init) => { + if (typeof init?.body !== 'string') { + throw new Error( + 'Reproduction setup failed: request body was not JSON.', + ); + } + + requestBody = JSON.parse(init.body) as Record; + + return new Response( + JSON.stringify({ + id: 'resp_issue_18871', + object: 'response', + created_at: 1_786_646_400, + status: 'completed', + model: 'test-model', + output: [ + { + id: 'msg_issue_18871', + type: 'message', + role: 'assistant', + status: 'completed', + content: [ + { + type: 'output_text', + text: 'ok', + annotations: [], + }, + ], + }, + ], + usage: { + input_tokens: 1, + output_tokens: 1, + total_tokens: 2, + }, + }), + { + status: 200, + headers: { 'content-type': 'application/json' }, + }, + ); + }, + })('test-model'); + + const result = await generateText({ + model, + prompt: 'Use the available tools.', + tools: { + weather: tool({ + description: 'Get the weather.', + inputSchema: z.object({ location: z.string() }), + }), + web_search: openai.tools.webSearch({}), + file_search: openai.tools.fileSearch({ + vectorStoreIds: ['vs_issue_18871'], + }), + }, + }); + + const sentTools = Array.isArray(requestBody?.tools) ? requestBody.tools : []; + const sentFunctionTools = sentTools.filter( + sentTool => + typeof sentTool === 'object' && + sentTool != null && + 'type' in sentTool && + sentTool.type === 'function', + ); + const sentProviderTools = sentTools.length - sentFunctionTools.length; + const unsupportedWarnings = + result.warnings?.filter(warning => warning.type === 'unsupported') ?? []; + const suppliedProviderTools = 2; + const silentlyDroppedProviderTools = + suppliedProviderTools - sentProviderTools - unsupportedWarnings.length; + + if (sentFunctionTools.length !== 1) { + throw new Error( + `Reproduction setup failed: expected one function tool in the request, received ${sentFunctionTools.length}.`, + ); + } + + console.log( + JSON.stringify( + { + sentToolTypes: sentTools.map(sentTool => + typeof sentTool === 'object' && sentTool != null && 'type' in sentTool + ? sentTool.type + : undefined, + ), + warnings: result.warnings ?? [], + }, + null, + 2, + ), + ); + + if (silentlyDroppedProviderTools > 0) { + throw new Error(FAILURE_SIGNAL); + } +} + +main().catch(error => { + console.error(error instanceof Error ? error.message : error); + process.exitCode = 1; +}); From da85085170e41e55291030ed45aeca3059127cc8 Mon Sep 17 00:00:00 2001 From: ai-sdk-factory <308175966+ai-sdk-factory@users.noreply.github.com> Date: Thu, 13 Aug 2026 19:38:47 +0000 Subject: [PATCH 2/3] failing tests Co-authored-by: Astro-Han <255364436+Astro-Han@users.noreply.github.com> --- .../open-responses-language-model.test.ts | 27 +++++++++++++++++++ 1 file changed, 27 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..53d382fb5396 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 @@ -189,6 +189,18 @@ describe('OpenResponsesLanguageModel', () => { }, strict: true, }, + { + type: 'provider', + id: 'openai.web_search', + name: 'web_search', + args: {}, + }, + { + type: 'provider', + id: 'openai.file_search', + name: 'file_search', + args: { vectorStoreIds: ['vs_123'] }, + }, ], }); }); @@ -196,6 +208,21 @@ describe('OpenResponsesLanguageModel', () => { it('should send correct request body with tools', async () => { expect(await server.calls[0].requestBodyJson).toMatchSnapshot(); }); + + it('should warn for each unsupported provider-defined tool', async () => { + expect(result.warnings).toMatchInlineSnapshot(` + [ + { + "feature": "provider-defined tool openai.web_search", + "type": "unsupported", + }, + { + "feature": "provider-defined tool openai.file_search", + "type": "unsupported", + }, + ] + `); + }); }); describe('top-level reasoning', () => { From 05854d6f52c4c65721f1cdc7b8209aa1279a09af Mon Sep 17 00:00:00 2001 From: ai-sdk-factory <308175966+ai-sdk-factory@users.noreply.github.com> Date: Thu, 13 Aug 2026 19:38:48 +0000 Subject: [PATCH 3/3] fix #18871 Co-authored-by: Astro-Han <255364436+Astro-Han@users.noreply.github.com> --- .ai-sdk-factory/reproduction-replay.json | 12 -- .changeset/tiny-tools-warn.md | 5 + ...ssue-18871-open-responses-dropped-tools.ts | 119 ------------------ .../open-responses-language-model.ts | 29 +++-- 4 files changed, 24 insertions(+), 141 deletions(-) delete mode 100644 .ai-sdk-factory/reproduction-replay.json create mode 100644 .changeset/tiny-tools-warn.md delete mode 100644 examples/ai-functions/src/reproduction/issue-18871-open-responses-dropped-tools.ts diff --git a/.ai-sdk-factory/reproduction-replay.json b/.ai-sdk-factory/reproduction-replay.json deleted file mode 100644 index f9814a189065..000000000000 --- a/.ai-sdk-factory/reproduction-replay.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": 1, - "issueId": 18871, - "command": "pnpm -C examples/ai-functions exec tsx src/reproduction/issue-18871-open-responses-dropped-tools.ts", - "artifactPaths": [ - "examples/ai-functions/src/reproduction/issue-18871-open-responses-dropped-tools.ts" - ], - "expectedFailure": { - "exitCode": 1, - "signal": "ISSUE #18871 REPRODUCED: provider-defined tools were dropped without unsupported warnings" - } -} diff --git a/.changeset/tiny-tools-warn.md b/.changeset/tiny-tools-warn.md new file mode 100644 index 000000000000..1c742ece5783 --- /dev/null +++ b/.changeset/tiny-tools-warn.md @@ -0,0 +1,5 @@ +--- +'@ai-sdk/open-responses': patch +--- + +Warn when provider-defined tools are unsupported instead of silently dropping them. diff --git a/examples/ai-functions/src/reproduction/issue-18871-open-responses-dropped-tools.ts b/examples/ai-functions/src/reproduction/issue-18871-open-responses-dropped-tools.ts deleted file mode 100644 index c25de127bf7f..000000000000 --- a/examples/ai-functions/src/reproduction/issue-18871-open-responses-dropped-tools.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { createOpenResponses } from '@ai-sdk/open-responses'; -import { openai } from '@ai-sdk/openai'; -import { generateText, tool } from 'ai'; -import { z } from 'zod'; - -const FAILURE_SIGNAL = - 'ISSUE #18871 REPRODUCED: provider-defined tools were dropped without unsupported warnings'; - -async function main() { - let requestBody: Record | undefined; - - const model = createOpenResponses({ - name: 'openai', - url: 'https://example.test/v1/responses', - fetch: async (_input, init) => { - if (typeof init?.body !== 'string') { - throw new Error( - 'Reproduction setup failed: request body was not JSON.', - ); - } - - requestBody = JSON.parse(init.body) as Record; - - return new Response( - JSON.stringify({ - id: 'resp_issue_18871', - object: 'response', - created_at: 1_786_646_400, - status: 'completed', - model: 'test-model', - output: [ - { - id: 'msg_issue_18871', - type: 'message', - role: 'assistant', - status: 'completed', - content: [ - { - type: 'output_text', - text: 'ok', - annotations: [], - }, - ], - }, - ], - usage: { - input_tokens: 1, - output_tokens: 1, - total_tokens: 2, - }, - }), - { - status: 200, - headers: { 'content-type': 'application/json' }, - }, - ); - }, - })('test-model'); - - const result = await generateText({ - model, - prompt: 'Use the available tools.', - tools: { - weather: tool({ - description: 'Get the weather.', - inputSchema: z.object({ location: z.string() }), - }), - web_search: openai.tools.webSearch({}), - file_search: openai.tools.fileSearch({ - vectorStoreIds: ['vs_issue_18871'], - }), - }, - }); - - const sentTools = Array.isArray(requestBody?.tools) ? requestBody.tools : []; - const sentFunctionTools = sentTools.filter( - sentTool => - typeof sentTool === 'object' && - sentTool != null && - 'type' in sentTool && - sentTool.type === 'function', - ); - const sentProviderTools = sentTools.length - sentFunctionTools.length; - const unsupportedWarnings = - result.warnings?.filter(warning => warning.type === 'unsupported') ?? []; - const suppliedProviderTools = 2; - const silentlyDroppedProviderTools = - suppliedProviderTools - sentProviderTools - unsupportedWarnings.length; - - if (sentFunctionTools.length !== 1) { - throw new Error( - `Reproduction setup failed: expected one function tool in the request, received ${sentFunctionTools.length}.`, - ); - } - - console.log( - JSON.stringify( - { - sentToolTypes: sentTools.map(sentTool => - typeof sentTool === 'object' && sentTool != null && 'type' in sentTool - ? sentTool.type - : undefined, - ), - warnings: result.warnings ?? [], - }, - null, - 2, - ), - ); - - if (silentlyDroppedProviderTools > 0) { - throw new Error(FAILURE_SIGNAL); - } -} - -main().catch(error => { - console.error(error instanceof Error ? error.message : error); - process.exitCode = 1; -}); 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..bed294e62507 100644 --- a/packages/open-responses/src/responses/open-responses-language-model.ts +++ b/packages/open-responses/src/responses/open-responses-language-model.ts @@ -117,15 +117,24 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 { warnings.push(...inputWarnings); // Convert function tools to the Open Responses format - const functionTools: FunctionToolParam[] | undefined = tools - ?.filter(tool => tool.type === 'function') - .map(tool => ({ - type: 'function' as const, - name: tool.name, - description: tool.description, - parameters: tool.inputSchema, - ...(tool.strict != null ? { strict: tool.strict } : {}), - })); + const functionTools: FunctionToolParam[] = []; + + for (const tool of tools ?? []) { + if (tool.type === 'provider') { + warnings.push({ + type: 'unsupported', + feature: `provider-defined tool ${tool.id}`, + }); + } else { + functionTools.push({ + type: 'function', + name: tool.name, + description: tool.description, + parameters: tool.inputSchema, + ...(tool.strict != null ? { strict: tool.strict } : {}), + }); + } + } // Convert tool choice to the Open Responses format const convertedToolChoice: ToolChoiceParam | undefined = @@ -194,7 +203,7 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 { }), } : undefined, - tools: functionTools?.length ? functionTools : undefined, + tools: functionTools.length ? functionTools : undefined, tool_choice: convertedToolChoice, ...(textFormat != null && { text: { format: textFormat } }), },