@ai-sdk/openai 4.0.18 → 4.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.d.ts +60 -3
- package/dist/index.js +1111 -885
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +67 -2
- package/dist/internal/index.js +1247 -1022
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +62 -0
- package/package.json +1 -1
- package/src/image/openai-image-model-options.ts +8 -7
- package/src/image/openai-image-model.ts +2 -2
- package/src/index.ts +1 -0
- package/src/internal/index.ts +1 -0
- package/src/openai-language-model-capabilities.ts +47 -22
- package/src/openai-tools.ts +7 -0
- package/src/responses/convert-to-openai-responses-input.ts +86 -0
- package/src/responses/openai-responses-api.ts +63 -1
- package/src/responses/openai-responses-language-model.ts +103 -0
- package/src/responses/openai-responses-prepare-tools.ts +20 -2
- package/src/tool/programmatic-tool-calling.ts +57 -0
package/docs/03-openai.mdx
CHANGED
|
@@ -457,6 +457,68 @@ metadata on tool-call parts. The SDK uses `providerMetadata.openai.namespace` or
|
|
|
457
457
|
`providerOptions.openai.namespace` to round-trip the namespace back to OpenAI on
|
|
458
458
|
subsequent requests.
|
|
459
459
|
|
|
460
|
+
#### Programmatic Tool Calling
|
|
461
|
+
|
|
462
|
+
OpenAI Programmatic Tool Calling lets supported Responses models generate and run
|
|
463
|
+
hosted JavaScript that coordinates client-side function tools. Add the hosted tool
|
|
464
|
+
with `openai.tools.programmaticToolCalling()`, then opt each eligible function into
|
|
465
|
+
programmatic calls with `allowedCallers`.
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
import { openai, type OpenAIToolOptions } from '@ai-sdk/openai';
|
|
469
|
+
import { generateText, isStepCount, tool } from 'ai';
|
|
470
|
+
import { z } from 'zod';
|
|
471
|
+
|
|
472
|
+
const result = await generateText({
|
|
473
|
+
model: openai('gpt-5.6'),
|
|
474
|
+
stopWhen: isStepCount(10),
|
|
475
|
+
prompt: 'Compare inventory with demand for sku_123.',
|
|
476
|
+
tools: {
|
|
477
|
+
program: openai.tools.programmaticToolCalling(),
|
|
478
|
+
getInventory: tool({
|
|
479
|
+
description: 'Get available inventory for a SKU.',
|
|
480
|
+
inputSchema: z.object({ sku: z.string() }),
|
|
481
|
+
execute: async ({ sku }) => ({ sku, availableUnits: 42 }),
|
|
482
|
+
providerOptions: {
|
|
483
|
+
openai: {
|
|
484
|
+
allowedCallers: ['programmatic'],
|
|
485
|
+
outputSchema: {
|
|
486
|
+
type: 'object',
|
|
487
|
+
properties: {
|
|
488
|
+
sku: { type: 'string' },
|
|
489
|
+
availableUnits: { type: 'number' },
|
|
490
|
+
},
|
|
491
|
+
required: ['sku', 'availableUnits'],
|
|
492
|
+
additionalProperties: false,
|
|
493
|
+
},
|
|
494
|
+
} satisfies OpenAIToolOptions,
|
|
495
|
+
},
|
|
496
|
+
}),
|
|
497
|
+
},
|
|
498
|
+
});
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
`allowedCallers` supports:
|
|
502
|
+
|
|
503
|
+
- `['direct']` (or omitted): only direct model tool calls
|
|
504
|
+
- `['programmatic']`: only calls from hosted JavaScript
|
|
505
|
+
- `['direct', 'programmatic']`: both invocation modes
|
|
506
|
+
|
|
507
|
+
Use `outputSchema` to describe the JSON value returned by the function. This schema
|
|
508
|
+
is sent to OpenAI as `output_schema` so generated JavaScript can safely use the
|
|
509
|
+
returned fields. It does not replace the tool's AI SDK output validation or execution
|
|
510
|
+
logic.
|
|
511
|
+
|
|
512
|
+
The provider preserves program code, replay fingerprints, program outputs, and nested
|
|
513
|
+
function caller metadata across the `generateText` and `streamText` tool loops,
|
|
514
|
+
including stateless requests with `store: false`. Programmatic invocation is
|
|
515
|
+
currently supported for AI SDK function tools; other OpenAI tool types remain
|
|
516
|
+
direct-only through this integration.
|
|
517
|
+
|
|
518
|
+
Treat generated programs and tool arguments as untrusted. Enforce authorization and
|
|
519
|
+
approval inside every tool, avoid programmatic access to side-effecting tools unless
|
|
520
|
+
the operation is idempotent, and set explicit step limits.
|
|
521
|
+
|
|
460
522
|
#### Web Search Tool
|
|
461
523
|
|
|
462
524
|
The OpenAI responses API supports web search through the `openai.tools.webSearch` tool.
|
package/package.json
CHANGED
|
@@ -26,13 +26,7 @@ export const modelMaxImagesPerCall: Record<OpenAIImageModelId, number> = {
|
|
|
26
26
|
'chatgpt-image-latest': 10,
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
const defaultResponseFormatPrefixes = [
|
|
30
|
-
'chatgpt-image-',
|
|
31
|
-
'gpt-image-1-mini',
|
|
32
|
-
'gpt-image-1.5',
|
|
33
|
-
'gpt-image-1',
|
|
34
|
-
'gpt-image-2',
|
|
35
|
-
];
|
|
29
|
+
const defaultResponseFormatPrefixes = ['chatgpt-image-', 'gpt-image-'];
|
|
36
30
|
|
|
37
31
|
export function hasDefaultResponseFormat(modelId: string): boolean {
|
|
38
32
|
return defaultResponseFormatPrefixes.some(prefix =>
|
|
@@ -40,6 +34,13 @@ export function hasDefaultResponseFormat(modelId: string): boolean {
|
|
|
40
34
|
);
|
|
41
35
|
}
|
|
42
36
|
|
|
37
|
+
export function getMaxImagesPerCall(modelId: OpenAIImageModelId): number {
|
|
38
|
+
return (
|
|
39
|
+
modelMaxImagesPerCall[modelId] ??
|
|
40
|
+
(modelId.startsWith('gpt-image-') ? 10 : 1)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
43
44
|
const baseImageModelOptionsObject = z.object({
|
|
44
45
|
/**
|
|
45
46
|
* Quality of the generated image(s).
|
|
@@ -20,8 +20,8 @@ import type { OpenAIConfig } from '../openai-config';
|
|
|
20
20
|
import { openaiFailedResponseHandler } from '../openai-error';
|
|
21
21
|
import { openaiImageResponseSchema } from './openai-image-api';
|
|
22
22
|
import {
|
|
23
|
+
getMaxImagesPerCall,
|
|
23
24
|
hasDefaultResponseFormat,
|
|
24
|
-
modelMaxImagesPerCall,
|
|
25
25
|
openaiImageModelEditOptions,
|
|
26
26
|
openaiImageModelGenerationOptions,
|
|
27
27
|
type OpenAIImageModelEditOptions,
|
|
@@ -51,7 +51,7 @@ export class OpenAIImageModel implements ImageModelV4 {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
get maxImagesPerCall(): number {
|
|
54
|
-
return
|
|
54
|
+
return getMaxImagesPerCall(this.modelId);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
get provider(): string {
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ export type {
|
|
|
7
7
|
/** @deprecated Use `OpenAILanguageModelResponsesOptions` instead. */
|
|
8
8
|
OpenAILanguageModelResponsesOptions as OpenAIResponsesProviderOptions,
|
|
9
9
|
} from './responses/openai-responses-language-model-options';
|
|
10
|
+
export type { OpenAIToolOptions } from './responses/openai-responses-prepare-tools';
|
|
10
11
|
export type {
|
|
11
12
|
OpenAILanguageModelChatOptions,
|
|
12
13
|
/** @deprecated Use `OpenAILanguageModelChatOptions` instead. */
|
package/src/internal/index.ts
CHANGED
|
@@ -16,5 +16,6 @@ export * from '../tool/apply-patch';
|
|
|
16
16
|
export * from '../tool/code-interpreter';
|
|
17
17
|
export * from '../tool/file-search';
|
|
18
18
|
export * from '../tool/image-generation';
|
|
19
|
+
export * from '../tool/programmatic-tool-calling';
|
|
19
20
|
export * from '../tool/web-search';
|
|
20
21
|
export * from '../tool/web-search-preview';
|
|
@@ -13,37 +13,37 @@ export type OpenAILanguageModelCapabilities = {
|
|
|
13
13
|
export function getOpenAILanguageModelCapabilities(
|
|
14
14
|
modelId: string,
|
|
15
15
|
): OpenAILanguageModelCapabilities {
|
|
16
|
+
const oSeriesVersion = getOSeriesVersion(modelId);
|
|
17
|
+
const gptVersion = getGptVersion(modelId);
|
|
18
|
+
const isGptChatModel =
|
|
19
|
+
gptVersion?.minor == null &&
|
|
20
|
+
(gptVersion?.variant?.startsWith('chat') ?? false);
|
|
21
|
+
const isGptNanoModel = gptVersion?.variant?.startsWith('nano') ?? false;
|
|
22
|
+
|
|
16
23
|
const supportsFlexProcessing =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
(modelId.startsWith('gpt-5') && !modelId.startsWith('gpt-5-chat'));
|
|
24
|
+
(oSeriesVersion != null && oSeriesVersion >= 3) ||
|
|
25
|
+
(gptVersion != null && gptVersion.major >= 5 && !isGptChatModel);
|
|
20
26
|
|
|
21
27
|
const supportsPriorityProcessing =
|
|
22
28
|
modelId.startsWith('gpt-4') ||
|
|
23
|
-
(
|
|
24
|
-
|
|
25
|
-
!
|
|
26
|
-
!
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
// This prevents issues with fine-tuned models, third-party models, and custom models
|
|
29
|
+
(gptVersion != null &&
|
|
30
|
+
gptVersion.major >= 5 &&
|
|
31
|
+
!isGptNanoModel &&
|
|
32
|
+
!isGptChatModel) ||
|
|
33
|
+
(oSeriesVersion != null && oSeriesVersion >= 3);
|
|
34
|
+
|
|
35
|
+
// Only recognizable OpenAI model families should use the developer role.
|
|
36
|
+
// Fine-tuned, third-party, and custom model IDs keep conservative defaults.
|
|
32
37
|
const isReasoningModel =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
modelId.startsWith('o4-mini') ||
|
|
36
|
-
(modelId.startsWith('gpt-5') && !modelId.startsWith('gpt-5-chat'));
|
|
38
|
+
oSeriesVersion != null ||
|
|
39
|
+
(gptVersion != null && gptVersion.major >= 5 && !isGptChatModel);
|
|
37
40
|
|
|
38
41
|
// https://platform.openai.com/docs/guides/latest-model#gpt-5-1-parameter-compatibility
|
|
39
42
|
// GPT-5.1 and later model families support temperature, topP, logProbs when reasoningEffort is none.
|
|
40
43
|
const supportsNonReasoningParameters =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
modelId.startsWith('gpt-5.4') ||
|
|
45
|
-
modelId.startsWith('gpt-5.5') ||
|
|
46
|
-
modelId.startsWith('gpt-5.6');
|
|
44
|
+
gptVersion != null &&
|
|
45
|
+
(gptVersion.major > 5 ||
|
|
46
|
+
(gptVersion.major === 5 && (gptVersion.minor ?? 0) >= 1));
|
|
47
47
|
|
|
48
48
|
const systemMessageMode = isReasoningModel ? 'developer' : 'system';
|
|
49
49
|
|
|
@@ -55,3 +55,28 @@ export function getOpenAILanguageModelCapabilities(
|
|
|
55
55
|
supportsNonReasoningParameters,
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
+
|
|
59
|
+
function getOSeriesVersion(modelId: string): number | undefined {
|
|
60
|
+
const match = /^o(\d+)(?:-|$)/.exec(modelId);
|
|
61
|
+
return match == null ? undefined : Number(match[1]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getGptVersion(modelId: string):
|
|
65
|
+
| {
|
|
66
|
+
major: number;
|
|
67
|
+
minor: number | undefined;
|
|
68
|
+
variant: string | undefined;
|
|
69
|
+
}
|
|
70
|
+
| undefined {
|
|
71
|
+
const match = /^gpt-(\d+)(?:\.(\d+))?(?:-(.+))?$/.exec(modelId);
|
|
72
|
+
|
|
73
|
+
if (match == null) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
major: Number(match[1]),
|
|
79
|
+
minor: match[2] == null ? undefined : Number(match[2]),
|
|
80
|
+
variant: match[3],
|
|
81
|
+
};
|
|
82
|
+
}
|
package/src/openai-tools.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { toolSearch } from './tool/tool-search';
|
|
|
10
10
|
import { webSearch } from './tool/web-search';
|
|
11
11
|
import { webSearchPreview } from './tool/web-search-preview';
|
|
12
12
|
import { mcp } from './tool/mcp';
|
|
13
|
+
import { programmaticToolCalling } from './tool/programmatic-tool-calling';
|
|
13
14
|
|
|
14
15
|
export const openaiTools = {
|
|
15
16
|
/**
|
|
@@ -135,6 +136,12 @@ export const openaiTools = {
|
|
|
135
136
|
*/
|
|
136
137
|
mcp,
|
|
137
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Programmatic Tool Calling lets OpenAI Responses models write and execute
|
|
141
|
+
* JavaScript that orchestrates eligible tools.
|
|
142
|
+
*/
|
|
143
|
+
programmaticToolCalling,
|
|
144
|
+
|
|
138
145
|
/**
|
|
139
146
|
* Tool search allows the model to dynamically search for and load deferred
|
|
140
147
|
* tools into the model's context as needed. This helps reduce overall token
|
|
@@ -33,16 +33,34 @@ import type {
|
|
|
33
33
|
OpenAIResponsesFunctionCallOutput,
|
|
34
34
|
OpenAIResponsesInput,
|
|
35
35
|
OpenAIResponsesReasoning,
|
|
36
|
+
OpenAIResponsesToolCaller,
|
|
36
37
|
} from './openai-responses-api';
|
|
37
38
|
import {
|
|
38
39
|
toolSearchInputSchema,
|
|
39
40
|
toolSearchOutputSchema,
|
|
40
41
|
} from '../tool/tool-search';
|
|
42
|
+
import {
|
|
43
|
+
programmaticToolCallingInputSchema,
|
|
44
|
+
programmaticToolCallingOutputSchema,
|
|
45
|
+
} from '../tool/programmatic-tool-calling';
|
|
41
46
|
|
|
42
47
|
function serializeToolCallArguments(input: unknown): string {
|
|
43
48
|
return JSON.stringify(input === undefined ? {} : input);
|
|
44
49
|
}
|
|
45
50
|
|
|
51
|
+
function mapToolCaller(
|
|
52
|
+
caller:
|
|
53
|
+
| { type: 'direct' }
|
|
54
|
+
| { type: 'program'; callerId: string }
|
|
55
|
+
| undefined,
|
|
56
|
+
): OpenAIResponsesToolCaller | undefined {
|
|
57
|
+
return caller == null
|
|
58
|
+
? undefined
|
|
59
|
+
: caller.type === 'program'
|
|
60
|
+
? { type: 'program', caller_id: caller.callerId }
|
|
61
|
+
: caller;
|
|
62
|
+
}
|
|
63
|
+
|
|
46
64
|
type OpenAIPromptCacheBreakpoint = { mode: 'explicit' };
|
|
47
65
|
|
|
48
66
|
function getPromptCacheBreakpoint(
|
|
@@ -350,6 +368,11 @@ export async function convertToOpenAIResponsesInput({
|
|
|
350
368
|
).providerMetadata?.[providerOptionsName]?.namespace) as
|
|
351
369
|
| string
|
|
352
370
|
| undefined;
|
|
371
|
+
const caller = part.providerOptions?.[providerOptionsName]
|
|
372
|
+
?.caller as
|
|
373
|
+
| { type: 'direct' }
|
|
374
|
+
| { type: 'program'; callerId: string }
|
|
375
|
+
| undefined;
|
|
353
376
|
|
|
354
377
|
if (hasConversation && id != null) {
|
|
355
378
|
break;
|
|
@@ -390,6 +413,27 @@ export async function convertToOpenAIResponsesInput({
|
|
|
390
413
|
break;
|
|
391
414
|
}
|
|
392
415
|
|
|
416
|
+
if (resolvedToolName === 'programmatic_tool_calling') {
|
|
417
|
+
if (store && id != null) {
|
|
418
|
+
input.push({ type: 'item_reference', id });
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const parsedInput = await validateTypes({
|
|
423
|
+
value: part.input,
|
|
424
|
+
schema: programmaticToolCallingInputSchema,
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
input.push({
|
|
428
|
+
type: 'program',
|
|
429
|
+
id: id ?? part.toolCallId,
|
|
430
|
+
call_id: part.toolCallId,
|
|
431
|
+
code: parsedInput.code,
|
|
432
|
+
fingerprint: parsedInput.fingerprint,
|
|
433
|
+
});
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
|
|
393
437
|
if (part.providerExecuted) {
|
|
394
438
|
if (store && id != null) {
|
|
395
439
|
input.push({ type: 'item_reference', id });
|
|
@@ -552,6 +596,9 @@ export async function convertToOpenAIResponsesInput({
|
|
|
552
596
|
name: resolvedToolName,
|
|
553
597
|
arguments: serializeToolCallArguments(part.input),
|
|
554
598
|
...(namespace != null && { namespace }),
|
|
599
|
+
...(caller != null && {
|
|
600
|
+
caller: mapToolCaller(caller),
|
|
601
|
+
}),
|
|
555
602
|
});
|
|
556
603
|
break;
|
|
557
604
|
}
|
|
@@ -613,6 +660,37 @@ export async function convertToOpenAIResponsesInput({
|
|
|
613
660
|
break;
|
|
614
661
|
}
|
|
615
662
|
|
|
663
|
+
if (resolvedResultToolName === 'programmatic_tool_calling') {
|
|
664
|
+
const itemId = (part.providerOptions?.[providerOptionsName]
|
|
665
|
+
?.itemId ??
|
|
666
|
+
(
|
|
667
|
+
part as {
|
|
668
|
+
providerMetadata?: {
|
|
669
|
+
[providerOptionsName]?: { itemId?: string };
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
).providerMetadata?.[providerOptionsName]?.itemId ??
|
|
673
|
+
part.toolCallId) as string;
|
|
674
|
+
|
|
675
|
+
if (store) {
|
|
676
|
+
input.push({ type: 'item_reference', id: itemId });
|
|
677
|
+
} else if (part.output.type === 'json') {
|
|
678
|
+
const parsedOutput = await validateTypes({
|
|
679
|
+
value: part.output.value,
|
|
680
|
+
schema: programmaticToolCallingOutputSchema,
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
input.push({
|
|
684
|
+
type: 'program_output',
|
|
685
|
+
id: itemId,
|
|
686
|
+
call_id: part.toolCallId,
|
|
687
|
+
result: parsedOutput.result,
|
|
688
|
+
status: parsedOutput.status,
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
|
|
616
694
|
/*
|
|
617
695
|
* Shell tool results are separate output items (shell_call_output)
|
|
618
696
|
* with their own item IDs distinct from the shell_call's item ID.
|
|
@@ -1163,10 +1241,18 @@ export async function convertToOpenAIResponsesInput({
|
|
|
1163
1241
|
break;
|
|
1164
1242
|
}
|
|
1165
1243
|
|
|
1244
|
+
const caller = mapToolCaller(
|
|
1245
|
+
part.providerOptions?.[providerOptionsName]?.caller as
|
|
1246
|
+
| { type: 'direct' }
|
|
1247
|
+
| { type: 'program'; callerId: string }
|
|
1248
|
+
| undefined,
|
|
1249
|
+
);
|
|
1250
|
+
|
|
1166
1251
|
input.push({
|
|
1167
1252
|
type: 'function_call_output',
|
|
1168
1253
|
call_id: part.toolCallId,
|
|
1169
1254
|
output: contentValue,
|
|
1255
|
+
...(caller != null && { caller }),
|
|
1170
1256
|
});
|
|
1171
1257
|
}
|
|
1172
1258
|
|
|
@@ -84,6 +84,30 @@ const openaiResponsesComputerCallSchema = z.object({
|
|
|
84
84
|
.nullish(),
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
+
const openaiResponsesToolCallerSchema = z.discriminatedUnion('type', [
|
|
88
|
+
z.object({ type: z.literal('direct') }),
|
|
89
|
+
z.object({
|
|
90
|
+
type: z.literal('program'),
|
|
91
|
+
caller_id: z.string(),
|
|
92
|
+
}),
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
const openaiResponsesProgramSchema = z.object({
|
|
96
|
+
type: z.literal('program'),
|
|
97
|
+
id: z.string(),
|
|
98
|
+
call_id: z.string(),
|
|
99
|
+
code: z.string(),
|
|
100
|
+
fingerprint: z.string(),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const openaiResponsesProgramOutputSchema = z.object({
|
|
104
|
+
type: z.literal('program_output'),
|
|
105
|
+
id: z.string(),
|
|
106
|
+
call_id: z.string(),
|
|
107
|
+
result: z.string(),
|
|
108
|
+
status: z.enum(['completed', 'incomplete']),
|
|
109
|
+
});
|
|
110
|
+
|
|
87
111
|
export type OpenAIResponsesInput = Array<OpenAIResponsesInputItem>;
|
|
88
112
|
|
|
89
113
|
export type OpenAIResponsesInputItem =
|
|
@@ -92,6 +116,8 @@ export type OpenAIResponsesInputItem =
|
|
|
92
116
|
| OpenAIResponsesAssistantMessage
|
|
93
117
|
| OpenAIResponsesFunctionCall
|
|
94
118
|
| OpenAIResponsesFunctionCallOutput
|
|
119
|
+
| OpenAIResponsesProgram
|
|
120
|
+
| OpenAIResponsesProgramOutput
|
|
95
121
|
| OpenAIResponsesCustomToolCall
|
|
96
122
|
| OpenAIResponsesCustomToolCallOutput
|
|
97
123
|
| OpenAIResponsesMcpApprovalResponse
|
|
@@ -201,6 +227,7 @@ export type OpenAIResponsesFunctionCall = {
|
|
|
201
227
|
arguments: string;
|
|
202
228
|
id?: string;
|
|
203
229
|
namespace?: string;
|
|
230
|
+
caller?: OpenAIResponsesToolCaller;
|
|
204
231
|
};
|
|
205
232
|
|
|
206
233
|
export type OpenAIResponsesFunctionCallOutput = {
|
|
@@ -231,6 +258,27 @@ export type OpenAIResponsesFunctionCallOutput = {
|
|
|
231
258
|
prompt_cache_breakpoint?: { mode: 'explicit' };
|
|
232
259
|
}
|
|
233
260
|
>;
|
|
261
|
+
caller?: OpenAIResponsesToolCaller;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
export type OpenAIResponsesToolCaller =
|
|
265
|
+
| { type: 'direct' }
|
|
266
|
+
| { type: 'program'; caller_id: string };
|
|
267
|
+
|
|
268
|
+
export type OpenAIResponsesProgram = {
|
|
269
|
+
type: 'program';
|
|
270
|
+
id: string;
|
|
271
|
+
call_id: string;
|
|
272
|
+
code: string;
|
|
273
|
+
fingerprint: string;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
export type OpenAIResponsesProgramOutput = {
|
|
277
|
+
type: 'program_output';
|
|
278
|
+
id: string;
|
|
279
|
+
call_id: string;
|
|
280
|
+
result: string;
|
|
281
|
+
status: 'completed' | 'incomplete';
|
|
234
282
|
};
|
|
235
283
|
|
|
236
284
|
export type OpenAIResponsesCustomToolCall = {
|
|
@@ -428,6 +476,8 @@ export type OpenAIResponsesFunctionTool = {
|
|
|
428
476
|
parameters: JSONSchema7;
|
|
429
477
|
strict?: boolean;
|
|
430
478
|
defer_loading?: boolean;
|
|
479
|
+
allowed_callers?: Array<'direct' | 'programmatic'>;
|
|
480
|
+
output_schema?: JSONSchema7;
|
|
431
481
|
};
|
|
432
482
|
|
|
433
483
|
export type OpenAIResponsesTool =
|
|
@@ -604,6 +654,9 @@ export type OpenAIResponsesTool =
|
|
|
604
654
|
execution?: 'server' | 'client';
|
|
605
655
|
description?: string;
|
|
606
656
|
parameters?: Record<string, unknown>;
|
|
657
|
+
}
|
|
658
|
+
| {
|
|
659
|
+
type: 'programmatic_tool_calling';
|
|
607
660
|
};
|
|
608
661
|
|
|
609
662
|
export type OpenAIResponsesReasoning = {
|
|
@@ -761,7 +814,10 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
|
|
|
761
814
|
name: z.string(),
|
|
762
815
|
arguments: z.string(),
|
|
763
816
|
namespace: z.string().nullish(),
|
|
817
|
+
caller: openaiResponsesToolCallerSchema.nullish(),
|
|
764
818
|
}),
|
|
819
|
+
openaiResponsesProgramSchema,
|
|
820
|
+
openaiResponsesProgramOutputSchema,
|
|
765
821
|
z.object({
|
|
766
822
|
type: z.literal('web_search_call'),
|
|
767
823
|
id: z.string(),
|
|
@@ -905,9 +961,12 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
|
|
|
905
961
|
call_id: z.string(),
|
|
906
962
|
name: z.string(),
|
|
907
963
|
arguments: z.string(),
|
|
908
|
-
status: z.
|
|
964
|
+
status: z.enum(['in_progress', 'completed', 'incomplete']),
|
|
909
965
|
namespace: z.string().nullish(),
|
|
966
|
+
caller: openaiResponsesToolCallerSchema.nullish(),
|
|
910
967
|
}),
|
|
968
|
+
openaiResponsesProgramSchema,
|
|
969
|
+
openaiResponsesProgramOutputSchema,
|
|
911
970
|
z.object({
|
|
912
971
|
type: z.literal('custom_tool_call'),
|
|
913
972
|
id: z.string(),
|
|
@@ -1413,7 +1472,10 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
|
|
|
1413
1472
|
arguments: z.string(),
|
|
1414
1473
|
id: z.string(),
|
|
1415
1474
|
namespace: z.string().nullish(),
|
|
1475
|
+
caller: openaiResponsesToolCallerSchema.nullish(),
|
|
1416
1476
|
}),
|
|
1477
|
+
openaiResponsesProgramSchema,
|
|
1478
|
+
openaiResponsesProgramOutputSchema,
|
|
1417
1479
|
z.object({
|
|
1418
1480
|
type: z.literal('custom_tool_call'),
|
|
1419
1481
|
call_id: z.string(),
|
|
@@ -48,6 +48,10 @@ import type {
|
|
|
48
48
|
toolSearchInputSchema,
|
|
49
49
|
toolSearchOutputSchema,
|
|
50
50
|
} from '../tool/tool-search';
|
|
51
|
+
import type {
|
|
52
|
+
programmaticToolCallingInputSchema,
|
|
53
|
+
programmaticToolCallingOutputSchema,
|
|
54
|
+
} from '../tool/programmatic-tool-calling';
|
|
51
55
|
import type { webSearchOutputSchema } from '../tool/web-search';
|
|
52
56
|
import {
|
|
53
57
|
convertOpenAIResponsesUsage,
|
|
@@ -313,6 +317,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
313
317
|
'openai.mcp': 'mcp',
|
|
314
318
|
'openai.apply_patch': 'apply_patch',
|
|
315
319
|
'openai.tool_search': 'tool_search',
|
|
320
|
+
'openai.programmatic_tool_calling': 'programmatic_tool_calling',
|
|
316
321
|
},
|
|
317
322
|
});
|
|
318
323
|
|
|
@@ -940,6 +945,56 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
940
945
|
[providerOptionsName]: {
|
|
941
946
|
itemId: part.id,
|
|
942
947
|
...(part.namespace != null && { namespace: part.namespace }),
|
|
948
|
+
...(part.caller != null && {
|
|
949
|
+
caller:
|
|
950
|
+
part.caller.type === 'program'
|
|
951
|
+
? {
|
|
952
|
+
type: 'program',
|
|
953
|
+
callerId: part.caller.caller_id,
|
|
954
|
+
}
|
|
955
|
+
: part.caller,
|
|
956
|
+
}),
|
|
957
|
+
},
|
|
958
|
+
},
|
|
959
|
+
});
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
case 'program': {
|
|
964
|
+
content.push({
|
|
965
|
+
type: 'tool-call',
|
|
966
|
+
toolCallId: part.call_id,
|
|
967
|
+
toolName: toolNameMapping.toCustomToolName(
|
|
968
|
+
'programmatic_tool_calling',
|
|
969
|
+
),
|
|
970
|
+
input: JSON.stringify({
|
|
971
|
+
code: part.code,
|
|
972
|
+
fingerprint: part.fingerprint,
|
|
973
|
+
} satisfies InferSchema<typeof programmaticToolCallingInputSchema>),
|
|
974
|
+
providerExecuted: true,
|
|
975
|
+
providerMetadata: {
|
|
976
|
+
[providerOptionsName]: {
|
|
977
|
+
itemId: part.id,
|
|
978
|
+
},
|
|
979
|
+
},
|
|
980
|
+
});
|
|
981
|
+
break;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
case 'program_output': {
|
|
985
|
+
content.push({
|
|
986
|
+
type: 'tool-result',
|
|
987
|
+
toolCallId: part.call_id,
|
|
988
|
+
toolName: toolNameMapping.toCustomToolName(
|
|
989
|
+
'programmatic_tool_calling',
|
|
990
|
+
),
|
|
991
|
+
result: {
|
|
992
|
+
result: part.result,
|
|
993
|
+
status: part.status,
|
|
994
|
+
} satisfies InferSchema<typeof programmaticToolCallingOutputSchema>,
|
|
995
|
+
providerMetadata: {
|
|
996
|
+
[providerOptionsName]: {
|
|
997
|
+
itemId: part.id,
|
|
943
998
|
},
|
|
944
999
|
},
|
|
945
1000
|
});
|
|
@@ -1631,6 +1686,54 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
1631
1686
|
...(value.item.namespace != null && {
|
|
1632
1687
|
namespace: value.item.namespace,
|
|
1633
1688
|
}),
|
|
1689
|
+
...(value.item.caller != null && {
|
|
1690
|
+
caller:
|
|
1691
|
+
value.item.caller.type === 'program'
|
|
1692
|
+
? {
|
|
1693
|
+
type: 'program',
|
|
1694
|
+
callerId: value.item.caller.caller_id,
|
|
1695
|
+
}
|
|
1696
|
+
: value.item.caller,
|
|
1697
|
+
}),
|
|
1698
|
+
},
|
|
1699
|
+
},
|
|
1700
|
+
});
|
|
1701
|
+
} else if (value.item.type === 'program') {
|
|
1702
|
+
controller.enqueue({
|
|
1703
|
+
type: 'tool-call',
|
|
1704
|
+
toolCallId: value.item.call_id,
|
|
1705
|
+
toolName: toolNameMapping.toCustomToolName(
|
|
1706
|
+
'programmatic_tool_calling',
|
|
1707
|
+
),
|
|
1708
|
+
input: JSON.stringify({
|
|
1709
|
+
code: value.item.code,
|
|
1710
|
+
fingerprint: value.item.fingerprint,
|
|
1711
|
+
} satisfies InferSchema<
|
|
1712
|
+
typeof programmaticToolCallingInputSchema
|
|
1713
|
+
>),
|
|
1714
|
+
providerExecuted: true,
|
|
1715
|
+
providerMetadata: {
|
|
1716
|
+
[providerOptionsName]: {
|
|
1717
|
+
itemId: value.item.id,
|
|
1718
|
+
},
|
|
1719
|
+
},
|
|
1720
|
+
});
|
|
1721
|
+
} else if (value.item.type === 'program_output') {
|
|
1722
|
+
controller.enqueue({
|
|
1723
|
+
type: 'tool-result',
|
|
1724
|
+
toolCallId: value.item.call_id,
|
|
1725
|
+
toolName: toolNameMapping.toCustomToolName(
|
|
1726
|
+
'programmatic_tool_calling',
|
|
1727
|
+
),
|
|
1728
|
+
result: {
|
|
1729
|
+
result: value.item.result,
|
|
1730
|
+
status: value.item.status,
|
|
1731
|
+
} satisfies InferSchema<
|
|
1732
|
+
typeof programmaticToolCallingOutputSchema
|
|
1733
|
+
>,
|
|
1734
|
+
providerMetadata: {
|
|
1735
|
+
[providerOptionsName]: {
|
|
1736
|
+
itemId: value.item.id,
|
|
1634
1737
|
},
|
|
1635
1738
|
},
|
|
1636
1739
|
});
|