@ai-sdk/openai 3.0.98 → 3.0.99

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.
@@ -6,6 +6,7 @@ import {
6
6
  type LanguageModelV3CallOptions,
7
7
  type LanguageModelV3Content,
8
8
  type LanguageModelV3FinishReason,
9
+ type LanguageModelV3FunctionTool,
9
10
  type LanguageModelV3GenerateResult,
10
11
  type LanguageModelV3ProviderTool,
11
12
  type LanguageModelV3StreamPart,
@@ -49,6 +50,10 @@ import {
49
50
  type OpenAIResponsesUsage,
50
51
  } from './convert-openai-responses-usage';
51
52
  import { convertToOpenAIResponsesInput } from './convert-to-openai-responses-input';
53
+ import {
54
+ expandParallelToolCall,
55
+ isUndeclaredParallelToolCall,
56
+ } from './expand-parallel-tool-call';
52
57
  import { mapOpenAIResponseFinishReason } from './map-openai-responses-finish-reason';
53
58
  import {
54
59
  openaiResponsesChunkSchema,
@@ -538,6 +543,10 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
538
543
 
539
544
  const content: Array<LanguageModelV3Content> = [];
540
545
  const logprobs: Array<OpenAIResponsesLogprobs> = [];
546
+ const functionTools =
547
+ options.tools?.filter(
548
+ (tool): tool is LanguageModelV3FunctionTool => tool.type === 'function',
549
+ ) ?? [];
541
550
 
542
551
  // flag that checks if there have been client-side tool calls (not executed by openai)
543
552
  let hasFunctionCall = false;
@@ -798,6 +807,22 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
798
807
  case 'function_call': {
799
808
  hasFunctionCall = true;
800
809
 
810
+ const expandedToolCalls = await expandParallelToolCall({
811
+ toolCall: {
812
+ toolCallId: part.call_id,
813
+ toolName: part.name,
814
+ input: part.arguments,
815
+ },
816
+ tools: functionTools,
817
+ providerOptionsName,
818
+ itemId: part.id,
819
+ });
820
+
821
+ if (expandedToolCalls != null) {
822
+ content.push(...expandedToolCalls);
823
+ break;
824
+ }
825
+
801
826
  content.push({
802
827
  type: 'tool-call',
803
828
  toolCallId: part.call_id,
@@ -1105,6 +1130,11 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1105
1130
  const approvalRequestIdToDummyToolCallIdFromPrompt =
1106
1131
  extractApprovalRequestIdToToolCallIdMapping(options.prompt);
1107
1132
 
1133
+ const functionTools =
1134
+ options.tools?.filter(
1135
+ (tool): tool is LanguageModelV3FunctionTool => tool.type === 'function',
1136
+ ) ?? [];
1137
+
1108
1138
  const approvalRequestIdToDummyToolCallIdFromStream = new Map<
1109
1139
  string,
1110
1140
  string
@@ -1131,6 +1161,8 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1131
1161
  endEmitted: boolean;
1132
1162
  };
1133
1163
  toolSearchExecution?: 'server' | 'client';
1164
+ suppressInputStreaming?: boolean;
1165
+ bufferedInputDeltas?: string[];
1134
1166
  }
1135
1167
  | undefined
1136
1168
  > = {};
@@ -1199,16 +1231,25 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1199
1231
 
1200
1232
  if (isResponseOutputItemAddedChunk(value)) {
1201
1233
  if (value.item.type === 'function_call') {
1234
+ const suppressInputStreaming = isUndeclaredParallelToolCall({
1235
+ toolName: value.item.name,
1236
+ tools: functionTools,
1237
+ });
1238
+
1202
1239
  ongoingToolCalls[value.output_index] = {
1203
1240
  toolName: value.item.name,
1204
1241
  toolCallId: value.item.call_id,
1242
+ suppressInputStreaming,
1243
+ bufferedInputDeltas: suppressInputStreaming ? [] : undefined,
1205
1244
  };
1206
1245
 
1207
- controller.enqueue({
1208
- type: 'tool-input-start',
1209
- id: value.item.call_id,
1210
- toolName: value.item.name,
1211
- });
1246
+ if (!suppressInputStreaming) {
1247
+ controller.enqueue({
1248
+ type: 'tool-input-start',
1249
+ id: value.item.call_id,
1250
+ toolName: value.item.name,
1251
+ });
1252
+ }
1212
1253
  } else if (value.item.type === 'custom_tool_call') {
1213
1254
  const toolName = toolNameMapping.toCustomToolName(
1214
1255
  value.item.name,
@@ -1439,34 +1480,111 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1439
1480
  },
1440
1481
  });
1441
1482
  } else if (value.item.type === 'function_call') {
1483
+ const item = value.item;
1484
+ const ongoingToolCall = ongoingToolCalls[value.output_index];
1442
1485
  ongoingToolCalls[value.output_index] = undefined;
1443
1486
  hasFunctionCall = true;
1444
1487
 
1445
- controller.enqueue({
1446
- type: 'tool-input-end',
1447
- id: value.item.call_id,
1448
- ...(value.item.namespace != null && {
1488
+ const suppressInputStreaming =
1489
+ ongoingToolCall?.suppressInputStreaming ??
1490
+ isUndeclaredParallelToolCall({
1491
+ toolName: item.name,
1492
+ tools: functionTools,
1493
+ });
1494
+
1495
+ const enqueueUnexpandedToolCall = () => {
1496
+ if (suppressInputStreaming) {
1497
+ controller.enqueue({
1498
+ type: 'tool-input-start',
1499
+ id: item.call_id,
1500
+ toolName: item.name,
1501
+ });
1502
+
1503
+ const bufferedInputDeltas =
1504
+ ongoingToolCall?.bufferedInputDeltas ?? [];
1505
+
1506
+ if (bufferedInputDeltas.length > 0) {
1507
+ for (const delta of bufferedInputDeltas) {
1508
+ controller.enqueue({
1509
+ type: 'tool-input-delta',
1510
+ id: item.call_id,
1511
+ delta,
1512
+ });
1513
+ }
1514
+ } else if (item.arguments.length > 0) {
1515
+ controller.enqueue({
1516
+ type: 'tool-input-delta',
1517
+ id: item.call_id,
1518
+ delta: item.arguments,
1519
+ });
1520
+ }
1521
+ }
1522
+
1523
+ controller.enqueue({
1524
+ type: 'tool-input-end',
1525
+ id: item.call_id,
1526
+ ...(item.namespace != null && {
1527
+ providerMetadata: {
1528
+ [providerOptionsName]: {
1529
+ namespace: item.namespace,
1530
+ },
1531
+ },
1532
+ }),
1533
+ });
1534
+
1535
+ controller.enqueue({
1536
+ type: 'tool-call',
1537
+ toolCallId: item.call_id,
1538
+ toolName: item.name,
1539
+ input: item.arguments,
1449
1540
  providerMetadata: {
1450
1541
  [providerOptionsName]: {
1451
- namespace: value.item.namespace,
1542
+ itemId: item.id,
1543
+ ...(item.namespace != null && {
1544
+ namespace: item.namespace,
1545
+ }),
1452
1546
  },
1453
1547
  },
1454
- }),
1455
- });
1548
+ });
1549
+ };
1456
1550
 
1457
- controller.enqueue({
1458
- type: 'tool-call',
1459
- toolCallId: value.item.call_id,
1460
- toolName: value.item.name,
1461
- input: value.item.arguments,
1462
- providerMetadata: {
1463
- [providerOptionsName]: {
1464
- itemId: value.item.id,
1465
- ...(value.item.namespace != null && {
1466
- namespace: value.item.namespace,
1467
- }),
1468
- },
1551
+ if (!suppressInputStreaming) {
1552
+ enqueueUnexpandedToolCall();
1553
+ return;
1554
+ }
1555
+
1556
+ return expandParallelToolCall({
1557
+ toolCall: {
1558
+ toolCallId: item.call_id,
1559
+ toolName: item.name,
1560
+ input: item.arguments,
1469
1561
  },
1562
+ tools: functionTools,
1563
+ providerOptionsName,
1564
+ itemId: item.id,
1565
+ }).then(expandedToolCalls => {
1566
+ if (expandedToolCalls == null) {
1567
+ enqueueUnexpandedToolCall();
1568
+ return;
1569
+ }
1570
+
1571
+ for (const toolCall of expandedToolCalls) {
1572
+ controller.enqueue({
1573
+ type: 'tool-input-start',
1574
+ id: toolCall.toolCallId,
1575
+ toolName: toolCall.toolName,
1576
+ });
1577
+ controller.enqueue({
1578
+ type: 'tool-input-delta',
1579
+ id: toolCall.toolCallId,
1580
+ delta: toolCall.input,
1581
+ });
1582
+ controller.enqueue({
1583
+ type: 'tool-input-end',
1584
+ id: toolCall.toolCallId,
1585
+ });
1586
+ controller.enqueue(toolCall);
1587
+ }
1470
1588
  });
1471
1589
  } else if (value.item.type === 'custom_tool_call') {
1472
1590
  ongoingToolCalls[value.output_index] = undefined;
@@ -1863,11 +1981,15 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
1863
1981
  const toolCall = ongoingToolCalls[value.output_index];
1864
1982
 
1865
1983
  if (toolCall != null) {
1866
- controller.enqueue({
1867
- type: 'tool-input-delta',
1868
- id: toolCall.toolCallId,
1869
- delta: value.delta,
1870
- });
1984
+ if (toolCall.suppressInputStreaming) {
1985
+ toolCall.bufferedInputDeltas?.push(value.delta);
1986
+ } else {
1987
+ controller.enqueue({
1988
+ type: 'tool-input-delta',
1989
+ id: toolCall.toolCallId,
1990
+ delta: value.delta,
1991
+ });
1992
+ }
1871
1993
  }
1872
1994
  } else if (isResponseCustomToolCallInputDeltaChunk(value)) {
1873
1995
  const toolCall = ongoingToolCalls[value.output_index];
@@ -2189,6 +2311,26 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
2189
2311
  },
2190
2312
 
2191
2313
  flush(controller) {
2314
+ for (const toolCall of Object.values(ongoingToolCalls)) {
2315
+ if (!toolCall?.suppressInputStreaming) {
2316
+ continue;
2317
+ }
2318
+
2319
+ controller.enqueue({
2320
+ type: 'tool-input-start',
2321
+ id: toolCall.toolCallId,
2322
+ toolName: toolCall.toolName,
2323
+ });
2324
+
2325
+ for (const delta of toolCall.bufferedInputDeltas ?? []) {
2326
+ controller.enqueue({
2327
+ type: 'tool-input-delta',
2328
+ id: toolCall.toolCallId,
2329
+ delta,
2330
+ });
2331
+ }
2332
+ }
2333
+
2192
2334
  const providerMetadata: SharedV3ProviderMetadata = {
2193
2335
  [providerOptionsName]: {
2194
2336
  responseId: responseId,