@depup/ai-sdk__openai 3.0.41-depup.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +3101 -0
  2. package/LICENSE +13 -0
  3. package/README.md +25 -0
  4. package/changes.json +5 -0
  5. package/dist/index.d.mts +1107 -0
  6. package/dist/index.d.ts +1107 -0
  7. package/dist/index.js +6408 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/index.mjs +6493 -0
  10. package/dist/index.mjs.map +1 -0
  11. package/dist/internal/index.d.mts +1137 -0
  12. package/dist/internal/index.d.ts +1137 -0
  13. package/dist/internal/index.js +6256 -0
  14. package/dist/internal/index.js.map +1 -0
  15. package/dist/internal/index.mjs +6306 -0
  16. package/dist/internal/index.mjs.map +1 -0
  17. package/docs/03-openai.mdx +2396 -0
  18. package/internal.d.ts +1 -0
  19. package/package.json +96 -0
  20. package/src/chat/convert-openai-chat-usage.ts +57 -0
  21. package/src/chat/convert-to-openai-chat-messages.ts +225 -0
  22. package/src/chat/get-response-metadata.ts +15 -0
  23. package/src/chat/map-openai-finish-reason.ts +19 -0
  24. package/src/chat/openai-chat-api.ts +198 -0
  25. package/src/chat/openai-chat-language-model.ts +703 -0
  26. package/src/chat/openai-chat-options.ts +192 -0
  27. package/src/chat/openai-chat-prepare-tools.ts +84 -0
  28. package/src/chat/openai-chat-prompt.ts +70 -0
  29. package/src/completion/convert-openai-completion-usage.ts +46 -0
  30. package/src/completion/convert-to-openai-completion-prompt.ts +93 -0
  31. package/src/completion/get-response-metadata.ts +15 -0
  32. package/src/completion/map-openai-finish-reason.ts +19 -0
  33. package/src/completion/openai-completion-api.ts +81 -0
  34. package/src/completion/openai-completion-language-model.ts +336 -0
  35. package/src/completion/openai-completion-options.ts +61 -0
  36. package/src/embedding/openai-embedding-api.ts +13 -0
  37. package/src/embedding/openai-embedding-model.ts +95 -0
  38. package/src/embedding/openai-embedding-options.ts +30 -0
  39. package/src/image/openai-image-api.ts +35 -0
  40. package/src/image/openai-image-model.ts +349 -0
  41. package/src/image/openai-image-options.ts +31 -0
  42. package/src/index.ts +23 -0
  43. package/src/internal/index.ts +19 -0
  44. package/src/openai-config.ts +18 -0
  45. package/src/openai-error.ts +22 -0
  46. package/src/openai-language-model-capabilities.ts +52 -0
  47. package/src/openai-provider.ts +270 -0
  48. package/src/openai-tools.ts +126 -0
  49. package/src/responses/convert-openai-responses-usage.ts +53 -0
  50. package/src/responses/convert-to-openai-responses-input.ts +735 -0
  51. package/src/responses/map-openai-responses-finish-reason.ts +22 -0
  52. package/src/responses/openai-responses-api.ts +1260 -0
  53. package/src/responses/openai-responses-language-model.ts +2098 -0
  54. package/src/responses/openai-responses-options.ts +299 -0
  55. package/src/responses/openai-responses-prepare-tools.ts +408 -0
  56. package/src/responses/openai-responses-provider-metadata.ts +62 -0
  57. package/src/speech/openai-speech-api.ts +38 -0
  58. package/src/speech/openai-speech-model.ts +137 -0
  59. package/src/speech/openai-speech-options.ts +26 -0
  60. package/src/tool/apply-patch.ts +141 -0
  61. package/src/tool/code-interpreter.ts +104 -0
  62. package/src/tool/custom.ts +64 -0
  63. package/src/tool/file-search.ts +145 -0
  64. package/src/tool/image-generation.ts +126 -0
  65. package/src/tool/local-shell.ts +72 -0
  66. package/src/tool/mcp.ts +125 -0
  67. package/src/tool/shell.ts +203 -0
  68. package/src/tool/web-search-preview.ts +141 -0
  69. package/src/tool/web-search.ts +181 -0
  70. package/src/transcription/openai-transcription-api.ts +37 -0
  71. package/src/transcription/openai-transcription-model.ts +232 -0
  72. package/src/transcription/openai-transcription-options.ts +53 -0
  73. package/src/transcription/transcription-test.mp3 +0 -0
  74. package/src/version.ts +6 -0
