@zhivex-ai/openai 0.8.0 → 0.9.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 +79 -0
- package/dist/image-generation.d.ts +109 -0
- package/dist/image-generation.d.ts.map +1 -0
- package/dist/image-generation.js +343 -0
- package/dist/image-generation.js.map +1 -0
- package/dist/index.d.ts +52 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +392 -35
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { toJSONSchema, z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { createOpenAIImageGenerationModel, normalizeOpenAIImageGenerationCall, normalizeOpenAIImageGenerationPartialImage } from "./image-generation.js";
|
|
3
|
+
import { CallbackRealtimeSession, ConfigurationError, ProviderHTTPError, ProviderResponseTooLargeError, readBodyWithLimit, readErrorBodyWithLimit, readJsonWithLimit, resolveAudioResponseLimits, openWebSocketConnection, hostedTool, providerDataPart, UnsupportedFeatureError, createProviderAdapter, encodeAudioFrame, encodeMediaFrame, isCallableToolDefinition, isHostedToolDefinition, normalizeFinishReason, streamSSE, toToolSet, toolResultPayload, withRetry, withTimeoutSignal } from "@zhivex-ai/core";
|
|
4
|
+
const MIB = 1024 * 1024;
|
|
5
|
+
const DEFAULT_HOSTED_IMAGE_EVENT_BYTES = 32 * MIB;
|
|
6
|
+
const DEFAULT_HOSTED_IMAGE_TOTAL_BYTES = 128 * MIB;
|
|
7
|
+
const HOSTED_IMAGE_EVENT_JSON_OVERHEAD_CHARS = MIB;
|
|
8
|
+
const normalizeOpenAIResponseLimit = (value, fallback, name) => {
|
|
9
|
+
const resolved = value ?? fallback;
|
|
10
|
+
if (!Number.isSafeInteger(resolved) || resolved <= 0) {
|
|
11
|
+
throw new ConfigurationError(`The "${name}" response limit must be a positive safe integer.`);
|
|
12
|
+
}
|
|
13
|
+
return resolved;
|
|
14
|
+
};
|
|
15
|
+
const resolveOpenAIResponseLimits = (limits = {}) => ({
|
|
16
|
+
...resolveAudioResponseLimits(limits),
|
|
17
|
+
hostedImageEventBytes: normalizeOpenAIResponseLimit(limits.hostedImageEventBytes, DEFAULT_HOSTED_IMAGE_EVENT_BYTES, "hostedImageEventBytes"),
|
|
18
|
+
hostedImageTotalBytes: normalizeOpenAIResponseLimit(limits.hostedImageTotalBytes, DEFAULT_HOSTED_IMAGE_TOTAL_BYTES, "hostedImageTotalBytes")
|
|
19
|
+
});
|
|
3
20
|
const openAIResponsesToolMetadataKey = "openai.responses_tool_type";
|
|
4
21
|
const openAIResponsesFunctionConfigMetadataKey = "openai.responses_function_config";
|
|
5
22
|
const openAIResponsesFunctionConfig = (tool) => {
|
|
@@ -27,6 +44,7 @@ const assertOpenAIToolPathInsideRoot = async (rootDir, targetPath, label) => {
|
|
|
27
44
|
}
|
|
28
45
|
return target;
|
|
29
46
|
};
|
|
47
|
+
export const openAIRealtimeMcpMetadataKey = "openai.realtime_mcp";
|
|
30
48
|
const capabilities = {
|
|
31
49
|
streaming: true,
|
|
32
50
|
tools: true,
|
|
@@ -154,17 +172,38 @@ const inferOpenAIRealtimeMode = (modelId, mode) => {
|
|
|
154
172
|
}
|
|
155
173
|
return "conversation";
|
|
156
174
|
};
|
|
157
|
-
const
|
|
175
|
+
const isOpenAIRealtimeReasoningModel = (modelId) => /^gpt-realtime-2(?:\.1(?:-mini)?)?(?:-\d{4}-\d{2}-\d{2}|@.*)?$/.test(modelId);
|
|
176
|
+
const openAIRealtimeSupportsImageInput = (modelId) => /^(?:gpt-realtime|gpt-realtime-mini|gpt-realtime-1\.5|gpt-realtime-2(?:\.1(?:-mini)?)?)(?:-\d{4}-\d{2}-\d{2}|@.*)?$/.test(modelId);
|
|
158
177
|
const realtimeCapabilities = (modelId) => ({
|
|
159
178
|
...capabilities,
|
|
160
179
|
streaming: false,
|
|
180
|
+
structuredOutput: false,
|
|
181
|
+
jsonMode: false,
|
|
182
|
+
embeddings: false,
|
|
161
183
|
audioInput: true,
|
|
162
184
|
audioOutput: !isOpenAIRealtimeTranscriptionModel(modelId),
|
|
163
185
|
tools: !isOpenAIRealtimeTranslationModel(modelId) && !isOpenAIRealtimeTranscriptionModel(modelId),
|
|
164
186
|
toolChoice: !isOpenAIRealtimeTranslationModel(modelId) && !isOpenAIRealtimeTranscriptionModel(modelId),
|
|
165
187
|
parallelToolCalls: !isOpenAIRealtimeTranslationModel(modelId) && !isOpenAIRealtimeTranscriptionModel(modelId),
|
|
166
188
|
vision: openAIRealtimeSupportsImageInput(modelId),
|
|
167
|
-
reasoning:
|
|
189
|
+
reasoning: isOpenAIRealtimeReasoningModel(modelId),
|
|
190
|
+
webSearch: false,
|
|
191
|
+
agentCapabilities: {
|
|
192
|
+
...capabilities.agentCapabilities,
|
|
193
|
+
supportTier: isOpenAIRealtimeTranslationModel(modelId) || isOpenAIRealtimeTranscriptionModel(modelId) ? "tier-c" : "tier-a",
|
|
194
|
+
toolChoiceNone: !isOpenAIRealtimeTranslationModel(modelId) && !isOpenAIRealtimeTranscriptionModel(modelId),
|
|
195
|
+
approvalRequests: !isOpenAIRealtimeTranslationModel(modelId) && !isOpenAIRealtimeTranscriptionModel(modelId),
|
|
196
|
+
hostedWebSearch: false,
|
|
197
|
+
hostedFileSearch: false,
|
|
198
|
+
remoteMcp: !isOpenAIRealtimeTranslationModel(modelId) && !isOpenAIRealtimeTranscriptionModel(modelId),
|
|
199
|
+
computerUse: false,
|
|
200
|
+
codeExecution: false,
|
|
201
|
+
shell: false,
|
|
202
|
+
applyPatch: false,
|
|
203
|
+
toolSearch: false,
|
|
204
|
+
skills: false,
|
|
205
|
+
toolsets: false
|
|
206
|
+
},
|
|
168
207
|
realtime: {
|
|
169
208
|
sessions: true,
|
|
170
209
|
audioInput: true,
|
|
@@ -178,6 +217,28 @@ const jsonHeaders = (apiKey) => ({
|
|
|
178
217
|
"content-type": "application/json",
|
|
179
218
|
authorization: `Bearer ${apiKey}`
|
|
180
219
|
});
|
|
220
|
+
const resolveOpenAIRealtimeHeaders = (apiKey, providerOptions, includeContentType = false) => {
|
|
221
|
+
const rawHeaders = providerOptions?.headers;
|
|
222
|
+
const customHeaders = rawHeaders && typeof rawHeaders === "object" && !Array.isArray(rawHeaders)
|
|
223
|
+
? { ...rawHeaders }
|
|
224
|
+
: {};
|
|
225
|
+
const safetyIdentifier = providerOptions?.safety_identifier;
|
|
226
|
+
for (const key of Object.keys(customHeaders)) {
|
|
227
|
+
const normalizedKey = key.toLowerCase();
|
|
228
|
+
if (normalizedKey === "authorization" ||
|
|
229
|
+
normalizedKey === "content-type" ||
|
|
230
|
+
(typeof safetyIdentifier === "string" && safetyIdentifier && normalizedKey === "openai-safety-identifier")) {
|
|
231
|
+
delete customHeaders[key];
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
...(includeContentType ? jsonHeaders(apiKey) : { authorization: `Bearer ${apiKey}` }),
|
|
236
|
+
...customHeaders,
|
|
237
|
+
...(typeof safetyIdentifier === "string" && safetyIdentifier
|
|
238
|
+
? { "OpenAI-Safety-Identifier": safetyIdentifier }
|
|
239
|
+
: {})
|
|
240
|
+
};
|
|
241
|
+
};
|
|
181
242
|
const resolveOpenAILanguageRequestOptions = (providerOptions, apiKey) => {
|
|
182
243
|
const bodyOptions = { ...(providerOptions ?? {}) };
|
|
183
244
|
const apiMode = bodyOptions.apiMode ?? "auto";
|
|
@@ -227,9 +288,12 @@ const toUint8Array = (data) => {
|
|
|
227
288
|
return Uint8Array.from(Buffer.from(data, "base64"));
|
|
228
289
|
};
|
|
229
290
|
const toBase64 = (data) => typeof data === "string" ? data : Buffer.from(data instanceof Uint8Array ? data : new Uint8Array(data)).toString("base64");
|
|
230
|
-
const createAudioFile = (audio) =>
|
|
231
|
-
|
|
232
|
-
|
|
291
|
+
const createAudioFile = (audio) => {
|
|
292
|
+
const bytes = toUint8Array(audio.data);
|
|
293
|
+
return new File([bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)], audio.filename ?? "audio", {
|
|
294
|
+
type: audio.mediaType
|
|
295
|
+
});
|
|
296
|
+
};
|
|
233
297
|
const inferOpenAIAudioFormat = (mediaType, explicitFormat) => {
|
|
234
298
|
if (explicitFormat) {
|
|
235
299
|
return explicitFormat;
|
|
@@ -255,14 +319,21 @@ const inferOpenAIAudioFormat = (mediaType, explicitFormat) => {
|
|
|
255
319
|
}
|
|
256
320
|
return normalized?.replace(/^audio\//, "") || mediaType;
|
|
257
321
|
};
|
|
258
|
-
const parseJson = async (response) => {
|
|
322
|
+
const parseJson = async (response, options = {}) => {
|
|
259
323
|
if (!response.ok) {
|
|
260
|
-
const body = await response.
|
|
324
|
+
const body = await readErrorBodyWithLimit(response, options.errorBodyBytes);
|
|
261
325
|
throw new ProviderHTTPError(`OpenAI request failed with status ${response.status}.`, response.status, {
|
|
262
326
|
responseBody: body
|
|
263
327
|
});
|
|
264
328
|
}
|
|
265
|
-
return
|
|
329
|
+
return options.maxBytes
|
|
330
|
+
? readJsonWithLimit(response, {
|
|
331
|
+
maxBytes: options.maxBytes,
|
|
332
|
+
provider: options.provider ?? "openai",
|
|
333
|
+
endpoint: options.endpoint,
|
|
334
|
+
abort: options.abort
|
|
335
|
+
})
|
|
336
|
+
: response.json();
|
|
266
337
|
};
|
|
267
338
|
const isOpenAIPromptCacheBreakpointPart = (part) => part.type === "provider-data" &&
|
|
268
339
|
part.provider === "openai" &&
|
|
@@ -381,6 +452,25 @@ const localResponsesTools = (tools) => new Map(Object.values(tools ?? {})
|
|
|
381
452
|
.filter(isCallableToolDefinition)
|
|
382
453
|
.map((tool) => [openAILocalResponsesToolType(tool), tool.name])
|
|
383
454
|
.filter((entry) => Boolean(entry[0])));
|
|
455
|
+
const responsesImageGenerationConfig = (tools, limits) => {
|
|
456
|
+
const definition = Object.values(tools ?? {}).find((tool) => isHostedToolDefinition(tool) && tool.provider === "openai" && tool.type === "image_generation");
|
|
457
|
+
if (!definition || !isHostedToolDefinition(definition)) {
|
|
458
|
+
return undefined;
|
|
459
|
+
}
|
|
460
|
+
const outputFormat = definition.config && typeof definition.config === "object" &&
|
|
461
|
+
["png", "jpeg", "webp"].includes(String(definition.config.output_format))
|
|
462
|
+
? definition.config.output_format
|
|
463
|
+
: "png";
|
|
464
|
+
return {
|
|
465
|
+
outputFormat,
|
|
466
|
+
...(limits
|
|
467
|
+
? {
|
|
468
|
+
eventMaxBytes: limits.hostedImageEventBytes,
|
|
469
|
+
totalMaxBytes: limits.hostedImageTotalBytes
|
|
470
|
+
}
|
|
471
|
+
: {})
|
|
472
|
+
};
|
|
473
|
+
};
|
|
384
474
|
const assertResponsesToolsSupported = (modelId, tools) => {
|
|
385
475
|
const currentCapabilities = modelCapabilities(modelId).agentCapabilities;
|
|
386
476
|
for (const definition of Object.values(tools ?? {})) {
|
|
@@ -439,6 +529,28 @@ const mapTools = (input) => input
|
|
|
439
529
|
};
|
|
440
530
|
})
|
|
441
531
|
: undefined;
|
|
532
|
+
const mapRealtimeTools = (input) => input
|
|
533
|
+
? Object.values(input).map((tool) => {
|
|
534
|
+
if (isCallableToolDefinition(tool)) {
|
|
535
|
+
return {
|
|
536
|
+
type: "function",
|
|
537
|
+
name: tool.name,
|
|
538
|
+
description: tool.description,
|
|
539
|
+
parameters: toJSONSchema(tool.schema)
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
if (tool.provider && tool.provider !== "openai") {
|
|
543
|
+
throw new UnsupportedFeatureError(`Provider "openai" does not support hosted tools declared for provider "${tool.provider}".`);
|
|
544
|
+
}
|
|
545
|
+
if (tool.type !== "mcp") {
|
|
546
|
+
throw new UnsupportedFeatureError(`Provider "openai" Realtime does not support the hosted tool type "${tool.type}". Use function or MCP tools.`);
|
|
547
|
+
}
|
|
548
|
+
return {
|
|
549
|
+
type: tool.type,
|
|
550
|
+
...(tool.config && typeof tool.config === "object" ? tool.config : {})
|
|
551
|
+
};
|
|
552
|
+
})
|
|
553
|
+
: undefined;
|
|
442
554
|
const mapResponsesTools = (input) => input
|
|
443
555
|
? Object.values(input).map((tool) => {
|
|
444
556
|
if (isCallableToolDefinition(tool)) {
|
|
@@ -489,11 +601,20 @@ const mapResponsesToolChoice = (toolChoice) => {
|
|
|
489
601
|
name: toolChoice.toolName
|
|
490
602
|
};
|
|
491
603
|
};
|
|
604
|
+
const mapRealtimeToolChoice = (toolChoice) => {
|
|
605
|
+
if (!toolChoice || typeof toolChoice === "string") {
|
|
606
|
+
return toolChoice;
|
|
607
|
+
}
|
|
608
|
+
return {
|
|
609
|
+
type: "function",
|
|
610
|
+
name: toolChoice.toolName
|
|
611
|
+
};
|
|
612
|
+
};
|
|
492
613
|
const mapRealtimeProviderOptions = (providerOptions) => {
|
|
493
614
|
if (!providerOptions) {
|
|
494
615
|
return {};
|
|
495
616
|
}
|
|
496
|
-
return Object.fromEntries(Object.entries(providerOptions).filter(([key]) => !["headers", "realtime_url", "realtime_query", "expires_after"].includes(key)));
|
|
617
|
+
return Object.fromEntries(Object.entries(providerOptions).filter(([key]) => !["headers", "safety_identifier", "realtime_url", "realtime_query", "expires_after"].includes(key)));
|
|
497
618
|
};
|
|
498
619
|
const mapRealtimeAudioFormat = (mediaType, sampleRateHz) => mediaType || sampleRateHz
|
|
499
620
|
? {
|
|
@@ -503,7 +624,7 @@ const mapRealtimeAudioFormat = (mediaType, sampleRateHz) => mediaType || sampleR
|
|
|
503
624
|
: undefined;
|
|
504
625
|
const mapRealtimeSessionConfig = (config, modelId) => {
|
|
505
626
|
const mode = inferOpenAIRealtimeMode(modelId ?? "", config.mode);
|
|
506
|
-
const tools =
|
|
627
|
+
const tools = mapRealtimeTools(toToolSet(config.tools));
|
|
507
628
|
const audio = {
|
|
508
629
|
input: {
|
|
509
630
|
format: mapRealtimeAudioFormat(config.inputAudioMediaType, config.inputSampleRateHz),
|
|
@@ -533,7 +654,7 @@ const mapRealtimeSessionConfig = (config, modelId) => {
|
|
|
533
654
|
instructions: config.translation?.instructions ?? config.instructions,
|
|
534
655
|
output_modalities: mode === "transcription" ? undefined : config.outputAudioMediaType || config.voice || mode === "translation" ? ["audio"] : ["text"],
|
|
535
656
|
tools: mode === "conversation" ? tools : undefined,
|
|
536
|
-
tool_choice: mode === "conversation" && config.toolChoice ?
|
|
657
|
+
tool_choice: mode === "conversation" && config.toolChoice ? mapRealtimeToolChoice(config.toolChoice) : undefined,
|
|
537
658
|
reasoning: mode === "conversation" && config.reasoning?.effort
|
|
538
659
|
? {
|
|
539
660
|
effort: config.reasoning.effort
|
|
@@ -571,11 +692,124 @@ const openAIRealtimeURL = (baseURL, modelId, mode, providerOptions) => {
|
|
|
571
692
|
return url.toString();
|
|
572
693
|
};
|
|
573
694
|
const parseRealtimeProviderMetadata = (payload) => payload;
|
|
695
|
+
const parseRealtimeJsonValue = (value) => {
|
|
696
|
+
if (typeof value === "string") {
|
|
697
|
+
try {
|
|
698
|
+
return JSON.parse(value);
|
|
699
|
+
}
|
|
700
|
+
catch {
|
|
701
|
+
return value;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return value == null ? null : value;
|
|
705
|
+
};
|
|
706
|
+
const openAIRealtimeMcpProviderDataEvent = (payload, data) => [
|
|
707
|
+
{
|
|
708
|
+
type: "realtime-provider-data",
|
|
709
|
+
provider: "openai",
|
|
710
|
+
data: {
|
|
711
|
+
...data,
|
|
712
|
+
raw_event: payload
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
];
|
|
716
|
+
const openAIRealtimeMcpCommonData = (payload, item) => {
|
|
717
|
+
const itemId = typeof payload.item_id === "string" ? payload.item_id : typeof item?.id === "string" ? item.id : undefined;
|
|
718
|
+
const serverLabel = typeof payload.server_label === "string"
|
|
719
|
+
? payload.server_label
|
|
720
|
+
: typeof item?.server_label === "string"
|
|
721
|
+
? item.server_label
|
|
722
|
+
: undefined;
|
|
723
|
+
const name = typeof payload.name === "string" ? payload.name : typeof item?.name === "string" ? item.name : undefined;
|
|
724
|
+
return {
|
|
725
|
+
...(itemId ? { item_id: itemId } : {}),
|
|
726
|
+
...(serverLabel ? { server_label: serverLabel } : {}),
|
|
727
|
+
...(name ? { name } : {})
|
|
728
|
+
};
|
|
729
|
+
};
|
|
574
730
|
const parseOpenAIRealtimeEvent = (payload) => {
|
|
575
731
|
const type = String(payload.type ?? "");
|
|
576
732
|
if (type === "session.created" || type === "session.updated") {
|
|
577
733
|
return [];
|
|
578
734
|
}
|
|
735
|
+
if (type === "mcp_list_tools.in_progress" || type === "mcp_list_tools.completed" || type === "mcp_list_tools.failed") {
|
|
736
|
+
const status = type.slice("mcp_list_tools.".length);
|
|
737
|
+
return openAIRealtimeMcpProviderDataEvent(payload, {
|
|
738
|
+
type: "mcp_list_tools",
|
|
739
|
+
status,
|
|
740
|
+
...openAIRealtimeMcpCommonData(payload),
|
|
741
|
+
...(payload.error !== undefined ? { error: parseRealtimeJsonValue(payload.error) } : {})
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
if (type === "conversation.item.done") {
|
|
745
|
+
const item = payload.item && typeof payload.item === "object" ? payload.item : undefined;
|
|
746
|
+
if (item?.type === "mcp_list_tools") {
|
|
747
|
+
return openAIRealtimeMcpProviderDataEvent(payload, {
|
|
748
|
+
type: "mcp_list_tools",
|
|
749
|
+
status: "completed",
|
|
750
|
+
...openAIRealtimeMcpCommonData(payload, item),
|
|
751
|
+
...(item.tools !== undefined ? { tools: parseRealtimeJsonValue(item.tools) } : {})
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
if (item?.type === "mcp_approval_request") {
|
|
755
|
+
const approvalRequestId = typeof item.id === "string" ? item.id : undefined;
|
|
756
|
+
return openAIRealtimeMcpProviderDataEvent(payload, {
|
|
757
|
+
type: "mcp_approval_request",
|
|
758
|
+
status: "approval_required",
|
|
759
|
+
...openAIRealtimeMcpCommonData(payload, item),
|
|
760
|
+
...(approvalRequestId ? { approval_request_id: approvalRequestId } : {}),
|
|
761
|
+
...(item.arguments !== undefined ? { arguments: parseRealtimeJsonValue(item.arguments) } : {})
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
if (item?.type === "mcp_approval_response") {
|
|
765
|
+
const approve = item.approve === true;
|
|
766
|
+
return openAIRealtimeMcpProviderDataEvent(payload, {
|
|
767
|
+
type: "mcp_approval_response",
|
|
768
|
+
status: approve ? "approved" : "rejected",
|
|
769
|
+
approve,
|
|
770
|
+
...openAIRealtimeMcpCommonData(payload, item),
|
|
771
|
+
...(typeof item.approval_request_id === "string" ? { approval_request_id: item.approval_request_id } : {}),
|
|
772
|
+
...(typeof item.reason === "string" ? { reason: item.reason } : {})
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
if (type === "response.mcp_call_arguments.delta" || type === "response.mcp_call_arguments.done") {
|
|
777
|
+
const isDone = type.endsWith(".done");
|
|
778
|
+
return openAIRealtimeMcpProviderDataEvent(payload, {
|
|
779
|
+
type: "mcp_call",
|
|
780
|
+
status: isDone ? "arguments_done" : "arguments_delta",
|
|
781
|
+
...openAIRealtimeMcpCommonData(payload),
|
|
782
|
+
...(isDone && payload.arguments !== undefined
|
|
783
|
+
? { arguments: parseRealtimeJsonValue(payload.arguments) }
|
|
784
|
+
: payload.delta !== undefined
|
|
785
|
+
? { delta: parseRealtimeJsonValue(payload.delta) }
|
|
786
|
+
: {})
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
if (type === "response.mcp_call.in_progress" || type === "response.mcp_call.completed" || type === "response.mcp_call.failed") {
|
|
790
|
+
const status = type.slice("response.mcp_call.".length);
|
|
791
|
+
return openAIRealtimeMcpProviderDataEvent(payload, {
|
|
792
|
+
type: "mcp_call",
|
|
793
|
+
status,
|
|
794
|
+
...openAIRealtimeMcpCommonData(payload),
|
|
795
|
+
...(payload.output !== undefined ? { output: parseRealtimeJsonValue(payload.output) } : {}),
|
|
796
|
+
...(payload.error !== undefined ? { error: parseRealtimeJsonValue(payload.error) } : {})
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
if (type === "response.output_item.done") {
|
|
800
|
+
const item = payload.item && typeof payload.item === "object" ? payload.item : undefined;
|
|
801
|
+
if (item?.type === "mcp_call") {
|
|
802
|
+
return openAIRealtimeMcpProviderDataEvent(payload, {
|
|
803
|
+
type: "mcp_call",
|
|
804
|
+
status: item.status === "failed" ? "failed" : "completed",
|
|
805
|
+
...openAIRealtimeMcpCommonData(payload, item),
|
|
806
|
+
...(item.arguments !== undefined ? { arguments: parseRealtimeJsonValue(item.arguments) } : {}),
|
|
807
|
+
...(item.output !== undefined ? { output: parseRealtimeJsonValue(item.output) } : {}),
|
|
808
|
+
...(item.error !== undefined ? { error: parseRealtimeJsonValue(item.error) } : {}),
|
|
809
|
+
...(typeof item.approval_request_id === "string" ? { approval_request_id: item.approval_request_id } : {})
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
}
|
|
579
813
|
if (type === "response.text.delta" || type === "response.output_text.delta") {
|
|
580
814
|
return [
|
|
581
815
|
{
|
|
@@ -905,7 +1139,7 @@ const parseResponsesProviderData = (item) => {
|
|
|
905
1139
|
}
|
|
906
1140
|
const typedItem = item;
|
|
907
1141
|
if (typeof typedItem.type !== "string" ||
|
|
908
|
-
["message", "function_call"].includes(typedItem.type)) {
|
|
1142
|
+
["message", "function_call", "image_generation_call"].includes(typedItem.type)) {
|
|
909
1143
|
return undefined;
|
|
910
1144
|
}
|
|
911
1145
|
return item;
|
|
@@ -1149,6 +1383,10 @@ const parseResponsesAssistantMessage = (json, multiAgentEnabled = false, localTo
|
|
|
1149
1383
|
parts
|
|
1150
1384
|
};
|
|
1151
1385
|
};
|
|
1386
|
+
const extractResponsesImageOutputs = (response, outputFormat = "png") => (Array.isArray(response.output) ? response.output : []).flatMap((item) => {
|
|
1387
|
+
const normalized = normalizeOpenAIImageGenerationCall(item, outputFormat);
|
|
1388
|
+
return normalized?.image ? [normalized.image] : [];
|
|
1389
|
+
});
|
|
1152
1390
|
const normalizeResponsesFinishReason = (status, hasToolCalls, hasRefusal = false) => {
|
|
1153
1391
|
if (hasToolCalls) {
|
|
1154
1392
|
return "tool-calls";
|
|
@@ -1199,11 +1437,26 @@ const addTokenUsage = (left, right) => {
|
|
|
1199
1437
|
totalTokens: sum(left.totalTokens, right.totalTokens)
|
|
1200
1438
|
};
|
|
1201
1439
|
};
|
|
1202
|
-
const streamResponses = async function* (response, multiAgentEnabled = false, localTools = new Map()) {
|
|
1440
|
+
const streamResponses = async function* (response, multiAgentEnabled = false, localTools = new Map(), imageGeneration) {
|
|
1203
1441
|
const toolBuffers = new Map();
|
|
1204
1442
|
const outputAgents = new Map();
|
|
1443
|
+
const hostedImageEventMaxBytes = imageGeneration?.eventMaxBytes ?? DEFAULT_HOSTED_IMAGE_EVENT_BYTES;
|
|
1444
|
+
const hostedImageTotalMaxBytes = imageGeneration?.totalMaxBytes ?? DEFAULT_HOSTED_IMAGE_TOTAL_BYTES;
|
|
1445
|
+
let hostedImageBytes = 0;
|
|
1205
1446
|
let sawToolCalls = false;
|
|
1206
1447
|
let sawRefusal = false;
|
|
1448
|
+
const recordHostedImageBytes = (image) => {
|
|
1449
|
+
const receivedBytes = hostedImageBytes + (image.data?.byteLength ?? 0);
|
|
1450
|
+
if (receivedBytes > hostedImageTotalMaxBytes) {
|
|
1451
|
+
throw new ProviderResponseTooLargeError({
|
|
1452
|
+
maxBytes: hostedImageTotalMaxBytes,
|
|
1453
|
+
receivedBytes,
|
|
1454
|
+
provider: "openai",
|
|
1455
|
+
endpoint: "hosted image generation stream"
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
hostedImageBytes = receivedBytes;
|
|
1459
|
+
};
|
|
1207
1460
|
const emitToolCall = (key) => {
|
|
1208
1461
|
const toolCall = toolBuffers.get(key);
|
|
1209
1462
|
if (!toolCall || toolCall.emitted || !toolCall.name) {
|
|
@@ -1221,12 +1474,31 @@ const streamResponses = async function* (response, multiAgentEnabled = false, lo
|
|
|
1221
1474
|
}
|
|
1222
1475
|
};
|
|
1223
1476
|
};
|
|
1224
|
-
|
|
1477
|
+
const maxHostedImageEventChars = Math.min(Number.MAX_SAFE_INTEGER, 4 * Math.ceil(hostedImageEventMaxBytes / 3) + HOSTED_IMAGE_EVENT_JSON_OVERHEAD_CHARS);
|
|
1478
|
+
for await (const event of streamSSE(response, imageGeneration
|
|
1479
|
+
? { maxEventChars: maxHostedImageEventChars, maxBufferChars: maxHostedImageEventChars }
|
|
1480
|
+
: undefined)) {
|
|
1225
1481
|
if (event.data === "[DONE]") {
|
|
1226
1482
|
return;
|
|
1227
1483
|
}
|
|
1228
1484
|
const json = JSON.parse(event.data);
|
|
1229
1485
|
const type = json.type;
|
|
1486
|
+
if (type === "response.image_generation_call.partial_image") {
|
|
1487
|
+
const normalized = normalizeOpenAIImageGenerationPartialImage(json, imageGeneration?.outputFormat ?? "png", hostedImageEventMaxBytes);
|
|
1488
|
+
if (normalized) {
|
|
1489
|
+
recordHostedImageBytes(normalized.image);
|
|
1490
|
+
yield {
|
|
1491
|
+
type: "image-generation",
|
|
1492
|
+
provider: "openai",
|
|
1493
|
+
image: normalized.image,
|
|
1494
|
+
partial: true,
|
|
1495
|
+
id: normalized.callId,
|
|
1496
|
+
index: normalized.partialImageIndex,
|
|
1497
|
+
providerMetadata: normalized.providerMetadata
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
continue;
|
|
1501
|
+
}
|
|
1230
1502
|
if (type === "error") {
|
|
1231
1503
|
throw new ProviderHTTPError(`OpenAI Responses stream failed: ${json.error?.message ?? json.message ?? "unknown error"}`, 500, { responseBody: JSON.stringify(json) });
|
|
1232
1504
|
}
|
|
@@ -1248,6 +1520,20 @@ const streamResponses = async function* (response, multiAgentEnabled = false, lo
|
|
|
1248
1520
|
if (type === "response.output_item.added" || type === "response.output_item.done") {
|
|
1249
1521
|
const item = json.item;
|
|
1250
1522
|
let handledLocalToolCall = false;
|
|
1523
|
+
if (item?.type === "image_generation_call" && type === "response.output_item.done") {
|
|
1524
|
+
const normalized = normalizeOpenAIImageGenerationCall(item, imageGeneration?.outputFormat ?? "png", hostedImageEventMaxBytes);
|
|
1525
|
+
if (normalized?.image) {
|
|
1526
|
+
recordHostedImageBytes(normalized.image);
|
|
1527
|
+
yield {
|
|
1528
|
+
type: "image-generation",
|
|
1529
|
+
provider: "openai",
|
|
1530
|
+
image: normalized.image,
|
|
1531
|
+
partial: false,
|
|
1532
|
+
id: normalized.id,
|
|
1533
|
+
providerMetadata: normalized.providerMetadata
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1251
1537
|
if (typeof json.output_index === "number" && item?.type === "message") {
|
|
1252
1538
|
outputAgents.set(json.output_index, {
|
|
1253
1539
|
agentName: item.agent?.agent_name
|
|
@@ -1404,6 +1690,14 @@ const streamGenerateResult = async function* (result) {
|
|
|
1404
1690
|
}
|
|
1405
1691
|
}
|
|
1406
1692
|
}
|
|
1693
|
+
for (const image of result.images ?? []) {
|
|
1694
|
+
yield {
|
|
1695
|
+
type: "image-generation",
|
|
1696
|
+
provider: "openai",
|
|
1697
|
+
image,
|
|
1698
|
+
partial: false
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1407
1701
|
yield {
|
|
1408
1702
|
type: "finish",
|
|
1409
1703
|
finishReason: result.finishReason,
|
|
@@ -1442,13 +1736,15 @@ class OpenAILanguageModel {
|
|
|
1442
1736
|
apiKey;
|
|
1443
1737
|
baseURL;
|
|
1444
1738
|
fetcher;
|
|
1739
|
+
responseLimits;
|
|
1445
1740
|
provider = "openai";
|
|
1446
1741
|
capabilities;
|
|
1447
|
-
constructor(modelId, apiKey, baseURL, fetcher) {
|
|
1742
|
+
constructor(modelId, apiKey, baseURL, fetcher, responseLimits) {
|
|
1448
1743
|
this.modelId = modelId;
|
|
1449
1744
|
this.apiKey = apiKey;
|
|
1450
1745
|
this.baseURL = baseURL;
|
|
1451
1746
|
this.fetcher = fetcher;
|
|
1747
|
+
this.responseLimits = responseLimits;
|
|
1452
1748
|
this.capabilities = modelCapabilities(modelId);
|
|
1453
1749
|
}
|
|
1454
1750
|
usesResponsesAPI(input, options) {
|
|
@@ -1524,6 +1820,7 @@ class OpenAILanguageModel {
|
|
|
1524
1820
|
messages: [assistantMessage],
|
|
1525
1821
|
text: extractMessageText(assistantMessage),
|
|
1526
1822
|
audio: extractAudioOutputs(assistantMessage),
|
|
1823
|
+
images: extractResponsesImageOutputs(json, responsesImageGenerationConfig(input.tools)?.outputFormat),
|
|
1527
1824
|
finishReason: normalizeResponsesFinishReason(json.status, hasToolCalls, hasRefusal),
|
|
1528
1825
|
providerFinishReason: json.status,
|
|
1529
1826
|
usage: accumulatedUsage,
|
|
@@ -1623,9 +1920,10 @@ class OpenAILanguageModel {
|
|
|
1623
1920
|
stream: true
|
|
1624
1921
|
})
|
|
1625
1922
|
}), input);
|
|
1923
|
+
const imageGeneration = responsesImageGenerationConfig(input.tools, this.responseLimits);
|
|
1626
1924
|
return (async function* () {
|
|
1627
1925
|
try {
|
|
1628
|
-
yield* streamResponses(response, options.multiAgentEnabled, localResponsesTools(input.tools));
|
|
1926
|
+
yield* streamResponses(response, options.multiAgentEnabled, localResponsesTools(input.tools), imageGeneration);
|
|
1629
1927
|
}
|
|
1630
1928
|
finally {
|
|
1631
1929
|
cleanup();
|
|
@@ -1751,17 +2049,25 @@ class OpenAITranscriptionModel {
|
|
|
1751
2049
|
apiKey;
|
|
1752
2050
|
baseURL;
|
|
1753
2051
|
fetcher;
|
|
2052
|
+
responseLimits;
|
|
1754
2053
|
provider = "openai";
|
|
1755
2054
|
capabilities = transcriptionCapabilities;
|
|
1756
|
-
constructor(modelId, apiKey, baseURL, fetcher) {
|
|
2055
|
+
constructor(modelId, apiKey, baseURL, fetcher, responseLimits = resolveAudioResponseLimits()) {
|
|
1757
2056
|
this.modelId = modelId;
|
|
1758
2057
|
this.apiKey = apiKey;
|
|
1759
2058
|
this.baseURL = baseURL;
|
|
1760
2059
|
this.fetcher = fetcher;
|
|
2060
|
+
this.responseLimits = responseLimits;
|
|
1761
2061
|
}
|
|
1762
2062
|
async transcribe(input) {
|
|
1763
|
-
const { signal, cleanup } = withTimeoutSignal(input);
|
|
2063
|
+
const { signal, cleanup, abort } = withTimeoutSignal(input);
|
|
1764
2064
|
const form = new FormData();
|
|
2065
|
+
for (const [key, value] of Object.entries(input.providerOptions ?? {})) {
|
|
2066
|
+
if (["model", "file", "prompt", "language"].includes(key)) {
|
|
2067
|
+
continue;
|
|
2068
|
+
}
|
|
2069
|
+
form.set(key, typeof value === "string" ? value : JSON.stringify(value));
|
|
2070
|
+
}
|
|
1765
2071
|
form.set("model", this.modelId);
|
|
1766
2072
|
form.set("file", createAudioFile(input.audio));
|
|
1767
2073
|
if (input.prompt) {
|
|
@@ -1770,9 +2076,6 @@ class OpenAITranscriptionModel {
|
|
|
1770
2076
|
if (input.language) {
|
|
1771
2077
|
form.set("language", input.language);
|
|
1772
2078
|
}
|
|
1773
|
-
for (const [key, value] of Object.entries(input.providerOptions ?? {})) {
|
|
1774
|
-
form.set(key, typeof value === "string" ? value : JSON.stringify(value));
|
|
1775
|
-
}
|
|
1776
2079
|
try {
|
|
1777
2080
|
const response = await withRetry(() => this.fetcher(`${this.baseURL}/audio/transcriptions`, {
|
|
1778
2081
|
method: "POST",
|
|
@@ -1780,7 +2083,12 @@ class OpenAITranscriptionModel {
|
|
|
1780
2083
|
signal,
|
|
1781
2084
|
body: form
|
|
1782
2085
|
}), input);
|
|
1783
|
-
const json = await parseJson(response
|
|
2086
|
+
const json = await parseJson(response, {
|
|
2087
|
+
maxBytes: this.responseLimits.transcriptionBytes,
|
|
2088
|
+
errorBodyBytes: this.responseLimits.errorBodyBytes,
|
|
2089
|
+
endpoint: "audio/transcriptions",
|
|
2090
|
+
abort
|
|
2091
|
+
});
|
|
1784
2092
|
return {
|
|
1785
2093
|
text: json.text ?? "",
|
|
1786
2094
|
rawResponse: json
|
|
@@ -1796,16 +2104,18 @@ class OpenAISpeechModel {
|
|
|
1796
2104
|
apiKey;
|
|
1797
2105
|
baseURL;
|
|
1798
2106
|
fetcher;
|
|
2107
|
+
responseLimits;
|
|
1799
2108
|
provider = "openai";
|
|
1800
2109
|
capabilities = speechCapabilities;
|
|
1801
|
-
constructor(modelId, apiKey, baseURL, fetcher) {
|
|
2110
|
+
constructor(modelId, apiKey, baseURL, fetcher, responseLimits = resolveAudioResponseLimits()) {
|
|
1802
2111
|
this.modelId = modelId;
|
|
1803
2112
|
this.apiKey = apiKey;
|
|
1804
2113
|
this.baseURL = baseURL;
|
|
1805
2114
|
this.fetcher = fetcher;
|
|
2115
|
+
this.responseLimits = responseLimits;
|
|
1806
2116
|
}
|
|
1807
2117
|
async generateSpeech(input) {
|
|
1808
|
-
const { signal, cleanup } = withTimeoutSignal(input);
|
|
2118
|
+
const { signal, cleanup, abort } = withTimeoutSignal(input);
|
|
1809
2119
|
try {
|
|
1810
2120
|
const response = await withRetry(() => this.fetcher(`${this.baseURL}/audio/speech`, {
|
|
1811
2121
|
method: "POST",
|
|
@@ -1819,13 +2129,18 @@ class OpenAISpeechModel {
|
|
|
1819
2129
|
})
|
|
1820
2130
|
}), input);
|
|
1821
2131
|
if (!response.ok) {
|
|
1822
|
-
const body = await response.
|
|
2132
|
+
const body = await readErrorBodyWithLimit(response, this.responseLimits.errorBodyBytes);
|
|
1823
2133
|
throw new ProviderHTTPError(`OpenAI request failed with status ${response.status}.`, response.status, {
|
|
1824
2134
|
responseBody: body
|
|
1825
2135
|
});
|
|
1826
2136
|
}
|
|
1827
2137
|
return {
|
|
1828
|
-
audio:
|
|
2138
|
+
audio: await readBodyWithLimit(response, {
|
|
2139
|
+
maxBytes: this.responseLimits.speechBytes,
|
|
2140
|
+
provider: "openai",
|
|
2141
|
+
endpoint: "audio/speech",
|
|
2142
|
+
abort
|
|
2143
|
+
}),
|
|
1829
2144
|
mediaType: response.headers.get("content-type") ?? "audio/mpeg",
|
|
1830
2145
|
rawResponse: undefined
|
|
1831
2146
|
};
|
|
@@ -1921,11 +2236,10 @@ class OpenAIRealtimeModel {
|
|
|
1921
2236
|
}
|
|
1922
2237
|
async connect(config = {}, options) {
|
|
1923
2238
|
const initialConfig = this.resolveConfig(config);
|
|
1924
|
-
const
|
|
1925
|
-
|
|
1926
|
-
};
|
|
2239
|
+
const providerOptions = initialConfig.providerOptions;
|
|
2240
|
+
const headers = resolveOpenAIRealtimeHeaders(this.apiKey, providerOptions);
|
|
1927
2241
|
const connection = await (this.connectionFactory ?? openWebSocketConnection)(this.realtimeURL ??
|
|
1928
|
-
openAIRealtimeURL(this.baseURL, this.modelId, inferOpenAIRealtimeMode(this.modelId, initialConfig.mode),
|
|
2242
|
+
openAIRealtimeURL(this.baseURL, this.modelId, inferOpenAIRealtimeMode(this.modelId, initialConfig.mode), providerOptions), headers, options);
|
|
1929
2243
|
const session = new CallbackRealtimeSession({
|
|
1930
2244
|
provider: this.provider,
|
|
1931
2245
|
modelId: this.modelId,
|
|
@@ -1998,6 +2312,26 @@ class OpenAIRealtimeModel {
|
|
|
1998
2312
|
if (mode !== "conversation") {
|
|
1999
2313
|
throw new UnsupportedFeatureError(`Provider "openai" model "${this.modelId}" does not support realtime tools in ${mode} mode.`);
|
|
2000
2314
|
}
|
|
2315
|
+
const mcpMetadata = result.providerMetadata?.[openAIRealtimeMcpMetadataKey];
|
|
2316
|
+
if (mcpMetadata && typeof mcpMetadata === "object" && !Array.isArray(mcpMetadata)) {
|
|
2317
|
+
const approval = mcpMetadata;
|
|
2318
|
+
if (approval.type === "mcp_approval_response" &&
|
|
2319
|
+
typeof approval.approval_request_id === "string" &&
|
|
2320
|
+
typeof approval.approve === "boolean") {
|
|
2321
|
+
return [
|
|
2322
|
+
{
|
|
2323
|
+
type: "conversation.item.create",
|
|
2324
|
+
item: {
|
|
2325
|
+
...(typeof approval.id === "string" ? { id: approval.id } : {}),
|
|
2326
|
+
type: "mcp_approval_response",
|
|
2327
|
+
approval_request_id: approval.approval_request_id,
|
|
2328
|
+
approve: approval.approve,
|
|
2329
|
+
...(typeof approval.reason === "string" ? { reason: approval.reason } : {})
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
];
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2001
2335
|
const payloads = [
|
|
2002
2336
|
{
|
|
2003
2337
|
type: "conversation.item.create",
|
|
@@ -2037,7 +2371,7 @@ class OpenAIRealtimeModel {
|
|
|
2037
2371
|
delete providerOptions.expires_after;
|
|
2038
2372
|
const response = await withRetry(() => this.fetcher(this.browserTokenURL ?? `${this.baseURL}/realtime/client_secrets`, {
|
|
2039
2373
|
method: "POST",
|
|
2040
|
-
headers:
|
|
2374
|
+
headers: resolveOpenAIRealtimeHeaders(this.apiKey, providerOptions, true),
|
|
2041
2375
|
body: JSON.stringify({
|
|
2042
2376
|
...(expiresAfter ? { expires_after: expiresAfter } : {}),
|
|
2043
2377
|
session: mapRealtimeSessionConfig({
|
|
@@ -2079,12 +2413,14 @@ export const createOpenAI = (options = {}) => {
|
|
|
2079
2413
|
}
|
|
2080
2414
|
const baseURL = options.baseURL ?? "https://api.openai.com/v1";
|
|
2081
2415
|
const fetcher = options.fetch ?? globalThis.fetch;
|
|
2416
|
+
const responseLimits = resolveOpenAIResponseLimits(options.responseLimits);
|
|
2082
2417
|
return createProviderAdapter({
|
|
2083
2418
|
name: "openai",
|
|
2084
|
-
languageModel: (modelId) => new OpenAILanguageModel(modelId, apiKey, baseURL, fetcher),
|
|
2419
|
+
languageModel: (modelId) => new OpenAILanguageModel(modelId, apiKey, baseURL, fetcher, responseLimits),
|
|
2085
2420
|
embeddingModel: (modelId) => new OpenAIEmbeddingModel(modelId, apiKey, baseURL, fetcher),
|
|
2086
|
-
transcriptionModel: (modelId) => new OpenAITranscriptionModel(modelId, apiKey, baseURL, fetcher),
|
|
2087
|
-
speechModel: (modelId) => new OpenAISpeechModel(modelId, apiKey, baseURL, fetcher),
|
|
2421
|
+
transcriptionModel: (modelId) => new OpenAITranscriptionModel(modelId, apiKey, baseURL, fetcher, responseLimits),
|
|
2422
|
+
speechModel: (modelId) => new OpenAISpeechModel(modelId, apiKey, baseURL, fetcher, responseLimits),
|
|
2423
|
+
imageGenerationModel: (modelId) => createOpenAIImageGenerationModel({ modelId, apiKey, baseURL, fetch: fetcher }),
|
|
2088
2424
|
realtimeModel: (modelId) => new OpenAIRealtimeModel(modelId, apiKey, baseURL, fetcher, options.realtimeConnectionFactory, options.realtimeURL, options.browserTokenURL),
|
|
2089
2425
|
groundedLanguageModel: (modelId) => new OpenAIGroundedLanguageModel(modelId, apiKey, baseURL, fetcher),
|
|
2090
2426
|
rawFetch: fetcher
|
|
@@ -2146,6 +2482,26 @@ export const openAIMcpApprovalResponse = (response) => providerDataPart("openai"
|
|
|
2146
2482
|
type: "mcp_approval_response",
|
|
2147
2483
|
...response
|
|
2148
2484
|
});
|
|
2485
|
+
export const openAIRealtimeMcpApprovalResult = (response) => ({
|
|
2486
|
+
toolCallId: response.approvalRequestId,
|
|
2487
|
+
toolName: response.name,
|
|
2488
|
+
output: {
|
|
2489
|
+
type: "mcp_approval_response",
|
|
2490
|
+
approve: response.approve,
|
|
2491
|
+
...(response.reason ? { reason: response.reason } : {})
|
|
2492
|
+
},
|
|
2493
|
+
isError: false,
|
|
2494
|
+
providerMetadata: {
|
|
2495
|
+
[openAIRealtimeMcpMetadataKey]: {
|
|
2496
|
+
type: "mcp_approval_response",
|
|
2497
|
+
status: response.approve ? "approved" : "rejected",
|
|
2498
|
+
approval_request_id: response.approvalRequestId,
|
|
2499
|
+
approve: response.approve,
|
|
2500
|
+
...(response.itemId ? { id: response.itemId } : {}),
|
|
2501
|
+
...(response.reason ? { reason: response.reason } : {})
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
});
|
|
2149
2505
|
export const openAIPromptCacheBreakpoint = () => providerDataPart("openai", {
|
|
2150
2506
|
type: "prompt_cache_breakpoint",
|
|
2151
2507
|
mode: "explicit"
|
|
@@ -2294,4 +2650,5 @@ export const openAIApplyPatchTool = (config) => ({
|
|
|
2294
2650
|
}),
|
|
2295
2651
|
execute: async ({ operation }) => applyOpenAIPatchOperation(operation, config)
|
|
2296
2652
|
});
|
|
2653
|
+
export * from "./image-generation.js";
|
|
2297
2654
|
//# sourceMappingURL=index.js.map
|