@ai-sdk/open-responses 2.0.26 → 2.0.28

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,22 @@
1
1
  # @ai-sdk/open-responses
2
2
 
3
+ ## 2.0.28
4
+
5
+ ### Patch Changes
6
+
7
+ - 3b9f025: Add a provider-native `reasoningEffort` option for Open Responses endpoints.
8
+ - 1c1a9d1: Preserve Open Responses output item order, text annotations, reasoning state, and reasoning part boundaries when replaying manually managed history.
9
+ - 6fe187f: fix(open-responses): close unfinished reasoning stream parts with their original IDs
10
+ - 1d0bdaa: Warn when provider-defined tools are unsupported instead of silently dropping them.
11
+
12
+ ## 2.0.27
13
+
14
+ ### Patch Changes
15
+
16
+ - 5441aa2: Preserve assistant reasoning parts when converting prompts to Open Responses input.
17
+ - Updated dependencies [7fbfc6d]
18
+ - @ai-sdk/provider-utils@5.0.27
19
+
3
20
  ## 2.0.26
4
21
 
5
22
  ### 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.26" : "0.0.0-test";
2
+ var VERSION = true ? "2.0.28" : "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,18 +107,89 @@ async function convertToOpenResponsesInput({
106
107
  break;
107
108
  }
108
109
  case "assistant": {
109
- const assistantContent = [];
110
- 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
+ };
111
125
  for (const part of content) {
112
126
  switch (part.type) {
127
+ case "reasoning": {
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 = {
140
+ type: "reasoning",
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
+ }
166
+ break;
167
+ }
113
168
  case "text": {
114
- 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
+ });
115
183
  break;
116
184
  }