@@ -0,0 +1,735 @@
1
+ import {
2
+ LanguageModelV3Prompt,
3
+ LanguageModelV3ToolApprovalResponsePart,
4
+ SharedV3Warning,
5
+ UnsupportedFunctionalityError,
6
+ } from '@ai-sdk/provider';
7
+ import {
8
+ convertToBase64,
9
+ isNonNullable,
10
+ parseProviderOptions,
11
+ ToolNameMapping,
12
+ validateTypes,
13
+ } from '@ai-sdk/provider-utils';
14
+ import { z } from 'zod/v4';
15
+ import {
16
+ applyPatchInputSchema,
17
+ applyPatchOutputSchema,
18
+ } from '../tool/apply-patch';
19
+ import {
20
+ localShellInputSchema,
21
+ localShellOutputSchema,
22
+ } from '../tool/local-shell';
23
+ import { shellInputSchema, shellOutputSchema } from '../tool/shell';
24
+ import {
25
+ OpenAIResponsesCustomToolCallOutput,
26
+ OpenAIResponsesFunctionCallOutput,
27
+ OpenAIResponsesInput,
28
+ OpenAIResponsesReasoning,
29
+ } from './openai-responses-api';
30
+
31
+ /**
32
+ * Check if a string is a file ID based on the given prefixes
33
+ * Returns false if prefixes is undefined (disables file ID detection)
34
+ */
35
+ function isFileId(data: string, prefixes?: readonly string[]): boolean {
36
+ if (!prefixes) return false;
37
+ return prefixes.some(prefix => data.startsWith(prefix));
38
+ }
39
+
40
+ export async function convertToOpenAIResponsesInput({
41
+ prompt,
42
+ toolNameMapping,
43
+ systemMessageMode,
44
+ providerOptionsName,
45
+ fileIdPrefixes,
46
+ store,
47
+ hasConversation = false,
48
+ hasLocalShellTool = false,
49
+ hasShellTool = false,
50
+ hasApplyPatchTool = false,
51
+ customProviderToolNames,
52
+ }: {
53
+ prompt: LanguageModelV3Prompt;
54
+ toolNameMapping: ToolNameMapping;
55
+ systemMessageMode: 'system' | 'developer' | 'remove';
56
+ providerOptionsName: string;
57
+ fileIdPrefixes?: readonly string[];
58
+ store: boolean;
59
+ hasConversation?: boolean; // when true, skip assistant messages that already have item IDs
60
+ hasLocalShellTool?: boolean;
61
+ hasShellTool?: boolean;
62
+ hasApplyPatchTool?: boolean;
63
+ customProviderToolNames?: Set<string>;
64
+ }): Promise<{
65
+ input: OpenAIResponsesInput;
66
+ warnings: Array<SharedV3Warning>;
67
+ }> {
68
+ const input: OpenAIResponsesInput = [];
69
+ const warnings: Array<SharedV3Warning> = [];
70
+ const processedApprovalIds = new Set<string>();
71
+
72
+ for (const { role, content } of prompt) {
73
+ switch (role) {
74
+ case 'system': {
75
+ switch (systemMessageMode) {
76
+ case 'system': {
77
+ input.push({ role: 'system', content });
78
+ break;
79
+ }
80
+ case 'developer': {
81
+ input.push({ role: 'developer', content });
82
+ break;
83
+ }
84
+ case 'remove': {
85
+ warnings.push({
86
+ type: 'other',
87
+ message: 'system messages are removed for this model',
88
+ });
89
+ break;
90
+ }
91
+ default: {
92
+ const _exhaustiveCheck: never = systemMessageMode;
93
+ throw new Error(
94
+ `Unsupported system message mode: ${_exhaustiveCheck}`,
95
+ );
96
+ }
97
+ }
98
+ break;
99
+ }
100
+
101
+ case 'user': {
102
+ input.push({
103
+ role: 'user',
104
+ content: content.map((part, index) => {
105
+ switch (part.type) {
106
+ case 'text': {
107
+ return { type: 'input_text', text: part.text };
108
+ }
109
+ case 'file': {
110
+ if (part.mediaType.startsWith('image/')) {
111
+ const mediaType =
112
+ part.mediaType === 'image/*'
113
+ ? 'image/jpeg'
114
+ : part.mediaType;
115
+
116
+ return {
117
+ type: 'input_image',
118
+ ...(part.data instanceof URL
119
+ ? { image_url: part.data.toString() }
120
+ : typeof part.data === 'string' &&
121
+ isFileId(part.data, fileIdPrefixes)
122
+ ? { file_id: part.data }
123
+ : {
124
+ image_url: `data:${mediaType};base64,${convertToBase64(part.data)}`,
125
+ }),
126
+ detail:
127
+ part.providerOptions?.[providerOptionsName]?.imageDetail,
128
+ };
129
+ } else if (part.mediaType === 'application/pdf') {
130
+ if (part.data instanceof URL) {
131
+ return {
132
+ type: 'input_file',
133
+ file_url: part.data.toString(),
134
+ };
135
+ }
136
+ return {
137
+ type: 'input_file',
138
+ ...(typeof part.data === 'string' &&
139
+ isFileId(part.data, fileIdPrefixes)
140
+ ? { file_id: part.data }
141
+ : {
142
+ filename: part.filename ?? `part-${index}.pdf`,
143
+ file_data: `data:application/pdf;base64,${convertToBase64(part.data)}`,
144
+ }),
145
+ };
146
+ } else {
147
+ throw new UnsupportedFunctionalityError({
148
+ functionality: `file part media type ${part.mediaType}`,
149
+ });
150
+ }
151
+ }
152
+ }
153
+ }),
154
+ });
155
+
156
+ break;
157
+ }
158
+
159
+ case 'assistant': {
160
+ const reasoningMessages: Record<string, OpenAIResponsesReasoning> = {};
161
+
162
+ for (const part of content) {
163
+ switch (part.type) {
164
+ case 'text': {
165
+ const providerOpts = part.providerOptions?.[providerOptionsName];
166
+ const id = providerOpts?.itemId as string | undefined;
167
+ const phase = providerOpts?.phase as
168
+ | 'commentary'
169
+ | 'final_answer'
170
+ | null
171
+ | undefined;
172
+
173
+ // when using conversation, skip items that already exist in the conversation context to avoid "Duplicate item found" errors
174
+ if (hasConversation && id != null) {
175
+ break;
176
+ }
177
+
178
+ // item references reduce the payload size
179
+ if (store && id != null) {
180
+ input.push({ type: 'item_reference', id });
181
+ break;
182
+ }
183
+
184
+ input.push({
185
+ role: 'assistant',
186
+ content: [{ type: 'output_text', text: part.text }],
187
+ id,
188
+ ...(phase != null && { phase }),
189
+ });
190
+
191
+ break;
192
+ }
193
+ case 'tool-call': {
194
+ const id = (part.providerOptions?.[providerOptionsName]?.itemId ??
195
+ (
196
+ part as {
197
+ providerMetadata?: {
198
+ [providerOptionsName]?: { itemId?: string };
199
+ };
200
+ }
201
+ ).providerMetadata?.[providerOptionsName]?.itemId) as
202
+ | string
203
+ | undefined;
204
+
205
+ if (hasConversation && id != null) {
206
+ break;
207
+ }
208
+
209
+ if (part.providerExecuted) {
210
+ if (store && id != null) {
211
+ input.push({ type: 'item_reference', id });
212
+ }
213
+ break;
214
+ }
215
+
216
+ if (store && id != null) {
217
+ input.push({ type: 'item_reference', id });
218
+ break;
219
+ }
220
+
221
+ const resolvedToolName = toolNameMapping.toProviderToolName(
222
+ part.toolName,
223
+ );
224
+
225
+ if (hasLocalShellTool && resolvedToolName === 'local_shell') {
226
+ const parsedInput = await validateTypes({
227
+ value: part.input,
228
+ schema: localShellInputSchema,
229
+ });
230
+ input.push({
231
+ type: 'local_shell_call',
232
+ call_id: part.toolCallId,
233
+ id: id!,
234
+ action: {
235
+ type: 'exec',
236
+ command: parsedInput.action.command,
237
+ timeout_ms: parsedInput.action.timeoutMs,
238
+ user: parsedInput.action.user,
239
+ working_directory: parsedInput.action.workingDirectory,
240
+ env: parsedInput.action.env,
241
+ },
242
+ });
243
+
244
+ break;
245
+ }
246
+
247
+ if (hasShellTool && resolvedToolName === 'shell') {
248
+ const parsedInput = await validateTypes({
249
+ value: part.input,
250
+ schema: shellInputSchema,
251
+ });
252
+ input.push({
253
+ type: 'shell_call',
254
+ call_id: part.toolCallId,
255
+ id: id!,
256
+ status: 'completed',
257
+ action: {
258
+ commands: parsedInput.action.commands,
259
+ timeout_ms: parsedInput.action.timeoutMs,
260
+ max_output_length: parsedInput.action.maxOutputLength,
261
+ },
262
+ });
263
+
264
+ break;
265
+ }
266
+
267
+ if (hasApplyPatchTool && resolvedToolName === 'apply_patch') {
268
+ const parsedInput = await validateTypes({
269
+ value: part.input,
270
+ schema: applyPatchInputSchema,
271
+ });
272
+ input.push({
273
+ type: 'apply_patch_call',
274
+ call_id: parsedInput.callId,
275
+ id: id!,
276
+ status: 'completed',
277
+ operation: parsedInput.operation,
278
+ });
279
+
280
+ break;
281
+ }
282
+
283
+ if (customProviderToolNames?.has(resolvedToolName)) {
284
+ input.push({
285
+ type: 'custom_tool_call',
286
+ call_id: part.toolCallId,
287
+ name: resolvedToolName,
288
+ input:
289
+ typeof part.input === 'string'
290
+ ? part.input
291
+ : JSON.stringify(part.input),
292
+ id,
293
+ });
294
+ break;
295
+ }
296
+
297
+ input.push({
298
+ type: 'function_call',
299
+ call_id: part.toolCallId,
300
+ name: resolvedToolName,
301
+ arguments: JSON.stringify(part.input),
302
+ id,
303
+ });
304
+ break;
305
+ }
306
+
307
+ // assistant tool result parts are from provider-executed tools:
308
+ case 'tool-result': {
309
+ // Skip execution-denied results - these are synthetic results from denied
310
+ // approvals and have no corresponding item in OpenAI's store.
311
+ // Check both the direct type and if it was transformed to json with execution-denied inside
312
+ if (
313
+ part.output.type === 'execution-denied' ||
314
+ (part.output.type === 'json' &&
315
+ typeof part.output.value === 'object' &&
316
+ part.output.value != null &&
317
+ 'type' in part.output.value &&
318
+ part.output.value.type === 'execution-denied')
319
+ ) {
320
+ break;
321
+ }
322
+
323
+ if (hasConversation) {
324
+ break;
325
+ }
326
+
327
+ const resolvedResultToolName = toolNameMapping.toProviderToolName(
328
+ part.toolName,
329
+ );
330
+
331
+ /*
332
+ * Shell tool results are separate output items (shell_call_output)
333
+ * with their own item IDs distinct from the shell_call's item ID.
334
+ * Since the pipeline only preserves the shell_call's item ID in
335
+ * callProviderMetadata, we reconstruct the full shell_call_output
336
+ * instead of using an item_reference with the wrong ID.
337
+ */
338
+ if (hasShellTool && resolvedResultToolName === 'shell') {
339
+ if (part.output.type === 'json') {
340
+ const parsedOutput = await validateTypes({
341
+ value: part.output.value,
342
+ schema: shellOutputSchema,
343
+ });
344
+ input.push({
345
+ type: 'shell_call_output',
346
+ call_id: part.toolCallId,
347
+ output: parsedOutput.output.map(item => ({
348
+ stdout: item.stdout,
349
+ stderr: item.stderr,
350
+ outcome:
351
+ item.outcome.type === 'timeout'
352
+ ? { type: 'timeout' as const }
353
+ : {
354
+ type: 'exit' as const,
355
+ exit_code: item.outcome.exitCode,
356
+ },
357
+ })),
358
+ });
359
+ }
360
+ break;
361
+ }
362
+
363
+ if (store) {
364
+ const itemId =
365
+ (
366
+ part.providerOptions?.[providerOptionsName] as
367
+ | { itemId?: string }
368
+ | undefined
369
+ )?.itemId ?? part.toolCallId;
370
+ input.push({ type: 'item_reference', id: itemId });
371
+ } else {
372
+ warnings.push({
373
+ type: 'other',
374
+ message: `Results for OpenAI tool ${part.toolName} are not sent to the API when store is false`,
375
+ });
376
+ }
377
+
378
+ break;
379
+ }
380
+
381
+ case 'reasoning': {
382
+ const providerOptions = await parseProviderOptions({
383
+ provider: providerOptionsName,
384
+ providerOptions: part.providerOptions,
385
+ schema: openaiResponsesReasoningProviderOptionsSchema,
386
+ });
387
+
388
+ const reasoningId = providerOptions?.itemId;
389
+
390
+ if (hasConversation && reasoningId != null) {
391
+ break;
392
+ }
393
+
394
+ if (reasoningId != null) {
395
+ const reasoningMessage = reasoningMessages[reasoningId];
396
+
397
+ if (store) {
398
+ // use item references to refer to reasoning (single reference)
399
+ // when the first part is encountered
400
+ if (reasoningMessage === undefined) {
401
+ input.push({ type: 'item_reference', id: reasoningId });
402
+
403
+ // store unused reasoning message to mark id as used
404
+ reasoningMessages[reasoningId] = {
405
+ type: 'reasoning',
406
+ id: reasoningId,
407
+ summary: [],
408
+ };
409
+ }
410
+ } else {
411
+ const summaryParts: Array<{
412
+ type: 'summary_text';
413
+ text: string;
414
+ }> = [];
415
+
416
+ if (part.text.length > 0) {
417
+ summaryParts.push({
418
+ type: 'summary_text',
419
+ text: part.text,
420
+ });
421
+ } else if (reasoningMessage !== undefined) {
422
+ warnings.push({
423
+ type: 'other',
424
+ message: `Cannot append empty reasoning part to existing reasoning sequence. Skipping reasoning part: ${JSON.stringify(part)}.`,
425
+ });
426
+ }
427
+
428
+ if (reasoningMessage === undefined) {
429
+ reasoningMessages[reasoningId] = {
430
+ type: 'reasoning',
431
+ id: reasoningId,
432
+ encrypted_content:
433
+ providerOptions?.reasoningEncryptedContent,
434
+ summary: summaryParts,
435
+ };
436
+ input.push(reasoningMessages[reasoningId]);
437
+ } else {
438
+ reasoningMessage.summary.push(...summaryParts);
439
+
440
+ // updated encrypted content to enable setting it in the last summary part:
441
+ if (providerOptions?.reasoningEncryptedContent != null) {
442
+ reasoningMessage.encrypted_content =
443
+ providerOptions.reasoningEncryptedContent;
444
+ }
445
+ }
446
+ }
447
+ } else {
448
+ // No itemId — fall back to encrypted_content if available.
449
+ // The OpenAI Responses API accepts reasoning items without an
450
+ // id when encrypted_content is provided, enabling multi-turn
451
+ // reasoning even when server-side item persistence is not used
452
+ // or when itemId has been stripped from providerOptions.
453
+ const encryptedContent =
454
+ providerOptions?.reasoningEncryptedContent;
455
+
456
+ if (encryptedContent != null) {
457
+ const summaryParts: Array<{
458
+ type: 'summary_text';
459
+ text: string;
460
+ }> = [];
461
+ if (part.text.length > 0) {
462
+ summaryParts.push({
463
+ type: 'summary_text',
464
+ text: part.text,
465
+ });
466
+ }
467
+ input.push({
468
+ type: 'reasoning',
469
+ encrypted_content: encryptedContent,
470
+ summary: summaryParts,
471
+ });
472
+ } else {
473
+ warnings.push({
474
+ type: 'other',
475
+ message: `Non-OpenAI reasoning parts are not supported. Skipping reasoning part: ${JSON.stringify(part)}.`,
476
+ });
477
+ }
478
+ }
479
+ break;
480
+ }
481
+ }
482
+ }
483
+
484
+ break;
485
+ }
486
+
487
+ case 'tool': {
488
+ for (const part of content) {
489
+ if (part.type === 'tool-approval-response') {
490
+ const approvalResponse =
491
+ part as LanguageModelV3ToolApprovalResponsePart;
492
+
493
+ if (processedApprovalIds.has(approvalResponse.approvalId)) {
494
+ continue;
495
+ }
496
+ processedApprovalIds.add(approvalResponse.approvalId);
497
+
498
+ if (store) {
499
+ input.push({
500
+ type: 'item_reference',
501
+ id: approvalResponse.approvalId,
502
+ });
503
+ }
504
+
505
+ input.push({
506
+ type: 'mcp_approval_response',
507
+ approval_request_id: approvalResponse.approvalId,
508
+ approve: approvalResponse.approved,
509
+ });
510
+ continue;
511
+ }
512
+
513
+ const output = part.output;
514
+
515
+ // Skip execution-denied with approvalId - already handled via tool-approval-response
516
+ if (output.type === 'execution-denied') {
517
+ const approvalId = (
518
+ output.providerOptions?.openai as { approvalId?: string }
519
+ )?.approvalId;
520
+
521
+ if (approvalId) {
522
+ continue;
523
+ }
524
+ }
525
+
526
+ const resolvedToolName = toolNameMapping.toProviderToolName(
527
+ part.toolName,
528
+ );
529
+
530
+ if (
531
+ hasLocalShellTool &&
532
+ resolvedToolName === 'local_shell' &&
533
+ output.type === 'json'
534
+ ) {
535
+ const parsedOutput = await validateTypes({
536
+ value: output.value,
537
+ schema: localShellOutputSchema,
538
+ });
539
+
540
+ input.push({
541
+ type: 'local_shell_call_output',
542
+ call_id: part.toolCallId,
543
+ output: parsedOutput.output,
544
+ });
545
+ continue;
546
+ }
547
+
548
+ if (
549
+ hasShellTool &&
550
+ resolvedToolName === 'shell' &&
551
+ output.type === 'json'
552
+ ) {
553
+ const parsedOutput = await validateTypes({
554
+ value: output.value,
555
+ schema: shellOutputSchema,
556
+ });
557
+
558
+ input.push({
559
+ type: 'shell_call_output',
560
+ call_id: part.toolCallId,
561
+ output: parsedOutput.output.map(item => ({
562
+ stdout: item.stdout,
563
+ stderr: item.stderr,
564
+ outcome:
565
+ item.outcome.type === 'timeout'
566
+ ? { type: 'timeout' as const }
567
+ : {
568
+ type: 'exit' as const,
569
+ exit_code: item.outcome.exitCode,
570
+ },
571
+ })),
572
+ });
573
+ continue;
574
+ }
575
+
576
+ if (
577
+ hasApplyPatchTool &&
578
+ part.toolName === 'apply_patch' &&
579
+ output.type === 'json'
580
+ ) {
581
+ const parsedOutput = await validateTypes({
582
+ value: output.value,
583
+ schema: applyPatchOutputSchema,
584
+ });
585
+
586
+ input.push({
587
+ type: 'apply_patch_call_output',
588
+ call_id: part.toolCallId,
589
+ status: parsedOutput.status,
590
+ output: parsedOutput.output,
591
+ });
592
+ continue;
593
+ }
594
+
595
+ if (customProviderToolNames?.has(resolvedToolName)) {
596
+ let outputValue: OpenAIResponsesCustomToolCallOutput['output'];
597
+ switch (output.type) {
598
+ case 'text':
599
+ case 'error-text':
600
+ outputValue = output.value;
601
+ break;
602
+ case 'execution-denied':
603
+ outputValue = output.reason ?? 'Tool execution denied.';
604
+ break;
605
+ case 'json':
606
+ case 'error-json':
607
+ outputValue = JSON.stringify(output.value);
608
+ break;
609
+ case 'content':
610
+ outputValue = output.value
611
+ .map(item => {
612
+ switch (item.type) {
613
+ case 'text':
614
+ return { type: 'input_text' as const, text: item.text };
615
+ case 'image-data':
616
+ return {
617
+ type: 'input_image' as const,
618
+ image_url: `data:${item.mediaType};base64,${item.data}`,
619
+ };
620
+ case 'image-url':
621
+ return {
622
+ type: 'input_image' as const,
623
+ image_url: item.url,
624
+ };
625
+ case 'file-data':
626
+ return {
627
+ type: 'input_file' as const,
628
+ filename: item.filename ?? 'data',
629
+ file_data: `data:${item.mediaType};base64,${item.data}`,
630
+ };
631
+ default:
632
+ warnings.push({
633
+ type: 'other',
634
+ message: `unsupported custom tool content part type: ${item.type}`,
635
+ });
636
+ return undefined;
637
+ }
638
+ })
639
+ .filter(isNonNullable);
640
+ break;
641
+ default:
642
+ outputValue = '';
643
+ }
644
+ input.push({
645
+ type: 'custom_tool_call_output',
646
+ call_id: part.toolCallId,
647
+ output: outputValue,
648
+ } satisfies OpenAIResponsesCustomToolCallOutput);
649
+ continue;
650
+ }
651
+
652
+ let contentValue: OpenAIResponsesFunctionCallOutput['output'];
653
+ switch (output.type) {
654
+ case 'text':
655
+ case 'error-text':
656
+ contentValue = output.value;
657
+ break;
658
+ case 'execution-denied':
659
+ contentValue = output.reason ?? 'Tool execution denied.';
660
+ break;
661
+ case 'json':
662
+ case 'error-json':
663
+ contentValue = JSON.stringify(output.value);
664
+ break;
665
+ case 'content':
666
+ contentValue = output.value
667
+ .map(item => {
668
+ switch (item.type) {
669
+ case 'text': {
670
+ return { type: 'input_text' as const, text: item.text };
671
+ }
672
+
673
+ case 'image-data': {
674
+ return {
675
+ type: 'input_image' as const,
676
+ image_url: `data:${item.mediaType};base64,${item.data}`,
677
+ };
678
+ }
679
+
680
+ case 'image-url': {
681
+ return {
682
+ type: 'input_image' as const,
683
+ image_url: item.url,
684
+ };
685
+ }
686
+
687
+ case 'file-data': {
688
+ return {
689
+ type: 'input_file' as const,
690
+ filename: item.filename ?? 'data',
691
+ file_data: `data:${item.mediaType};base64,${item.data}`,
692
+ };
693
+ }
694
+
695
+ default: {
696
+ warnings.push({
697
+ type: 'other',
698
+ message: `unsupported tool content part type: ${item.type}`,
699
+ });
700
+ return undefined;
701
+ }
702
+ }
703
+ })
704
+ .filter(isNonNullable);
705
+ break;
706
+ }
707
+
708
+ input.push({
709
+ type: 'function_call_output',
710
+ call_id: part.toolCallId,
711
+ output: contentValue,
712
+ });
713
+ }
714
+
715
+ break;
716
+ }
717
+
718
+ default: {
719
+ const _exhaustiveCheck: never = role;
720
+ throw new Error(`Unsupported role: ${_exhaustiveCheck}`);
721
+ }
722
+ }
723
+ }
724
+
725
+ return { input, warnings };
726
+ }
727
+
728
+ const openaiResponsesReasoningProviderOptionsSchema = z.object({
729
+ itemId: z.string().nullish(),
730
+ reasoningEncryptedContent: z.string().nullish(),
731
+ });
732
+
733
+ export type OpenAIResponsesReasoningProviderOptions = z.infer<
734
+ typeof openaiResponsesReasoningProviderOptionsSchema
735
+ >;