@ai-sdk/openai 4.0.0-beta.35 → 4.0.0-beta.37

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai",
3
- "version": "4.0.0-beta.35",
3
+ "version": "4.0.0-beta.37",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@ai-sdk/provider": "4.0.0-beta.12",
39
- "@ai-sdk/provider-utils": "5.0.0-beta.23"
39
+ "@ai-sdk/provider-utils": "5.0.0-beta.25"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "20.17.24",
@@ -1,5 +1,4 @@
1
1
  import {
2
- InvalidResponseDataError,
3
2
  LanguageModelV4,
4
3
  LanguageModelV4CallOptions,
5
4
  LanguageModelV4Content,
@@ -13,12 +12,12 @@ import {
13
12
  import {
14
13
  FetchFunction,
15
14
  ParseResult,
15
+ StreamingToolCallTracker,
16
16
  combineHeaders,
17
17
  createEventSourceResponseHandler,
18
18
  createJsonResponseHandler,
19
19
  generateId,
20
20
  isCustomReasoning,
21
- isParsableJson,
22
21
  parseProviderOptions,
23
22
  postJsonToApi,
24
23
  serializeModelOptions,
@@ -453,15 +452,10 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
453
452
  fetch: this.config.fetch,
454
453
  });
455
454
 
456
- const toolCalls: Array<{
457
- id: string;
458
- type: 'function';
459
- function: {
460
- name: string;
461
- arguments: string;
462
- };
463
- hasFinished: boolean;
464
- }> = [];
455
+ const toolCallTracker = new StreamingToolCallTracker({
456
+ generateId,
457
+ typeValidation: 'if-present',
458
+ });
465
459
 
466
460
  let finishReason: LanguageModelV4FinishReason = {
467
461
  unified: 'other',
@@ -571,124 +565,10 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
571
565
 
572
566
  if (delta.tool_calls != null) {
573
567
  for (const toolCallDelta of delta.tool_calls) {
574
- const index = toolCallDelta.index;
575
-
576
- // Tool call start. OpenAI returns all information except the arguments in the first chunk.
577
- if (toolCalls[index] == null) {
578
- if (
579
- toolCallDelta.type != null &&
580
- toolCallDelta.type !== 'function'
581
- ) {
582
- throw new InvalidResponseDataError({
583
- data: toolCallDelta,
584
- message: `Expected 'function' type.`,
585
- });
586
- }
587
-
588
- if (toolCallDelta.id == null) {
589
- throw new InvalidResponseDataError({
590
- data: toolCallDelta,
591
- message: `Expected 'id' to be a string.`,
592
- });
593
- }
594
-
595
- if (toolCallDelta.function?.name == null) {
596
- throw new InvalidResponseDataError({
597
- data: toolCallDelta,
598
- message: `Expected 'function.name' to be a string.`,
599
- });
600
- }
601
-
602
- controller.enqueue({
603
- type: 'tool-input-start',
604
- id: toolCallDelta.id,
605
- toolName: toolCallDelta.function.name,
606
- });
607
-
608
- toolCalls[index] = {
609
- id: toolCallDelta.id,
610
- type: 'function',
611
- function: {
612
- name: toolCallDelta.function.name,
613
- arguments: toolCallDelta.function.arguments ?? '',
614
- },
615
- hasFinished: false,
616
- };
617
-
618
- const toolCall = toolCalls[index];
619
-
620
- if (
621
- toolCall.function?.name != null &&
622
- toolCall.function?.arguments != null
623
- ) {
624
- // send delta if the argument text has already started:
625
- if (toolCall.function.arguments.length > 0) {
626
- controller.enqueue({
627
- type: 'tool-input-delta',
628
- id: toolCall.id,
629
- delta: toolCall.function.arguments,
630
- });
631
- }
632
-
633
- // check if tool call is complete
634
- // (some providers send the full tool call in one chunk):
635
- if (isParsableJson(toolCall.function.arguments)) {
636
- controller.enqueue({
637
- type: 'tool-input-end',
638
- id: toolCall.id,
639
- });
640
-
641
- controller.enqueue({
642
- type: 'tool-call',
643
- toolCallId: toolCall.id ?? generateId(),
644
- toolName: toolCall.function.name,
645
- input: toolCall.function.arguments,
646
- });
647
- toolCall.hasFinished = true;
648
- }
649
- }
650
-
651
- continue;
652
- }
653
-
654
- // existing tool call, merge if not finished
655
- const toolCall = toolCalls[index];
656
-
657
- if (toolCall.hasFinished) {
658
- continue;
659
- }
660
-
661
- if (toolCallDelta.function?.arguments != null) {
662
- toolCall.function!.arguments +=
663
- toolCallDelta.function?.arguments ?? '';
664
- }
665
-
666
- // send delta
667
- controller.enqueue({
668
- type: 'tool-input-delta',
669
- id: toolCall.id,
670
- delta: toolCallDelta.function.arguments ?? '',
671
- });
672
-
673
- // check if tool call is complete
674
- if (
675
- toolCall.function?.name != null &&
676
- toolCall.function?.arguments != null &&
677
- isParsableJson(toolCall.function.arguments)
678
- ) {
679
- controller.enqueue({
680
- type: 'tool-input-end',
681
- id: toolCall.id,
682
- });
683
-
684
- controller.enqueue({
685
- type: 'tool-call',
686
- toolCallId: toolCall.id ?? generateId(),
687
- toolName: toolCall.function.name,
688
- input: toolCall.function.arguments,
689
- });
690
- toolCall.hasFinished = true;
691
- }
568
+ toolCallTracker.processDelta(
569
+ toolCallDelta,
570
+ controller.enqueue.bind(controller),
571
+ );
692
572
  }
693
573
  }
694
574
 
@@ -711,6 +591,8 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
711
591
  controller.enqueue({ type: 'text-end', id: '0' });
712
592
  }
713
593
 
594
+ toolCallTracker.flush(controller.enqueue.bind(controller));
595
+
714
596
  controller.enqueue({
715
597
  type: 'finish',
716
598
  finishReason,