@ai-sdk/open-responses 2.0.31 → 2.0.35

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.
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  UnsupportedFunctionalityError,
3
+ type LanguageModelV4ProviderTool,
3
4
  type LanguageModelV4Prompt,
4
5
  type SharedV4Warning,
5
6
  } from '@ai-sdk/provider';
@@ -8,6 +9,12 @@ import {
8
9
  getTopLevelMediaType,
9
10
  resolveFullMediaType,
10
11
  } from '@ai-sdk/provider-utils';
12
+ import {
13
+ isOpenResponsesExtensionItem,
14
+ type OpenResponsesExtensionInputPart,
15
+ type OpenResponsesExtensionItem,
16
+ type OpenResponsesExtensionRegistry,
17
+ } from '../open-responses-extension';
11
18
  import type {
12
19
  FunctionCallOutputItemParam,
13
20
  InputFileContentParam,
@@ -22,9 +29,13 @@ import type {
22
29
  export async function convertToOpenResponsesInput({
23
30
  prompt,
24
31
  providerOptionsName = 'open-responses',
32
+ extensionRegistry,
33
+ providerToolsByName = new Map(),
25
34
  }: {
26
35
  prompt: LanguageModelV4Prompt;
27
36
  providerOptionsName?: string;
37
+ extensionRegistry?: OpenResponsesExtensionRegistry;
38
+ providerToolsByName?: Map<string, LanguageModelV4ProviderTool>;
28
39
  }): Promise<{
29
40
  input: OpenResponsesRequestBody['input'];
30
41
  instructions: string | undefined;
@@ -33,6 +44,7 @@ export async function convertToOpenResponsesInput({
33
44
  const input: OpenResponsesRequestBody['input'] = [];
34
45
  const warnings: Array<SharedV4Warning> = [];
35
46
  const systemMessages: string[] = [];
47
+ const replayedExtensionItems = new Set<string>();
36
48
 
37
49
  for (const { role, content } of prompt) {
38
50
  switch (role) {
@@ -125,6 +137,52 @@ export async function convertToOpenResponsesInput({
125
137
  };
126
138
 
127
139
  for (const part of content) {
140
+ const extensionReplay = getExtensionReplay({
141
+ part,
142
+ providerOptionsName,
143
+ extensionRegistry,
144
+ });
145
+
146
+ if (extensionReplay != null) {
147
+ flushAssistantContent();
148
+ const replayItem = extensionReplay.item;
149
+ if (replayItem != null) {
150
+ const replayKey = `${replayItem.type}:${replayItem.id}`;
151
+ if (!replayedExtensionItems.has(replayKey)) {
152
+ input.push(replayItem);
153
+ replayedExtensionItems.add(replayKey);
154
+ }
155
+ }
156
+ continue;
157
+ }
158
+
159
+ if (part.type === 'tool-call' || part.type === 'tool-result') {
160
+ const providerTool = providerToolsByName.get(part.toolName);
161
+ const extension =
162
+ providerTool == null
163
+ ? undefined
164
+ : extensionRegistry?.byProviderToolId.get(providerTool.id);
165
+
166
+ if (providerTool != null && extension != null) {
167
+ flushAssistantContent();
168
+ const encoded = await encodeExtensionInputPart({
169
+ extensionRegistry,
170
+ part,
171
+ providerTool,
172
+ });
173
+
174
+ if (encoded == null) {
175
+ warnings.push({
176
+ type: 'unsupported',
177
+ feature: `provider-defined tool ${providerTool.id} ${part.type} history`,
178
+ });
179
+ } else {
180
+ input.push(...encoded);
181
+ }
182
+ continue;
183
+ }
184
+ }
185
+
128
186
  switch (part.type) {
129
187
  case 'reasoning': {
130
188
  flushAssistantContent();
@@ -243,6 +301,48 @@ export async function convertToOpenResponsesInput({
243
301
  case 'tool': {
244
302
  for (const part of content) {
245
303
  if (part.type === 'tool-result') {
304
+ const extensionReplay = getExtensionReplay({
305
+ part,
306
+ providerOptionsName,
307
+ extensionRegistry,
308
+ });
309
+
310
+ if (extensionReplay != null) {
311
+ const replayItem = extensionReplay.item;
312
+ if (replayItem != null) {
313
+ const replayKey = `${replayItem.type}:${replayItem.id}`;
314
+ if (!replayedExtensionItems.has(replayKey)) {
315
+ input.push(replayItem);
316
+ replayedExtensionItems.add(replayKey);
317
+ }
318
+ }
319
+ continue;
320
+ }
321
+
322
+ const providerTool = providerToolsByName.get(part.toolName);
323
+ const extension =
324
+ providerTool == null
325
+ ? undefined
326
+ : extensionRegistry?.byProviderToolId.get(providerTool.id);
327
+
328
+ if (providerTool != null && extension != null) {
329
+ const encoded = await encodeExtensionInputPart({
330
+ extensionRegistry,
331
+ part,
332
+ providerTool,
333
+ });
334
+
335
+ if (encoded == null) {
336
+ warnings.push({
337
+ type: 'unsupported',
338
+ feature: `provider-defined tool ${providerTool.id} tool-result history`,
339
+ });
340
+ } else {
341
+ input.push(...encoded);
342
+ }
343
+ continue;
344
+ }
345
+
246
346
  const output = part.output;
247
347
  let contentValue: FunctionCallOutputItemParam['output'];
248
348
 
@@ -346,6 +446,99 @@ export async function convertToOpenResponsesInput({
346
446
  };
347
447
  }
348
448
 
449
+ async function encodeExtensionInputPart({
450
+ extensionRegistry,
451
+ part,
452
+ providerTool,
453
+ }: {
454
+ extensionRegistry: OpenResponsesExtensionRegistry | undefined;
455
+ part: OpenResponsesExtensionInputPart;
456
+ providerTool: LanguageModelV4ProviderTool;
457
+ }): Promise<OpenResponsesExtensionItem[] | undefined> {
458
+ const extension = extensionRegistry?.byProviderToolId.get(providerTool.id);
459
+ const encodeInputItem = extension?.encodeInputItem;
460
+ const itemTypes = extension?.itemTypes;
461
+ if (encodeInputItem == null || itemTypes == null) {
462
+ return undefined;
463
+ }
464
+
465
+ try {
466
+ const value = await encodeInputItem({
467
+ part,
468
+ tool: providerTool,
469
+ });
470
+ const items =
471
+ value == null ? undefined : Array.isArray(value) ? value : [value];
472
+
473
+ if (
474
+ items == null ||
475
+ items.length === 0 ||
476
+ !items.every(
477
+ item =>
478
+ isOpenResponsesExtensionItem(item) && itemTypes.includes(item.type),
479
+ )
480
+ ) {
481
+ return undefined;
482
+ }
483
+
484
+ return items;
485
+ } catch {
486
+ return undefined;
487
+ }
488
+ }
489
+
490
+ function getExtensionReplay({
491
+ part,
492
+ providerOptionsName,
493
+ extensionRegistry,
494
+ }: {
495
+ part: {
496
+ providerOptions?: Record<string, unknown>;
497
+ };
498
+ providerOptionsName: string;
499
+ extensionRegistry: OpenResponsesExtensionRegistry | undefined;
500
+ }): { item?: OpenResponsesExtensionItem } | undefined {
501
+ const extensionData = getProviderData(
502
+ part,
503
+ providerOptionsName,
504
+ )?.openResponsesExtension;
505
+
506
+ if (
507
+ extensionData == null ||
508
+ typeof extensionData !== 'object' ||
509
+ Array.isArray(extensionData)
510
+ ) {
511
+ return undefined;
512
+ }
513
+
514
+ const { id, item, itemId } = extensionData as {
515
+ id?: unknown;
516
+ item?: unknown;
517
+ itemId?: unknown;
518
+ };
519
+
520
+ if (typeof id !== 'string') {
521
+ return undefined;
522
+ }
523
+
524
+ const extension = extensionRegistry?.byExtensionId.get(
525
+ id as LanguageModelV4ProviderTool['id'],
526
+ );
527
+
528
+ if (extension == null) {
529
+ return undefined;
530
+ }
531
+
532
+ if (
533
+ isOpenResponsesExtensionItem(item) &&
534
+ extension.itemTypes?.includes(item.type)
535
+ ) {
536
+ return { item };
537
+ }
538
+
539
+ return typeof itemId === 'string' ? {} : undefined;
540
+ }
541
+
349
542
  function getProviderData(
350
543
  part: {
351
544
  providerOptions?: Record<string, unknown>;
@@ -1,6 +1,11 @@
1
1
  import type { JSONSchema7 } from '@ai-sdk/provider';
2
2
  import { lazySchema, zodSchema } from '@ai-sdk/provider-utils';
3
3
  import { z } from 'zod/v4';
4
+ import type {
5
+ OpenResponsesExtensionEvent,
6
+ OpenResponsesExtensionItem,
7
+ OpenResponsesExtensionRecord,
8
+ } from '../open-responses-extension';
4
9
 
5
10
  export const openResponsesErrorSchema = lazySchema(() =>
6
11
  zodSchema(
@@ -263,7 +268,8 @@ export type AllowedToolsParam = {
263
268
  export type ToolChoiceParam =
264
269
  | ToolChoiceValueEnum
265
270
  | SpecificFunctionParam
266
- | AllowedToolsParam;
271
+ | AllowedToolsParam
272
+ | OpenResponsesExtensionRecord;
267
273
 
268
274
  // ============================================================================
269
275
  // Configuration Types
@@ -483,7 +489,8 @@ export type OutputItem =
483
489
  | FunctionCall
484
490
  | FunctionCallOutput
485
491
  | Message
486
- | ReasoningBody;
492
+ | ReasoningBody
493
+ | OpenResponsesExtensionItem;
487
494
 
488
495
  /**
489
496
  * Details about why the response was incomplete.
@@ -626,6 +633,7 @@ export type OpenResponsesRequestBody = {
626
633
  | AssistantMessageItemParam
627
634
  | FunctionCallItemParam
628
635
  | FunctionCallOutputItemParam
636
+ | OpenResponsesExtensionRecord
629
637
  >;
630
638
 
631
639
  /**
@@ -643,7 +651,7 @@ export type OpenResponsesRequestBody = {
643
651
  /**
644
652
  * A list of tools that the model may call while generating the response.
645
653
  */
646
- tools?: FunctionToolParam[];
654
+ tools?: Array<FunctionToolParam | OpenResponsesExtensionRecord>;
647
655
 
648
656
  /**
649
657
  * Controls which tool the model should use, if any.
@@ -1235,4 +1243,6 @@ export type OpenResponsesChunk =
1235
1243
  | ResponseReasoningSummaryPartAddedEvent
1236
1244
  | ResponseReasoningSummaryPartDoneEvent
1237
1245
  // Error Event
1238
- | ResponseErrorEvent;
1246
+ | ResponseErrorEvent
1247
+ // Registered extension event
1248
+ | OpenResponsesExtensionEvent;
@@ -1,4 +1,5 @@
1
1
  import type { FetchFunction } from '@ai-sdk/provider-utils';
2
+ import type { OpenResponsesExtensionRegistry } from '../open-responses-extension';
2
3
 
3
4
  export type OpenResponsesConfig = {
4
5
  provider: string;
@@ -7,4 +8,5 @@ export type OpenResponsesConfig = {
7
8
  headers?: () => Record<string, string | undefined>;
8
9
  fetch?: FetchFunction;
9
10
  generateId: () => string;
11
+ extensionRegistry?: OpenResponsesExtensionRegistry;
10
12
  };