@openai/agents-openai 0.5.4 → 0.7.0
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/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/metadata.js +3 -3
- package/dist/metadata.mjs +3 -3
- package/dist/openaiChatCompletionsConverter.d.ts +1 -0
- package/dist/openaiChatCompletionsConverter.js +122 -16
- package/dist/openaiChatCompletionsConverter.js.map +1 -1
- package/dist/openaiChatCompletionsConverter.mjs +121 -16
- package/dist/openaiChatCompletionsConverter.mjs.map +1 -1
- package/dist/openaiChatCompletionsModel.d.ts +2 -1
- package/dist/openaiChatCompletionsModel.js +20 -3
- package/dist/openaiChatCompletionsModel.js.map +1 -1
- package/dist/openaiChatCompletionsModel.mjs +22 -5
- package/dist/openaiChatCompletionsModel.mjs.map +1 -1
- package/dist/openaiResponsesModel.d.ts +19 -5
- package/dist/openaiResponsesModel.js +724 -74
- package/dist/openaiResponsesModel.js.map +1 -1
- package/dist/openaiResponsesModel.mjs +726 -76
- package/dist/openaiResponsesModel.mjs.map +1 -1
- package/dist/retryAdvice.d.ts +2 -0
- package/dist/retryAdvice.js +98 -0
- package/dist/retryAdvice.js.map +1 -0
- package/dist/retryAdvice.mjs +95 -0
- package/dist/retryAdvice.mjs.map +1 -0
- package/dist/tools.d.ts +22 -1
- package/dist/tools.js +38 -0
- package/dist/tools.js.map +1 -1
- package/dist/tools.mjs +37 -0
- package/dist/tools.mjs.map +1 -1
- package/dist/types/providerData.d.ts +4 -0
- package/dist/utils/providerData.d.ts +26 -2
- package/dist/utils/providerData.js +60 -8
- package/dist/utils/providerData.js.map +1 -1
- package/dist/utils/providerData.mjs +57 -8
- package/dist/utils/providerData.mjs.map +1 -1
- package/package.json +3 -3
|
@@ -1,13 +1,114 @@
|
|
|
1
1
|
import { RequestUsage, Usage, withResponseSpan, createResponseSpan, setCurrentSpan, resetCurrentSpan, UserError, } from '@openai/agents-core';
|
|
2
2
|
import OpenAI from 'openai';
|
|
3
3
|
import logger from "./logger.mjs";
|
|
4
|
+
import { getOpenAIRetryAdvice } from "./retryAdvice.mjs";
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
import { HEADERS } from "./defaults.mjs";
|
|
6
7
|
import { ResponsesWebSocketConnection, ResponsesWebSocketInternalError, isWebSocketNotOpenError, shouldWrapNoEventWebSocketError, throwIfAborted, webSocketFrameToText, withAbortSignal, withTimeout, } from "./responsesWebSocketConnection.mjs";
|
|
7
8
|
import { applyHeadersToAccumulator, createHeaderAccumulator, ensureResponsesWebSocketPath, headerAccumulatorToRecord, headerAccumulatorToSDKHeaders, mergeQueryParamsIntoURL, splitResponsesTransportOverrides, } from "./responsesTransportUtils.mjs";
|
|
8
9
|
import { CodeInterpreterStatus, FileSearchStatus, ImageGenerationStatus, WebSearchStatus, } from "./tools.mjs";
|
|
9
|
-
import { camelOrSnakeToSnakeCase } from "./utils/providerData.mjs";
|
|
10
|
-
import { encodeUint8ArrayToBase64 } from '@openai/agents-core/utils';
|
|
10
|
+
import { camelOrSnakeToSnakeCase, getSnakeCasedProviderDataWithoutReservedKeys, } from "./utils/providerData.mjs";
|
|
11
|
+
import { encodeUint8ArrayToBase64, getToolSearchExecution, getToolSearchProviderCallId, } from '@openai/agents-core/utils';
|
|
12
|
+
/**
|
|
13
|
+
* Tool search outputs are replayed through agents-core protocol items, which use camelCase
|
|
14
|
+
* field names, while the Responses API wire shape uses snake_case. Keep this codec even with
|
|
15
|
+
* first-class upstream types because the local protocol still normalizes these payloads.
|
|
16
|
+
*/
|
|
17
|
+
function toOpenAIToolSearchOutputToolPayload(tool, _withinNamespace = false) {
|
|
18
|
+
if (!tool || typeof tool !== 'object' || Array.isArray(tool)) {
|
|
19
|
+
return tool;
|
|
20
|
+
}
|
|
21
|
+
if (tool.type === 'tool_reference' && typeof tool.functionName === 'string') {
|
|
22
|
+
return {
|
|
23
|
+
type: 'tool_reference',
|
|
24
|
+
function_name: tool.functionName,
|
|
25
|
+
...(typeof tool.namespace === 'string'
|
|
26
|
+
? { namespace: tool.namespace }
|
|
27
|
+
: {}),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (tool.type === 'namespace' && Array.isArray(tool.tools)) {
|
|
31
|
+
return {
|
|
32
|
+
...tool,
|
|
33
|
+
tools: tool.tools.map((entry) => toOpenAIToolSearchOutputToolPayload(entry, true)),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (tool.type === 'function') {
|
|
37
|
+
const { deferLoading, ...rest } = tool;
|
|
38
|
+
return {
|
|
39
|
+
...rest,
|
|
40
|
+
...(typeof deferLoading === 'boolean'
|
|
41
|
+
? { defer_loading: deferLoading }
|
|
42
|
+
: {}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return tool;
|
|
46
|
+
}
|
|
47
|
+
function fromOpenAIToolSearchOutputToolPayload(tool, _withinNamespace = false) {
|
|
48
|
+
if (!tool || typeof tool !== 'object' || Array.isArray(tool)) {
|
|
49
|
+
return tool;
|
|
50
|
+
}
|
|
51
|
+
if (tool.type === 'tool_reference' &&
|
|
52
|
+
typeof tool.function_name === 'string') {
|
|
53
|
+
return {
|
|
54
|
+
type: 'tool_reference',
|
|
55
|
+
functionName: tool.function_name,
|
|
56
|
+
...(typeof tool.namespace === 'string'
|
|
57
|
+
? { namespace: tool.namespace }
|
|
58
|
+
: {}),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (tool.type === 'namespace' && Array.isArray(tool.tools)) {
|
|
62
|
+
return {
|
|
63
|
+
...tool,
|
|
64
|
+
tools: tool.tools.map((entry) => fromOpenAIToolSearchOutputToolPayload(entry, true)),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (tool.type === 'function') {
|
|
68
|
+
const { defer_loading, ...rest } = tool;
|
|
69
|
+
return {
|
|
70
|
+
...rest,
|
|
71
|
+
...(typeof defer_loading === 'boolean'
|
|
72
|
+
? { deferLoading: defer_loading }
|
|
73
|
+
: {}),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return tool;
|
|
77
|
+
}
|
|
78
|
+
function isNeverSentWebSocketError(error) {
|
|
79
|
+
if (isWebSocketNotOpenError(error)) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
const errorCause = error instanceof Error
|
|
83
|
+
? error.cause
|
|
84
|
+
: undefined;
|
|
85
|
+
if (error instanceof ResponsesWebSocketInternalError &&
|
|
86
|
+
error.code === 'connection_closed_before_opening') {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
if (errorCause instanceof ResponsesWebSocketInternalError &&
|
|
90
|
+
errorCause.code === 'connection_closed_before_opening') {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
function isAmbiguousWebSocketReplayError(error) {
|
|
96
|
+
if (error instanceof ResponsesWebSocketInternalError &&
|
|
97
|
+
error.code === 'connection_closed_before_terminal_response_event') {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
const errorCause = error instanceof Error
|
|
101
|
+
? error.cause
|
|
102
|
+
: undefined;
|
|
103
|
+
return (errorCause instanceof ResponsesWebSocketInternalError &&
|
|
104
|
+
errorCause.code === 'connection_closed_before_terminal_response_event');
|
|
105
|
+
}
|
|
106
|
+
function hasSerializedComputerDisplayMetadata(tool) {
|
|
107
|
+
return (typeof tool.environment === 'string' &&
|
|
108
|
+
Array.isArray(tool.dimensions) &&
|
|
109
|
+
tool.dimensions.length === 2 &&
|
|
110
|
+
tool.dimensions.every((value) => typeof value === 'number'));
|
|
111
|
+
}
|
|
11
112
|
const HostedToolChoice = z.enum([
|
|
12
113
|
'file_search',
|
|
13
114
|
'web_search',
|
|
@@ -16,12 +117,16 @@ const HostedToolChoice = z.enum([
|
|
|
16
117
|
'image_generation',
|
|
17
118
|
'mcp',
|
|
18
119
|
// Specialized local tools
|
|
19
|
-
'computer_use_preview',
|
|
20
120
|
'shell',
|
|
21
121
|
'apply_patch',
|
|
22
122
|
]);
|
|
23
123
|
const DefaultToolChoice = z.enum(['auto', 'required', 'none']);
|
|
24
|
-
|
|
124
|
+
const BuiltinComputerToolChoice = z.enum([
|
|
125
|
+
'computer',
|
|
126
|
+
'computer_use',
|
|
127
|
+
'computer_use_preview',
|
|
128
|
+
]);
|
|
129
|
+
function getToolChoice(toolChoice, options) {
|
|
25
130
|
if (typeof toolChoice === 'undefined') {
|
|
26
131
|
return undefined;
|
|
27
132
|
}
|
|
@@ -29,12 +134,202 @@ function getToolChoice(toolChoice) {
|
|
|
29
134
|
if (resultDefaultCheck.success) {
|
|
30
135
|
return resultDefaultCheck.data;
|
|
31
136
|
}
|
|
137
|
+
const builtinComputerToolChoice = BuiltinComputerToolChoice.safeParse(toolChoice);
|
|
138
|
+
if (builtinComputerToolChoice.success) {
|
|
139
|
+
if (hasBuiltinComputerTool(options?.tools) ||
|
|
140
|
+
options?.allowPromptSuppliedComputerTool === true) {
|
|
141
|
+
return getBuiltinComputerToolChoice(builtinComputerToolChoice.data, {
|
|
142
|
+
model: options?.model,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
if (builtinComputerToolChoice.data === 'computer_use_preview') {
|
|
146
|
+
return { type: 'computer_use_preview' };
|
|
147
|
+
}
|
|
148
|
+
return { type: 'function', name: builtinComputerToolChoice.data };
|
|
149
|
+
}
|
|
32
150
|
const result = HostedToolChoice.safeParse(toolChoice);
|
|
33
151
|
if (result.success) {
|
|
34
152
|
return { type: result.data };
|
|
35
153
|
}
|
|
36
154
|
return { type: 'function', name: toolChoice };
|
|
37
155
|
}
|
|
156
|
+
function normalizeToolSearchStatus(status) {
|
|
157
|
+
return status === 'in_progress' ||
|
|
158
|
+
status === 'completed' ||
|
|
159
|
+
status === 'incomplete'
|
|
160
|
+
? status
|
|
161
|
+
: null;
|
|
162
|
+
}
|
|
163
|
+
function hasBuiltinComputerTool(tools) {
|
|
164
|
+
return (tools ?? []).some((tool) => tool.type === 'computer' ||
|
|
165
|
+
tool.type === 'computer_use' ||
|
|
166
|
+
tool.type === 'computer_use_preview');
|
|
167
|
+
}
|
|
168
|
+
function isPreviewComputerModel(model) {
|
|
169
|
+
return typeof model === 'string' && model.startsWith('computer-use-preview');
|
|
170
|
+
}
|
|
171
|
+
function shouldUsePreviewComputerTool(options) {
|
|
172
|
+
if (isPreviewComputerModel(options?.model)) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
if (typeof options?.model === 'string') {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
if (options?.toolChoice === 'computer' ||
|
|
179
|
+
options?.toolChoice === 'computer_use') {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
function getBuiltinComputerToolChoice(toolChoice, options) {
|
|
185
|
+
if (shouldUsePreviewComputerTool({
|
|
186
|
+
model: options?.model,
|
|
187
|
+
toolChoice,
|
|
188
|
+
})) {
|
|
189
|
+
return { type: 'computer_use_preview' };
|
|
190
|
+
}
|
|
191
|
+
if (toolChoice === 'computer_use') {
|
|
192
|
+
return { type: 'computer_use' };
|
|
193
|
+
}
|
|
194
|
+
return { type: 'computer' };
|
|
195
|
+
}
|
|
196
|
+
function isBuiltinComputerToolType(type) {
|
|
197
|
+
return (type === 'computer' ||
|
|
198
|
+
type === 'computer_use' ||
|
|
199
|
+
type === 'computer_use_preview');
|
|
200
|
+
}
|
|
201
|
+
function isCompatibleBuiltinComputerToolChoice(toolChoiceType, toolType) {
|
|
202
|
+
if (!isBuiltinComputerToolType(toolChoiceType)) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
if (toolChoiceType === 'computer_use_preview') {
|
|
206
|
+
return toolType === 'computer_use_preview';
|
|
207
|
+
}
|
|
208
|
+
return toolType === 'computer';
|
|
209
|
+
}
|
|
210
|
+
function isToolChoiceAvailable(toolChoice, tools) {
|
|
211
|
+
if (toolChoice === 'auto' || toolChoice === 'none') {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
if (toolChoice === 'required') {
|
|
215
|
+
return tools.length > 0;
|
|
216
|
+
}
|
|
217
|
+
if (toolChoice.type === 'function') {
|
|
218
|
+
return hasFunctionToolChoiceName(toolChoice.name, tools);
|
|
219
|
+
}
|
|
220
|
+
return tools.some((tool) => isCompatibleBuiltinComputerToolChoice(toolChoice.type, tool.type)
|
|
221
|
+
? true
|
|
222
|
+
: tool.type === toolChoice.type);
|
|
223
|
+
}
|
|
224
|
+
function hasFunctionToolChoiceName(toolChoiceName, tools, namespacePrefix) {
|
|
225
|
+
return (findFunctionToolChoice(toolChoiceName, tools, namespacePrefix) !== undefined);
|
|
226
|
+
}
|
|
227
|
+
function findFunctionToolChoice(toolChoiceName, tools, namespacePrefix) {
|
|
228
|
+
for (const tool of tools) {
|
|
229
|
+
if (isNamedFunctionTool(tool)) {
|
|
230
|
+
const qualifiedName = namespacePrefix
|
|
231
|
+
? `${namespacePrefix}.${tool.name}`
|
|
232
|
+
: tool.name;
|
|
233
|
+
if (toolChoiceName === qualifiedName) {
|
|
234
|
+
return tool;
|
|
235
|
+
}
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (isNamespaceTool(tool)) {
|
|
239
|
+
const nestedNamespace = namespacePrefix
|
|
240
|
+
? `${namespacePrefix}.${tool.name}`
|
|
241
|
+
: tool.name;
|
|
242
|
+
const matchedTool = findFunctionToolChoice(toolChoiceName, tool.tools, nestedNamespace);
|
|
243
|
+
if (matchedTool) {
|
|
244
|
+
return matchedTool;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
function collectAvailableToolChoiceNames(tools, namespacePrefix) {
|
|
251
|
+
const availableToolChoices = [];
|
|
252
|
+
for (const tool of tools) {
|
|
253
|
+
if (isNamedFunctionTool(tool)) {
|
|
254
|
+
availableToolChoices.push(namespacePrefix ? `${namespacePrefix}.${tool.name}` : tool.name);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (isNamespaceTool(tool)) {
|
|
258
|
+
const nestedNamespace = namespacePrefix
|
|
259
|
+
? `${namespacePrefix}.${tool.name}`
|
|
260
|
+
: tool.name;
|
|
261
|
+
availableToolChoices.push(...collectAvailableToolChoiceNames(tool.tools, nestedNamespace));
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
availableToolChoices.push(tool.type);
|
|
265
|
+
}
|
|
266
|
+
return availableToolChoices;
|
|
267
|
+
}
|
|
268
|
+
function isNamedFunctionTool(tool) {
|
|
269
|
+
return (tool.type === 'function' &&
|
|
270
|
+
typeof tool.name === 'string');
|
|
271
|
+
}
|
|
272
|
+
function isNamespaceTool(tool) {
|
|
273
|
+
const candidate = tool;
|
|
274
|
+
return (tool.type === 'namespace' &&
|
|
275
|
+
typeof candidate.name === 'string' &&
|
|
276
|
+
Array.isArray(candidate.tools));
|
|
277
|
+
}
|
|
278
|
+
function getExtraBodyToolsForToolChoiceValidation(extraBody) {
|
|
279
|
+
if (!extraBody || !Array.isArray(extraBody.tools)) {
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
return extraBody.tools;
|
|
283
|
+
}
|
|
284
|
+
function assertSupportedToolChoice(toolChoice, tools, options) {
|
|
285
|
+
const allowPromptSuppliedTools = options?.allowPromptSuppliedTools === true;
|
|
286
|
+
if (!toolChoice ||
|
|
287
|
+
toolChoice === 'auto' ||
|
|
288
|
+
toolChoice === 'required' ||
|
|
289
|
+
toolChoice === 'none' ||
|
|
290
|
+
toolChoice.type !== 'function') {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const matchedFunctionTool = findFunctionToolChoice(toolChoice.name, tools);
|
|
294
|
+
if (!matchedFunctionTool &&
|
|
295
|
+
allowPromptSuppliedTools &&
|
|
296
|
+
toolChoice.name !== 'tool_search') {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (matchedFunctionTool
|
|
300
|
+
?.defer_loading === true) {
|
|
301
|
+
throw new UserError(`modelSettings.toolChoice="${toolChoice.name}" cannot force a deferred function tool in Responses. Use "auto" so tool_search can load it.`);
|
|
302
|
+
}
|
|
303
|
+
if (toolChoice.name === 'tool_search' &&
|
|
304
|
+
!hasFunctionToolChoiceName(toolChoice.name, tools)) {
|
|
305
|
+
throw new UserError('modelSettings.toolChoice="tool_search" is only supported for a custom function named "tool_search". Responses does not support forcing the built-in tool_search tool. Use "auto" instead.');
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function getCompatibleToolChoice(toolChoice, tools, options) {
|
|
309
|
+
const allowPromptSuppliedTools = options?.allowPromptSuppliedTools === true;
|
|
310
|
+
if (typeof toolChoice === 'undefined') {
|
|
311
|
+
return undefined;
|
|
312
|
+
}
|
|
313
|
+
if (isToolChoiceAvailable(toolChoice, tools) || allowPromptSuppliedTools) {
|
|
314
|
+
return toolChoice;
|
|
315
|
+
}
|
|
316
|
+
const availableToolChoices = [
|
|
317
|
+
...new Set(collectAvailableToolChoiceNames(tools)),
|
|
318
|
+
];
|
|
319
|
+
const availableToolChoicesMessage = availableToolChoices.length > 0
|
|
320
|
+
? ` Available tools: ${availableToolChoices.join(', ')}.`
|
|
321
|
+
: ' No tools are available in the outgoing Responses request.';
|
|
322
|
+
if (toolChoice === 'required') {
|
|
323
|
+
throw new UserError(`modelSettings.toolChoice="required" requires at least one available tool in the outgoing Responses request.${availableToolChoicesMessage}`);
|
|
324
|
+
}
|
|
325
|
+
if (toolChoice === 'auto' || toolChoice === 'none') {
|
|
326
|
+
throw new Error(`Unexpected unavailable tool choice: ${JSON.stringify(toolChoice)}`);
|
|
327
|
+
}
|
|
328
|
+
if (toolChoice.type === 'function') {
|
|
329
|
+
throw new UserError(`modelSettings.toolChoice="${toolChoice.name}" does not match any available tool in the outgoing Responses request.${availableToolChoicesMessage}`);
|
|
330
|
+
}
|
|
331
|
+
throw new UserError(`modelSettings.toolChoice="${toolChoice.type}" is unavailable in the outgoing Responses request.${availableToolChoicesMessage}`);
|
|
332
|
+
}
|
|
38
333
|
function getResponseFormat(outputType, otherProperties) {
|
|
39
334
|
if (outputType === 'text') {
|
|
40
335
|
return otherProperties;
|
|
@@ -88,12 +383,13 @@ function convertLegacyToolOutputContent(output) {
|
|
|
88
383
|
const legacyImageUrl = output.imageUrl;
|
|
89
384
|
const legacyFileId = output.fileId;
|
|
90
385
|
const dataValue = output.data;
|
|
386
|
+
const topLevelInlineMediaType = getImageInlineMediaType(output);
|
|
91
387
|
if (typeof output.image === 'string' && output.image.length > 0) {
|
|
92
388
|
structured.image = output.image;
|
|
93
389
|
}
|
|
94
390
|
else if (isRecord(output.image)) {
|
|
95
391
|
const imageObj = output.image;
|
|
96
|
-
const inlineMediaType = getImageInlineMediaType(imageObj);
|
|
392
|
+
const inlineMediaType = getImageInlineMediaType(imageObj) ?? topLevelInlineMediaType;
|
|
97
393
|
if (typeof imageObj.url === 'string' && imageObj.url.length > 0) {
|
|
98
394
|
structured.image = imageObj.url;
|
|
99
395
|
}
|
|
@@ -133,7 +429,7 @@ function convertLegacyToolOutputContent(output) {
|
|
|
133
429
|
base64Data = encodeUint8ArrayToBase64(dataValue);
|
|
134
430
|
}
|
|
135
431
|
if (base64Data) {
|
|
136
|
-
structured.image = base64Data;
|
|
432
|
+
structured.image = formatInlineData(base64Data, topLevelInlineMediaType);
|
|
137
433
|
}
|
|
138
434
|
}
|
|
139
435
|
if (output.providerData) {
|
|
@@ -378,9 +674,16 @@ function getImageInlineMediaType(source) {
|
|
|
378
674
|
if (typeof source.mediaType === 'string' && source.mediaType.length > 0) {
|
|
379
675
|
return source.mediaType;
|
|
380
676
|
}
|
|
677
|
+
if (typeof source.mimeType === 'string' &&
|
|
678
|
+
source.mimeType.length > 0) {
|
|
679
|
+
return source.mimeType;
|
|
680
|
+
}
|
|
381
681
|
return undefined;
|
|
382
682
|
}
|
|
383
683
|
function formatInlineData(data, mediaType) {
|
|
684
|
+
if (typeof data === 'string' && data.startsWith('data:')) {
|
|
685
|
+
return data;
|
|
686
|
+
}
|
|
384
687
|
const base64 = typeof data === 'string' ? data : encodeUint8ArrayToBase64(data);
|
|
385
688
|
return mediaType ? `data:${mediaType};base64,${base64}` : base64;
|
|
386
689
|
}
|
|
@@ -497,11 +800,70 @@ function toOpenAIShellEnvironment(environment) {
|
|
|
497
800
|
}
|
|
498
801
|
throw new UserError(`Unsupported shell environment type: ${String(environment.type)}`);
|
|
499
802
|
}
|
|
500
|
-
function getTools(tools, handoffs) {
|
|
803
|
+
function getTools(tools, handoffs, options) {
|
|
501
804
|
const openaiTools = [];
|
|
502
805
|
const include = [];
|
|
806
|
+
const namespaceStateByName = new Map();
|
|
807
|
+
let hasDeferredSearchableTool = false;
|
|
808
|
+
let hasToolSearch = false;
|
|
809
|
+
const usePreviewComputerTool = shouldUsePreviewComputerTool({
|
|
810
|
+
model: options?.model,
|
|
811
|
+
toolChoice: options?.toolChoice,
|
|
812
|
+
});
|
|
503
813
|
for (const tool of tools) {
|
|
504
|
-
|
|
814
|
+
if (tool.type === 'function') {
|
|
815
|
+
const isDeferredFunction = tool.deferLoading === true;
|
|
816
|
+
hasDeferredSearchableTool ||= isDeferredFunction;
|
|
817
|
+
const namespaceName = typeof tool.namespace === 'string' ? tool.namespace.trim() : '';
|
|
818
|
+
if (namespaceName.length > 0) {
|
|
819
|
+
const namespaceDescription = typeof tool.namespaceDescription === 'string'
|
|
820
|
+
? tool.namespaceDescription.trim()
|
|
821
|
+
: '';
|
|
822
|
+
if (namespaceDescription.length === 0) {
|
|
823
|
+
throw new UserError(`All tools in namespace "${namespaceName}" must provide a non-empty description.`);
|
|
824
|
+
}
|
|
825
|
+
let namespaceState = namespaceStateByName.get(namespaceName);
|
|
826
|
+
if (!namespaceState) {
|
|
827
|
+
namespaceState = {
|
|
828
|
+
index: openaiTools.length,
|
|
829
|
+
description: namespaceDescription,
|
|
830
|
+
functionNames: new Set(),
|
|
831
|
+
tools: [],
|
|
832
|
+
};
|
|
833
|
+
namespaceStateByName.set(namespaceName, namespaceState);
|
|
834
|
+
openaiTools.push({});
|
|
835
|
+
}
|
|
836
|
+
else if (namespaceState.description !== namespaceDescription) {
|
|
837
|
+
throw new UserError(`All tools in namespace "${namespaceName}" must share the same description.`);
|
|
838
|
+
}
|
|
839
|
+
const { tool: openaiTool, include: openaiIncludes } = converTool(tool, {
|
|
840
|
+
usePreviewComputerTool,
|
|
841
|
+
});
|
|
842
|
+
if (namespaceState.functionNames.has(tool.name)) {
|
|
843
|
+
throw new UserError(`Namespace "${namespaceName}" cannot contain duplicate function tool name "${tool.name}".`);
|
|
844
|
+
}
|
|
845
|
+
namespaceState.functionNames.add(tool.name);
|
|
846
|
+
namespaceState.tools.push(openaiTool);
|
|
847
|
+
if (openaiIncludes && openaiIncludes.length > 0) {
|
|
848
|
+
for (const item of openaiIncludes) {
|
|
849
|
+
include.push(item);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
continue;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (tool.type === 'hosted_tool' &&
|
|
856
|
+
tool.providerData?.type === 'tool_search') {
|
|
857
|
+
hasToolSearch = true;
|
|
858
|
+
}
|
|
859
|
+
if (tool.type === 'hosted_tool' &&
|
|
860
|
+
tool.providerData?.type === 'mcp' &&
|
|
861
|
+
tool.providerData.defer_loading === true) {
|
|
862
|
+
hasDeferredSearchableTool = true;
|
|
863
|
+
}
|
|
864
|
+
const { tool: openaiTool, include: openaiIncludes } = converTool(tool, {
|
|
865
|
+
usePreviewComputerTool,
|
|
866
|
+
});
|
|
505
867
|
openaiTools.push(openaiTool);
|
|
506
868
|
if (openaiIncludes && openaiIncludes.length > 0) {
|
|
507
869
|
for (const item of openaiIncludes) {
|
|
@@ -509,31 +871,57 @@ function getTools(tools, handoffs) {
|
|
|
509
871
|
}
|
|
510
872
|
}
|
|
511
873
|
}
|
|
874
|
+
if (hasDeferredSearchableTool && !hasToolSearch) {
|
|
875
|
+
throw new UserError('Deferred function tools and hosted MCP tools with deferLoading: true require toolSearchTool() in the same request.');
|
|
876
|
+
}
|
|
877
|
+
for (const [namespaceName, namespaceState,] of namespaceStateByName.entries()) {
|
|
878
|
+
openaiTools[namespaceState.index] = {
|
|
879
|
+
type: 'namespace',
|
|
880
|
+
name: namespaceName,
|
|
881
|
+
description: namespaceState.description,
|
|
882
|
+
tools: namespaceState.tools,
|
|
883
|
+
};
|
|
884
|
+
}
|
|
512
885
|
return {
|
|
513
886
|
tools: [...openaiTools, ...handoffs.map(getHandoffTool)],
|
|
514
887
|
include,
|
|
515
888
|
};
|
|
516
889
|
}
|
|
517
|
-
function converTool(tool) {
|
|
890
|
+
function converTool(tool, options) {
|
|
518
891
|
if (tool.type === 'function') {
|
|
892
|
+
const openaiTool = {
|
|
893
|
+
type: 'function',
|
|
894
|
+
name: tool.name,
|
|
895
|
+
description: tool.description,
|
|
896
|
+
parameters: tool.parameters,
|
|
897
|
+
strict: tool.strict,
|
|
898
|
+
};
|
|
899
|
+
if (tool.deferLoading) {
|
|
900
|
+
openaiTool.defer_loading = true;
|
|
901
|
+
}
|
|
519
902
|
return {
|
|
520
|
-
tool:
|
|
521
|
-
type: 'function',
|
|
522
|
-
name: tool.name,
|
|
523
|
-
description: tool.description,
|
|
524
|
-
parameters: tool.parameters,
|
|
525
|
-
strict: tool.strict,
|
|
526
|
-
},
|
|
903
|
+
tool: openaiTool,
|
|
527
904
|
include: undefined,
|
|
528
905
|
};
|
|
529
906
|
}
|
|
530
907
|
else if (tool.type === 'computer') {
|
|
908
|
+
if (options?.usePreviewComputerTool) {
|
|
909
|
+
if (!hasSerializedComputerDisplayMetadata(tool)) {
|
|
910
|
+
throw new UserError('Preview computer tools require environment and dimensions. Provide them on your Computer implementation or target a GA computer model such as gpt-5.4.');
|
|
911
|
+
}
|
|
912
|
+
return {
|
|
913
|
+
tool: {
|
|
914
|
+
type: 'computer_use_preview',
|
|
915
|
+
environment: tool.environment,
|
|
916
|
+
display_width: tool.dimensions[0],
|
|
917
|
+
display_height: tool.dimensions[1],
|
|
918
|
+
},
|
|
919
|
+
include: undefined,
|
|
920
|
+
};
|
|
921
|
+
}
|
|
531
922
|
return {
|
|
532
923
|
tool: {
|
|
533
|
-
type: '
|
|
534
|
-
environment: tool.environment,
|
|
535
|
-
display_width: tool.dimensions[0],
|
|
536
|
-
display_height: tool.dimensions[1],
|
|
924
|
+
type: 'computer',
|
|
537
925
|
},
|
|
538
926
|
include: undefined,
|
|
539
927
|
};
|
|
@@ -604,6 +992,17 @@ function converTool(tool) {
|
|
|
604
992
|
include: undefined,
|
|
605
993
|
};
|
|
606
994
|
}
|
|
995
|
+
else if (tool.providerData?.type === 'tool_search') {
|
|
996
|
+
return {
|
|
997
|
+
tool: {
|
|
998
|
+
type: 'tool_search',
|
|
999
|
+
execution: tool.providerData.execution,
|
|
1000
|
+
description: tool.providerData.description,
|
|
1001
|
+
parameters: tool.providerData.parameters,
|
|
1002
|
+
},
|
|
1003
|
+
include: undefined,
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
607
1006
|
else if (tool.providerData?.type === 'image_generation') {
|
|
608
1007
|
return {
|
|
609
1008
|
tool: {
|
|
@@ -623,17 +1022,22 @@ function converTool(tool) {
|
|
|
623
1022
|
};
|
|
624
1023
|
}
|
|
625
1024
|
else if (tool.providerData?.type === 'mcp') {
|
|
1025
|
+
const openaiTool = {
|
|
1026
|
+
type: 'mcp',
|
|
1027
|
+
server_label: tool.providerData.server_label,
|
|
1028
|
+
server_url: tool.providerData.server_url,
|
|
1029
|
+
connector_id: tool.providerData.connector_id,
|
|
1030
|
+
authorization: tool.providerData.authorization,
|
|
1031
|
+
allowed_tools: tool.providerData.allowed_tools,
|
|
1032
|
+
headers: tool.providerData.headers,
|
|
1033
|
+
require_approval: convertMCPRequireApproval(tool.providerData.require_approval),
|
|
1034
|
+
server_description: tool.providerData.server_description,
|
|
1035
|
+
};
|
|
1036
|
+
if (tool.providerData.defer_loading === true) {
|
|
1037
|
+
openaiTool.defer_loading = true;
|
|
1038
|
+
}
|
|
626
1039
|
return {
|
|
627
|
-
tool:
|
|
628
|
-
type: 'mcp',
|
|
629
|
-
server_label: tool.providerData.server_label,
|
|
630
|
-
server_url: tool.providerData.server_url,
|
|
631
|
-
connector_id: tool.providerData.connector_id,
|
|
632
|
-
authorization: tool.providerData.authorization,
|
|
633
|
-
allowed_tools: tool.providerData.allowed_tools,
|
|
634
|
-
headers: tool.providerData.headers,
|
|
635
|
-
require_approval: convertMCPRequireApproval(tool.providerData.require_approval),
|
|
636
|
-
},
|
|
1040
|
+
tool: openaiTool,
|
|
637
1041
|
include: undefined,
|
|
638
1042
|
};
|
|
639
1043
|
}
|
|
@@ -672,7 +1076,10 @@ function getInputMessageContent(entry) {
|
|
|
672
1076
|
return {
|
|
673
1077
|
type: 'input_text',
|
|
674
1078
|
text: entry.text,
|
|
675
|
-
...
|
|
1079
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(entry.providerData, [
|
|
1080
|
+
'type',
|
|
1081
|
+
'text',
|
|
1082
|
+
]),
|
|
676
1083
|
};
|
|
677
1084
|
}
|
|
678
1085
|
else if (entry.type === 'input_image') {
|
|
@@ -694,7 +1101,12 @@ function getInputMessageContent(entry) {
|
|
|
694
1101
|
}
|
|
695
1102
|
return {
|
|
696
1103
|
...imageEntry,
|
|
697
|
-
...
|
|
1104
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(entry.providerData, [
|
|
1105
|
+
'type',
|
|
1106
|
+
'detail',
|
|
1107
|
+
'image_url',
|
|
1108
|
+
'file_id',
|
|
1109
|
+
]),
|
|
698
1110
|
};
|
|
699
1111
|
}
|
|
700
1112
|
else if (entry.type === 'input_file') {
|
|
@@ -739,25 +1151,54 @@ function getInputMessageContent(entry) {
|
|
|
739
1151
|
}
|
|
740
1152
|
return {
|
|
741
1153
|
...fileEntry,
|
|
742
|
-
...
|
|
1154
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(entry.providerData, [
|
|
1155
|
+
'type',
|
|
1156
|
+
'file_data',
|
|
1157
|
+
'file_url',
|
|
1158
|
+
'file_id',
|
|
1159
|
+
'filename',
|
|
1160
|
+
]),
|
|
743
1161
|
};
|
|
744
1162
|
}
|
|
745
1163
|
throw new UserError(`Unsupported input content type: ${JSON.stringify(entry)}`);
|
|
746
1164
|
}
|
|
1165
|
+
function getProviderDataField(providerData, keys) {
|
|
1166
|
+
if (!providerData ||
|
|
1167
|
+
typeof providerData !== 'object' ||
|
|
1168
|
+
Array.isArray(providerData)) {
|
|
1169
|
+
return undefined;
|
|
1170
|
+
}
|
|
1171
|
+
const record = providerData;
|
|
1172
|
+
for (const key of keys) {
|
|
1173
|
+
if (typeof record[key] !== 'undefined') {
|
|
1174
|
+
return record[key];
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
return undefined;
|
|
1178
|
+
}
|
|
747
1179
|
function getOutputMessageContent(entry) {
|
|
748
1180
|
if (entry.type === 'output_text') {
|
|
1181
|
+
const annotations = getProviderDataField(entry.providerData, ['annotations']);
|
|
1182
|
+
const normalizedAnnotations = Array.isArray(annotations) ? annotations : [];
|
|
749
1183
|
return {
|
|
750
1184
|
type: 'output_text',
|
|
751
1185
|
text: entry.text,
|
|
752
|
-
annotations:
|
|
753
|
-
...
|
|
1186
|
+
annotations: normalizedAnnotations,
|
|
1187
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(entry.providerData, [
|
|
1188
|
+
'type',
|
|
1189
|
+
'text',
|
|
1190
|
+
'annotations',
|
|
1191
|
+
]),
|
|
754
1192
|
};
|
|
755
1193
|
}
|
|
756
1194
|
if (entry.type === 'refusal') {
|
|
757
1195
|
return {
|
|
758
1196
|
type: 'refusal',
|
|
759
1197
|
refusal: entry.refusal,
|
|
760
|
-
...
|
|
1198
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(entry.providerData, [
|
|
1199
|
+
'type',
|
|
1200
|
+
'refusal',
|
|
1201
|
+
]),
|
|
761
1202
|
};
|
|
762
1203
|
}
|
|
763
1204
|
throw new UserError(`Unsupported output content type: ${JSON.stringify(entry)}`);
|
|
@@ -768,7 +1209,11 @@ function getMessageItem(item) {
|
|
|
768
1209
|
id: item.id,
|
|
769
1210
|
role: 'system',
|
|
770
1211
|
content: item.content,
|
|
771
|
-
...
|
|
1212
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
|
|
1213
|
+
'id',
|
|
1214
|
+
'role',
|
|
1215
|
+
'content',
|
|
1216
|
+
]),
|
|
772
1217
|
};
|
|
773
1218
|
}
|
|
774
1219
|
if (item.role === 'user') {
|
|
@@ -777,14 +1222,22 @@ function getMessageItem(item) {
|
|
|
777
1222
|
id: item.id,
|
|
778
1223
|
role: 'user',
|
|
779
1224
|
content: item.content,
|
|
780
|
-
...
|
|
1225
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
|
|
1226
|
+
'id',
|
|
1227
|
+
'role',
|
|
1228
|
+
'content',
|
|
1229
|
+
]),
|
|
781
1230
|
};
|
|
782
1231
|
}
|
|
783
1232
|
return {
|
|
784
1233
|
id: item.id,
|
|
785
1234
|
role: 'user',
|
|
786
1235
|
content: item.content.map(getInputMessageContent),
|
|
787
|
-
...
|
|
1236
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
|
|
1237
|
+
'id',
|
|
1238
|
+
'role',
|
|
1239
|
+
'content',
|
|
1240
|
+
]),
|
|
788
1241
|
};
|
|
789
1242
|
}
|
|
790
1243
|
if (item.role === 'assistant') {
|
|
@@ -794,7 +1247,13 @@ function getMessageItem(item) {
|
|
|
794
1247
|
role: 'assistant',
|
|
795
1248
|
content: item.content.map(getOutputMessageContent),
|
|
796
1249
|
status: item.status,
|
|
797
|
-
...
|
|
1250
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
|
|
1251
|
+
'type',
|
|
1252
|
+
'id',
|
|
1253
|
+
'role',
|
|
1254
|
+
'content',
|
|
1255
|
+
'status',
|
|
1256
|
+
]),
|
|
798
1257
|
};
|
|
799
1258
|
return assistantMessage;
|
|
800
1259
|
}
|
|
@@ -841,6 +1300,52 @@ function getInputItems(input) {
|
|
|
841
1300
|
if (isMessageItem(item)) {
|
|
842
1301
|
return getMessageItem(item);
|
|
843
1302
|
}
|
|
1303
|
+
if (item.type === 'tool_search_call') {
|
|
1304
|
+
const status = normalizeToolSearchStatus(item.status);
|
|
1305
|
+
const callId = getToolSearchProviderCallId(item);
|
|
1306
|
+
const execution = getToolSearchExecution(item);
|
|
1307
|
+
const toolSearchCall = {
|
|
1308
|
+
type: 'tool_search_call',
|
|
1309
|
+
id: item.id,
|
|
1310
|
+
...(status !== null ? { status } : {}),
|
|
1311
|
+
arguments: item.arguments,
|
|
1312
|
+
...(callId ? { call_id: callId } : {}),
|
|
1313
|
+
...(execution ? { execution } : {}),
|
|
1314
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
|
|
1315
|
+
'type',
|
|
1316
|
+
'id',
|
|
1317
|
+
'status',
|
|
1318
|
+
'arguments',
|
|
1319
|
+
'call_id',
|
|
1320
|
+
'callId',
|
|
1321
|
+
'execution',
|
|
1322
|
+
]),
|
|
1323
|
+
};
|
|
1324
|
+
return toolSearchCall;
|
|
1325
|
+
}
|
|
1326
|
+
if (item.type === 'tool_search_output') {
|
|
1327
|
+
const status = normalizeToolSearchStatus(item.status);
|
|
1328
|
+
const callId = getToolSearchProviderCallId(item);
|
|
1329
|
+
const execution = getToolSearchExecution(item);
|
|
1330
|
+
const toolSearchOutput = {
|
|
1331
|
+
type: 'tool_search_output',
|
|
1332
|
+
id: item.id,
|
|
1333
|
+
...(status !== null ? { status } : {}),
|
|
1334
|
+
tools: item.tools.map((tool) => toOpenAIToolSearchOutputToolPayload(tool)),
|
|
1335
|
+
...(callId ? { call_id: callId } : {}),
|
|
1336
|
+
...(execution ? { execution } : {}),
|
|
1337
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
|
|
1338
|
+
'type',
|
|
1339
|
+
'id',
|
|
1340
|
+
'status',
|
|
1341
|
+
'tools',
|
|
1342
|
+
'call_id',
|
|
1343
|
+
'callId',
|
|
1344
|
+
'execution',
|
|
1345
|
+
]),
|
|
1346
|
+
};
|
|
1347
|
+
return toolSearchOutput;
|
|
1348
|
+
}
|
|
844
1349
|
if (item.type === 'function_call') {
|
|
845
1350
|
const entry = {
|
|
846
1351
|
id: item.id,
|
|
@@ -849,7 +1354,18 @@ function getInputItems(input) {
|
|
|
849
1354
|
call_id: item.callId,
|
|
850
1355
|
arguments: item.arguments,
|
|
851
1356
|
status: item.status,
|
|
852
|
-
...
|
|
1357
|
+
...(typeof item.namespace === 'string'
|
|
1358
|
+
? { namespace: item.namespace }
|
|
1359
|
+
: {}),
|
|
1360
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
|
|
1361
|
+
'id',
|
|
1362
|
+
'type',
|
|
1363
|
+
'name',
|
|
1364
|
+
'call_id',
|
|
1365
|
+
'arguments',
|
|
1366
|
+
'status',
|
|
1367
|
+
'namespace',
|
|
1368
|
+
]),
|
|
853
1369
|
};
|
|
854
1370
|
return entry;
|
|
855
1371
|
}
|
|
@@ -861,45 +1377,98 @@ function getInputItems(input) {
|
|
|
861
1377
|
call_id: item.callId,
|
|
862
1378
|
output: normalizedOutput,
|
|
863
1379
|
status: item.status,
|
|
864
|
-
...
|
|
1380
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
|
|
1381
|
+
'type',
|
|
1382
|
+
'id',
|
|
1383
|
+
'call_id',
|
|
1384
|
+
'output',
|
|
1385
|
+
'status',
|
|
1386
|
+
'namespace',
|
|
1387
|
+
]),
|
|
865
1388
|
};
|
|
866
1389
|
return entry;
|
|
867
1390
|
}
|
|
868
1391
|
if (item.type === 'reasoning') {
|
|
1392
|
+
const encryptedContent = getProviderDataField(item.providerData, [
|
|
1393
|
+
'encryptedContent',
|
|
1394
|
+
'encrypted_content',
|
|
1395
|
+
]);
|
|
869
1396
|
const entry = {
|
|
870
1397
|
id: item.id,
|
|
871
1398
|
type: 'reasoning',
|
|
872
1399
|
summary: item.content.map((content) => ({
|
|
873
1400
|
type: 'summary_text',
|
|
874
1401
|
text: content.text,
|
|
875
|
-
...
|
|
1402
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(content.providerData, ['type', 'text']),
|
|
876
1403
|
})),
|
|
877
|
-
|
|
878
|
-
|
|
1404
|
+
...(typeof encryptedContent === 'string'
|
|
1405
|
+
? { encrypted_content: encryptedContent }
|
|
1406
|
+
: {}),
|
|
1407
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
|
|
1408
|
+
'id',
|
|
1409
|
+
'type',
|
|
1410
|
+
'summary',
|
|
1411
|
+
'encrypted_content',
|
|
1412
|
+
]),
|
|
879
1413
|
};
|
|
880
1414
|
return entry;
|
|
881
1415
|
}
|
|
882
1416
|
if (item.type === 'computer_call') {
|
|
1417
|
+
const pendingSafetyChecks = getProviderDataField(item.providerData, ['pendingSafetyChecks', 'pending_safety_checks']);
|
|
1418
|
+
const normalizedPendingSafetyChecks = Array.isArray(pendingSafetyChecks) ? pendingSafetyChecks : [];
|
|
1419
|
+
const batchedActions = Array.isArray(item.actions)
|
|
1420
|
+
? (item
|
|
1421
|
+
.actions ?? [])
|
|
1422
|
+
: [];
|
|
1423
|
+
const actionPayload = batchedActions.length > 0
|
|
1424
|
+
? {
|
|
1425
|
+
action: item.action ?? batchedActions[0],
|
|
1426
|
+
actions: batchedActions,
|
|
1427
|
+
}
|
|
1428
|
+
: item.action
|
|
1429
|
+
? { action: item.action }
|
|
1430
|
+
: {};
|
|
883
1431
|
const entry = {
|
|
884
1432
|
type: 'computer_call',
|
|
885
1433
|
call_id: item.callId,
|
|
886
1434
|
id: item.id,
|
|
887
|
-
action: item.action,
|
|
888
1435
|
status: item.status,
|
|
889
|
-
pending_safety_checks:
|
|
890
|
-
...
|
|
1436
|
+
pending_safety_checks: normalizedPendingSafetyChecks,
|
|
1437
|
+
...actionPayload,
|
|
1438
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
|
|
1439
|
+
'type',
|
|
1440
|
+
'call_id',
|
|
1441
|
+
'id',
|
|
1442
|
+
'action',
|
|
1443
|
+
'actions',
|
|
1444
|
+
'status',
|
|
1445
|
+
'pending_safety_checks',
|
|
1446
|
+
]),
|
|
891
1447
|
};
|
|
892
1448
|
return entry;
|
|
893
1449
|
}
|
|
894
1450
|
if (item.type === 'computer_call_result') {
|
|
1451
|
+
const acknowledgedSafetyChecks = getProviderDataField(item.providerData, [
|
|
1452
|
+
'acknowledgedSafetyChecks',
|
|
1453
|
+
'acknowledged_safety_checks',
|
|
1454
|
+
]);
|
|
895
1455
|
const entry = {
|
|
896
1456
|
type: 'computer_call_output',
|
|
897
1457
|
id: item.id,
|
|
898
1458
|
call_id: item.callId,
|
|
899
1459
|
output: buildResponseOutput(item),
|
|
900
1460
|
status: item.providerData?.status,
|
|
901
|
-
acknowledged_safety_checks:
|
|
902
|
-
|
|
1461
|
+
acknowledged_safety_checks: Array.isArray(acknowledgedSafetyChecks)
|
|
1462
|
+
? acknowledgedSafetyChecks
|
|
1463
|
+
: [],
|
|
1464
|
+
...getSnakeCasedProviderDataWithoutReservedKeys(item.providerData, [
|
|
1465
|
+
'type',
|
|
1466
|
+
'id',
|
|
1467
|
+
'call_id',
|
|
1468
|
+
'output',
|
|
1469
|
+
'status',
|
|
1470
|
+
'acknowledged_safety_checks',
|
|
1471
|
+
]),
|
|
903
1472
|
};
|
|
904
1473
|
return entry;
|
|
905
1474
|
}
|
|
@@ -1152,6 +1721,30 @@ function convertToOutputItem(items) {
|
|
|
1152
1721
|
providerData,
|
|
1153
1722
|
};
|
|
1154
1723
|
}
|
|
1724
|
+
else if (item.type === 'tool_search_call') {
|
|
1725
|
+
const { id, type: _type, status, arguments: args, ...providerData } = item;
|
|
1726
|
+
const output = {
|
|
1727
|
+
type: 'tool_search_call',
|
|
1728
|
+
id,
|
|
1729
|
+
status,
|
|
1730
|
+
arguments: args,
|
|
1731
|
+
providerData,
|
|
1732
|
+
};
|
|
1733
|
+
return output;
|
|
1734
|
+
}
|
|
1735
|
+
else if (item.type === 'tool_search_output') {
|
|
1736
|
+
const { id, type: _type, status, tools, ...providerData } = item;
|
|
1737
|
+
const output = {
|
|
1738
|
+
type: 'tool_search_output',
|
|
1739
|
+
id,
|
|
1740
|
+
status,
|
|
1741
|
+
tools: Array.isArray(tools)
|
|
1742
|
+
? tools.map((tool) => fromOpenAIToolSearchOutputToolPayload(tool))
|
|
1743
|
+
: [],
|
|
1744
|
+
providerData,
|
|
1745
|
+
};
|
|
1746
|
+
return output;
|
|
1747
|
+
}
|
|
1155
1748
|
else if (item.type === 'file_search_call' ||
|
|
1156
1749
|
item.type === 'web_search_call' ||
|
|
1157
1750
|
item.type === 'image_generation_call' ||
|
|
@@ -1174,12 +1767,14 @@ function convertToOutputItem(items) {
|
|
|
1174
1767
|
return output;
|
|
1175
1768
|
}
|
|
1176
1769
|
else if (item.type === 'function_call') {
|
|
1177
|
-
const
|
|
1770
|
+
const functionCall = item;
|
|
1771
|
+
const { call_id, name, namespace, status, arguments: args, ...providerData } = functionCall;
|
|
1178
1772
|
const output = {
|
|
1179
1773
|
type: 'function_call',
|
|
1180
|
-
id:
|
|
1774
|
+
id: functionCall.id,
|
|
1181
1775
|
callId: call_id,
|
|
1182
1776
|
name,
|
|
1777
|
+
...(typeof namespace === 'string' ? { namespace } : {}),
|
|
1183
1778
|
status,
|
|
1184
1779
|
arguments: args,
|
|
1185
1780
|
providerData,
|
|
@@ -1187,12 +1782,13 @@ function convertToOutputItem(items) {
|
|
|
1187
1782
|
return output;
|
|
1188
1783
|
}
|
|
1189
1784
|
else if (item.type === 'function_call_output') {
|
|
1190
|
-
const { call_id, status, output: rawOutput, name: toolName, function_name: functionName, ...providerData } = item;
|
|
1785
|
+
const { call_id, status, output: rawOutput, name: toolName, function_name: functionName, namespace, ...providerData } = item;
|
|
1191
1786
|
const output = {
|
|
1192
1787
|
type: 'function_call_result',
|
|
1193
1788
|
id: item.id,
|
|
1194
1789
|
callId: call_id,
|
|
1195
1790
|
name: toolName ?? functionName ?? call_id,
|
|
1791
|
+
...(typeof namespace === 'string' ? { namespace } : {}),
|
|
1196
1792
|
status: status ?? 'completed',
|
|
1197
1793
|
output: convertFunctionCallOutputToProtocol(rawOutput),
|
|
1198
1794
|
providerData,
|
|
@@ -1200,13 +1796,18 @@ function convertToOutputItem(items) {
|
|
|
1200
1796
|
return output;
|
|
1201
1797
|
}
|
|
1202
1798
|
else if (item.type === 'computer_call') {
|
|
1203
|
-
const { call_id, status, action, ...providerData } = item;
|
|
1799
|
+
const { call_id, status, action, actions, ...providerData } = item;
|
|
1800
|
+
const normalizedActions = Array.isArray(actions) && actions.length > 0 ? actions : undefined;
|
|
1801
|
+
if (!normalizedActions && !action) {
|
|
1802
|
+
throw new UserError(`Unsupported computer call item without an action or actions: ${JSON.stringify(item)}`);
|
|
1803
|
+
}
|
|
1204
1804
|
const output = {
|
|
1205
1805
|
type: 'computer_call',
|
|
1206
1806
|
id: item.id,
|
|
1207
1807
|
callId: call_id,
|
|
1208
1808
|
status,
|
|
1209
|
-
action,
|
|
1809
|
+
action: action ?? normalizedActions?.[0],
|
|
1810
|
+
...(normalizedActions ? { actions: normalizedActions } : {}),
|
|
1210
1811
|
providerData,
|
|
1211
1812
|
};
|
|
1212
1813
|
return output;
|
|
@@ -1451,15 +2052,22 @@ export class OpenAIResponsesModel {
|
|
|
1451
2052
|
this._client = client;
|
|
1452
2053
|
this._model = model;
|
|
1453
2054
|
}
|
|
2055
|
+
getRetryAdvice(args) {
|
|
2056
|
+
return getOpenAIRetryAdvice(args);
|
|
2057
|
+
}
|
|
1454
2058
|
async _fetchResponse(request, stream) {
|
|
1455
2059
|
const builtRequest = this._buildResponsesCreateRequest(request, stream);
|
|
1456
|
-
const
|
|
2060
|
+
const requestOptions = {
|
|
1457
2061
|
headers: builtRequest.sdkRequestHeaders,
|
|
1458
2062
|
signal: builtRequest.signal,
|
|
1459
2063
|
...(builtRequest.transportExtraQuery
|
|
1460
2064
|
? { query: builtRequest.transportExtraQuery }
|
|
1461
2065
|
: {}),
|
|
1462
|
-
}
|
|
2066
|
+
};
|
|
2067
|
+
if (request._internal?.runnerManagedRetry === true) {
|
|
2068
|
+
requestOptions.maxRetries = 0;
|
|
2069
|
+
}
|
|
2070
|
+
const responsePromise = this._client.responses.create(builtRequest.requestData, requestOptions);
|
|
1463
2071
|
let response;
|
|
1464
2072
|
if (stream) {
|
|
1465
2073
|
const withResponse = responsePromise
|
|
@@ -1486,9 +2094,30 @@ export class OpenAIResponsesModel {
|
|
|
1486
2094
|
}
|
|
1487
2095
|
_buildResponsesCreateRequest(request, stream) {
|
|
1488
2096
|
const input = getInputItems(request.input);
|
|
1489
|
-
const
|
|
1490
|
-
|
|
2097
|
+
const prompt = getPrompt(request.prompt);
|
|
2098
|
+
// When a prompt template already declares a model, skip sending the agent's default model.
|
|
2099
|
+
// If the caller explicitly requests an override, include the resolved model name in the request.
|
|
2100
|
+
const shouldSendModel = !request.prompt || request.overridePromptModel === true;
|
|
2101
|
+
const effectiveRequestModel = shouldSendModel ? this._model : undefined;
|
|
1491
2102
|
const { providerData: providerDataWithoutTransport, overrides: transportOverrides, } = splitResponsesTransportOverrides(request.modelSettings.providerData);
|
|
2103
|
+
const { tools, include } = getTools(request.tools, request.handoffs, {
|
|
2104
|
+
model: effectiveRequestModel,
|
|
2105
|
+
toolChoice: request.modelSettings.toolChoice,
|
|
2106
|
+
});
|
|
2107
|
+
const toolChoiceValidationTools = [
|
|
2108
|
+
...tools,
|
|
2109
|
+
...getExtraBodyToolsForToolChoiceValidation(transportOverrides.extraBody),
|
|
2110
|
+
];
|
|
2111
|
+
const allowPromptSuppliedTools = Boolean(request.prompt) &&
|
|
2112
|
+
!(request.toolsExplicitlyProvided === true && tools.length === 0);
|
|
2113
|
+
const toolChoice = getToolChoice(request.modelSettings.toolChoice, {
|
|
2114
|
+
tools: toolChoiceValidationTools,
|
|
2115
|
+
model: effectiveRequestModel,
|
|
2116
|
+
allowPromptSuppliedComputerTool: allowPromptSuppliedTools,
|
|
2117
|
+
});
|
|
2118
|
+
assertSupportedToolChoice(toolChoice, toolChoiceValidationTools, {
|
|
2119
|
+
allowPromptSuppliedTools,
|
|
2120
|
+
});
|
|
1492
2121
|
const { text, ...restOfProviderData } = providerDataWithoutTransport;
|
|
1493
2122
|
if (request.modelSettings.reasoning) {
|
|
1494
2123
|
// Merge top-level reasoning settings with provider data.
|
|
@@ -1503,25 +2132,19 @@ export class OpenAIResponsesModel {
|
|
|
1503
2132
|
mergedText = { ...request.modelSettings.text, ...text };
|
|
1504
2133
|
}
|
|
1505
2134
|
const responseFormat = getResponseFormat(request.outputType, mergedText);
|
|
1506
|
-
const prompt = getPrompt(request.prompt);
|
|
1507
2135
|
let parallelToolCalls = undefined;
|
|
1508
2136
|
if (typeof request.modelSettings.parallelToolCalls === 'boolean') {
|
|
1509
|
-
if (request.modelSettings.parallelToolCalls && tools.length === 0) {
|
|
1510
|
-
throw new Error('Parallel tool calls are not supported without tools');
|
|
1511
|
-
}
|
|
1512
2137
|
parallelToolCalls = request.modelSettings.parallelToolCalls;
|
|
1513
2138
|
}
|
|
1514
|
-
// When a prompt template already declares a model, skip sending the agent's default model.
|
|
1515
|
-
// If the caller explicitly requests an override, include the resolved model name in the request.
|
|
1516
|
-
const shouldSendModel = !request.prompt || request.overridePromptModel === true;
|
|
1517
2139
|
const shouldSendTools = tools.length > 0 ||
|
|
1518
2140
|
request.toolsExplicitlyProvided === true ||
|
|
1519
2141
|
!request.prompt;
|
|
1520
|
-
const
|
|
1521
|
-
|
|
1522
|
-
|
|
2142
|
+
const compatibleToolChoice = getCompatibleToolChoice(toolChoice, toolChoiceValidationTools, {
|
|
2143
|
+
allowPromptSuppliedTools,
|
|
2144
|
+
});
|
|
2145
|
+
const shouldOmitToolChoice = typeof compatibleToolChoice === 'undefined';
|
|
1523
2146
|
let requestData = {
|
|
1524
|
-
...(
|
|
2147
|
+
...(effectiveRequestModel ? { model: effectiveRequestModel } : {}),
|
|
1525
2148
|
instructions: normalizeInstructions(request.systemInstructions),
|
|
1526
2149
|
input,
|
|
1527
2150
|
include,
|
|
@@ -1538,7 +2161,7 @@ export class OpenAIResponsesModel {
|
|
|
1538
2161
|
truncation: request.modelSettings.truncation,
|
|
1539
2162
|
max_output_tokens: request.modelSettings.maxTokens,
|
|
1540
2163
|
...(!shouldOmitToolChoice
|
|
1541
|
-
? { tool_choice:
|
|
2164
|
+
? { tool_choice: compatibleToolChoice }
|
|
1542
2165
|
: {}),
|
|
1543
2166
|
parallel_tool_calls: parallelToolCalls,
|
|
1544
2167
|
stream,
|
|
@@ -1729,6 +2352,23 @@ export class OpenAIResponsesWSModel extends OpenAIResponsesModel {
|
|
|
1729
2352
|
this.#websocketBaseURL = options.websocketBaseURL;
|
|
1730
2353
|
this.#reuseConnection = options.reuseConnection ?? true;
|
|
1731
2354
|
}
|
|
2355
|
+
getRetryAdvice(args) {
|
|
2356
|
+
if (isNeverSentWebSocketError(args.error)) {
|
|
2357
|
+
return {
|
|
2358
|
+
suggested: true,
|
|
2359
|
+
replaySafety: 'safe',
|
|
2360
|
+
reason: args.error instanceof Error ? args.error.message : undefined,
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
if (isAmbiguousWebSocketReplayError(args.error)) {
|
|
2364
|
+
return {
|
|
2365
|
+
suggested: false,
|
|
2366
|
+
replaySafety: 'unsafe',
|
|
2367
|
+
reason: args.error instanceof Error ? args.error.message : undefined,
|
|
2368
|
+
};
|
|
2369
|
+
}
|
|
2370
|
+
return super.getRetryAdvice(args);
|
|
2371
|
+
}
|
|
1732
2372
|
async _fetchResponse(request, stream) {
|
|
1733
2373
|
// The websocket transport always uses streamed Responses events, then callers either
|
|
1734
2374
|
// consume the stream directly or collapse it into the final terminal response.
|
|
@@ -1737,12 +2377,22 @@ export class OpenAIResponsesWSModel extends OpenAIResponsesModel {
|
|
|
1737
2377
|
return this.#iterWebSocketResponseEvents(builtRequest);
|
|
1738
2378
|
}
|
|
1739
2379
|
let finalResponse;
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
2380
|
+
let receivedAnyEvent = false;
|
|
2381
|
+
try {
|
|
2382
|
+
for await (const event of this.#iterWebSocketResponseEvents(builtRequest)) {
|
|
2383
|
+
receivedAnyEvent = true;
|
|
2384
|
+
const eventType = event.type;
|
|
2385
|
+
if (isTerminalResponsesStreamEventType(eventType)) {
|
|
2386
|
+
finalResponse = event
|
|
2387
|
+
.response;
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
catch (error) {
|
|
2392
|
+
if (receivedAnyEvent && error instanceof Error) {
|
|
2393
|
+
error.unsafeToReplay = true;
|
|
1745
2394
|
}
|
|
2395
|
+
throw error;
|
|
1746
2396
|
}
|
|
1747
2397
|
if (!finalResponse) {
|
|
1748
2398
|
throw new Error('Responses websocket stream ended without a terminal response event.');
|