@ai-sdk/open-responses 2.0.27 → 2.0.29

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/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @ai-sdk/open-responses
2
2
 
3
+ ## 2.0.29
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [e6087c9]
8
+ - @ai-sdk/provider-utils@5.0.28
9
+
10
+ ## 2.0.28
11
+
12
+ ### Patch Changes
13
+
14
+ - 3b9f025: Add a provider-native `reasoningEffort` option for Open Responses endpoints.
15
+ - 1c1a9d1: Preserve Open Responses output item order, text annotations, reasoning state, and reasoning part boundaries when replaying manually managed history.
16
+ - 6fe187f: fix(open-responses): close unfinished reasoning stream parts with their original IDs
17
+ - 1d0bdaa: Warn when provider-defined tools are unsupported instead of silently dropping them.
18
+
3
19
  ## 2.0.27
4
20
 
5
21
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -33,6 +33,7 @@ interface OpenResponsesProviderSettings {
33
33
  declare function createOpenResponses(options: OpenResponsesProviderSettings): OpenResponsesProvider;
34
34
 
35
35
  declare const openResponsesLanguageModelOptions: _ai_sdk_provider_utils.LazySchema<{
36
+ reasoningEffort?: string | null | undefined;
36
37
  reasoningSummary?: "auto" | "concise" | "detailed" | null | undefined;
37
38
  }>;
38
39
  type OpenResponsesLanguageModelOptions = InferSchema<typeof openResponsesLanguageModelOptions>;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = true ? "2.0.27" : "0.0.0-test";
2
+ var VERSION = true ? "2.0.29" : "0.0.0-test";
3
3
 
4
4
  // src/open-responses-provider.ts
5
5
  import {
@@ -40,9 +40,10 @@ import {
40
40
  resolveFullMediaType
41
41
  } from "@ai-sdk/provider-utils";
42
42
  async function convertToOpenResponsesInput({
43
- prompt
43
+ prompt,
44
+ providerOptionsName = "open-responses"
44
45
  }) {
45
- var _a, _b, _c;
46
+ var _a, _b, _c, _d;
46
47
  const input = [];
47
48
  const warnings = [];
48
49
  const systemMessages = [];
@@ -106,27 +107,89 @@ async function convertToOpenResponsesInput({
106
107
  break;
107
108
  }
108
109
  case "assistant": {
109
- const assistantContent = [];
110
- const reasoningItems = [];
111
- const toolCalls = [];
110
+ let assistantContent = [];
111
+ let assistantMessageId;
112
+ const flushAssistantContent = () => {
113
+ if (assistantContent.length === 0) {
114
+ return;
115
+ }
116
+ input.push({
117
+ type: "message",
118
+ role: "assistant",
119
+ content: assistantContent,
120
+ ...assistantMessageId != null && { id: assistantMessageId }
121
+ });
122
+ assistantContent = [];
123
+ assistantMessageId = void 0;
124
+ };
112
125
  for (const part of content) {
113
126
  switch (part.type) {
114
127
  case "reasoning": {
115
- reasoningItems.push({
128
+ flushAssistantContent();
129
+ const providerData = getProviderData(part, providerOptionsName);
130
+ const itemId = typeof (providerData == null ? void 0 : providerData.itemId) === "string" ? providerData.itemId : void 0;
131
+ const summary = parseReasoningSummary(
132
+ providerData == null ? void 0 : providerData.reasoningSummary
133
+ );
134
+ const reasoningContent = parseReasoningContent(
135
+ providerData == null ? void 0 : providerData.reasoningContent
136
+ );
137
+ const hasReasoningContent = providerData != null && "reasoningContent" in providerData;
138
+ const encryptedContent = typeof (providerData == null ? void 0 : providerData.reasoningEncryptedContent) === "string" ? providerData.reasoningEncryptedContent : void 0;
139
+ const reasoningItem = {
116
140
  type: "reasoning",
117
- summary: [],
118
- content: [{ type: "reasoning_text", text: part.text }]
119
- });
141
+ summary: summary != null ? summary : [],
142
+ ...itemId != null && { id: itemId },
143
+ ...reasoningContent != null ? { content: reasoningContent } : !hasReasoningContent && part.text.length > 0 ? {
144
+ content: [
145
+ {
146
+ type: "reasoning_text",
147
+ text: part.text
148
+ }
149
+ ]
150
+ } : {},
151
+ ...encryptedContent != null && {
152
+ encrypted_content: encryptedContent
153
+ }
154
+ };
155
+ const previousItem = input[input.length - 1];
156
+ if (reasoningItem.id != null && (previousItem == null ? void 0 : previousItem.type) === "reasoning" && previousItem.id === reasoningItem.id) {
157
+ if (reasoningItem.content != null) {
158
+ previousItem.content = [
159
+ ...(_b = previousItem.content) != null ? _b : [],
160
+ ...reasoningItem.content
161
+ ];
162
+ }
163
+ } else {
164
+ input.push(reasoningItem);
165
+ }
120
166
  break;
121
167
  }
122
168
  case "text": {
123
- assistantContent.push({ type: "output_text", text: part.text });
169
+ const providerData = getProviderData(part, providerOptionsName);
170
+ const itemId = typeof (providerData == null ? void 0 : providerData.itemId) === "string" ? providerData.itemId : void 0;
171
+ const annotations = parseOutputTextAnnotations(
172
+ providerData == null ? void 0 : providerData.annotations
173
+ );
174
+ if (assistantContent.length > 0 && assistantMessageId !== itemId) {
175
+ flushAssistantContent();
176
+ }
177
+ assistantMessageId = itemId;
178
+ assistantContent.push({
179
+ type: "output_text",
180
+ text: part.text,
181
+ ...annotations != null && { annotations }
182
+ });
124
183
  break;
125
184
  }
126
185
  case "tool-call": {
186
+ flushAssistantContent();
127
187
  const argumentsValue = typeof part.input === "string" ? part.input : JSON.stringify(part.input);
128
- toolCalls.push({
188
+ const providerData = getProviderData(part, providerOptionsName);
189
+ const itemId = typeof (providerData == null ? void 0 : providerData.itemId) === "string" ? providerData.itemId : void 0;
190
+ input.push({
129
191
  type: "function_call",
192
+ ...itemId != null && { id: itemId },
130
193
  call_id: part.toolCallId,
131
194
  name: part.toolName,
132
195
  arguments: argumentsValue
@@ -135,19 +198,7 @@ async function convertToOpenResponsesInput({
135
198
  }
136
199
  }
137
200
  }
138
- for (const reasoningItem of reasoningItems) {
139
- input.push(reasoningItem);
140
- }
141
- if (assistantContent.length > 0) {
142
- input.push({
143
- type: "message",
144
- role: "assistant",
145
- content: assistantContent
146
- });
147
- }
148
- for (const toolCall of toolCalls) {
149
- input.push(toolCall);
150
- }
201
+ flushAssistantContent();
151
202
  break;
152
203
  }
153
204
  case "tool": {
@@ -161,7 +212,7 @@ async function convertToOpenResponsesInput({
161
212
  contentValue = output.value;
162
213
  break;
163
214
  case "execution-denied":
164
- contentValue = (_b = output.reason) != null ? _b : "Tool call execution denied.";
215
+ contentValue = (_c = output.reason) != null ? _c : "Tool call execution denied.";
165
216
  break;
166
217
  case "json":
167
218
  case "error-json":
@@ -192,7 +243,7 @@ async function convertToOpenResponsesInput({
192
243
  } else {
193
244
  contentParts.push({
194
245
  type: "input_file",
195
- filename: (_c = item.filename) != null ? _c : "data",
246
+ filename: (_d = item.filename) != null ? _d : "data",
196
247
  file_data: `data:${fullMediaType};base64,${convertToBase64(item.data.data)}`
197
248
  });
198
249
  }
@@ -246,6 +297,47 @@ async function convertToOpenResponsesInput({
246
297
  warnings
247
298
  };
248
299
  }
300
+ function getProviderData(part, providerOptionsName) {
301
+ var _a, _b, _c;
302
+ const providerData = (_c = (_a = part.providerOptions) == null ? void 0 : _a[providerOptionsName]) != null ? _c : (_b = part.providerMetadata) == null ? void 0 : _b[providerOptionsName];
303
+ return providerData != null && typeof providerData === "object" && !Array.isArray(providerData) ? providerData : void 0;
304
+ }
305
+ function parseReasoningSummary(value) {
306
+ if (!Array.isArray(value) || !value.every(
307
+ (part) => part != null && typeof part === "object" && part.type === "summary_text" && typeof part.text === "string"
308
+ )) {
309
+ return void 0;
310
+ }
311
+ return value.map((part) => ({
312
+ type: "summary_text",
313
+ text: part.text
314
+ }));
315
+ }
316
+ function parseReasoningContent(value) {
317
+ if (!Array.isArray(value) || !value.every(
318
+ (part) => part != null && typeof part === "object" && part.type === "reasoning_text" && typeof part.text === "string"
319
+ )) {
320
+ return void 0;
321
+ }
322
+ return value.map((part) => ({
323
+ type: "reasoning_text",
324
+ text: part.text
325
+ }));
326
+ }
327
+ function parseOutputTextAnnotations(value) {
328
+ if (!Array.isArray(value) || !value.every(
329
+ (annotation) => annotation != null && typeof annotation === "object" && annotation.type === "url_citation" && typeof annotation.start_index === "number" && typeof annotation.end_index === "number" && typeof annotation.url === "string" && typeof annotation.title === "string"
330
+ )) {
331
+ return void 0;
332
+ }
333
+ return value.map((annotation) => ({
334
+ type: "url_citation",
335
+ start_index: annotation.start_index,
336
+ end_index: annotation.end_index,
337
+ url: annotation.url,
338
+ title: annotation.title
339
+ }));
340
+ }
249
341
 
250
342
  // src/responses/open-responses-api.ts
251
343
  import { lazySchema, zodSchema } from "@ai-sdk/provider-utils";
@@ -290,6 +382,11 @@ import { z as z2 } from "zod/v4";
290
382
  var openResponsesLanguageModelOptions = lazySchema2(
291
383
  () => zodSchema2(
292
384
  z2.object({
385
+ /**
386
+ * Provider-native reasoning effort. The value is passed through to the
387
+ * endpoint and takes precedence over the top-level `reasoning` setting.
388
+ */
389
+ reasoningEffort: z2.string().nullish(),
293
390
  /**
294
391
  * Controls reasoning summary output from the model.
295
392
  * Valid values: 'concise', 'detailed', 'auto'.
@@ -337,7 +434,7 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
337
434
  toolChoice,
338
435
  responseFormat
339
436
  }) {
340
- var _a;
437
+ var _a, _b;
341
438
  const warnings = [];
342
439
  if (stopSequences != null) {
343
440
  warnings.push({ type: "unsupported", feature: "stopSequences" });
@@ -353,16 +450,27 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
353
450
  instructions,
354
451
  warnings: inputWarnings
355
452
  } = await convertToOpenResponsesInput({
356
- prompt
453
+ prompt,
454
+ providerOptionsName: this.config.providerOptionsName
357
455
  });
358
456
  warnings.push(...inputWarnings);
359
- const functionTools = tools == null ? void 0 : tools.filter((tool) => tool.type === "function").map((tool) => ({
360
- type: "function",
361
- name: tool.name,
362
- description: tool.description,
363
- parameters: tool.inputSchema,
364
- ...tool.strict != null ? { strict: tool.strict } : {}
365
- }));
457
+ const functionTools = [];
458
+ for (const tool of tools != null ? tools : []) {
459
+ if (tool.type === "provider") {
460
+ warnings.push({
461
+ type: "unsupported",
462
+ feature: `provider-defined tool ${tool.id}`
463
+ });
464
+ } else {
465
+ functionTools.push({
466
+ type: "function",
467
+ name: tool.name,
468
+ description: tool.description,
469
+ parameters: tool.inputSchema,
470
+ ...tool.strict != null ? { strict: tool.strict } : {}
471
+ });
472
+ }
473
+ }
366
474
  const convertedToolChoice = toolChoice == null ? void 0 : toolChoice.type === "tool" ? { type: "function", name: toolChoice.toolName } : toolChoice.type;
367
475
  const textFormat = (responseFormat == null ? void 0 : responseFormat.type) === "json" ? {
368
476
  type: "json_schema",
@@ -378,7 +486,7 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
378
486
  providerOptions,
379
487
  schema: openResponsesLanguageModelOptions
380
488
  });
381
- const resolvedReasoningEffort = isCustomReasoning(reasoning) ? reasoning === "none" ? "none" : mapReasoningToProviderEffort({
489
+ const resolvedReasoningEffort = (_b = openResponsesOptions == null ? void 0 : openResponsesOptions.reasoningEffort) != null ? _b : isCustomReasoning(reasoning) ? reasoning === "none" ? "none" : mapReasoningToProviderEffort({
382
490
  reasoning,
383
491
  effortMap: {
384
492
  minimal: "low",
@@ -407,7 +515,7 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
407
515
  summary: openResponsesOptions.reasoningSummary
408
516
  }
409
517
  } : void 0,
410
- tools: (functionTools == null ? void 0 : functionTools.length) ? functionTools : void 0,
518
+ tools: functionTools.length ? functionTools : void 0,
411
519
  tool_choice: convertedToolChoice,
412
520
  ...textFormat != null && { text: { format: textFormat } }
413
521
  },
@@ -415,7 +523,7 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
415
523
  };
416
524
  }
417
525
  async doGenerate(options) {
418
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
526
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
419
527
  const { body, warnings } = await this.getArgs(options);
420
528
  const {
421
529
  responseHeaders,
@@ -467,19 +575,42 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
467
575
  switch (part.type) {
468
576
  // TODO AI SDK 7 adjust reasoning in the specification to better support the reasoning structure from open responses.
469
577
  case "reasoning": {
470
- for (const contentPart of (_e = part.content) != null ? _e : []) {
578
+ if (((_f = (_e = part.content) == null ? void 0 : _e.length) != null ? _f : 0) > 0) {
579
+ for (const contentPart of part.content) {
580
+ content.push({
581
+ type: "reasoning",
582
+ text: contentPart.text,
583
+ providerMetadata: createReasoningProviderMetadata({
584
+ part,
585
+ providerOptionsName: this.config.providerOptionsName,
586
+ reasoningContent: [contentPart]
587
+ })
588
+ });
589
+ }
590
+ } else {
471
591
  content.push({
472
592
  type: "reasoning",
473
- text: contentPart.text
593
+ text: part.summary.map((summaryPart) => summaryPart.text).join(""),
594
+ providerMetadata: createReasoningProviderMetadata({
595
+ part,
596
+ providerOptionsName: this.config.providerOptionsName
597
+ })
474
598
  });
475
599
  }
476
600
  break;
477
601
  }
478
602
  case "message": {
479
603
  for (const contentPart of part.content) {
604
+ const annotations = getOutputTextAnnotations(contentPart);
480
605
  content.push({
481
606
  type: "text",
482
- text: contentPart.text
607
+ text: contentPart.text,
608
+ providerMetadata: {
609
+ [this.config.providerOptionsName]: {
610
+ itemId: part.id,
611
+ ...annotations.length > 0 && { annotations }
612
+ }
613
+ }
483
614
  });
484
615
  }
485
616
  break;
@@ -490,7 +621,10 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
490
621
  type: "tool-call",
491
622
  toolCallId: part.call_id,
492
623
  toolName: part.name,
493
- input: part.arguments
624
+ input: part.arguments,
625
+ providerMetadata: {
626
+ [this.config.providerOptionsName]: { itemId: part.id }
627
+ }
494
628
  });
495
629
  break;
496
630
  }
@@ -498,17 +632,17 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
498
632
  }
499
633
  const usage = response.usage;
500
634
  const inputTokens = usage == null ? void 0 : usage.input_tokens;
501
- const cachedInputTokens = (_f = usage == null ? void 0 : usage.input_tokens_details) == null ? void 0 : _f.cached_tokens;
635
+ const cachedInputTokens = (_g = usage == null ? void 0 : usage.input_tokens_details) == null ? void 0 : _g.cached_tokens;
502
636
  const outputTokens = usage == null ? void 0 : usage.output_tokens;
503
- const reasoningTokens = (_g = usage == null ? void 0 : usage.output_tokens_details) == null ? void 0 : _g.reasoning_tokens;
637
+ const reasoningTokens = (_h = usage == null ? void 0 : usage.output_tokens_details) == null ? void 0 : _h.reasoning_tokens;
504
638
  return {
505
639
  content,
506
640
  finishReason: {
507
641
  unified: mapOpenResponsesFinishReason({
508
- finishReason: (_h = response.incomplete_details) == null ? void 0 : _h.reason,
642
+ finishReason: (_i = response.incomplete_details) == null ? void 0 : _i.reason,
509
643
  hasToolCalls
510
644
  }),
511
- raw: (_j = (_i = response.incomplete_details) == null ? void 0 : _i.reason) != null ? _j : void 0
645
+ raw: (_k = (_j = response.incomplete_details) == null ? void 0 : _j.reason) != null ? _k : void 0
512
646
  },
513
647
  usage: {
514
648
  inputTokens: {
@@ -589,13 +723,14 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
589
723
  };
590
724
  usage.raw = responseUsage;
591
725
  };
592
- let isActiveReasoning = false;
726
+ let activeReasoningId;
593
727
  let hasToolCalls = false;
594
728
  let finishReason = {
595
729
  unified: "other",
596
730
  raw: void 0
597
731
  };
598
732
  const toolCallsByItemId = /* @__PURE__ */ new Map();
733
+ const providerOptionsName = this.config.providerOptionsName;
599
734
  return {
600
735
  stream: response.pipeThrough(
601
736
  new TransformStream({
@@ -650,7 +785,12 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
650
785
  type: "tool-call",
651
786
  toolCallId,
652
787
  toolName,
653
- input
788
+ input,
789
+ providerMetadata: {
790
+ [providerOptionsName]: {
791
+ itemId: chunk.item.id
792
+ }
793
+ }
654
794
  });
655
795
  hasToolCalls = true;
656
796
  toolCallsByItemId.delete(chunk.item.id);
@@ -659,7 +799,7 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
659
799
  type: "reasoning-start",
660
800
  id: chunk.item.id
661
801
  });
662
- isActiveReasoning = true;
802
+ activeReasoningId = chunk.item.id;
663
803
  } else if (chunk.type === "response.reasoning_text.delta") {
664
804
  const reasoningChunk = chunk;
665
805
  controller.enqueue({
@@ -668,8 +808,17 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
668
808
  delta: reasoningChunk.delta
669
809
  });
670
810
  } else if (chunk.type === "response.output_item.done" && chunk.item.type === "reasoning") {
671
- controller.enqueue({ type: "reasoning-end", id: chunk.item.id });
672
- isActiveReasoning = false;
811
+ controller.enqueue({
812
+ type: "reasoning-end",
813
+ id: chunk.item.id,
814
+ providerMetadata: createReasoningProviderMetadata({
815
+ part: chunk.item,
816
+ providerOptionsName
817
+ })
818
+ });
819
+ if (activeReasoningId === chunk.item.id) {
820
+ activeReasoningId = void 0;
821
+ }
673
822
  } else if (chunk.type === "response.output_item.added" && chunk.item.type === "message") {
674
823
  controller.enqueue({ type: "text-start", id: chunk.item.id });
675
824
  } else if (chunk.type === "response.output_text.delta") {
@@ -679,7 +828,19 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
679
828
  delta: chunk.delta
680
829
  });
681
830
  } else if (chunk.type === "response.output_item.done" && chunk.item.type === "message") {
682
- controller.enqueue({ type: "text-end", id: chunk.item.id });
831
+ const annotations = chunk.item.content.flatMap(
832
+ getOutputTextAnnotations
833
+ );
834
+ controller.enqueue({
835
+ type: "text-end",
836
+ id: chunk.item.id,
837
+ providerMetadata: {
838
+ [providerOptionsName]: {
839
+ itemId: chunk.item.id,
840
+ ...annotations.length > 0 && { annotations }
841
+ }
842
+ }
843
+ });
683
844
  } else if (chunk.type === "response.completed" || chunk.type === "response.incomplete") {
684
845
  const reason = (_f = chunk.response.incomplete_details) == null ? void 0 : _f.reason;
685
846
  finishReason = {
@@ -699,8 +860,11 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
699
860
  }
700
861
  },
701
862
  flush(controller) {
702
- if (isActiveReasoning) {
703
- controller.enqueue({ type: "reasoning-end", id: "reasoning-0" });
863
+ if (activeReasoningId != null) {
864
+ controller.enqueue({
865
+ type: "reasoning-end",
866
+ id: activeReasoningId
867
+ });
704
868
  }
705
869
  controller.enqueue({
706
870
  type: "finish",
@@ -716,6 +880,42 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
716
880
  };
717
881
  }
718
882
  };
883
+ function createReasoningProviderMetadata({
884
+ part,
885
+ providerOptionsName,
886
+ reasoningContent = part.content
887
+ }) {
888
+ return {
889
+ [providerOptionsName]: {
890
+ itemId: part.id,
891
+ reasoningSummary: part.summary.map((summaryPart) => ({
892
+ type: "summary_text",
893
+ text: summaryPart.text
894
+ })),
895
+ reasoningContent: reasoningContent == null ? null : reasoningContent.map((contentPart) => ({
896
+ type: "reasoning_text",
897
+ text: contentPart.text
898
+ })),
899
+ ...part.encrypted_content != null && {
900
+ reasoningEncryptedContent: part.encrypted_content
901
+ }
902
+ }
903
+ };
904
+ }
905
+ function getOutputTextAnnotations(value) {
906
+ if (value == null || typeof value !== "object" || !("annotations" in value) || !Array.isArray(value.annotations) || !value.annotations.every(
907
+ (annotation) => annotation != null && typeof annotation === "object" && annotation.type === "url_citation" && typeof annotation.start_index === "number" && typeof annotation.end_index === "number" && typeof annotation.url === "string" && typeof annotation.title === "string"
908
+ )) {
909
+ return [];
910
+ }
911
+ return value.annotations.map((annotation) => ({
912
+ type: "url_citation",
913
+ start_index: annotation.start_index,
914
+ end_index: annotation.end_index,
915
+ url: annotation.url,
916
+ title: annotation.title
917
+ }));
918
+ }
719
919
 
720
920
  // src/open-responses-provider.ts
721
921
  function createOpenResponses(options) {