@ai-sdk/open-responses 2.0.50 → 2.0.52
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 +18 -0
- package/dist/index.d.ts +105 -4
- package/dist/index.js +221 -78
- package/dist/index.js.map +1 -1
- package/docs/06-open-responses.mdx +66 -2
- package/package.json +2 -2
- package/src/index.ts +1 -0
- package/src/open-responses-provider.ts +50 -3
- package/src/open-responses-tools.ts +52 -0
- package/src/responses/convert-to-open-responses-input.ts +24 -8
- package/src/responses/open-responses-api.ts +83 -4
- package/src/responses/open-responses-config.ts +10 -1
- package/src/responses/open-responses-language-model.ts +175 -18
|
@@ -48,12 +48,18 @@ import {
|
|
|
48
48
|
type OpenResponsesResponseBody,
|
|
49
49
|
type OpenResponsesChunk,
|
|
50
50
|
type ReasoningBody,
|
|
51
|
+
type ResponseError,
|
|
51
52
|
type ToolChoiceParam,
|
|
52
53
|
} from './open-responses-api';
|
|
53
54
|
import { mapOpenResponsesFinishReason } from './map-open-responses-finish-reason';
|
|
54
55
|
import type { OpenResponsesConfig } from './open-responses-config';
|
|
55
56
|
import { openResponsesLanguageModelOptions } from './open-responses-language-model-options';
|
|
56
57
|
|
|
58
|
+
const defaultFailedResponseHandler = createJsonErrorResponseHandler({
|
|
59
|
+
errorSchema: openResponsesErrorSchema,
|
|
60
|
+
errorToMessage: error => error.error.message,
|
|
61
|
+
});
|
|
62
|
+
|
|
57
63
|
export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
58
64
|
readonly specificationVersion = 'v4';
|
|
59
65
|
|
|
@@ -147,6 +153,7 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
147
153
|
extensionRegistry: this.extensionRegistry,
|
|
148
154
|
providerToolsByName,
|
|
149
155
|
strictResponseInput: this.config.strictResponseInput,
|
|
156
|
+
customToolId: this.config.customToolId,
|
|
150
157
|
});
|
|
151
158
|
|
|
152
159
|
warnings.push(...inputWarnings);
|
|
@@ -166,6 +173,43 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
166
173
|
|
|
167
174
|
for (const tool of tools ?? []) {
|
|
168
175
|
if (tool.type === 'provider') {
|
|
176
|
+
if (
|
|
177
|
+
this.config.customToolId != null &&
|
|
178
|
+
tool.id === this.config.customToolId
|
|
179
|
+
) {
|
|
180
|
+
const format =
|
|
181
|
+
tool.args.format != null &&
|
|
182
|
+
typeof tool.args.format === 'object' &&
|
|
183
|
+
!Array.isArray(tool.args.format)
|
|
184
|
+
? tool.args.format
|
|
185
|
+
: undefined;
|
|
186
|
+
|
|
187
|
+
convertedTools.push({
|
|
188
|
+
type: 'custom',
|
|
189
|
+
name: tool.name,
|
|
190
|
+
description:
|
|
191
|
+
typeof tool.args.description === 'string'
|
|
192
|
+
? tool.args.description
|
|
193
|
+
: undefined,
|
|
194
|
+
format:
|
|
195
|
+
format != null &&
|
|
196
|
+
'type' in format &&
|
|
197
|
+
format.type === 'grammar' &&
|
|
198
|
+
'syntax' in format &&
|
|
199
|
+
(format.syntax === 'regex' || format.syntax === 'lark') &&
|
|
200
|
+
'definition' in format &&
|
|
201
|
+
typeof format.definition === 'string'
|
|
202
|
+
? {
|
|
203
|
+
type: 'grammar',
|
|
204
|
+
syntax: format.syntax,
|
|
205
|
+
definition: format.definition,
|
|
206
|
+
}
|
|
207
|
+
: format != null && 'type' in format && format.type === 'text'
|
|
208
|
+
? { type: 'text' }
|
|
209
|
+
: undefined,
|
|
210
|
+
});
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
169
213
|
const extension = this.extensionRegistry.byProviderToolId.get(tool.id);
|
|
170
214
|
let encoded: OpenResponsesExtensionRecord | undefined;
|
|
171
215
|
|
|
@@ -219,7 +263,16 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
219
263
|
);
|
|
220
264
|
|
|
221
265
|
if (registeredTool == null) {
|
|
222
|
-
if (
|
|
266
|
+
if (
|
|
267
|
+
this.config.customToolId != null &&
|
|
268
|
+
providerToolsByName.get(toolChoice.toolName)?.id ===
|
|
269
|
+
this.config.customToolId
|
|
270
|
+
) {
|
|
271
|
+
convertedToolChoice = {
|
|
272
|
+
type: 'custom',
|
|
273
|
+
name: toolChoice.toolName,
|
|
274
|
+
};
|
|
275
|
+
} else if (!providerToolsByName.has(toolChoice.toolName)) {
|
|
223
276
|
convertedToolChoice = {
|
|
224
277
|
type: 'function',
|
|
225
278
|
name: toolChoice.toolName,
|
|
@@ -255,7 +308,7 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
255
308
|
}
|
|
256
309
|
|
|
257
310
|
const textFormat =
|
|
258
|
-
responseFormat?.type === 'json'
|
|
311
|
+
responseFormat?.type === 'json' && this.config.structuredOutputs !== false
|
|
259
312
|
? responseFormat.schema != null
|
|
260
313
|
? {
|
|
261
314
|
type: 'json_schema' as const,
|
|
@@ -267,6 +320,13 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
267
320
|
: { type: 'json_object' as const }
|
|
268
321
|
: undefined;
|
|
269
322
|
|
|
323
|
+
if (
|
|
324
|
+
responseFormat?.type === 'json' &&
|
|
325
|
+
this.config.structuredOutputs === false
|
|
326
|
+
) {
|
|
327
|
+
warnings.push({ type: 'unsupported', feature: 'responseFormat' });
|
|
328
|
+
}
|
|
329
|
+
|
|
270
330
|
const openResponsesOptions = await parseProviderOptions({
|
|
271
331
|
provider: this.config.providerOptionsName,
|
|
272
332
|
providerOptions,
|
|
@@ -334,10 +394,8 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
334
394
|
url: this.config.url,
|
|
335
395
|
headers: combineHeaders(this.config.headers?.(), options.headers),
|
|
336
396
|
body,
|
|
337
|
-
failedResponseHandler:
|
|
338
|
-
|
|
339
|
-
errorToMessage: error => error.error.message,
|
|
340
|
-
}),
|
|
397
|
+
failedResponseHandler:
|
|
398
|
+
this.config.failedResponseHandler ?? defaultFailedResponseHandler,
|
|
341
399
|
successfulResponseHandler: createJsonResponseHandler(
|
|
342
400
|
// do not validate the response body, only apply types to the response body
|
|
343
401
|
jsonSchema<OpenResponsesResponseBody>(() => {
|
|
@@ -349,14 +407,18 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
349
407
|
});
|
|
350
408
|
|
|
351
409
|
if (response.error) {
|
|
410
|
+
const errorMetadata = this.config.getResponseErrorMetadata?.(
|
|
411
|
+
response.error,
|
|
412
|
+
);
|
|
352
413
|
throw new APICallError({
|
|
353
414
|
message: response.error.message,
|
|
354
415
|
url: this.config.url,
|
|
355
416
|
requestBodyValues: body,
|
|
356
|
-
statusCode: 400,
|
|
417
|
+
statusCode: errorMetadata?.statusCode ?? 400,
|
|
418
|
+
isRetryable: errorMetadata?.isRetryable,
|
|
357
419
|
responseHeaders,
|
|
358
420
|
responseBody: rawResponse as string,
|
|
359
|
-
|
|
421
|
+
data: response.error,
|
|
360
422
|
});
|
|
361
423
|
}
|
|
362
424
|
|
|
@@ -440,6 +502,20 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
440
502
|
break;
|
|
441
503
|
}
|
|
442
504
|
|
|
505
|
+
case 'custom_tool_call': {
|
|
506
|
+
hasToolCalls = true;
|
|
507
|
+
content.push({
|
|
508
|
+
type: 'tool-call',
|
|
509
|
+
toolCallId: part.call_id,
|
|
510
|
+
toolName: part.name,
|
|
511
|
+
input: JSON.stringify(part.input),
|
|
512
|
+
providerMetadata: {
|
|
513
|
+
[this.config.providerOptionsName]: { itemId: part.id },
|
|
514
|
+
},
|
|
515
|
+
});
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
|
|
443
519
|
default: {
|
|
444
520
|
if (!isOpenResponsesExtensionItem(part)) {
|
|
445
521
|
break;
|
|
@@ -464,6 +540,7 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
464
540
|
const usage = response.usage;
|
|
465
541
|
const inputTokens = usage?.input_tokens;
|
|
466
542
|
const cachedInputTokens = usage?.input_tokens_details?.cached_tokens;
|
|
543
|
+
const cacheWriteTokens = usage?.input_tokens_details?.cache_write_tokens;
|
|
467
544
|
const outputTokens = usage?.output_tokens;
|
|
468
545
|
const reasoningTokens = usage?.output_tokens_details?.reasoning_tokens;
|
|
469
546
|
|
|
@@ -479,9 +556,12 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
479
556
|
usage: {
|
|
480
557
|
inputTokens: {
|
|
481
558
|
total: inputTokens,
|
|
482
|
-
noCache:
|
|
559
|
+
noCache:
|
|
560
|
+
(inputTokens ?? 0) -
|
|
561
|
+
(cachedInputTokens ?? 0) -
|
|
562
|
+
(cacheWriteTokens ?? 0),
|
|
483
563
|
cacheRead: cachedInputTokens,
|
|
484
|
-
cacheWrite:
|
|
564
|
+
cacheWrite: cacheWriteTokens,
|
|
485
565
|
},
|
|
486
566
|
outputTokens: {
|
|
487
567
|
total: outputTokens,
|
|
@@ -515,10 +595,8 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
515
595
|
...body,
|
|
516
596
|
stream: true,
|
|
517
597
|
} satisfies OpenResponsesRequestBody,
|
|
518
|
-
failedResponseHandler:
|
|
519
|
-
|
|
520
|
-
errorToMessage: error => error.error.message,
|
|
521
|
-
}),
|
|
598
|
+
failedResponseHandler:
|
|
599
|
+
this.config.failedResponseHandler ?? defaultFailedResponseHandler,
|
|
522
600
|
successfulResponseHandler: createEventSourceResponseHandler(z.any()),
|
|
523
601
|
abortSignal: options.abortSignal,
|
|
524
602
|
fetch: this.config.fetch,
|
|
@@ -548,15 +626,20 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
548
626
|
const inputTokens = responseUsage.input_tokens;
|
|
549
627
|
const cachedInputTokens =
|
|
550
628
|
responseUsage.input_tokens_details?.cached_tokens;
|
|
629
|
+
const cacheWriteTokens =
|
|
630
|
+
responseUsage.input_tokens_details?.cache_write_tokens;
|
|
551
631
|
const outputTokens = responseUsage.output_tokens;
|
|
552
632
|
const reasoningTokens =
|
|
553
633
|
responseUsage.output_tokens_details?.reasoning_tokens;
|
|
554
634
|
|
|
555
635
|
usage.inputTokens = {
|
|
556
636
|
total: inputTokens,
|
|
557
|
-
noCache:
|
|
637
|
+
noCache:
|
|
638
|
+
(inputTokens ?? 0) -
|
|
639
|
+
(cachedInputTokens ?? 0) -
|
|
640
|
+
(cacheWriteTokens ?? 0),
|
|
558
641
|
cacheRead: cachedInputTokens,
|
|
559
|
-
cacheWrite:
|
|
642
|
+
cacheWrite: cacheWriteTokens,
|
|
560
643
|
};
|
|
561
644
|
usage.outputTokens = {
|
|
562
645
|
total: outputTokens,
|
|
@@ -576,9 +659,14 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
576
659
|
string,
|
|
577
660
|
{ toolName?: string; toolCallId?: string; arguments?: string }
|
|
578
661
|
>();
|
|
662
|
+
const customToolCallsByItemId = new Map<
|
|
663
|
+
string,
|
|
664
|
+
{ toolName?: string; toolCallId?: string; input?: string }
|
|
665
|
+
>();
|
|
579
666
|
const providerOptionsName = this.config.providerOptionsName;
|
|
580
667
|
const extensionRegistry = this.extensionRegistry;
|
|
581
668
|
const extensionStreamState = new Map<string, unknown>();
|
|
669
|
+
const getResponseErrorMetadata = this.config.getResponseErrorMetadata;
|
|
582
670
|
|
|
583
671
|
return {
|
|
584
672
|
stream: response.pipeThrough(
|
|
@@ -670,7 +758,10 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
670
758
|
const toolCall = toolCallsByItemId.get(chunk.item.id);
|
|
671
759
|
const toolName = toolCall?.toolName ?? chunk.item.name;
|
|
672
760
|
const toolCallId = toolCall?.toolCallId ?? chunk.item.call_id;
|
|
673
|
-
const input =
|
|
761
|
+
const input =
|
|
762
|
+
chunk.item.arguments !== ''
|
|
763
|
+
? chunk.item.arguments
|
|
764
|
+
: (toolCall?.arguments ?? '');
|
|
674
765
|
|
|
675
766
|
controller.enqueue({
|
|
676
767
|
type: 'tool-call',
|
|
@@ -686,6 +777,67 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
686
777
|
hasToolCalls = true;
|
|
687
778
|
|
|
688
779
|
toolCallsByItemId.delete(chunk.item.id);
|
|
780
|
+
} else if (
|
|
781
|
+
chunk.type === 'response.output_item.added' &&
|
|
782
|
+
chunk.item.type === 'custom_tool_call'
|
|
783
|
+
) {
|
|
784
|
+
customToolCallsByItemId.set(chunk.item.id, {
|
|
785
|
+
toolName: chunk.item.name,
|
|
786
|
+
toolCallId: chunk.item.call_id,
|
|
787
|
+
input: chunk.item.input,
|
|
788
|
+
});
|
|
789
|
+
controller.enqueue({
|
|
790
|
+
type: 'tool-input-start',
|
|
791
|
+
id: chunk.item.call_id,
|
|
792
|
+
toolName: chunk.item.name,
|
|
793
|
+
});
|
|
794
|
+
} else if (chunk.type === 'response.custom_tool_call_input.delta') {
|
|
795
|
+
const toolCall = customToolCallsByItemId.get(chunk.item_id);
|
|
796
|
+
if (toolCall == null) {
|
|
797
|
+
customToolCallsByItemId.set(chunk.item_id, {
|
|
798
|
+
input: chunk.delta,
|
|
799
|
+
});
|
|
800
|
+
} else {
|
|
801
|
+
toolCall.input = (toolCall.input ?? '') + chunk.delta;
|
|
802
|
+
}
|
|
803
|
+
controller.enqueue({
|
|
804
|
+
type: 'tool-input-delta',
|
|
805
|
+
id:
|
|
806
|
+
customToolCallsByItemId.get(chunk.item_id)?.toolCallId ??
|
|
807
|
+
chunk.item_id,
|
|
808
|
+
delta: chunk.delta,
|
|
809
|
+
});
|
|
810
|
+
} else if (chunk.type === 'response.custom_tool_call_input.done') {
|
|
811
|
+
const toolCall = customToolCallsByItemId.get(chunk.item_id);
|
|
812
|
+
if (toolCall == null) {
|
|
813
|
+
customToolCallsByItemId.set(chunk.item_id, {
|
|
814
|
+
input: chunk.input,
|
|
815
|
+
});
|
|
816
|
+
} else {
|
|
817
|
+
toolCall.input = chunk.input;
|
|
818
|
+
}
|
|
819
|
+
} else if (
|
|
820
|
+
chunk.type === 'response.output_item.done' &&
|
|
821
|
+
chunk.item.type === 'custom_tool_call'
|
|
822
|
+
) {
|
|
823
|
+
const toolCall = customToolCallsByItemId.get(chunk.item.id);
|
|
824
|
+
const toolCallId = toolCall?.toolCallId ?? chunk.item.call_id;
|
|
825
|
+
const input =
|
|
826
|
+
chunk.item.input !== ''
|
|
827
|
+
? chunk.item.input
|
|
828
|
+
: (toolCall?.input ?? '');
|
|
829
|
+
controller.enqueue({ type: 'tool-input-end', id: toolCallId });
|
|
830
|
+
controller.enqueue({
|
|
831
|
+
type: 'tool-call',
|
|
832
|
+
toolCallId,
|
|
833
|
+
toolName: toolCall?.toolName ?? chunk.item.name,
|
|
834
|
+
input: JSON.stringify(input),
|
|
835
|
+
providerMetadata: {
|
|
836
|
+
[providerOptionsName]: { itemId: chunk.item.id },
|
|
837
|
+
},
|
|
838
|
+
});
|
|
839
|
+
hasToolCalls = true;
|
|
840
|
+
customToolCallsByItemId.delete(chunk.item.id);
|
|
689
841
|
} else if (
|
|
690
842
|
chunk.type === 'response.output_item.done' &&
|
|
691
843
|
isOpenResponsesExtensionItem(chunk.item)
|
|
@@ -804,6 +956,7 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
804
956
|
type: chunk.type,
|
|
805
957
|
error: chunk.response.error,
|
|
806
958
|
data: chunk,
|
|
959
|
+
getResponseErrorMetadata,
|
|
807
960
|
}),
|
|
808
961
|
});
|
|
809
962
|
}
|
|
@@ -818,6 +971,7 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
818
971
|
type: chunk.type,
|
|
819
972
|
error: chunk.error,
|
|
820
973
|
data: chunk,
|
|
974
|
+
getResponseErrorMetadata,
|
|
821
975
|
}),
|
|
822
976
|
});
|
|
823
977
|
}
|
|
@@ -850,15 +1004,18 @@ function createOpenResponsesStreamError({
|
|
|
850
1004
|
type,
|
|
851
1005
|
error,
|
|
852
1006
|
data,
|
|
1007
|
+
getResponseErrorMetadata,
|
|
853
1008
|
}: {
|
|
854
1009
|
type: 'error' | 'response.failed';
|
|
855
|
-
error:
|
|
1010
|
+
error: ResponseError;
|
|
856
1011
|
data: unknown;
|
|
1012
|
+
getResponseErrorMetadata: OpenResponsesConfig['getResponseErrorMetadata'];
|
|
857
1013
|
}) {
|
|
858
1014
|
return createProviderStreamError({
|
|
859
1015
|
message: error.message,
|
|
860
1016
|
type,
|
|
861
1017
|
code: error.code,
|
|
1018
|
+
...getResponseErrorMetadata?.(error),
|
|
862
1019
|
data,
|
|
863
1020
|
});
|
|
864
1021
|
}
|