117
185
  case "tool-call": {
186
+ flushAssistantContent();
118
187
  const argumentsValue = typeof part.input === "string" ? part.input : JSON.stringify(part.input);
119
- 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({
120
191
  type: "function_call",
192
+ ...itemId != null && { id: itemId },
121
193
  call_id: part.toolCallId,
122
194
  name: part.toolName,
123
195
  arguments: argumentsValue
@@ -126,16 +198,7 @@ async function convertToOpenResponsesInput({
126
198
  }
127
199
  }
128
200
  }
129
- if (assistantContent.length > 0) {
130
- input.push({
131
- type: "message",
132
- role: "assistant",
133
- content: assistantContent
134
- });
135
- }
136
- for (const toolCall of toolCalls) {
137
- input.push(toolCall);
138
- }
201
+ flushAssistantContent();
139
202
  break;
140
203
  }
141
204
  case "tool": {
@@ -149,7 +212,7 @@ async function convertToOpenResponsesInput({
149
212
  contentValue = output.value;
150
213
  break;
151
214
  case "execution-denied":
152
- contentValue = (_b = output.reason) != null ? _b : "Tool call execution denied.";
215
+ contentValue = (_c = output.reason) != null ? _c : "Tool call execution denied.";
153
216
  break;
154
217
  case "json":
155
218
  case "error-json":
@@ -180,7 +243,7 @@ async function convertToOpenResponsesInput({
180
243
  } else {
181
244
  contentParts.push({
182
245
  type: "input_file",
183
- filename: (_c = item.filename) != null ? _c : "data",
246
+ filename: (_d = item.filename) != null ? _d : "data",
184
247
  file_data: `data:${fullMediaType};base64,${convertToBase64(item.data.data)}`
185
248
  });
186
249
  }
@@ -234,6 +297,47 @@ async function convertToOpenResponsesInput({
234
297
  warnings
235
298
  };
236
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
+ }
237
341
 
238
342
  // src/responses/open-responses-api.ts
239
343
  import { lazySchema, zodSchema } from "@ai-sdk/provider-utils";
@@ -278,6 +382,11 @@ import { z as z2 } from "zod/v4";
278
382
  var openResponsesLanguageModelOptions = lazySchema2(
279
383
  () => zodSchema2(
280
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(),
281
390
  /**
282
391
  * Controls reasoning summary output from the model.
283
392
  * Valid values: 'concise', 'detailed', 'auto'.
@@ -325,7 +434,7 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
325
434
  toolChoice,
326
435
  responseFormat
327
436
  }) {
328
- var _a;
437
+ var _a, _b;
329
438
  const warnings = [];
330
439
  if (stopSequences != null) {
331
440
  warnings.push({ type: "unsupported", feature: "stopSequences" });
@@ -341,16 +450,27 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
341
450
  instructions,
342
451
  warnings: inputWarnings
343
452
  } = await convertToOpenResponsesInput({
344
- prompt
453
+ prompt,
454
+ providerOptionsName: this.config.providerOptionsName
345
455
  });
346
456
  warnings.push(...inputWarnings);
347
- const functionTools = tools == null ? void 0 : tools.filter((tool) => tool.type === "function").map((tool) => ({
348
- type: "function",
349
- name: tool.name,
350
- description: tool.description,
351
- parameters: tool.inputSchema,
352
- ...tool.strict != null ? { strict: tool.strict } : {}
353
- }));
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
+ }
354
474
  const convertedToolChoice = toolChoice == null ? void 0 : toolChoice.type === "tool" ? { type: "function", name: toolChoice.toolName } : toolChoice.type;
355
475
  const textFormat = (responseFormat == null ? void 0 : responseFormat.type) === "json" ? {
356
476
  type: "json_schema",
@@ -366,7 +486,7 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
366
486
  providerOptions,
367
487
  schema: openResponsesLanguageModelOptions
368
488
  });
369
- 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({
370
490
  reasoning,
371
491
  effortMap: {
372
492
  minimal: "low",
@@ -395,7 +515,7 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
395
515
  summary: openResponsesOptions.reasoningSummary
396
516
  }
397
517
  } : void 0,
398
- tools: (functionTools == null ? void 0 : functionTools.length) ? functionTools : void 0,
518
+ tools: functionTools.length ? functionTools : void 0,
399
519
  tool_choice: convertedToolChoice,
400
520
  ...textFormat != null && { text: { format: textFormat } }
401
521
  },
@@ -403,7 +523,7 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
403
523
  };
404
524
  }
405
525
  async doGenerate(options) {
406
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
526
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
407
527
  const { body, warnings } = await this.getArgs(options);
408
528
  const {
409
529
  responseHeaders,
@@ -455,19 +575,42 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
455
575
  switch (part.type) {
456
576
  // TODO AI SDK 7 adjust reasoning in the specification to better support the reasoning structure from open responses.
457
577
  case "reasoning": {
458
- 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 {
459
591
  content.push({
460
592
  type: "reasoning",
461
- text: contentPart.text
593
+ text: part.summary.map((summaryPart) => summaryPart.text).join(""),
594
+ providerMetadata: createReasoningProviderMetadata({
595
+ part,
596
+ providerOptionsName: this.config.providerOptionsName
597
+ })
462
598
  });
463
599
  }
464
600
  break;
465
601
  }
466
602
  case "message": {
467
603
  for (const contentPart of part.content) {
604
+ const annotations = getOutputTextAnnotations(contentPart);
468
605
  content.push({
469
606
  type: "text",
470
- text: contentPart.text
607
+ text: contentPart.text,
608
+ providerMetadata: {
609
+ [this.config.providerOptionsName]: {
610
+ itemId: part.id,
611
+ ...annotations.length > 0 && { annotations }
612
+ }
613
+ }
471
614
  });
472
615
  }
473
616
  break;
@@ -478,7 +621,10 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
478
621
  type: "tool-call",
479
622
  toolCallId: part.call_id,
480
623
  toolName: part.name,
481
- input: part.arguments
624
+ input: part.arguments,
625
+ providerMetadata: {
626
+ [this.config.providerOptionsName]: { itemId: part.id }
627
+ }
482
628
  });
483
629
  break;
484
630
  }
@@ -486,17 +632,17 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
486
632
  }
487
633
  const usage = response.usage;
488
634
  const inputTokens = usage == null ? void 0 : usage.input_tokens;
489
- 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;
490
636
  const outputTokens = usage == null ? void 0 : usage.output_tokens;
491
- 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;
492
638
  return {
493
639
  content,
494
640
  finishReason: {
495
641
  unified: mapOpenResponsesFinishReason({
496
- finishReason: (_h = response.incomplete_details) == null ? void 0 : _h.reason,
642
+ finishReason: (_i = response.incomplete_details) == null ? void 0 : _i.reason,
497
643
  hasToolCalls
498
644
  }),
499
- 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
500
646
  },
501
647
  usage: {
502
648
  inputTokens: {
@@ -577,13 +723,14 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
577
723
  };
578
724
  usage.raw = responseUsage;
579
725
  };
580
- let isActiveReasoning = false;
726
+ let activeReasoningId;
581
727
  let hasToolCalls = false;
582
728
  let finishReason = {
583
729
  unified: "other",
584
730
  raw: void 0
585
731
  };
586
732
  const toolCallsByItemId = /* @__PURE__ */ new Map();
733
+ const providerOptionsName = this.config.providerOptionsName;
587
734
  return {
588
735
  stream: response.pipeThrough(
589
736
  new TransformStream({
@@ -638,7 +785,12 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
638
785
  type: "tool-call",
639
786
  toolCallId,
640
787
  toolName,
641
- input
788
+ input,
789
+ providerMetadata: {
790
+ [providerOptionsName]: {
791
+ itemId: chunk.item.id
792
+ }
793
+ }
642
794
  });
643
795
  hasToolCalls = true;
644
796
  toolCallsByItemId.delete(chunk.item.id);
@@ -647,7 +799,7 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
647
799
  type: "reasoning-start",
648
800
  id: chunk.item.id
649
801
  });
650
- isActiveReasoning = true;
802
+ activeReasoningId = chunk.item.id;
651
803
  } else if (chunk.type === "response.reasoning_text.delta") {
652
804
  const reasoningChunk = chunk;
653
805
  controller.enqueue({
@@ -656,8 +808,17 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
656
808
  delta: reasoningChunk.delta
657
809
  });
658
810
  } else if (chunk.type === "response.output_item.done" && chunk.item.type === "reasoning") {
659
- controller.enqueue({ type: "reasoning-end", id: chunk.item.id });
660
- 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
+ }
661
822
  } else if (chunk.type === "response.output_item.added" && chunk.item.type === "message") {
662
823
  controller.enqueue({ type: "text-start", id: chunk.item.id });
663
824
  } else if (chunk.type === "response.output_text.delta") {
@@ -667,7 +828,19 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
667
828
  delta: chunk.delta
668
829
  });
669
830
  } else if (chunk.type === "response.output_item.done" && chunk.item.type === "message") {
670
- 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
+ });
671
844
  } else if (chunk.type === "response.completed" || chunk.type === "response.incomplete") {
672
845
  const reason = (_f = chunk.response.incomplete_details) == null ? void 0 : _f.reason;
673
846
  finishReason = {
@@ -687,8 +860,11 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
687
860
  }
688
861
  },
689
862
  flush(controller) {
690
- if (isActiveReasoning) {
691
- controller.enqueue({ type: "reasoning-end", id: "reasoning-0" });
863
+ if (activeReasoningId != null) {
864
+ controller.enqueue({
865
+ type: "reasoning-end",
866
+ id: activeReasoningId
867
+ });
692
868
  }
693
869
  controller.enqueue({
694
870
  type: "finish",
@@ -704,6 +880,42 @@ var OpenResponsesLanguageModel = class _OpenResponsesLanguageModel {
704
880
  };
705
881
  }
706
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
+ }
707
919
 
708
920
  // src/open-responses-provider.ts
709
921
  function createOpenResponses(options) {