@zhivex-ai/openai 0.7.2 → 0.8.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/README.md +221 -11
- package/dist/index.d.ts +99 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +733 -194
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { toJSONSchema, z } from "zod";
|
|
2
2
|
import { CallbackRealtimeSession, ConfigurationError, ProviderHTTPError, openWebSocketConnection, hostedTool, providerDataPart, UnsupportedFeatureError, createProviderAdapter, encodeAudioFrame, encodeMediaFrame, isCallableToolDefinition, isHostedToolDefinition, normalizeFinishReason, streamSSE, toToolSet, toolResultPayload, withRetry, withTimeoutSignal } from "@zhivex-ai/core";
|
|
3
3
|
const openAIResponsesToolMetadataKey = "openai.responses_tool_type";
|
|
4
|
+
const openAIResponsesFunctionConfigMetadataKey = "openai.responses_function_config";
|
|
5
|
+
const openAIResponsesFunctionConfig = (tool) => {
|
|
6
|
+
const config = tool.metadata?.[openAIResponsesFunctionConfigMetadataKey];
|
|
7
|
+
return config && typeof config === "object" && !Array.isArray(config) ? config : undefined;
|
|
8
|
+
};
|
|
4
9
|
const openAILocalResponsesToolType = (tool) => {
|
|
5
10
|
const type = tool.metadata?.[openAIResponsesToolMetadataKey];
|
|
6
11
|
return typeof type === "string" ? type : undefined;
|
|
@@ -53,30 +58,51 @@ const capabilities = {
|
|
|
53
58
|
}
|
|
54
59
|
};
|
|
55
60
|
const normalizeModelId = (modelId) => modelId.trim().toLowerCase();
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
};
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
61
|
+
const isOpenAIGpt56Model = (modelId) => /^gpt-5\.6(?:$|-(?:sol|terra|luna)(?:-|$)|-\d{4}-\d{2}-\d{2})/.test(normalizeModelId(modelId));
|
|
62
|
+
const isOpenAIGpt55BaseModel = (modelId) => /^gpt-5\.5(?:$|-\d{4}-\d{2}-\d{2})/.test(normalizeModelId(modelId));
|
|
63
|
+
const isOpenAIGpt55ProModel = (modelId) => /^gpt-5\.5-pro(?:$|-\d{4}-\d{2}-\d{2})/.test(normalizeModelId(modelId));
|
|
64
|
+
const isOpenAIGpt54BaseModel = (modelId) => /^gpt-5\.4(?:$|-\d{4}-\d{2}-\d{2})/.test(normalizeModelId(modelId));
|
|
65
|
+
const isOpenAIGpt54MiniModel = (modelId) => /^gpt-5\.4-mini(?:$|-\d{4}-\d{2}-\d{2})/.test(normalizeModelId(modelId));
|
|
66
|
+
const isOpenAIGpt54NanoModel = (modelId) => /^gpt-5\.4-nano(?:$|-\d{4}-\d{2}-\d{2})/.test(normalizeModelId(modelId));
|
|
67
|
+
const isOpenAIGpt54ProModel = (modelId) => /^gpt-5\.4-pro(?:$|-\d{4}-\d{2}-\d{2})/.test(normalizeModelId(modelId));
|
|
68
|
+
const supportsOpenAIToolSearch = (modelId) => isOpenAIGpt56Model(modelId) || isOpenAIGpt55BaseModel(modelId) || isOpenAIGpt54BaseModel(modelId) || isOpenAIGpt54MiniModel(modelId);
|
|
69
|
+
const supportsOpenAIComputerUse = (modelId) => isOpenAIGpt56Model(modelId) || isOpenAIGpt55BaseModel(modelId) || isOpenAIGpt54BaseModel(modelId) || isOpenAIGpt54MiniModel(modelId);
|
|
70
|
+
const supportsOpenAIShell = (modelId) => isOpenAIGpt56Model(modelId) ||
|
|
71
|
+
isOpenAIGpt55BaseModel(modelId) ||
|
|
72
|
+
isOpenAIGpt55ProModel(modelId) ||
|
|
73
|
+
isOpenAIGpt54BaseModel(modelId) ||
|
|
74
|
+
isOpenAIGpt54MiniModel(modelId) ||
|
|
75
|
+
isOpenAIGpt54NanoModel(modelId) ||
|
|
76
|
+
isOpenAIGpt54ProModel(modelId);
|
|
77
|
+
const supportsOpenAIApplyPatchAndSkills = (modelId) => isOpenAIGpt56Model(modelId) ||
|
|
78
|
+
isOpenAIGpt55BaseModel(modelId) ||
|
|
79
|
+
isOpenAIGpt54BaseModel(modelId) ||
|
|
80
|
+
isOpenAIGpt54MiniModel(modelId) ||
|
|
81
|
+
isOpenAIGpt54NanoModel(modelId);
|
|
65
82
|
const supportsOpenAIChatAudio = (modelId) => {
|
|
66
83
|
const normalized = normalizeModelId(modelId);
|
|
67
84
|
return /^(?:gpt-audio(?:-|$)|gpt-4o(?:-mini)?-audio-preview(?:-|$))/.test(normalized);
|
|
68
85
|
};
|
|
69
86
|
const modelCapabilities = (modelId) => ({
|
|
70
87
|
...capabilities,
|
|
88
|
+
explicitPromptCaching: isOpenAIGpt56Model(modelId),
|
|
89
|
+
files: isOpenAIGpt56Model(modelId),
|
|
90
|
+
reasoningEfforts: isOpenAIGpt56Model(modelId)
|
|
91
|
+
? ["none", "low", "medium", "high", "xhigh", "max"]
|
|
92
|
+
: undefined,
|
|
93
|
+
reasoningModes: isOpenAIGpt56Model(modelId) ? ["standard", "pro"] : undefined,
|
|
94
|
+
reasoningContexts: isOpenAIGpt56Model(modelId) ? ["auto", "current_turn", "all_turns"] : undefined,
|
|
71
95
|
audioInput: supportsOpenAIChatAudio(modelId),
|
|
72
96
|
audioOutput: supportsOpenAIChatAudio(modelId),
|
|
73
97
|
agentCapabilities: {
|
|
74
98
|
...capabilities.agentCapabilities,
|
|
75
99
|
computerUse: supportsOpenAIComputerUse(modelId),
|
|
76
|
-
shell:
|
|
77
|
-
applyPatch:
|
|
78
|
-
skills:
|
|
79
|
-
toolSearch: supportsOpenAIToolSearch(modelId)
|
|
100
|
+
shell: supportsOpenAIShell(modelId),
|
|
101
|
+
applyPatch: supportsOpenAIApplyPatchAndSkills(modelId),
|
|
102
|
+
skills: supportsOpenAIApplyPatchAndSkills(modelId),
|
|
103
|
+
toolSearch: supportsOpenAIToolSearch(modelId),
|
|
104
|
+
programmaticToolCalling: isOpenAIGpt56Model(modelId),
|
|
105
|
+
multiAgent: isOpenAIGpt56Model(modelId)
|
|
80
106
|
}
|
|
81
107
|
});
|
|
82
108
|
const transcriptionCapabilities = {
|
|
@@ -152,6 +178,44 @@ const jsonHeaders = (apiKey) => ({
|
|
|
152
178
|
"content-type": "application/json",
|
|
153
179
|
authorization: `Bearer ${apiKey}`
|
|
154
180
|
});
|
|
181
|
+
const resolveOpenAILanguageRequestOptions = (providerOptions, apiKey) => {
|
|
182
|
+
const bodyOptions = { ...(providerOptions ?? {}) };
|
|
183
|
+
const apiMode = bodyOptions.apiMode ?? "auto";
|
|
184
|
+
const customHeaders = { ...(bodyOptions.headers ?? {}) };
|
|
185
|
+
const betaValues = new Set(bodyOptions.betas ?? []);
|
|
186
|
+
const multiAgentEnabled = bodyOptions.multi_agent?.enabled === true;
|
|
187
|
+
if (multiAgentEnabled) {
|
|
188
|
+
betaValues.add("responses_multi_agent=v1");
|
|
189
|
+
}
|
|
190
|
+
const existingBetaHeaderKey = Object.keys(customHeaders).find((key) => key.toLowerCase() === "openai-beta");
|
|
191
|
+
if (existingBetaHeaderKey) {
|
|
192
|
+
for (const value of customHeaders[existingBetaHeaderKey]?.split(",") ?? []) {
|
|
193
|
+
if (value.trim()) {
|
|
194
|
+
betaValues.add(value.trim());
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
delete customHeaders[existingBetaHeaderKey];
|
|
198
|
+
}
|
|
199
|
+
delete bodyOptions.apiMode;
|
|
200
|
+
delete bodyOptions.headers;
|
|
201
|
+
delete bodyOptions.betas;
|
|
202
|
+
return {
|
|
203
|
+
apiMode,
|
|
204
|
+
bodyOptions,
|
|
205
|
+
multiAgentEnabled,
|
|
206
|
+
headers: {
|
|
207
|
+
...jsonHeaders(apiKey),
|
|
208
|
+
...customHeaders,
|
|
209
|
+
...(betaValues.size ? { "OpenAI-Beta": [...betaValues].join(",") } : {})
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
const openAIResponsesBodyOptions = (options, modelId) => options.store === false && isOpenAIGpt56Model(modelId)
|
|
214
|
+
? {
|
|
215
|
+
...options,
|
|
216
|
+
include: [...new Set([...(options.include ?? []), "reasoning.encrypted_content"])]
|
|
217
|
+
}
|
|
218
|
+
: options;
|
|
155
219
|
const getRequestOptions = (input) => withTimeoutSignal(input);
|
|
156
220
|
const toUint8Array = (data) => {
|
|
157
221
|
if (data instanceof Uint8Array) {
|
|
@@ -200,32 +264,84 @@ const parseJson = async (response) => {
|
|
|
200
264
|
}
|
|
201
265
|
return response.json();
|
|
202
266
|
};
|
|
267
|
+
const isOpenAIPromptCacheBreakpointPart = (part) => part.type === "provider-data" &&
|
|
268
|
+
part.provider === "openai" &&
|
|
269
|
+
part.data !== null &&
|
|
270
|
+
typeof part.data === "object" &&
|
|
271
|
+
part.data.type === "prompt_cache_breakpoint";
|
|
272
|
+
const promptCacheBreakpointForContentPart = (part) => {
|
|
273
|
+
const metadata = part
|
|
274
|
+
.providerMetadata;
|
|
275
|
+
const openAIMetadata = metadata?.openai && typeof metadata.openai === "object" && !Array.isArray(metadata.openai)
|
|
276
|
+
? metadata.openai
|
|
277
|
+
: metadata;
|
|
278
|
+
const breakpoint = openAIMetadata?.prompt_cache_breakpoint;
|
|
279
|
+
return breakpoint && typeof breakpoint === "object" && !Array.isArray(breakpoint)
|
|
280
|
+
? { prompt_cache_breakpoint: { mode: "explicit" } }
|
|
281
|
+
: {};
|
|
282
|
+
};
|
|
283
|
+
const imageDetailForPart = (part) => {
|
|
284
|
+
const metadata = part.providerMetadata;
|
|
285
|
+
const detail = metadata?.openaiDetail ??
|
|
286
|
+
(metadata?.openai && typeof metadata.openai === "object" && !Array.isArray(metadata.openai)
|
|
287
|
+
? metadata.openai.detail
|
|
288
|
+
: undefined);
|
|
289
|
+
return detail === "auto" || detail === "low" || detail === "high" || detail === "original" ? detail : undefined;
|
|
290
|
+
};
|
|
291
|
+
const markLastContentBlockForPromptCaching = (content) => {
|
|
292
|
+
const last = content.at(-1);
|
|
293
|
+
if (!last) {
|
|
294
|
+
throw new ConfigurationError("OpenAI prompt cache breakpoints must follow a cacheable content part.");
|
|
295
|
+
}
|
|
296
|
+
last.prompt_cache_breakpoint = { mode: "explicit" };
|
|
297
|
+
};
|
|
203
298
|
const mapContentParts = (message) => {
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
299
|
+
const hasRichContent = message.parts.some((part) => part.type === "image" ||
|
|
300
|
+
part.type === "audio" ||
|
|
301
|
+
part.type === "file" ||
|
|
302
|
+
Object.keys(promptCacheBreakpointForContentPart(part)).length > 0 ||
|
|
303
|
+
isOpenAIPromptCacheBreakpointPart(part));
|
|
304
|
+
if (!hasRichContent) {
|
|
305
|
+
return message.parts
|
|
306
|
+
.filter((part) => part.type === "text")
|
|
307
|
+
.map((part) => part.text)
|
|
308
|
+
.join("");
|
|
309
|
+
}
|
|
310
|
+
const content = [];
|
|
311
|
+
for (const part of message.parts) {
|
|
312
|
+
if (part.type === "text") {
|
|
313
|
+
content.push({ type: "text", text: part.text, ...promptCacheBreakpointForContentPart(part) });
|
|
314
|
+
}
|
|
315
|
+
else if (part.type === "image") {
|
|
316
|
+
const detail = imageDetailForPart(part);
|
|
317
|
+
content.push({
|
|
318
|
+
type: "image_url",
|
|
319
|
+
image_url: { url: part.image, ...(detail ? { detail } : {}) },
|
|
320
|
+
...promptCacheBreakpointForContentPart(part)
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
else if (part.type === "file") {
|
|
324
|
+
content.push({
|
|
325
|
+
type: "file",
|
|
326
|
+
file: { file_id: part.data },
|
|
327
|
+
...promptCacheBreakpointForContentPart(part)
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
else if (part.type === "audio") {
|
|
331
|
+
content.push({
|
|
332
|
+
type: "input_audio",
|
|
333
|
+
input_audio: {
|
|
334
|
+
data: toBase64(part.data),
|
|
335
|
+
format: inferOpenAIAudioFormat(part.mediaType, part.format)
|
|
336
|
+
},
|
|
337
|
+
...promptCacheBreakpointForContentPart(part)
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
else if (isOpenAIPromptCacheBreakpointPart(part)) {
|
|
341
|
+
markLastContentBlockForPromptCaching(content);
|
|
342
|
+
}
|
|
209
343
|
}
|
|
210
|
-
return
|
|
211
|
-
...textParts.map((part) => ({
|
|
212
|
-
type: "text",
|
|
213
|
-
text: part.text
|
|
214
|
-
})),
|
|
215
|
-
...imageParts.map((part) => ({
|
|
216
|
-
type: "image_url",
|
|
217
|
-
image_url: {
|
|
218
|
-
url: part.image
|
|
219
|
-
}
|
|
220
|
-
})),
|
|
221
|
-
...audioParts.map((part) => ({
|
|
222
|
-
type: "input_audio",
|
|
223
|
-
input_audio: {
|
|
224
|
-
data: toBase64(part.data),
|
|
225
|
-
format: inferOpenAIAudioFormat(part.mediaType, part.format)
|
|
226
|
-
}
|
|
227
|
-
}))
|
|
228
|
-
];
|
|
344
|
+
return content;
|
|
229
345
|
};
|
|
230
346
|
const mapMessages = (messages) => messages.map((message) => {
|
|
231
347
|
if (message.role === "tool") {
|
|
@@ -258,6 +374,13 @@ const mapMessages = (messages) => messages.map((message) => {
|
|
|
258
374
|
return payload;
|
|
259
375
|
});
|
|
260
376
|
const hasResponsesOnlyTools = (tools) => Object.values(tools ?? {}).some((tool) => isHostedToolDefinition(tool) || (isCallableToolDefinition(tool) && openAILocalResponsesToolType(tool)));
|
|
377
|
+
const hasProgrammaticToolCalling = (tools) => Object.values(tools ?? {}).some((tool) => isHostedToolDefinition(tool)
|
|
378
|
+
? tool.type === "programmatic_tool_calling"
|
|
379
|
+
: openAILocalResponsesToolType(tool) === "programmatic_tool_calling");
|
|
380
|
+
const localResponsesTools = (tools) => new Map(Object.values(tools ?? {})
|
|
381
|
+
.filter(isCallableToolDefinition)
|
|
382
|
+
.map((tool) => [openAILocalResponsesToolType(tool), tool.name])
|
|
383
|
+
.filter((entry) => Boolean(entry[0])));
|
|
261
384
|
const assertResponsesToolsSupported = (modelId, tools) => {
|
|
262
385
|
const currentCapabilities = modelCapabilities(modelId).agentCapabilities;
|
|
263
386
|
for (const definition of Object.values(tools ?? {})) {
|
|
@@ -268,9 +391,26 @@ const assertResponsesToolsSupported = (modelId, tools) => {
|
|
|
268
391
|
if (type === "computer_use_preview" && !currentCapabilities?.computerUse) {
|
|
269
392
|
throw new UnsupportedFeatureError(`Provider "openai" model "${modelId}" does not support the Responses computer_use tool.`);
|
|
270
393
|
}
|
|
271
|
-
if (
|
|
394
|
+
if (type === "computer" && !currentCapabilities?.computerUse) {
|
|
395
|
+
throw new UnsupportedFeatureError(`Provider "openai" model "${modelId}" does not support the Responses computer tool.`);
|
|
396
|
+
}
|
|
397
|
+
if (type === "shell" && !supportsOpenAIShell(modelId)) {
|
|
272
398
|
throw new UnsupportedFeatureError(`Provider "openai" model "${modelId}" does not support the Responses ${type} tool.`);
|
|
273
399
|
}
|
|
400
|
+
if (type === "apply_patch" && !supportsOpenAIApplyPatchAndSkills(modelId)) {
|
|
401
|
+
throw new UnsupportedFeatureError(`Provider "openai" model "${modelId}" does not support the Responses ${type} tool.`);
|
|
402
|
+
}
|
|
403
|
+
if (type === "skill" && !currentCapabilities?.skills) {
|
|
404
|
+
throw new UnsupportedFeatureError(`Provider "openai" model "${modelId}" does not support the Responses skills tool.`);
|
|
405
|
+
}
|
|
406
|
+
if (type === "programmatic_tool_calling" && !isOpenAIGpt56Model(modelId)) {
|
|
407
|
+
throw new UnsupportedFeatureError(`Provider "openai" model "${modelId}" does not support Programmatic Tool Calling.`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
const assertOpenAIResponsesOptionsSupported = (modelId, options) => {
|
|
412
|
+
if (options.multi_agent?.enabled && !isOpenAIGpt56Model(modelId)) {
|
|
413
|
+
throw new UnsupportedFeatureError(`Provider "openai" model "${modelId}" does not support Multi-agent.`);
|
|
274
414
|
}
|
|
275
415
|
};
|
|
276
416
|
const normalizeWebSearchConfig = (config = {}) => ({
|
|
@@ -313,7 +453,8 @@ const mapResponsesTools = (input) => input
|
|
|
313
453
|
type: "function",
|
|
314
454
|
name: tool.name,
|
|
315
455
|
description: tool.description,
|
|
316
|
-
parameters: toJSONSchema(tool.schema)
|
|
456
|
+
parameters: toJSONSchema(tool.schema),
|
|
457
|
+
...openAIResponsesFunctionConfig(tool)
|
|
317
458
|
};
|
|
318
459
|
}
|
|
319
460
|
if (tool.provider && tool.provider !== "openai") {
|
|
@@ -339,6 +480,15 @@ const mapToolChoice = (toolChoice) => {
|
|
|
339
480
|
}
|
|
340
481
|
};
|
|
341
482
|
};
|
|
483
|
+
const mapResponsesToolChoice = (toolChoice) => {
|
|
484
|
+
if (!toolChoice || typeof toolChoice === "string") {
|
|
485
|
+
return toolChoice;
|
|
486
|
+
}
|
|
487
|
+
return {
|
|
488
|
+
type: "function",
|
|
489
|
+
name: toolChoice.toolName
|
|
490
|
+
};
|
|
491
|
+
};
|
|
342
492
|
const mapRealtimeProviderOptions = (providerOptions) => {
|
|
343
493
|
if (!providerOptions) {
|
|
344
494
|
return {};
|
|
@@ -592,18 +742,45 @@ const mapResponsesStructuredOutput = (input) => {
|
|
|
592
742
|
}
|
|
593
743
|
};
|
|
594
744
|
};
|
|
595
|
-
const
|
|
745
|
+
const getOpenAIReasoning = (input) => {
|
|
596
746
|
if (!input.reasoning) {
|
|
597
|
-
return
|
|
747
|
+
return undefined;
|
|
598
748
|
}
|
|
599
749
|
if (input.reasoning.budgetTokens !== undefined) {
|
|
600
750
|
throw new UnsupportedFeatureError('Provider "openai" does not support "reasoning.budgetTokens".');
|
|
601
751
|
}
|
|
752
|
+
return input.reasoning;
|
|
753
|
+
};
|
|
754
|
+
const mapChatReasoning = (input) => {
|
|
755
|
+
const reasoning = getOpenAIReasoning(input);
|
|
756
|
+
if (!reasoning) {
|
|
757
|
+
return {};
|
|
758
|
+
}
|
|
759
|
+
if (reasoning.mode !== undefined || reasoning.context !== undefined) {
|
|
760
|
+
throw new UnsupportedFeatureError('Provider "openai" only supports "reasoning.mode" and "reasoning.context" through the Responses API.');
|
|
761
|
+
}
|
|
602
762
|
return {
|
|
603
|
-
reasoning_effort:
|
|
763
|
+
...(reasoning.effort !== undefined ? { reasoning_effort: reasoning.effort } : {}),
|
|
604
764
|
max_completion_tokens: input.maxTokens
|
|
605
765
|
};
|
|
606
766
|
};
|
|
767
|
+
const mapResponsesReasoning = (input, providerReasoning) => {
|
|
768
|
+
const reasoning = getOpenAIReasoning(input);
|
|
769
|
+
if (!reasoning) {
|
|
770
|
+
return {};
|
|
771
|
+
}
|
|
772
|
+
return {
|
|
773
|
+
reasoning: {
|
|
774
|
+
...(providerReasoning && typeof providerReasoning === "object" && !Array.isArray(providerReasoning)
|
|
775
|
+
? providerReasoning
|
|
776
|
+
: {}),
|
|
777
|
+
...(reasoning.effort !== undefined ? { effort: reasoning.effort } : {}),
|
|
778
|
+
...(reasoning.mode !== undefined ? { mode: reasoning.mode } : {}),
|
|
779
|
+
...(reasoning.context !== undefined ? { context: reasoning.context } : {}),
|
|
780
|
+
...(reasoning.includeThoughts ? { summary: "auto" } : {})
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
};
|
|
607
784
|
const getProviderResponseId = (messages) => {
|
|
608
785
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
609
786
|
const message = messages[index];
|
|
@@ -627,20 +804,52 @@ const getProviderResponseId = (messages) => {
|
|
|
627
804
|
const serializeToolOutput = (message) => message.parts
|
|
628
805
|
.filter((part) => part.type === "tool-result")
|
|
629
806
|
.map((part) => {
|
|
630
|
-
|
|
807
|
+
const responsesToolType = part.toolResult.providerMetadata?.responsesToolType;
|
|
808
|
+
if (part.toolResult.toolName === "shell" || responsesToolType === "shell") {
|
|
809
|
+
const rawOutput = part.toolResult.output;
|
|
810
|
+
const rawOutputs = Array.isArray(rawOutput)
|
|
811
|
+
? rawOutput
|
|
812
|
+
: rawOutput && typeof rawOutput === "object" && Array.isArray(rawOutput.output)
|
|
813
|
+
? rawOutput.output
|
|
814
|
+
: rawOutput
|
|
815
|
+
? [rawOutput]
|
|
816
|
+
: [];
|
|
817
|
+
const firstRawOutput = rawOutputs[0] && typeof rawOutputs[0] === "object"
|
|
818
|
+
? rawOutputs[0]
|
|
819
|
+
: undefined;
|
|
820
|
+
const output = rawOutputs.map((entry) => {
|
|
821
|
+
const result = entry && typeof entry === "object" ? entry : {};
|
|
822
|
+
const outcome = result.outcome && typeof result.outcome === "object"
|
|
823
|
+
? result.outcome
|
|
824
|
+
: { type: "exit", exit_code: 0 };
|
|
825
|
+
return {
|
|
826
|
+
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
827
|
+
stderr: typeof result.stderr === "string" ? result.stderr : "",
|
|
828
|
+
outcome: {
|
|
829
|
+
type: outcome.type ?? "exit",
|
|
830
|
+
exit_code: outcome.exit_code ?? outcome.exitCode
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
});
|
|
631
834
|
return {
|
|
632
835
|
type: "shell_call_output",
|
|
633
836
|
call_id: part.toolResult.toolCallId,
|
|
837
|
+
max_output_length: rawOutput && typeof rawOutput === "object"
|
|
838
|
+
? (rawOutput.max_output_length ??
|
|
839
|
+
rawOutput.maxOutputLength ??
|
|
840
|
+
firstRawOutput?.max_output_length ??
|
|
841
|
+
firstRawOutput?.maxOutputLength)
|
|
842
|
+
: undefined,
|
|
634
843
|
output: part.toolResult.isError
|
|
635
|
-
? {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
:
|
|
844
|
+
? [{
|
|
845
|
+
stdout: "",
|
|
846
|
+
stderr: part.toolResult.error?.message ?? "Shell execution failed.",
|
|
847
|
+
outcome: { type: "exit", exit_code: 1 }
|
|
848
|
+
}]
|
|
849
|
+
: output
|
|
641
850
|
};
|
|
642
851
|
}
|
|
643
|
-
if (part.toolResult.toolName === "apply_patch") {
|
|
852
|
+
if (part.toolResult.toolName === "apply_patch" || responsesToolType === "apply_patch") {
|
|
644
853
|
const output = part.toolResult.output;
|
|
645
854
|
const outputRecord = output && typeof output === "object" && !Array.isArray(output)
|
|
646
855
|
? output
|
|
@@ -652,10 +861,23 @@ const serializeToolOutput = (message) => message.parts
|
|
|
652
861
|
output: part.toolResult.isError ? part.toolResult.error?.message : outputRecord?.output
|
|
653
862
|
};
|
|
654
863
|
}
|
|
864
|
+
if (part.toolResult.toolName === "computer" || responsesToolType === "computer") {
|
|
865
|
+
if (part.toolResult.isError) {
|
|
866
|
+
throw new Error(`OpenAI computer action execution failed: ${part.toolResult.error?.message ?? "unknown error"}`);
|
|
867
|
+
}
|
|
868
|
+
return {
|
|
869
|
+
type: "computer_call_output",
|
|
870
|
+
call_id: part.toolResult.toolCallId,
|
|
871
|
+
output: part.toolResult.output
|
|
872
|
+
};
|
|
873
|
+
}
|
|
655
874
|
return {
|
|
656
875
|
type: "function_call_output",
|
|
657
876
|
call_id: part.toolResult.toolCallId,
|
|
658
|
-
output: JSON.stringify(part.toolResult.isError ? part.toolResult.error : part.toolResult.output ?? null)
|
|
877
|
+
output: JSON.stringify(part.toolResult.isError ? part.toolResult.error : part.toolResult.output ?? null),
|
|
878
|
+
...(part.toolResult.providerMetadata?.caller !== undefined
|
|
879
|
+
? { caller: part.toolResult.providerMetadata.caller }
|
|
880
|
+
: {})
|
|
659
881
|
};
|
|
660
882
|
});
|
|
661
883
|
const serializeProviderDataInput = (message) => message.parts
|
|
@@ -663,15 +885,27 @@ const serializeProviderDataInput = (message) => message.parts
|
|
|
663
885
|
part.provider === "openai" &&
|
|
664
886
|
part.data !== null &&
|
|
665
887
|
typeof part.data === "object" &&
|
|
666
|
-
part.data.type === "
|
|
888
|
+
typeof part.data.type === "string" &&
|
|
889
|
+
!["prompt_cache_breakpoint", "responses_output"].includes(part.data.type))
|
|
667
890
|
.map((part) => part.data);
|
|
891
|
+
const serializedResponsesOutput = (message) => message.parts.flatMap((part) => {
|
|
892
|
+
if (part.type !== "provider-data" ||
|
|
893
|
+
part.provider !== "openai" ||
|
|
894
|
+
!part.data ||
|
|
895
|
+
typeof part.data !== "object" ||
|
|
896
|
+
part.data.type !== "responses_output" ||
|
|
897
|
+
!Array.isArray(part.data.items)) {
|
|
898
|
+
return [];
|
|
899
|
+
}
|
|
900
|
+
return part.data.items;
|
|
901
|
+
});
|
|
668
902
|
const parseResponsesProviderData = (item) => {
|
|
669
903
|
if (!item || typeof item !== "object") {
|
|
670
904
|
return undefined;
|
|
671
905
|
}
|
|
672
906
|
const typedItem = item;
|
|
673
907
|
if (typeof typedItem.type !== "string" ||
|
|
674
|
-
["message", "function_call"
|
|
908
|
+
["message", "function_call"].includes(typedItem.type)) {
|
|
675
909
|
return undefined;
|
|
676
910
|
}
|
|
677
911
|
return item;
|
|
@@ -679,7 +913,13 @@ const parseResponsesProviderData = (item) => {
|
|
|
679
913
|
const parseShellCallInput = (item) => {
|
|
680
914
|
const action = item.action && typeof item.action === "object" ? item.action : undefined;
|
|
681
915
|
return {
|
|
682
|
-
command: typeof action?.command === "string"
|
|
916
|
+
command: typeof action?.command === "string"
|
|
917
|
+
? action.command
|
|
918
|
+
: Array.isArray(action?.commands) && typeof action.commands[0] === "string"
|
|
919
|
+
? action.commands[0]
|
|
920
|
+
: typeof item.command === "string"
|
|
921
|
+
? item.command
|
|
922
|
+
: undefined,
|
|
683
923
|
action: action,
|
|
684
924
|
maxOutputLength: typeof action?.max_output_length === "number"
|
|
685
925
|
? action.max_output_length
|
|
@@ -753,26 +993,52 @@ const toResponsesInput = (messages) => {
|
|
|
753
993
|
input.push(...serializeToolOutput(message));
|
|
754
994
|
continue;
|
|
755
995
|
}
|
|
996
|
+
const rawResponsesOutput = serializedResponsesOutput(message);
|
|
997
|
+
if (message.role === "assistant" && rawResponsesOutput.length) {
|
|
998
|
+
input.push(...rawResponsesOutput);
|
|
999
|
+
continue;
|
|
1000
|
+
}
|
|
756
1001
|
input.push(...serializeProviderDataInput(message));
|
|
757
1002
|
const content = [];
|
|
1003
|
+
const assistantOutputItems = [];
|
|
758
1004
|
for (const part of message.parts) {
|
|
759
1005
|
switch (part.type) {
|
|
760
1006
|
case "text":
|
|
761
|
-
content.push({ type: "input_text", text: part.text });
|
|
1007
|
+
content.push({ type: "input_text", text: part.text, ...promptCacheBreakpointForContentPart(part) });
|
|
762
1008
|
break;
|
|
763
1009
|
case "image":
|
|
764
|
-
|
|
1010
|
+
const detail = imageDetailForPart(part);
|
|
1011
|
+
content.push({
|
|
1012
|
+
type: "input_image",
|
|
1013
|
+
image_url: part.image,
|
|
1014
|
+
...(detail ? { detail } : {}),
|
|
1015
|
+
...promptCacheBreakpointForContentPart(part)
|
|
1016
|
+
});
|
|
1017
|
+
break;
|
|
1018
|
+
case "file":
|
|
1019
|
+
content.push({
|
|
1020
|
+
type: "input_file",
|
|
1021
|
+
file_id: part.data,
|
|
1022
|
+
...promptCacheBreakpointForContentPart(part)
|
|
1023
|
+
});
|
|
765
1024
|
break;
|
|
766
1025
|
case "tool-call":
|
|
767
1026
|
if (message.role === "assistant") {
|
|
768
|
-
|
|
1027
|
+
const caller = part.toolCall.providerMetadata?.caller;
|
|
1028
|
+
assistantOutputItems.push({
|
|
769
1029
|
type: "function_call",
|
|
770
1030
|
call_id: part.toolCall.id,
|
|
771
1031
|
name: part.toolCall.name,
|
|
772
|
-
arguments: JSON.stringify(part.toolCall.input)
|
|
1032
|
+
arguments: JSON.stringify(part.toolCall.input),
|
|
1033
|
+
...(caller !== undefined ? { caller } : {})
|
|
773
1034
|
});
|
|
774
1035
|
}
|
|
775
1036
|
break;
|
|
1037
|
+
case "provider-data":
|
|
1038
|
+
if (isOpenAIPromptCacheBreakpointPart(part)) {
|
|
1039
|
+
markLastContentBlockForPromptCaching(content);
|
|
1040
|
+
}
|
|
1041
|
+
break;
|
|
776
1042
|
}
|
|
777
1043
|
}
|
|
778
1044
|
if (content.length) {
|
|
@@ -781,49 +1047,76 @@ const toResponsesInput = (messages) => {
|
|
|
781
1047
|
content
|
|
782
1048
|
});
|
|
783
1049
|
}
|
|
1050
|
+
input.push(...assistantOutputItems);
|
|
784
1051
|
}
|
|
785
1052
|
return input;
|
|
786
1053
|
};
|
|
787
|
-
const parseResponsesAssistantMessage = (json) => {
|
|
1054
|
+
const parseResponsesAssistantMessage = (json, multiAgentEnabled = false, localTools = new Map()) => {
|
|
788
1055
|
const parts = [];
|
|
789
1056
|
for (const [index, item] of (json.output ?? []).entries()) {
|
|
790
1057
|
if (item?.type === "message") {
|
|
1058
|
+
if (multiAgentEnabled && (item.agent?.agent_name !== "/root" || item.phase !== "final_answer")) {
|
|
1059
|
+
continue;
|
|
1060
|
+
}
|
|
791
1061
|
for (const content of item.content ?? []) {
|
|
792
1062
|
if (typeof content?.text === "string" && content.text) {
|
|
793
1063
|
parts.push({ type: "text", text: content.text });
|
|
794
1064
|
}
|
|
1065
|
+
else if (typeof content?.refusal === "string" && content.refusal) {
|
|
1066
|
+
parts.push({ type: "text", text: content.refusal });
|
|
1067
|
+
}
|
|
795
1068
|
}
|
|
796
1069
|
continue;
|
|
797
1070
|
}
|
|
798
1071
|
if (item?.type === "function_call") {
|
|
1072
|
+
const callId = item.call_id ?? item.id ?? `${item.name}-${index}`;
|
|
799
1073
|
parts.push({
|
|
800
1074
|
type: "tool-call",
|
|
801
1075
|
toolCall: {
|
|
802
|
-
id:
|
|
1076
|
+
id: callId,
|
|
803
1077
|
name: item.name,
|
|
804
|
-
input: JSON.parse(item.arguments ?? "{}")
|
|
1078
|
+
input: JSON.parse(item.arguments ?? "{}"),
|
|
1079
|
+
...(item.caller && typeof item.caller === "object"
|
|
1080
|
+
? { providerMetadata: { caller: item.caller } }
|
|
1081
|
+
: {})
|
|
805
1082
|
}
|
|
806
1083
|
});
|
|
807
1084
|
continue;
|
|
808
1085
|
}
|
|
809
|
-
if (item?.type === "shell_call") {
|
|
1086
|
+
if (item?.type === "shell_call" && localTools.has("shell")) {
|
|
810
1087
|
parts.push({
|
|
811
1088
|
type: "tool-call",
|
|
812
1089
|
toolCall: {
|
|
813
1090
|
id: item.call_id ?? item.id ?? `shell-${index}`,
|
|
814
|
-
name: "shell",
|
|
815
|
-
input: parseShellCallInput(item)
|
|
1091
|
+
name: localTools.get("shell"),
|
|
1092
|
+
input: parseShellCallInput(item),
|
|
1093
|
+
providerMetadata: { responsesToolType: "shell" }
|
|
816
1094
|
}
|
|
817
1095
|
});
|
|
818
1096
|
continue;
|
|
819
1097
|
}
|
|
820
|
-
if (item?.type === "apply_patch_call") {
|
|
1098
|
+
if (item?.type === "apply_patch_call" && localTools.has("apply_patch")) {
|
|
821
1099
|
parts.push({
|
|
822
1100
|
type: "tool-call",
|
|
823
1101
|
toolCall: {
|
|
824
1102
|
id: item.call_id ?? item.id ?? `apply_patch-${index}`,
|
|
825
|
-
name: "apply_patch",
|
|
826
|
-
input: parseApplyPatchCallInput(item)
|
|
1103
|
+
name: localTools.get("apply_patch"),
|
|
1104
|
+
input: parseApplyPatchCallInput(item),
|
|
1105
|
+
providerMetadata: { responsesToolType: "apply_patch" }
|
|
1106
|
+
}
|
|
1107
|
+
});
|
|
1108
|
+
continue;
|
|
1109
|
+
}
|
|
1110
|
+
if (item?.type === "computer_call" && localTools.has("computer")) {
|
|
1111
|
+
parts.push({
|
|
1112
|
+
type: "tool-call",
|
|
1113
|
+
toolCall: {
|
|
1114
|
+
id: item.call_id ?? item.id ?? `computer-${index}`,
|
|
1115
|
+
name: localTools.get("computer"),
|
|
1116
|
+
input: {
|
|
1117
|
+
actions: Array.isArray(item.actions) ? item.actions : []
|
|
1118
|
+
},
|
|
1119
|
+
providerMetadata: { responsesToolType: "computer" }
|
|
827
1120
|
}
|
|
828
1121
|
});
|
|
829
1122
|
continue;
|
|
@@ -833,7 +1126,13 @@ const parseResponsesAssistantMessage = (json) => {
|
|
|
833
1126
|
parts.push(providerDataPart("openai", providerData));
|
|
834
1127
|
}
|
|
835
1128
|
}
|
|
836
|
-
if (
|
|
1129
|
+
if (Array.isArray(json.output) && json.output.length) {
|
|
1130
|
+
parts.push(providerDataPart("openai", {
|
|
1131
|
+
type: "responses_output",
|
|
1132
|
+
items: json.output
|
|
1133
|
+
}));
|
|
1134
|
+
}
|
|
1135
|
+
if (!multiAgentEnabled && !parts.some((part) => part.type === "text") && typeof json.output_text === "string" && json.output_text) {
|
|
837
1136
|
parts.push({ type: "text", text: json.output_text });
|
|
838
1137
|
}
|
|
839
1138
|
if (typeof json.id === "string") {
|
|
@@ -850,10 +1149,13 @@ const parseResponsesAssistantMessage = (json) => {
|
|
|
850
1149
|
parts
|
|
851
1150
|
};
|
|
852
1151
|
};
|
|
853
|
-
const normalizeResponsesFinishReason = (status, hasToolCalls) => {
|
|
1152
|
+
const normalizeResponsesFinishReason = (status, hasToolCalls, hasRefusal = false) => {
|
|
854
1153
|
if (hasToolCalls) {
|
|
855
1154
|
return "tool-calls";
|
|
856
1155
|
}
|
|
1156
|
+
if (hasRefusal) {
|
|
1157
|
+
return "refusal";
|
|
1158
|
+
}
|
|
857
1159
|
if (status === "completed") {
|
|
858
1160
|
return "stop";
|
|
859
1161
|
}
|
|
@@ -862,9 +1164,46 @@ const normalizeResponsesFinishReason = (status, hasToolCalls) => {
|
|
|
862
1164
|
}
|
|
863
1165
|
return normalizeFinishReason(status);
|
|
864
1166
|
};
|
|
865
|
-
const
|
|
1167
|
+
const mapResponsesUsage = (usage) => usage
|
|
1168
|
+
? {
|
|
1169
|
+
inputTokens: usage.input_tokens,
|
|
1170
|
+
cachedInputTokens: usage.input_tokens_details?.cached_tokens,
|
|
1171
|
+
cacheWriteTokens: usage.input_tokens_details?.cache_write_tokens,
|
|
1172
|
+
outputTokens: usage.output_tokens,
|
|
1173
|
+
reasoningTokens: usage.output_tokens_details?.reasoning_tokens,
|
|
1174
|
+
totalTokens: usage.total_tokens
|
|
1175
|
+
}
|
|
1176
|
+
: undefined;
|
|
1177
|
+
const mapChatUsage = (usage) => usage
|
|
1178
|
+
? {
|
|
1179
|
+
inputTokens: usage.prompt_tokens,
|
|
1180
|
+
cachedInputTokens: usage.prompt_tokens_details?.cached_tokens,
|
|
1181
|
+
cacheWriteTokens: usage.prompt_tokens_details?.cache_write_tokens,
|
|
1182
|
+
outputTokens: usage.completion_tokens,
|
|
1183
|
+
reasoningTokens: usage.completion_tokens_details?.reasoning_tokens,
|
|
1184
|
+
totalTokens: usage.total_tokens
|
|
1185
|
+
}
|
|
1186
|
+
: undefined;
|
|
1187
|
+
const addTokenUsage = (left, right) => {
|
|
1188
|
+
if (!left)
|
|
1189
|
+
return right;
|
|
1190
|
+
if (!right)
|
|
1191
|
+
return left;
|
|
1192
|
+
const sum = (a, b) => a === undefined && b === undefined ? undefined : (a ?? 0) + (b ?? 0);
|
|
1193
|
+
return {
|
|
1194
|
+
inputTokens: sum(left.inputTokens, right.inputTokens),
|
|
1195
|
+
cachedInputTokens: sum(left.cachedInputTokens, right.cachedInputTokens),
|
|
1196
|
+
cacheWriteTokens: sum(left.cacheWriteTokens, right.cacheWriteTokens),
|
|
1197
|
+
outputTokens: sum(left.outputTokens, right.outputTokens),
|
|
1198
|
+
reasoningTokens: sum(left.reasoningTokens, right.reasoningTokens),
|
|
1199
|
+
totalTokens: sum(left.totalTokens, right.totalTokens)
|
|
1200
|
+
};
|
|
1201
|
+
};
|
|
1202
|
+
const streamResponses = async function* (response, multiAgentEnabled = false, localTools = new Map()) {
|
|
866
1203
|
const toolBuffers = new Map();
|
|
1204
|
+
const outputAgents = new Map();
|
|
867
1205
|
let sawToolCalls = false;
|
|
1206
|
+
let sawRefusal = false;
|
|
868
1207
|
const emitToolCall = (key) => {
|
|
869
1208
|
const toolCall = toolBuffers.get(key);
|
|
870
1209
|
if (!toolCall || toolCall.emitted || !toolCall.name) {
|
|
@@ -877,7 +1216,8 @@ const streamResponses = async function* (response) {
|
|
|
877
1216
|
toolCall: {
|
|
878
1217
|
id: toolCall.callId,
|
|
879
1218
|
name: toolCall.name,
|
|
880
|
-
input: JSON.parse(toolCall.args || "{}")
|
|
1219
|
+
input: JSON.parse(toolCall.args || "{}"),
|
|
1220
|
+
...(toolCall.caller !== undefined ? { providerMetadata: { caller: toolCall.caller } } : {})
|
|
881
1221
|
}
|
|
882
1222
|
};
|
|
883
1223
|
};
|
|
@@ -887,12 +1227,32 @@ const streamResponses = async function* (response) {
|
|
|
887
1227
|
}
|
|
888
1228
|
const json = JSON.parse(event.data);
|
|
889
1229
|
const type = json.type;
|
|
890
|
-
if (type === "
|
|
1230
|
+
if (type === "error") {
|
|
1231
|
+
throw new ProviderHTTPError(`OpenAI Responses stream failed: ${json.error?.message ?? json.message ?? "unknown error"}`, 500, { responseBody: JSON.stringify(json) });
|
|
1232
|
+
}
|
|
1233
|
+
if (type === "response.output_text.delta" &&
|
|
1234
|
+
typeof json.delta === "string" &&
|
|
1235
|
+
(!multiAgentEnabled ||
|
|
1236
|
+
(outputAgents.get(json.output_index)?.agentName ?? "/root") === "/root")) {
|
|
1237
|
+
yield { type: "text-delta", textDelta: json.delta };
|
|
1238
|
+
continue;
|
|
1239
|
+
}
|
|
1240
|
+
if (type === "response.refusal.delta" &&
|
|
1241
|
+
typeof json.delta === "string" &&
|
|
1242
|
+
(!multiAgentEnabled ||
|
|
1243
|
+
(outputAgents.get(json.output_index)?.agentName ?? "/root") === "/root")) {
|
|
1244
|
+
sawRefusal = true;
|
|
891
1245
|
yield { type: "text-delta", textDelta: json.delta };
|
|
892
1246
|
continue;
|
|
893
1247
|
}
|
|
894
1248
|
if (type === "response.output_item.added" || type === "response.output_item.done") {
|
|
895
1249
|
const item = json.item;
|
|
1250
|
+
let handledLocalToolCall = false;
|
|
1251
|
+
if (typeof json.output_index === "number" && item?.type === "message") {
|
|
1252
|
+
outputAgents.set(json.output_index, {
|
|
1253
|
+
agentName: item.agent?.agent_name
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
896
1256
|
if (item?.type === "function_call") {
|
|
897
1257
|
const key = item.id ?? json.item_id ?? `${json.output_index ?? toolBuffers.size}`;
|
|
898
1258
|
const existing = toolBuffers.get(key) ?? {
|
|
@@ -903,6 +1263,9 @@ const streamResponses = async function* (response) {
|
|
|
903
1263
|
};
|
|
904
1264
|
existing.callId = item.call_id ?? existing.callId;
|
|
905
1265
|
existing.name ||= item.name ?? "";
|
|
1266
|
+
if (item.caller && typeof item.caller === "object") {
|
|
1267
|
+
existing.caller = item.caller;
|
|
1268
|
+
}
|
|
906
1269
|
if (typeof item.arguments === "string") {
|
|
907
1270
|
existing.args = item.arguments;
|
|
908
1271
|
}
|
|
@@ -914,36 +1277,65 @@ const streamResponses = async function* (response) {
|
|
|
914
1277
|
}
|
|
915
1278
|
}
|
|
916
1279
|
}
|
|
917
|
-
if (item?.type === "shell_call" && type === "response.output_item.done") {
|
|
1280
|
+
if (item?.type === "shell_call" && type === "response.output_item.done" && localTools.has("shell")) {
|
|
918
1281
|
yield {
|
|
919
1282
|
type: "tool-call",
|
|
920
1283
|
toolCall: {
|
|
921
1284
|
id: item.call_id ?? item.id ?? `${json.output_index ?? "shell"}`,
|
|
922
|
-
name: "shell",
|
|
923
|
-
input: parseShellCallInput(item)
|
|
1285
|
+
name: localTools.get("shell"),
|
|
1286
|
+
input: parseShellCallInput(item),
|
|
1287
|
+
providerMetadata: { responsesToolType: "shell" }
|
|
924
1288
|
}
|
|
925
1289
|
};
|
|
926
1290
|
sawToolCalls = true;
|
|
1291
|
+
handledLocalToolCall = true;
|
|
927
1292
|
}
|
|
928
|
-
if (item?.type === "apply_patch_call" && type === "response.output_item.done") {
|
|
1293
|
+
if (item?.type === "apply_patch_call" && type === "response.output_item.done" && localTools.has("apply_patch")) {
|
|
929
1294
|
yield {
|
|
930
1295
|
type: "tool-call",
|
|
931
1296
|
toolCall: {
|
|
932
1297
|
id: item.call_id ?? item.id ?? `${json.output_index ?? "apply_patch"}`,
|
|
933
|
-
name: "apply_patch",
|
|
934
|
-
input: parseApplyPatchCallInput(item)
|
|
1298
|
+
name: localTools.get("apply_patch"),
|
|
1299
|
+
input: parseApplyPatchCallInput(item),
|
|
1300
|
+
providerMetadata: { responsesToolType: "apply_patch" }
|
|
935
1301
|
}
|
|
936
1302
|
};
|
|
937
1303
|
sawToolCalls = true;
|
|
1304
|
+
handledLocalToolCall = true;
|
|
1305
|
+
}
|
|
1306
|
+
if (item?.type === "computer_call" && type === "response.output_item.done" && localTools.has("computer")) {
|
|
1307
|
+
yield {
|
|
1308
|
+
type: "tool-call",
|
|
1309
|
+
toolCall: {
|
|
1310
|
+
id: item.call_id ?? item.id ?? `${json.output_index ?? "computer"}`,
|
|
1311
|
+
name: localTools.get("computer"),
|
|
1312
|
+
input: {
|
|
1313
|
+
actions: Array.isArray(item.actions) ? item.actions : []
|
|
1314
|
+
},
|
|
1315
|
+
providerMetadata: { responsesToolType: "computer" }
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
sawToolCalls = true;
|
|
1319
|
+
handledLocalToolCall = true;
|
|
938
1320
|
}
|
|
939
1321
|
const providerData = parseResponsesProviderData(item);
|
|
940
|
-
if (providerData && type === "response.output_item.done") {
|
|
1322
|
+
if (providerData && type === "response.output_item.done" && !handledLocalToolCall) {
|
|
941
1323
|
yield {
|
|
942
1324
|
type: "provider-data",
|
|
943
1325
|
provider: "openai",
|
|
944
1326
|
data: providerData
|
|
945
1327
|
};
|
|
946
1328
|
}
|
|
1329
|
+
if (item && type === "response.output_item.done") {
|
|
1330
|
+
yield {
|
|
1331
|
+
type: "provider-data",
|
|
1332
|
+
provider: "openai",
|
|
1333
|
+
data: {
|
|
1334
|
+
type: "responses_output",
|
|
1335
|
+
items: [item]
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
947
1339
|
continue;
|
|
948
1340
|
}
|
|
949
1341
|
if (type === "response.function_call_arguments.delta") {
|
|
@@ -978,21 +1370,47 @@ const streamResponses = async function* (response) {
|
|
|
978
1370
|
}
|
|
979
1371
|
if (type === "response.completed" || type === "response.failed" || type === "response.incomplete") {
|
|
980
1372
|
const responseData = json.response ?? {};
|
|
1373
|
+
if (typeof responseData.id === "string") {
|
|
1374
|
+
yield {
|
|
1375
|
+
type: "provider-data",
|
|
1376
|
+
provider: "openai",
|
|
1377
|
+
data: { responseId: responseData.id }
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
981
1380
|
yield {
|
|
982
1381
|
type: "finish",
|
|
983
|
-
finishReason: normalizeResponsesFinishReason(responseData.status, sawToolCalls),
|
|
1382
|
+
finishReason: normalizeResponsesFinishReason(responseData.status, sawToolCalls, sawRefusal),
|
|
984
1383
|
providerFinishReason: responseData.status,
|
|
985
|
-
usage: responseData.usage
|
|
986
|
-
? {
|
|
987
|
-
inputTokens: responseData.usage.input_tokens,
|
|
988
|
-
outputTokens: responseData.usage.output_tokens,
|
|
989
|
-
totalTokens: responseData.usage.total_tokens
|
|
990
|
-
}
|
|
991
|
-
: undefined
|
|
1384
|
+
usage: mapResponsesUsage(responseData.usage)
|
|
992
1385
|
};
|
|
993
1386
|
}
|
|
994
1387
|
}
|
|
995
1388
|
};
|
|
1389
|
+
const streamGenerateResult = async function* (result) {
|
|
1390
|
+
for (const message of result.messages ?? (result.message ? [result.message] : [])) {
|
|
1391
|
+
for (const part of message.parts) {
|
|
1392
|
+
if (part.type === "text" && part.text) {
|
|
1393
|
+
yield { type: "text-delta", textDelta: part.text };
|
|
1394
|
+
}
|
|
1395
|
+
else if (part.type === "tool-call") {
|
|
1396
|
+
yield { type: "tool-call", toolCall: part.toolCall };
|
|
1397
|
+
}
|
|
1398
|
+
else if (part.type === "provider-data") {
|
|
1399
|
+
yield {
|
|
1400
|
+
type: "provider-data",
|
|
1401
|
+
provider: part.provider,
|
|
1402
|
+
data: part.data
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
yield {
|
|
1408
|
+
type: "finish",
|
|
1409
|
+
finishReason: result.finishReason,
|
|
1410
|
+
providerFinishReason: result.providerFinishReason,
|
|
1411
|
+
usage: result.usage
|
|
1412
|
+
};
|
|
1413
|
+
};
|
|
996
1414
|
const extractSources = (value) => {
|
|
997
1415
|
const sources = [];
|
|
998
1416
|
const visit = (node) => {
|
|
@@ -1033,69 +1451,117 @@ class OpenAILanguageModel {
|
|
|
1033
1451
|
this.fetcher = fetcher;
|
|
1034
1452
|
this.capabilities = modelCapabilities(modelId);
|
|
1035
1453
|
}
|
|
1036
|
-
usesResponsesAPI(input) {
|
|
1037
|
-
|
|
1454
|
+
usesResponsesAPI(input, options) {
|
|
1455
|
+
const requiresResponses = hasResponsesOnlyTools(input.tools) || options.multiAgentEnabled;
|
|
1456
|
+
if (options.apiMode === "chat") {
|
|
1457
|
+
if (requiresResponses) {
|
|
1458
|
+
throw new UnsupportedFeatureError('Provider "openai" cannot use apiMode "chat" with Responses-only tools or Multi-agent.');
|
|
1459
|
+
}
|
|
1460
|
+
return false;
|
|
1461
|
+
}
|
|
1462
|
+
return options.apiMode === "responses" || requiresResponses || isOpenAIGpt56Model(this.modelId);
|
|
1038
1463
|
}
|
|
1039
|
-
async generateViaResponses(input, signal) {
|
|
1464
|
+
async generateViaResponses(input, signal, options) {
|
|
1465
|
+
const responseBodyOptions = openAIResponsesBodyOptions(options.bodyOptions, this.modelId);
|
|
1040
1466
|
assertResponsesToolsSupported(this.modelId, input.tools);
|
|
1041
|
-
|
|
1467
|
+
assertOpenAIResponsesOptionsSupported(this.modelId, responseBodyOptions);
|
|
1468
|
+
const previousResponse = responseBodyOptions.store === false ? undefined : getProviderResponseId(input.messages);
|
|
1042
1469
|
const messages = previousResponse && previousResponse.index < input.messages.length - 1
|
|
1043
1470
|
? input.messages.slice(previousResponse.index + 1)
|
|
1044
1471
|
: input.messages;
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1472
|
+
let nextPreviousResponseId = previousResponse?.responseId;
|
|
1473
|
+
let nextInput = messages.length ? toResponsesInput(messages) : [];
|
|
1474
|
+
let accumulatedUsage;
|
|
1475
|
+
let statelessInternalOutputs = [];
|
|
1476
|
+
for (let continuation = 0; continuation < 8; continuation += 1) {
|
|
1477
|
+
const response = await withRetry(() => this.fetcher(`${this.baseURL}/responses`, {
|
|
1478
|
+
method: "POST",
|
|
1479
|
+
headers: options.headers,
|
|
1480
|
+
signal,
|
|
1481
|
+
body: JSON.stringify({
|
|
1482
|
+
...responseBodyOptions,
|
|
1483
|
+
model: this.modelId,
|
|
1484
|
+
...(nextPreviousResponseId ? { previous_response_id: nextPreviousResponseId } : {}),
|
|
1485
|
+
...(nextInput.length ? { input: nextInput } : {}),
|
|
1486
|
+
tools: mapResponsesTools(input.tools),
|
|
1487
|
+
...(input.toolChoice ? { tool_choice: mapResponsesToolChoice(input.toolChoice) } : {}),
|
|
1488
|
+
text: mapResponsesStructuredOutput(input),
|
|
1489
|
+
temperature: input.temperature,
|
|
1490
|
+
max_output_tokens: input.maxTokens,
|
|
1491
|
+
...mapResponsesReasoning(input, responseBodyOptions.reasoning)
|
|
1492
|
+
})
|
|
1493
|
+
}), input);
|
|
1494
|
+
const json = await parseJson(response);
|
|
1495
|
+
accumulatedUsage = addTokenUsage(accumulatedUsage, mapResponsesUsage(json.usage));
|
|
1496
|
+
const assistantMessage = parseResponsesAssistantMessage(json, options.multiAgentEnabled, localResponsesTools(input.tools));
|
|
1497
|
+
const currentOutput = Array.isArray(json.output)
|
|
1498
|
+
? json.output
|
|
1499
|
+
: [];
|
|
1500
|
+
const completeStatelessOutput = [...statelessInternalOutputs, ...currentOutput];
|
|
1501
|
+
if (responseBodyOptions.store === false && completeStatelessOutput.length) {
|
|
1502
|
+
assistantMessage.parts = assistantMessage.parts.filter((part) => part.type !== "provider-data" ||
|
|
1503
|
+
part.provider !== "openai" ||
|
|
1504
|
+
!part.data ||
|
|
1505
|
+
typeof part.data !== "object" ||
|
|
1506
|
+
part.data.type !== "responses_output");
|
|
1507
|
+
assistantMessage.parts.push(providerDataPart("openai", {
|
|
1508
|
+
type: "responses_output",
|
|
1509
|
+
items: completeStatelessOutput
|
|
1510
|
+
}));
|
|
1511
|
+
}
|
|
1512
|
+
const hasToolCalls = assistantMessage.parts.some((part) => part.type === "tool-call");
|
|
1513
|
+
const hasRefusal = (json.output ?? []).some((item) => item?.type === "message" &&
|
|
1514
|
+
(item.content ?? []).some((content) => content?.type === "refusal"));
|
|
1515
|
+
const hasFinalMessage = (json.output ?? []).some((item) => item?.type === "message" &&
|
|
1516
|
+
(!options.multiAgentEnabled || (item.agent?.agent_name === "/root" && item.phase === "final_answer")));
|
|
1517
|
+
const shouldContinueProgram = hasProgrammaticToolCalling(input.tools) &&
|
|
1518
|
+
json.status === "completed" &&
|
|
1519
|
+
!hasToolCalls &&
|
|
1520
|
+
!hasFinalMessage &&
|
|
1521
|
+
(json.output ?? []).some((item) => item?.type === "program" || item?.type === "program_output");
|
|
1522
|
+
if (!shouldContinueProgram) {
|
|
1523
|
+
return {
|
|
1524
|
+
messages: [assistantMessage],
|
|
1525
|
+
text: extractMessageText(assistantMessage),
|
|
1526
|
+
audio: extractAudioOutputs(assistantMessage),
|
|
1527
|
+
finishReason: normalizeResponsesFinishReason(json.status, hasToolCalls, hasRefusal),
|
|
1528
|
+
providerFinishReason: json.status,
|
|
1529
|
+
usage: accumulatedUsage,
|
|
1530
|
+
rawResponse: json
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
if (responseBodyOptions.store === false) {
|
|
1534
|
+
statelessInternalOutputs = completeStatelessOutput;
|
|
1535
|
+
nextInput = [...nextInput, ...currentOutput];
|
|
1536
|
+
}
|
|
1537
|
+
else {
|
|
1538
|
+
nextPreviousResponseId = json.id;
|
|
1539
|
+
nextInput = [];
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
throw new ProviderHTTPError("OpenAI Programmatic Tool Calling exceeded 8 internal continuations.", 500);
|
|
1078
1543
|
}
|
|
1079
1544
|
async generate(input) {
|
|
1080
1545
|
const { signal, cleanup } = getRequestOptions(input);
|
|
1546
|
+
const options = resolveOpenAILanguageRequestOptions(input.providerOptions, this.apiKey);
|
|
1081
1547
|
try {
|
|
1082
|
-
if (this.usesResponsesAPI(input)) {
|
|
1083
|
-
return await this.generateViaResponses(input, signal);
|
|
1548
|
+
if (this.usesResponsesAPI(input, options)) {
|
|
1549
|
+
return await this.generateViaResponses(input, signal, options);
|
|
1084
1550
|
}
|
|
1085
1551
|
const response = await withRetry(() => this.fetcher(`${this.baseURL}/chat/completions`, {
|
|
1086
1552
|
method: "POST",
|
|
1087
|
-
headers:
|
|
1553
|
+
headers: options.headers,
|
|
1088
1554
|
signal,
|
|
1089
1555
|
body: JSON.stringify({
|
|
1090
|
-
...
|
|
1556
|
+
...options.bodyOptions,
|
|
1091
1557
|
model: this.modelId,
|
|
1092
1558
|
messages: mapMessages(input.messages),
|
|
1093
1559
|
tools: mapTools(input.tools),
|
|
1094
|
-
tool_choice: mapToolChoice(input.toolChoice),
|
|
1560
|
+
...(input.toolChoice ? { tool_choice: mapToolChoice(input.toolChoice) } : {}),
|
|
1095
1561
|
response_format: mapStructuredOutput(input),
|
|
1096
1562
|
temperature: input.temperature,
|
|
1097
1563
|
...(input.reasoning ? {} : { max_tokens: input.maxTokens }),
|
|
1098
|
-
...
|
|
1564
|
+
...mapChatReasoning(input),
|
|
1099
1565
|
stream: false
|
|
1100
1566
|
})
|
|
1101
1567
|
}), input);
|
|
@@ -1109,11 +1575,7 @@ class OpenAILanguageModel {
|
|
|
1109
1575
|
audio: extractAudioOutputs(assistantMessage),
|
|
1110
1576
|
finishReason: normalizeFinishReason(choice?.finish_reason),
|
|
1111
1577
|
providerFinishReason: choice?.finish_reason,
|
|
1112
|
-
usage:
|
|
1113
|
-
inputTokens: json.usage?.prompt_tokens,
|
|
1114
|
-
outputTokens: json.usage?.completion_tokens,
|
|
1115
|
-
totalTokens: json.usage?.total_tokens
|
|
1116
|
-
},
|
|
1578
|
+
usage: mapChatUsage(json.usage),
|
|
1117
1579
|
rawResponse: json
|
|
1118
1580
|
};
|
|
1119
1581
|
}
|
|
@@ -1122,29 +1584,48 @@ class OpenAILanguageModel {
|
|
|
1122
1584
|
}
|
|
1123
1585
|
}
|
|
1124
1586
|
async stream(input) {
|
|
1125
|
-
|
|
1587
|
+
const options = resolveOpenAILanguageRequestOptions(input.providerOptions, this.apiKey);
|
|
1588
|
+
if (this.usesResponsesAPI(input, options)) {
|
|
1589
|
+
const responseBodyOptions = openAIResponsesBodyOptions(options.bodyOptions, this.modelId);
|
|
1126
1590
|
assertResponsesToolsSupported(this.modelId, input.tools);
|
|
1591
|
+
assertOpenAIResponsesOptionsSupported(this.modelId, responseBodyOptions);
|
|
1127
1592
|
const { signal, cleanup } = getRequestOptions(input);
|
|
1593
|
+
if (hasProgrammaticToolCalling(input.tools)) {
|
|
1594
|
+
const result = await this.generateViaResponses(input, signal, options);
|
|
1595
|
+
return (async function* () {
|
|
1596
|
+
try {
|
|
1597
|
+
yield* streamGenerateResult(result);
|
|
1598
|
+
}
|
|
1599
|
+
finally {
|
|
1600
|
+
cleanup();
|
|
1601
|
+
}
|
|
1602
|
+
})();
|
|
1603
|
+
}
|
|
1604
|
+
const previousResponse = responseBodyOptions.store === false ? undefined : getProviderResponseId(input.messages);
|
|
1605
|
+
const messages = previousResponse && previousResponse.index < input.messages.length - 1
|
|
1606
|
+
? input.messages.slice(previousResponse.index + 1)
|
|
1607
|
+
: input.messages;
|
|
1128
1608
|
const response = await withRetry(() => this.fetcher(`${this.baseURL}/responses`, {
|
|
1129
1609
|
method: "POST",
|
|
1130
|
-
headers:
|
|
1610
|
+
headers: options.headers,
|
|
1131
1611
|
signal,
|
|
1132
1612
|
body: JSON.stringify({
|
|
1133
|
-
...
|
|
1613
|
+
...responseBodyOptions,
|
|
1134
1614
|
model: this.modelId,
|
|
1135
|
-
|
|
1615
|
+
...(previousResponse ? { previous_response_id: previousResponse.responseId } : {}),
|
|
1616
|
+
...(messages.length ? { input: toResponsesInput(messages) } : {}),
|
|
1136
1617
|
tools: mapResponsesTools(input.tools),
|
|
1137
|
-
tool_choice:
|
|
1618
|
+
...(input.toolChoice ? { tool_choice: mapResponsesToolChoice(input.toolChoice) } : {}),
|
|
1138
1619
|
text: mapResponsesStructuredOutput(input),
|
|
1139
1620
|
temperature: input.temperature,
|
|
1140
1621
|
max_output_tokens: input.maxTokens,
|
|
1141
|
-
...
|
|
1622
|
+
...mapResponsesReasoning(input, responseBodyOptions.reasoning),
|
|
1142
1623
|
stream: true
|
|
1143
1624
|
})
|
|
1144
1625
|
}), input);
|
|
1145
1626
|
return (async function* () {
|
|
1146
1627
|
try {
|
|
1147
|
-
yield* streamResponses(response);
|
|
1628
|
+
yield* streamResponses(response, options.multiAgentEnabled, localResponsesTools(input.tools));
|
|
1148
1629
|
}
|
|
1149
1630
|
finally {
|
|
1150
1631
|
cleanup();
|
|
@@ -1154,18 +1635,18 @@ class OpenAILanguageModel {
|
|
|
1154
1635
|
const { signal, cleanup } = getRequestOptions(input);
|
|
1155
1636
|
const response = await withRetry(() => this.fetcher(`${this.baseURL}/chat/completions`, {
|
|
1156
1637
|
method: "POST",
|
|
1157
|
-
headers:
|
|
1638
|
+
headers: options.headers,
|
|
1158
1639
|
signal,
|
|
1159
1640
|
body: JSON.stringify({
|
|
1160
|
-
...
|
|
1641
|
+
...options.bodyOptions,
|
|
1161
1642
|
model: this.modelId,
|
|
1162
1643
|
messages: mapMessages(input.messages),
|
|
1163
1644
|
tools: mapTools(input.tools),
|
|
1164
|
-
tool_choice: mapToolChoice(input.toolChoice),
|
|
1645
|
+
...(input.toolChoice ? { tool_choice: mapToolChoice(input.toolChoice) } : {}),
|
|
1165
1646
|
response_format: mapStructuredOutput(input),
|
|
1166
1647
|
temperature: input.temperature,
|
|
1167
1648
|
...(input.reasoning ? {} : { max_tokens: input.maxTokens }),
|
|
1168
|
-
...
|
|
1649
|
+
...mapChatReasoning(input),
|
|
1169
1650
|
stream: true,
|
|
1170
1651
|
stream_options: { include_usage: true }
|
|
1171
1652
|
})
|
|
@@ -1208,13 +1689,7 @@ class OpenAILanguageModel {
|
|
|
1208
1689
|
type: "finish",
|
|
1209
1690
|
finishReason: normalizeFinishReason(choice.finish_reason),
|
|
1210
1691
|
providerFinishReason: choice.finish_reason,
|
|
1211
|
-
usage: json.usage
|
|
1212
|
-
? {
|
|
1213
|
-
inputTokens: json.usage.prompt_tokens,
|
|
1214
|
-
outputTokens: json.usage.completion_tokens,
|
|
1215
|
-
totalTokens: json.usage.total_tokens
|
|
1216
|
-
}
|
|
1217
|
-
: undefined
|
|
1692
|
+
usage: mapChatUsage(json.usage)
|
|
1218
1693
|
};
|
|
1219
1694
|
}
|
|
1220
1695
|
}
|
|
@@ -1375,29 +1850,28 @@ class OpenAIGroundedLanguageModel {
|
|
|
1375
1850
|
}
|
|
1376
1851
|
async generate(input) {
|
|
1377
1852
|
const { signal, cleanup } = withTimeoutSignal(input);
|
|
1853
|
+
const options = resolveOpenAILanguageRequestOptions(input.providerOptions, this.apiKey);
|
|
1854
|
+
const responseBodyOptions = openAIResponsesBodyOptions(options.bodyOptions, this.modelId);
|
|
1378
1855
|
try {
|
|
1379
1856
|
const response = await withRetry(() => this.fetcher(`${this.baseURL}/responses`, {
|
|
1380
1857
|
method: "POST",
|
|
1381
|
-
headers:
|
|
1858
|
+
headers: options.headers,
|
|
1382
1859
|
signal,
|
|
1383
1860
|
body: JSON.stringify({
|
|
1384
|
-
...
|
|
1861
|
+
...responseBodyOptions,
|
|
1385
1862
|
model: this.modelId,
|
|
1386
1863
|
input: toResponsesInput(input.messages),
|
|
1387
1864
|
tools: [{ type: "web_search_preview" }],
|
|
1388
1865
|
temperature: input.temperature,
|
|
1389
|
-
max_output_tokens: input.maxTokens
|
|
1866
|
+
max_output_tokens: input.maxTokens,
|
|
1867
|
+
...mapResponsesReasoning(input, responseBodyOptions.reasoning)
|
|
1390
1868
|
})
|
|
1391
1869
|
}), input);
|
|
1392
1870
|
const json = await parseJson(response);
|
|
1393
1871
|
return {
|
|
1394
1872
|
text: json.output_text ?? "",
|
|
1395
1873
|
sources: extractSources(json),
|
|
1396
|
-
usage:
|
|
1397
|
-
inputTokens: json.usage?.input_tokens,
|
|
1398
|
-
outputTokens: json.usage?.output_tokens,
|
|
1399
|
-
totalTokens: json.usage?.total_tokens
|
|
1400
|
-
},
|
|
1874
|
+
usage: mapResponsesUsage(json.usage),
|
|
1401
1875
|
finishReason: normalizeFinishReason(json.status),
|
|
1402
1876
|
providerFinishReason: json.status,
|
|
1403
1877
|
rawResponse: json
|
|
@@ -1644,6 +2118,22 @@ export const openAIToolSearchTool = (config = {}) => hostedTool({
|
|
|
1644
2118
|
toolClass: "tool-search",
|
|
1645
2119
|
config: config
|
|
1646
2120
|
});
|
|
2121
|
+
export const openAIProgrammaticToolCallingTool = () => hostedTool({
|
|
2122
|
+
name: "programmatic_tool_calling",
|
|
2123
|
+
provider: "openai",
|
|
2124
|
+
type: "programmatic_tool_calling",
|
|
2125
|
+
toolClass: "code-execution"
|
|
2126
|
+
});
|
|
2127
|
+
export const openAIProgrammaticTool = (definition, options = {}) => ({
|
|
2128
|
+
...definition,
|
|
2129
|
+
metadata: {
|
|
2130
|
+
...definition.metadata,
|
|
2131
|
+
[openAIResponsesFunctionConfigMetadataKey]: {
|
|
2132
|
+
allowed_callers: options.allowedCallers ?? ["programmatic"],
|
|
2133
|
+
...(options.outputSchema ? { output_schema: toJSONSchema(options.outputSchema) } : {})
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
});
|
|
1647
2137
|
export const openAIRemoteMcpTool = (config) => hostedTool({
|
|
1648
2138
|
name: config.server_label ?? "mcp",
|
|
1649
2139
|
provider: "openai",
|
|
@@ -1656,6 +2146,10 @@ export const openAIMcpApprovalResponse = (response) => providerDataPart("openai"
|
|
|
1656
2146
|
type: "mcp_approval_response",
|
|
1657
2147
|
...response
|
|
1658
2148
|
});
|
|
2149
|
+
export const openAIPromptCacheBreakpoint = () => providerDataPart("openai", {
|
|
2150
|
+
type: "prompt_cache_breakpoint",
|
|
2151
|
+
mode: "explicit"
|
|
2152
|
+
});
|
|
1659
2153
|
export const openAIComputerUseTool = (config) => hostedTool({
|
|
1660
2154
|
name: "computer",
|
|
1661
2155
|
provider: "openai",
|
|
@@ -1663,63 +2157,106 @@ export const openAIComputerUseTool = (config) => hostedTool({
|
|
|
1663
2157
|
toolClass: "computer-use",
|
|
1664
2158
|
config: config
|
|
1665
2159
|
});
|
|
2160
|
+
export const openAIComputerTool = (config) => ({
|
|
2161
|
+
name: config.name ?? "computer",
|
|
2162
|
+
description: "Execute batched actions requested by the OpenAI Responses computer tool and return a screenshot.",
|
|
2163
|
+
requiresApproval: config.requiresApproval ?? true,
|
|
2164
|
+
metadata: {
|
|
2165
|
+
[openAIResponsesToolMetadataKey]: "computer",
|
|
2166
|
+
"openai.responses_tool_config": {}
|
|
2167
|
+
},
|
|
2168
|
+
schema: z.object({
|
|
2169
|
+
actions: z.array(z.record(z.string(), z.unknown()))
|
|
2170
|
+
}),
|
|
2171
|
+
execute: async (input) => {
|
|
2172
|
+
const output = await config.execute(input);
|
|
2173
|
+
return {
|
|
2174
|
+
...output,
|
|
2175
|
+
detail: "original"
|
|
2176
|
+
};
|
|
2177
|
+
}
|
|
2178
|
+
});
|
|
1666
2179
|
const runOpenAIShellCommand = async (input, config) => {
|
|
1667
2180
|
if (config.execute) {
|
|
1668
|
-
|
|
2181
|
+
const result = await config.execute(input);
|
|
2182
|
+
return Array.isArray(result) ? result : [result];
|
|
1669
2183
|
}
|
|
1670
|
-
const
|
|
1671
|
-
|
|
2184
|
+
const commands = input.action?.commands?.length
|
|
2185
|
+
? input.action.commands
|
|
2186
|
+
: [input.command ?? input.action?.command].filter((command) => Boolean(command));
|
|
2187
|
+
if (!commands.length) {
|
|
1672
2188
|
throw new Error("OpenAI shell tool did not provide a command.");
|
|
1673
2189
|
}
|
|
1674
2190
|
const { exec } = await import("node:child_process");
|
|
1675
2191
|
const { promisify } = await import("node:util");
|
|
1676
2192
|
const execAsync = promisify(exec);
|
|
1677
|
-
const maxBuffer = Math.max(1024, config.maxOutputLength ??
|
|
2193
|
+
const maxBuffer = Math.max(1024, config.maxOutputLength ??
|
|
2194
|
+
input.maxOutputLength ??
|
|
2195
|
+
input.max_output_length ??
|
|
2196
|
+
input.action?.maxOutputLength ??
|
|
2197
|
+
input.action?.max_output_length ??
|
|
2198
|
+
20000);
|
|
1678
2199
|
const cwd = config.rootDir
|
|
1679
2200
|
? await assertOpenAIToolPathInsideRoot(config.rootDir, config.cwd ?? ".", "shell cwd")
|
|
1680
2201
|
: config.cwd;
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
}
|
|
2202
|
+
const output = [];
|
|
2203
|
+
for (const command of commands) {
|
|
2204
|
+
try {
|
|
2205
|
+
const result = await execAsync(command, {
|
|
2206
|
+
cwd,
|
|
2207
|
+
timeout: config.timeoutMs ?? input.action?.timeout_ms,
|
|
2208
|
+
maxBuffer
|
|
2209
|
+
});
|
|
2210
|
+
output.push({
|
|
2211
|
+
stdout: result.stdout,
|
|
2212
|
+
stderr: result.stderr,
|
|
2213
|
+
outcome: { type: "exit", exit_code: 0 },
|
|
2214
|
+
maxOutputLength: maxBuffer
|
|
2215
|
+
});
|
|
2216
|
+
}
|
|
2217
|
+
catch (error) {
|
|
2218
|
+
const err = error;
|
|
2219
|
+
output.push({
|
|
2220
|
+
stdout: err.stdout ?? "",
|
|
2221
|
+
stderr: err.stderr ?? err.message ?? "",
|
|
2222
|
+
outcome: {
|
|
2223
|
+
type: err.killed || err.signal === "SIGTERM" ? "timeout" : "exit",
|
|
2224
|
+
exit_code: typeof err.code === "number" ? err.code : 1
|
|
2225
|
+
},
|
|
2226
|
+
maxOutputLength: maxBuffer
|
|
2227
|
+
});
|
|
2228
|
+
}
|
|
1708
2229
|
}
|
|
2230
|
+
return output;
|
|
1709
2231
|
};
|
|
2232
|
+
export const openAIHostedShellTool = (config) => hostedTool({
|
|
2233
|
+
name: "shell",
|
|
2234
|
+
provider: "openai",
|
|
2235
|
+
type: "shell",
|
|
2236
|
+
toolClass: "shell",
|
|
2237
|
+
config: {
|
|
2238
|
+
environment: config.environment,
|
|
2239
|
+
...(config.allowedCallers ? { allowed_callers: config.allowedCallers } : {})
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
1710
2242
|
export const openAIShellTool = (config = {}) => ({
|
|
1711
2243
|
name: config.name ?? "shell",
|
|
1712
2244
|
description: "Run a shell command requested by the OpenAI Responses shell tool.",
|
|
1713
2245
|
requiresApproval: true,
|
|
1714
2246
|
metadata: {
|
|
1715
2247
|
[openAIResponsesToolMetadataKey]: "shell",
|
|
1716
|
-
"openai.responses_tool_config": {
|
|
2248
|
+
"openai.responses_tool_config": {
|
|
2249
|
+
environment: config.environment ?? { type: "local" },
|
|
2250
|
+
...(config.allowedCallers ? { allowed_callers: config.allowedCallers } : {})
|
|
2251
|
+
}
|
|
1717
2252
|
},
|
|
1718
2253
|
schema: z.object({
|
|
1719
2254
|
command: z.string().optional(),
|
|
1720
2255
|
action: z
|
|
1721
2256
|
.object({
|
|
1722
2257
|
command: z.string().optional(),
|
|
2258
|
+
commands: z.array(z.string()).optional(),
|
|
2259
|
+
timeout_ms: z.number().optional(),
|
|
1723
2260
|
max_output_length: z.number().optional(),
|
|
1724
2261
|
maxOutputLength: z.number().optional()
|
|
1725
2262
|
})
|
|
@@ -1742,7 +2279,9 @@ export const openAIApplyPatchTool = (config) => ({
|
|
|
1742
2279
|
requiresApproval: true,
|
|
1743
2280
|
metadata: {
|
|
1744
2281
|
[openAIResponsesToolMetadataKey]: "apply_patch",
|
|
1745
|
-
"openai.responses_tool_config": {
|
|
2282
|
+
"openai.responses_tool_config": {
|
|
2283
|
+
...(config.allowedCallers ? { allowed_callers: config.allowedCallers } : {})
|
|
2284
|
+
}
|
|
1746
2285
|
},
|
|
1747
2286
|
schema: z.object({
|
|
1748
2287
|
operation: z
|