diff --git a/.changeset/afraid-worms-yell.md b/.changeset/afraid-worms-yell.md new file mode 100644 index 000000000000..683ec99e694c --- /dev/null +++ b/.changeset/afraid-worms-yell.md @@ -0,0 +1,5 @@ +--- +'@ai-sdk/amazon-bedrock': patch +--- + +fix(provider/amazon-bedrock): resolve opus 4.1 reasoning mode validation error diff --git a/.changeset/metal-shrimps-fix.md b/.changeset/metal-shrimps-fix.md new file mode 100644 index 000000000000..95fcececc8d7 --- /dev/null +++ b/.changeset/metal-shrimps-fix.md @@ -0,0 +1,5 @@ +--- +'@ai-sdk/openai': patch +--- + +feat(provider/openai): add code interpreter tool (responses api) diff --git a/content/docs/02-foundations/02-providers-and-models.mdx b/content/docs/02-foundations/02-providers-and-models.mdx index a827a650bfe8..5ff2fc447967 100644 --- a/content/docs/02-foundations/02-providers-and-models.mdx +++ b/content/docs/02-foundations/02-providers-and-models.mdx @@ -112,17 +112,6 @@ Here are the capabilities of popular models: | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5-mini` | | | | | | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5-nano` | | | | | | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5-chat-latest` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4.1` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4.1-mini` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4.1-nano` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4o` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4o-mini` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4.1` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `o3-mini` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `o3` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `o4-mini` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `o1` | | | | | | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-opus-4-latest` | | | | | | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-sonnet-4-latest` | | | | | | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-3-7-sonnet-latest` | | | | | diff --git a/content/docs/07-reference/01-ai-sdk-core/70-step-count-is.mdx b/content/docs/07-reference/01-ai-sdk-core/70-step-count-is.mdx new file mode 100644 index 000000000000..2f9c7ca186b4 --- /dev/null +++ b/content/docs/07-reference/01-ai-sdk-core/70-step-count-is.mdx @@ -0,0 +1,84 @@ +--- +title: stepCountIs +description: API Reference for stepCountIs. +--- + +# `stepCountIs()` + +Creates a stop condition that stops when the number of steps reaches a specified count. + +This function is used with `stopWhen` in `generateText` and `streamText` to control when a tool-calling loop should stop based on the number of steps executed. + +```ts +import { openai } from '@ai-sdk/openai'; +import { generateText, stepCountIs } from 'ai'; + +const result = await generateText({ + model: openai('gpt-4o'), + tools: { + // your tools + }, + // Stop after 5 steps + stopWhen: stepCountIs(5), +}); +``` + +## Import + + + +## API Signature + +### Parameters + + + +### Returns + +A `StopCondition` function that returns `true` when the step count reaches the specified number. The function can be used with the `stopWhen` parameter in `generateText` and `streamText`. + +## Examples + +### Basic Usage + +Stop after 3 steps: + +```ts +import { generateText, stepCountIs } from 'ai'; + +const result = await generateText({ + model: yourModel, + tools: yourTools, + stopWhen: stepCountIs(3), +}); +``` + +### Combining with Other Conditions + +You can combine multiple stop conditions in an array: + +```ts +import { generateText, stepCountIs, hasToolCall } from 'ai'; + +const result = await generateText({ + model: yourModel, + tools: yourTools, + // Stop after 10 steps OR when finalAnswer tool is called + stopWhen: [stepCountIs(10), hasToolCall('finalAnswer')], +}); +``` + +## See also + +- [`hasToolCall()`](/docs/reference/ai-sdk-core/has-tool-call) +- [`generateText()`](/docs/reference/ai-sdk-core/generate-text) +- [`streamText()`](/docs/reference/ai-sdk-core/stream-text) diff --git a/content/docs/07-reference/01-ai-sdk-core/71-has-tool-call.mdx b/content/docs/07-reference/01-ai-sdk-core/71-has-tool-call.mdx new file mode 100644 index 000000000000..a068d35bb864 --- /dev/null +++ b/content/docs/07-reference/01-ai-sdk-core/71-has-tool-call.mdx @@ -0,0 +1,120 @@ +--- +title: hasToolCall +description: API Reference for hasToolCall. +--- + +# `hasToolCall()` + +Creates a stop condition that stops when a specific tool is called. + +This function is used with `stopWhen` in `generateText` and `streamText` to control when a tool-calling loop should stop based on whether a particular tool has been invoked. + +```ts +import { openai } from '@ai-sdk/openai'; +import { generateText, hasToolCall } from 'ai'; + +const result = await generateText({ + model: openai('gpt-4o'), + tools: { + weather: weatherTool, + finalAnswer: finalAnswerTool, + }, + // Stop when the finalAnswer tool is called + stopWhen: hasToolCall('finalAnswer'), +}); +``` + +## Import + + + +## API Signature + +### Parameters + + + +### Returns + +A `StopCondition` function that returns `true` when the specified tool is called in the current step. The function can be used with the `stopWhen` parameter in `generateText` and `streamText`. + +## Examples + +### Basic Usage + +Stop when a specific tool is called: + +```ts +import { generateText, hasToolCall } from 'ai'; + +const result = await generateText({ + model: yourModel, + tools: { + submitAnswer: submitAnswerTool, + search: searchTool, + }, + stopWhen: hasToolCall('submitAnswer'), +}); +``` + +### Combining with Other Conditions + +You can combine multiple stop conditions in an array: + +```ts +import { generateText, hasToolCall, stepCountIs } from 'ai'; + +const result = await generateText({ + model: yourModel, + tools: { + weather: weatherTool, + search: searchTool, + finalAnswer: finalAnswerTool, + }, + // Stop when weather tool is called OR finalAnswer is called OR after 5 steps + stopWhen: [ + hasToolCall('weather'), + hasToolCall('finalAnswer'), + stepCountIs(5), + ], +}); +``` + +### Agent Pattern + +Common pattern for agents that run until they provide a final answer: + +```ts +import { generateText, hasToolCall } from 'ai'; + +const result = await generateText({ + model: yourModel, + tools: { + search: searchTool, + calculate: calculateTool, + finalAnswer: { + description: 'Provide the final answer to the user', + parameters: z.object({ + answer: z.string(), + }), + execute: async ({ answer }) => answer, + }, + }, + stopWhen: hasToolCall('finalAnswer'), +}); +``` + +## See also + +- [`stepCountIs()`](/docs/reference/ai-sdk-core/step-count-is) +- [`generateText()`](/docs/reference/ai-sdk-core/generate-text) +- [`streamText()`](/docs/reference/ai-sdk-core/stream-text) diff --git a/content/providers/01-ai-sdk-providers/03-openai.mdx b/content/providers/01-ai-sdk-providers/03-openai.mdx index 614527ff1af8..a6e8569ced7e 100644 --- a/content/providers/01-ai-sdk-providers/03-openai.mdx +++ b/content/providers/01-ai-sdk-providers/03-openai.mdx @@ -729,7 +729,7 @@ The following OpenAI-specific metadata is returned: #### Web Search -The OpenAI responses provider supports web search through the `openai.tools.webSearchPreview` tool. +The OpenAI responses API supports web search through the `openai.tools.webSearchPreview` tool. You can force the use of the web search tool by setting the `toolChoice` parameter to `{ type: 'tool', toolName: 'web_search_preview' }`. @@ -830,7 +830,7 @@ The `textVerbosity` parameter scales output length without changing the underlyi #### File Search -The OpenAI responses provider supports file search through the `openai.tools.fileSearch` tool. +The OpenAI responses API supports file search through the `openai.tools.fileSearch` tool. You can force the use of the file search tool by setting the `toolChoice` parameter to `{ type: 'tool', toolName: 'file_search' }`. @@ -866,6 +866,38 @@ const result = await generateText({ be customized. +#### Code Interpreter + +The OpenAI responses API supports the code interpreter tool through the `openai.tools.codeInterpreter` tool. This allows models to write and execute Python code. + +```ts +import { openai } from '@ai-sdk/openai'; +import { generateText } from 'ai'; + +const result = await generateText({ + model: openai.responses('gpt-5'), + prompt: 'Write and run Python code to calculate the factorial of 10', + tools: { + code_interpreter: openai.tools.codeInterpreter({ + // optional configuration: + container: { + fileIds: ['file-123', 'file-456'], // optional file IDs to make available + }, + }), + }, +}); +``` + +The code interpreter tool can be configured with: + +- **container**: Either a container ID string or an object with `fileIds` to specify uploaded files that should be available to the code interpreter + + + The tool must be named `code_interpreter` when using OpenAI's code interpreter + functionality. This name is required by OpenAI's API specification and cannot + be customized. + + #### Image Support The OpenAI Responses API supports Image inputs for appropriate models. diff --git a/content/providers/01-ai-sdk-providers/index.mdx b/content/providers/01-ai-sdk-providers/index.mdx index e6f923e79b96..440842c5dbda 100644 --- a/content/providers/01-ai-sdk-providers/index.mdx +++ b/content/providers/01-ai-sdk-providers/index.mdx @@ -33,14 +33,6 @@ Not all providers support all AI SDK features. Here's a quick comparison of the | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5-mini` | | | | | | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5-nano` | | | | | | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5-chat-latest` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4.1` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4.1-mini` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4.1-nano` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4o` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4o-mini` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4.1` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-4` | | | | | -| [OpenAI](/providers/ai-sdk-providers/openai) | `o1` | | | | | | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-3.7-sonnet-latest` | | | | | | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-3.5-sonnet-latest` | | | | | | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-3.5-haiku-latest` | | | | | diff --git a/examples/ai-core/src/stream-text/openai-code-interpreter.ts b/examples/ai-core/src/stream-text/openai-code-interpreter.ts new file mode 100644 index 000000000000..84ade9e80d27 --- /dev/null +++ b/examples/ai-core/src/stream-text/openai-code-interpreter.ts @@ -0,0 +1,22 @@ +import { openai } from '@ai-sdk/openai'; +import { stepCountIs, streamText } from 'ai'; +import 'dotenv/config'; + +async function main() { + const result = streamText({ + model: openai.responses('gpt-5'), + stopWhen: stepCountIs(5), + tools: { + code_interpreter: openai.tools.codeInterpreter({}), + }, + prompt: + 'Write and run Python code to simulate rolling two dice 10000 times and show a table of the results.' + + 'The table should have three columns: "Sum", "Count", and "Percentage".', + }); + + for await (const chunk of result.textStream) { + process.stdout.write(chunk); + } +} + +main().catch(console.error); diff --git a/packages/amazon-bedrock/src/bedrock-chat-language-model.test.ts b/packages/amazon-bedrock/src/bedrock-chat-language-model.test.ts index ea60368e116b..1d543956e526 100644 --- a/packages/amazon-bedrock/src/bedrock-chat-language-model.test.ts +++ b/packages/amazon-bedrock/src/bedrock-chat-language-model.test.ts @@ -1434,9 +1434,8 @@ describe('doStream', () => { budget_tokens: 2000, }, }, - // Should have adjusted maxOutputTokens (100 + 2000) inferenceConfig: { - maxOutputTokens: 2100, + maxTokens: 2100, }, }); @@ -1667,7 +1666,7 @@ describe('doGenerate', () => { expect(await server.calls[0].requestBodyJson).toMatchObject({ inferenceConfig: { - maxOutputTokens: 100, + maxTokens: 100, temperature: 0.5, topP: 0.5, topK: 1, @@ -2044,9 +2043,8 @@ describe('doGenerate', () => { budget_tokens: 2000, }, }, - // Should have adjusted maxOutputTokens (100 + 2000) inferenceConfig: { - maxOutputTokens: 2100, + maxTokens: 2100, }, }); diff --git a/packages/amazon-bedrock/src/bedrock-chat-language-model.ts b/packages/amazon-bedrock/src/bedrock-chat-language-model.ts index 0bb2b6a28f67..bf9956ea8d09 100644 --- a/packages/amazon-bedrock/src/bedrock-chat-language-model.ts +++ b/packages/amazon-bedrock/src/bedrock-chat-language-model.ts @@ -159,19 +159,19 @@ export class BedrockChatLanguageModel implements LanguageModelV2 { const thinkingBudget = bedrockOptions.reasoningConfig?.budgetTokens; const inferenceConfig = { - ...(maxOutputTokens != null && { maxOutputTokens }), + ...(maxOutputTokens != null && { maxTokens: maxOutputTokens }), ...(temperature != null && { temperature }), ...(topP != null && { topP }), ...(topK != null && { topK }), ...(stopSequences != null && { stopSequences }), }; - // Adjust maxOutputTokens if thinking is enabled + // Adjust maxTokens if thinking is enabled if (isThinking && thinkingBudget != null) { - if (inferenceConfig.maxOutputTokens != null) { - inferenceConfig.maxOutputTokens += thinkingBudget; + if (inferenceConfig.maxTokens != null) { + inferenceConfig.maxTokens += thinkingBudget; } else { - inferenceConfig.maxOutputTokens = thinkingBudget + 4096; // Default + thinking budget maxOutputTokens = 4096, TODO update default in v5 + inferenceConfig.maxTokens = thinkingBudget + 4096; // Default + thinking budget maxTokens = 4096, TODO update default in v5 } // Add them to additional model request fields // Add thinking config to additionalModelRequestFields diff --git a/packages/openai/src/openai-provider.ts b/packages/openai/src/openai-provider.ts index 41677ba4eaaf..760be930551d 100644 --- a/packages/openai/src/openai-provider.ts +++ b/packages/openai/src/openai-provider.ts @@ -33,7 +33,7 @@ export interface OpenAIProvider extends ProviderV2 { /** Creates an OpenAI model for text generation. */ - languageModel(modelId: OpenAIResponsesModelId): OpenAIResponsesLanguageModel; + languageModel(modelId: OpenAIResponsesModelId): LanguageModelV2; /** Creates an OpenAI chat model for text generation. diff --git a/packages/openai/src/openai-tools.ts b/packages/openai/src/openai-tools.ts index c9292fae5029..1789bebd89b6 100644 --- a/packages/openai/src/openai-tools.ts +++ b/packages/openai/src/openai-tools.ts @@ -1,20 +1,11 @@ +import { codeInterpreter } from './tool/code-interpreter'; import { fileSearch } from './tool/file-search'; import { webSearchPreview } from './tool/web-search-preview'; - export { fileSearch } from './tool/file-search'; export { webSearchPreview } from './tool/web-search-preview'; -export type { - OpenAITool, - OpenAITools, - OpenAIToolChoice, - OpenAIFunctionTool, - OpenAIFileSearchTool, - OpenAIWebSearchPreviewTool, - OpenAIWebSearchUserLocation, -} from './openai-types'; - export const openaiTools = { + codeInterpreter, fileSearch, webSearchPreview, }; diff --git a/packages/openai/src/openai-types.ts b/packages/openai/src/openai-types.ts index 7d098325453b..5b2aa83a6cdd 100644 --- a/packages/openai/src/openai-types.ts +++ b/packages/openai/src/openai-types.ts @@ -1,5 +1,6 @@ import { JSONSchema7 } from '@ai-sdk/provider'; +// TODO clean up this file and move the definitions into the tools /** * OpenAI function tool definition */ @@ -55,13 +56,25 @@ export interface OpenAIWebSearchPreviewTool { user_location?: OpenAIWebSearchUserLocation; } +/** + * OpenAI code interpreter tool definition + */ +export interface OpenAICodeInterpreterTool { + type: 'code_interpreter'; + container: { + type: 'auto'; + file_ids: string[]; + }; +} + /** * Union type for all OpenAI tools */ export type OpenAITool = | OpenAIFunctionTool | OpenAIFileSearchTool - | OpenAIWebSearchPreviewTool; + | OpenAIWebSearchPreviewTool + | OpenAICodeInterpreterTool; /** * OpenAI tool choice options diff --git a/packages/openai/src/responses/openai-responses-api-types.ts b/packages/openai/src/responses/openai-responses-api-types.ts index 0bae9ec8a343..7f6f711c3545 100644 --- a/packages/openai/src/responses/openai-responses-api-types.ts +++ b/packages/openai/src/responses/openai-responses-api-types.ts @@ -89,6 +89,10 @@ export type OpenAIResponsesTool = region: string; }; } + | { + type: 'code_interpreter'; + container: string | { type: 'auto'; file_ids: string[] | undefined }; + } | { type: 'file_search'; vector_store_ids?: string[]; diff --git a/packages/openai/src/responses/openai-responses-prepare-tools.test.ts b/packages/openai/src/responses/openai-responses-prepare-tools.test.ts new file mode 100644 index 000000000000..7077320f5b78 --- /dev/null +++ b/packages/openai/src/responses/openai-responses-prepare-tools.test.ts @@ -0,0 +1,205 @@ +import { prepareResponsesTools } from './openai-responses-prepare-tools'; + +describe('prepareResponsesTools', () => { + describe('code interpreter', () => { + it('should prepare code interpreter tool with no container (auto mode)', () => { + const result = prepareResponsesTools({ + tools: [ + { + type: 'provider-defined', + id: 'openai.code_interpreter', + name: 'code_interpreter', + args: {}, + }, + ], + strictJsonSchema: false, + }); + + expect(result.tools).toEqual([ + { + type: 'code_interpreter', + container: { type: 'auto', file_ids: undefined }, + }, + ]); + expect(result.toolWarnings).toEqual([]); + }); + + it('should prepare code interpreter tool with string container ID', () => { + const result = prepareResponsesTools({ + tools: [ + { + type: 'provider-defined', + id: 'openai.code_interpreter', + name: 'code_interpreter', + args: { + container: 'container-123', + }, + }, + ], + strictJsonSchema: false, + }); + + expect(result.tools).toEqual([ + { + type: 'code_interpreter', + container: 'container-123', + }, + ]); + expect(result.toolWarnings).toEqual([]); + }); + + it('should prepare code interpreter tool with file IDs container', () => { + const result = prepareResponsesTools({ + tools: [ + { + type: 'provider-defined', + id: 'openai.code_interpreter', + name: 'code_interpreter', + args: { + container: { + fileIds: ['file-1', 'file-2', 'file-3'], + }, + }, + }, + ], + strictJsonSchema: false, + }); + + expect(result.tools).toEqual([ + { + type: 'code_interpreter', + container: { type: 'auto', file_ids: ['file-1', 'file-2', 'file-3'] }, + }, + ]); + expect(result.toolWarnings).toEqual([]); + }); + + it('should prepare code interpreter tool with empty file IDs array', () => { + const result = prepareResponsesTools({ + tools: [ + { + type: 'provider-defined', + id: 'openai.code_interpreter', + name: 'code_interpreter', + args: { + container: { + fileIds: [], + }, + }, + }, + ], + strictJsonSchema: false, + }); + + expect(result.tools).toEqual([ + { + type: 'code_interpreter', + container: { type: 'auto', file_ids: [] }, + }, + ]); + expect(result.toolWarnings).toEqual([]); + }); + + it('should prepare code interpreter tool with undefined file IDs', () => { + const result = prepareResponsesTools({ + tools: [ + { + type: 'provider-defined', + id: 'openai.code_interpreter', + name: 'code_interpreter', + args: { + container: { + fileIds: undefined, + }, + }, + }, + ], + strictJsonSchema: false, + }); + + expect(result.tools).toEqual([ + { + type: 'code_interpreter', + container: { type: 'auto', file_ids: undefined }, + }, + ]); + expect(result.toolWarnings).toEqual([]); + }); + + it('should handle tool choice selection with code interpreter', () => { + const result = prepareResponsesTools({ + tools: [ + { + type: 'provider-defined', + id: 'openai.code_interpreter', + name: 'code_interpreter', + args: {}, + }, + ], + toolChoice: { + type: 'tool', + toolName: 'code_interpreter', + }, + strictJsonSchema: false, + }); + + expect(result.tools).toEqual([ + { + type: 'code_interpreter', + container: { type: 'auto', file_ids: undefined }, + }, + ]); + expect(result.toolChoice).toEqual({ + type: 'code_interpreter', + }); + expect(result.toolWarnings).toEqual([]); + }); + + it('should handle multiple tools including code interpreter', () => { + const result = prepareResponsesTools({ + tools: [ + { + type: 'function', + name: 'testFunction', + description: 'A test function', + inputSchema: { + type: 'object', + properties: { + input: { type: 'string' }, + }, + }, + }, + { + type: 'provider-defined', + id: 'openai.code_interpreter', + name: 'code_interpreter', + args: { + container: 'my-container', + }, + }, + ], + strictJsonSchema: true, + }); + + expect(result.tools).toEqual([ + { + type: 'function', + name: 'testFunction', + description: 'A test function', + parameters: { + type: 'object', + properties: { + input: { type: 'string' }, + }, + }, + strict: true, + }, + { + type: 'code_interpreter', + container: 'my-container', + }, + ]); + expect(result.toolWarnings).toEqual([]); + }); + }); +}); diff --git a/packages/openai/src/responses/openai-responses-prepare-tools.ts b/packages/openai/src/responses/openai-responses-prepare-tools.ts index eb5414d0906a..b70aa842d466 100644 --- a/packages/openai/src/responses/openai-responses-prepare-tools.ts +++ b/packages/openai/src/responses/openai-responses-prepare-tools.ts @@ -5,6 +5,7 @@ import { } from '@ai-sdk/provider'; import { OpenAIResponsesTool } from './openai-responses-api-types'; import { fileSearchArgsSchema } from '../tool/file-search'; +import { codeInterpreterArgsSchema } from '../tool/code-interpreter'; export function prepareResponsesTools({ tools, @@ -22,7 +23,8 @@ export function prepareResponsesTools({ | 'required' | { type: 'file_search' } | { type: 'web_search_preview' } - | { type: 'function'; name: string }; + | { type: 'function'; name: string } + | { type: 'code_interpreter' }; toolWarnings: LanguageModelV2CallWarning[]; } { // when the tools array is empty, change it to undefined to prevent errors: @@ -63,6 +65,7 @@ export function prepareResponsesTools({ break; } case 'openai.web_search_preview': + // TODO update this with proper validation openaiTools.push({ type: 'web_search_preview', search_context_size: tool.args.searchContextSize as @@ -76,6 +79,18 @@ export function prepareResponsesTools({ }, }); break; + case 'openai.code_interpreter': + const args = codeInterpreterArgsSchema.parse(tool.args); + openaiTools.push({ + type: 'code_interpreter', + container: + args.container == null + ? { type: 'auto', file_ids: undefined } + : typeof args.container === 'string' + ? args.container + : { type: 'auto', file_ids: args.container.fileIds }, + }); + break; default: toolWarnings.push({ type: 'unsupported-tool', tool }); break; @@ -102,11 +117,11 @@ export function prepareResponsesTools({ return { tools: openaiTools, toolChoice: - toolChoice.toolName === 'file_search' - ? { type: 'file_search' } - : toolChoice.toolName === 'web_search_preview' - ? { type: 'web_search_preview' } - : { type: 'function', name: toolChoice.toolName }, + toolChoice.toolName === 'code_interpreter' || + toolChoice.toolName === 'file_search' || + toolChoice.toolName === 'web_search_preview' + ? { type: toolChoice.toolName } + : { type: 'function', name: toolChoice.toolName }, toolWarnings, }; default: { diff --git a/packages/openai/src/tool/code-interpreter.ts b/packages/openai/src/tool/code-interpreter.ts new file mode 100644 index 000000000000..40f2c08eed50 --- /dev/null +++ b/packages/openai/src/tool/code-interpreter.ts @@ -0,0 +1,29 @@ +import { createProviderDefinedToolFactory } from '@ai-sdk/provider-utils'; +import { z } from 'zod/v4'; + +export const codeInterpreterArgsSchema = z.object({ + container: z + .union([ + z.string(), + z.object({ + fileIds: z.array(z.string()).optional(), + }), + ]) + .optional(), +}); + +export const codeInterpreter = createProviderDefinedToolFactory< + {}, + { + /** + * The code interpreter container. + * Can be a container ID + * or an object that specifies uploaded file IDs to make available to your code. + */ + container?: string | { fileIds?: string[] }; + } +>({ + id: 'openai.code_interpreter', + name: 'code_interpreter', + inputSchema: z.object({}), +});