@ai-sdk/openai 4.0.43 → 4.0.45

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 LanguageModelV4CallOptions,
7
7
  type LanguageModelV4Content,
8
8
  type LanguageModelV4FinishReason,
9
+ type LanguageModelV4FunctionTool,
9
10
  type LanguageModelV4GenerateResult,
10
11
  type LanguageModelV4ProviderTool,
11
12
  type LanguageModelV4StreamPart,
@@ -58,6 +59,10 @@ import {
58
59
  type OpenAIResponsesUsage,
59
60
  } from './convert-openai-responses-usage';
60
61
  import { convertToOpenAIResponsesInput } from './convert-to-openai-responses-input';
62
+ import {
63
+ expandParallelToolCall,
64
+ isUndeclaredParallelToolCall,
65
+ } from './expand-parallel-tool-call';
61
66
  import { mapOpenAIResponseFinishReason } from './map-openai-responses-finish-reason';
62
67
  import {
63
68
  openaiResponsesChunkSchema,
@@ -691,6 +696,10 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
691
696
 
692
697
  const content: Array<LanguageModelV4Content> = [];
693
698
  const logprobs: Array<OpenAIResponsesLogprobs> = [];
699
+ const functionTools =
700
+ options.tools?.filter(
701
+ (tool): tool is LanguageModelV4FunctionTool => tool.type === 'function',
702
+ ) ?? [];
694
703
 
695
704
  // flag that checks if there have been client-side tool calls (not executed by openai)
696
705
  let hasFunctionCall = false;
@@ -951,6 +960,22 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
951
960
  case 'function_call': {
952
961
  hasFunctionCall = true;
953
962
 
963
+ const expandedToolCalls = await expandParallelToolCall({
964
+ toolCall: {
965
+ toolCallId: part.call_id,
966
+ toolName: part.name,
967
+ input: part.arguments,
968
+ },
969
+ tools: functionTools,
970
+ providerOptionsName,
971
+ itemId: part.id,
972
+ });
973
+
974
+ if (expandedToolCalls != null) {
975
+ content.push(...expandedToolCalls);
976
+ break;
977
+ }
978
+
954
979
  content.push({
955
980
  type: 'tool-call',
956
981
  toolCallId: part.call_id,
@@ -1342,6 +1367,11 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1342
1367
  const approvalRequestIdToDummyToolCallIdFromPrompt =
1343
1368
  extractApprovalRequestIdToToolCallIdMapping(options.prompt);
1344
1369
 
1370
+ const functionTools =
1371
+ options.tools?.filter(
1372
+ (tool): tool is LanguageModelV4FunctionTool => tool.type === 'function',
1373
+ ) ?? [];
1374
+
1345
1375
  const approvalRequestIdToDummyToolCallIdFromStream = new Map<
1346
1376
  string,
1347
1377
  string
@@ -1368,6 +1398,8 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1368
1398
  endEmitted: boolean;
1369
1399
  };
1370
1400
  toolSearchExecution?: 'server' | 'client';
1401
+ suppressInputStreaming?: boolean;
1402
+ bufferedInputDeltas?: string[];
1371
1403
  }
1372
1404
  | undefined
1373
1405
  > = {};
@@ -1450,16 +1482,25 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1450
1482
 
1451
1483
  if (isResponseOutputItemAddedChunk(value)) {
1452
1484
  if (value.item.type === 'function_call') {
1485
+ const suppressInputStreaming = isUndeclaredParallelToolCall({
1486
+ toolName: value.item.name,
1487
+ tools: functionTools,
1488
+ });
1489
+
1453
1490
  ongoingToolCalls[value.output_index] = {
1454
1491
  toolName: value.item.name,
1455
1492
  toolCallId: value.item.call_id,
1493
+ suppressInputStreaming,
1494
+ bufferedInputDeltas: suppressInputStreaming ? [] : undefined,
1456
1495
  };
1457
1496
 
1458
- controller.enqueue({
1459
- type: 'tool-input-start',
1460
- id: value.item.call_id,
1461
- toolName: value.item.name,
1462
- });
1497
+ if (!suppressInputStreaming) {
1498
+ controller.enqueue({
1499
+ type: 'tool-input-start',
1500
+ id: value.item.call_id,
1501
+ toolName: value.item.name,
1502
+ });
1503
+ }
1463
1504
  } else if (value.item.type === 'custom_tool_call') {
1464
1505
  const toolName = toolNameMapping.toCustomToolName(
1465
1506
  value.item.name,
@@ -1697,43 +1738,120 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
1697
1738
  });
1698
1739
  activeOutputItemIds[value.output_index] = undefined;
1699
1740
  } else if (value.item.type === 'function_call') {
1741
+ const item = value.item;
1742
+ const ongoingToolCall = ongoingToolCalls[value.output_index];
1700
1743
  ongoingToolCalls[value.output_index] = undefined;
1701
1744
  hasFunctionCall = true;
1702
1745
 
1703
- controller.enqueue({
1704
- type: 'tool-input-end',
1705
- id: value.item.call_id,
1706
- ...(value.item.namespace != null && {
1746
+ const suppressInputStreaming =
1747
+ ongoingToolCall?.suppressInputStreaming ??
1748
+ isUndeclaredParallelToolCall({
1749
+ toolName: item.name,
1750
+ tools: functionTools,
1751
+ });
1752
+
1753
+ const enqueueUnexpandedToolCall = () => {
1754
+ if (suppressInputStreaming) {
1755
+ controller.enqueue({
1756
+ type: 'tool-input-start',
1757
+ id: item.call_id,
1758
+ toolName: item.name,
1759
+ });
1760
+
1761
+ const bufferedInputDeltas =
1762
+ ongoingToolCall?.bufferedInputDeltas ?? [];
1763
+
1764
+ if (bufferedInputDeltas.length > 0) {
1765
+ for (const delta of bufferedInputDeltas) {
1766
+ controller.enqueue({
1767
+ type: 'tool-input-delta',
1768
+ id: item.call_id,
1769
+ delta,
1770
+ });
1771
+ }
1772
+ } else if (item.arguments.length > 0) {
1773
+ controller.enqueue({
1774
+ type: 'tool-input-delta',
1775
+ id: item.call_id,
1776
+ delta: item.arguments,
1777
+ });
1778
+ }
1779
+ }
1780
+
1781
+ controller.enqueue({
1782
+ type: 'tool-input-end',
1783
+ id: item.call_id,
1784
+ ...(item.namespace != null && {
1785
+ providerMetadata: {
1786
+ [providerOptionsName]: {
1787
+ namespace: item.namespace,
1788
+ },
1789
+ },
1790
+ }),
1791
+ });
1792
+
1793
+ controller.enqueue({
1794
+ type: 'tool-call',
1795
+ toolCallId: item.call_id,
1796
+ toolName: item.name,
1797
+ input: item.arguments,
1707
1798
  providerMetadata: {
1708
1799
  [providerOptionsName]: {
1709
- namespace: value.item.namespace,
1800
+ itemId: item.id,
1801
+ ...(item.namespace != null && {
1802
+ namespace: item.namespace,
1803
+ }),
1804
+ ...(item.caller != null && {
1805
+ caller:
1806
+ item.caller.type === 'program'
1807
+ ? {
1808
+ type: 'program',
1809
+ callerId: item.caller.caller_id,
1810
+ }
1811
+ : item.caller,
1812
+ }),
1710
1813
  },
1711
1814
  },
1712
- }),
1713
- });
1815
+ });
1816
+ };
1714
1817
 
1715
- controller.enqueue({
1716
- type: 'tool-call',
1717
- toolCallId: value.item.call_id,
1718
- toolName: value.item.name,
1719
- input: value.item.arguments,
1720
- providerMetadata: {
1721
- [providerOptionsName]: {
1722
- itemId: value.item.id,
1723
- ...(value.item.namespace != null && {
1724
- namespace: value.item.namespace,
1725
- }),
1726
- ...(value.item.caller != null && {
1727
- caller:
1728
- value.item.caller.type === 'program'
1729
- ? {
1730
- type: 'program',
1731
- callerId: value.item.caller.caller_id,
1732
- }
1733
- : value.item.caller,
1734
- }),
1735
- },
1818
+ if (!suppressInputStreaming) {
1819
+ enqueueUnexpandedToolCall();
1820
+ return;
1821
+ }
1822
+
1823
+ return expandParallelToolCall({
1824
+ toolCall: {
1825
+ toolCallId: item.call_id,
1826
+ toolName: item.name,
1827
+ input: item.arguments,
1736
1828
  },
1829
+ tools: functionTools,
1830
+ providerOptionsName,
1831
+ itemId: item.id,
1832
+ }).then(expandedToolCalls => {
1833
+ if (expandedToolCalls == null) {
1834
+ enqueueUnexpandedToolCall();
1835
+ return;
1836
+ }
1837
+
1838
+ for (const toolCall of expandedToolCalls) {
1839
+ controller.enqueue({
1840
+ type: 'tool-input-start',
1841
+ id: toolCall.toolCallId,
1842
+ toolName: toolCall.toolName,
1843
+ });
1844
+ controller.enqueue({
1845
+ type: 'tool-input-delta',
1846
+ id: toolCall.toolCallId,
1847
+ delta: toolCall.input,
1848
+ });
1849
+ controller.enqueue({
1850
+ type: 'tool-input-end',
1851
+ id: toolCall.toolCallId,
1852
+ });
1853
+ controller.enqueue(toolCall);
1854
+ }
1737
1855
  });
1738
1856
  } else if (value.item.type === 'program') {
1739
1857
  controller.enqueue({
@@ -2216,11 +2334,15 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2216
2334
  const toolCall = ongoingToolCalls[value.output_index];
2217
2335
 
2218
2336
  if (toolCall != null) {
2219
- controller.enqueue({
2220
- type: 'tool-input-delta',
2221
- id: toolCall.toolCallId,
2222
- delta: value.delta,
2223
- });
2337
+ if (toolCall.suppressInputStreaming) {
2338
+ toolCall.bufferedInputDeltas?.push(value.delta);
2339
+ } else {
2340
+ controller.enqueue({
2341
+ type: 'tool-input-delta',
2342
+ id: toolCall.toolCallId,
2343
+ delta: value.delta,
2344
+ });
2345
+ }
2224
2346
  }
2225
2347
  } else if (isResponseCustomToolCallInputDeltaChunk(value)) {
2226
2348
  const toolCall = ongoingToolCalls[value.output_index];
@@ -2561,6 +2683,26 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
2561
2683
  },
2562
2684
 
2563
2685
  flush(controller) {
2686
+ for (const toolCall of Object.values(ongoingToolCalls)) {
2687
+ if (!toolCall?.suppressInputStreaming) {
2688
+ continue;
2689
+ }
2690
+
2691
+ controller.enqueue({
2692
+ type: 'tool-input-start',
2693
+ id: toolCall.toolCallId,
2694
+ toolName: toolCall.toolName,
2695
+ });
2696
+
2697
+ for (const delta of toolCall.bufferedInputDeltas ?? []) {
2698
+ controller.enqueue({
2699
+ type: 'tool-input-delta',
2700
+ id: toolCall.toolCallId,
2701
+ delta,
2702
+ });
2703
+ }
2704
+ }
2705
+
2564
2706
  const providerMetadata: SharedV4ProviderMetadata = {
2565
2707
  [providerOptionsName]: {
2566
2708
  responseId: responseId,