@ai-sdk/open-responses 2.0.31 → 2.0.35
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 +31 -0
- package/dist/index.d.ts +96 -3
- package/dist/index.js +560 -14
- package/dist/index.js.map +1 -1
- package/docs/06-open-responses.mdx +116 -2
- package/package.json +2 -2
- package/src/index.ts +10 -0
- package/src/open-responses-extension.ts +387 -0
- package/src/open-responses-provider.ts +15 -0
- package/src/responses/convert-to-open-responses-input.ts +193 -0
- package/src/responses/open-responses-api.ts +14 -4
- package/src/responses/open-responses-config.ts +2 -0
- package/src/responses/open-responses-language-model.ts +313 -17
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
UnsupportedFunctionalityError,
|
|
3
|
+
type LanguageModelV4ProviderTool,
|
|
3
4
|
type LanguageModelV4Prompt,
|
|
4
5
|
type SharedV4Warning,
|
|
5
6
|
} from '@ai-sdk/provider';
|
|
@@ -8,6 +9,12 @@ import {
|
|
|
8
9
|
getTopLevelMediaType,
|
|
9
10
|
resolveFullMediaType,
|
|
10
11
|
} from '@ai-sdk/provider-utils';
|
|
12
|
+
import {
|
|
13
|
+
isOpenResponsesExtensionItem,
|
|
14
|
+
type OpenResponsesExtensionInputPart,
|
|
15
|
+
type OpenResponsesExtensionItem,
|
|
16
|
+
type OpenResponsesExtensionRegistry,
|
|
17
|
+
} from '../open-responses-extension';
|
|
11
18
|
import type {
|
|
12
19
|
FunctionCallOutputItemParam,
|
|
13
20
|
InputFileContentParam,
|
|
@@ -22,9 +29,13 @@ import type {
|
|
|
22
29
|
export async function convertToOpenResponsesInput({
|
|
23
30
|
prompt,
|
|
24
31
|
providerOptionsName = 'open-responses',
|
|
32
|
+
extensionRegistry,
|
|
33
|
+
providerToolsByName = new Map(),
|
|
25
34
|
}: {
|
|
26
35
|
prompt: LanguageModelV4Prompt;
|
|
27
36
|
providerOptionsName?: string;
|
|
37
|
+
extensionRegistry?: OpenResponsesExtensionRegistry;
|
|
38
|
+
providerToolsByName?: Map<string, LanguageModelV4ProviderTool>;
|
|
28
39
|
}): Promise<{
|
|
29
40
|
input: OpenResponsesRequestBody['input'];
|
|
30
41
|
instructions: string | undefined;
|
|
@@ -33,6 +44,7 @@ export async function convertToOpenResponsesInput({
|
|
|
33
44
|
const input: OpenResponsesRequestBody['input'] = [];
|
|
34
45
|
const warnings: Array<SharedV4Warning> = [];
|
|
35
46
|
const systemMessages: string[] = [];
|
|
47
|
+
const replayedExtensionItems = new Set<string>();
|
|
36
48
|
|
|
37
49
|
for (const { role, content } of prompt) {
|
|
38
50
|
switch (role) {
|
|
@@ -125,6 +137,52 @@ export async function convertToOpenResponsesInput({
|
|
|
125
137
|
};
|
|
126
138
|
|
|
127
139
|
for (const part of content) {
|
|
140
|
+
const extensionReplay = getExtensionReplay({
|
|
141
|
+
part,
|
|
142
|
+
providerOptionsName,
|
|
143
|
+
extensionRegistry,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (extensionReplay != null) {
|
|
147
|
+
flushAssistantContent();
|
|
148
|
+
const replayItem = extensionReplay.item;
|
|
149
|
+
if (replayItem != null) {
|
|
150
|
+
const replayKey = `${replayItem.type}:${replayItem.id}`;
|
|
151
|
+
if (!replayedExtensionItems.has(replayKey)) {
|
|
152
|
+
input.push(replayItem);
|
|
153
|
+
replayedExtensionItems.add(replayKey);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (part.type === 'tool-call' || part.type === 'tool-result') {
|
|
160
|
+
const providerTool = providerToolsByName.get(part.toolName);
|
|
161
|
+
const extension =
|
|
162
|
+
providerTool == null
|
|
163
|
+
? undefined
|
|
164
|
+
: extensionRegistry?.byProviderToolId.get(providerTool.id);
|
|
165
|
+
|
|
166
|
+
if (providerTool != null && extension != null) {
|
|
167
|
+
flushAssistantContent();
|
|
168
|
+
const encoded = await encodeExtensionInputPart({
|
|
169
|
+
extensionRegistry,
|
|
170
|
+
part,
|
|
171
|
+
providerTool,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
if (encoded == null) {
|
|
175
|
+
warnings.push({
|
|
176
|
+
type: 'unsupported',
|
|
177
|
+
feature: `provider-defined tool ${providerTool.id} ${part.type} history`,
|
|
178
|
+
});
|
|
179
|
+
} else {
|
|
180
|
+
input.push(...encoded);
|
|
181
|
+
}
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
128
186
|
switch (part.type) {
|
|
129
187
|
case 'reasoning': {
|
|
130
188
|
flushAssistantContent();
|
|
@@ -243,6 +301,48 @@ export async function convertToOpenResponsesInput({
|
|
|
243
301
|
case 'tool': {
|
|
244
302
|
for (const part of content) {
|
|
245
303
|
if (part.type === 'tool-result') {
|
|
304
|
+
const extensionReplay = getExtensionReplay({
|
|
305
|
+
part,
|
|
306
|
+
providerOptionsName,
|
|
307
|
+
extensionRegistry,
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
if (extensionReplay != null) {
|
|
311
|
+
const replayItem = extensionReplay.item;
|
|
312
|
+
if (replayItem != null) {
|
|
313
|
+
const replayKey = `${replayItem.type}:${replayItem.id}`;
|
|
314
|
+
if (!replayedExtensionItems.has(replayKey)) {
|
|
315
|
+
input.push(replayItem);
|
|
316
|
+
replayedExtensionItems.add(replayKey);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const providerTool = providerToolsByName.get(part.toolName);
|
|
323
|
+
const extension =
|
|
324
|
+
providerTool == null
|
|
325
|
+
? undefined
|
|
326
|
+
: extensionRegistry?.byProviderToolId.get(providerTool.id);
|
|
327
|
+
|
|
328
|
+
if (providerTool != null && extension != null) {
|
|
329
|
+
const encoded = await encodeExtensionInputPart({
|
|
330
|
+
extensionRegistry,
|
|
331
|
+
part,
|
|
332
|
+
providerTool,
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
if (encoded == null) {
|
|
336
|
+
warnings.push({
|
|
337
|
+
type: 'unsupported',
|
|
338
|
+
feature: `provider-defined tool ${providerTool.id} tool-result history`,
|
|
339
|
+
});
|
|
340
|
+
} else {
|
|
341
|
+
input.push(...encoded);
|
|
342
|
+
}
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
|
|
246
346
|
const output = part.output;
|
|
247
347
|
let contentValue: FunctionCallOutputItemParam['output'];
|
|
248
348
|
|
|
@@ -346,6 +446,99 @@ export async function convertToOpenResponsesInput({
|
|
|
346
446
|
};
|
|
347
447
|
}
|
|
348
448
|
|
|
449
|
+
async function encodeExtensionInputPart({
|
|
450
|
+
extensionRegistry,
|
|
451
|
+
part,
|
|
452
|
+
providerTool,
|
|
453
|
+
}: {
|
|
454
|
+
extensionRegistry: OpenResponsesExtensionRegistry | undefined;
|
|
455
|
+
part: OpenResponsesExtensionInputPart;
|
|
456
|
+
providerTool: LanguageModelV4ProviderTool;
|
|
457
|
+
}): Promise<OpenResponsesExtensionItem[] | undefined> {
|
|
458
|
+
const extension = extensionRegistry?.byProviderToolId.get(providerTool.id);
|
|
459
|
+
const encodeInputItem = extension?.encodeInputItem;
|
|
460
|
+
const itemTypes = extension?.itemTypes;
|
|
461
|
+
if (encodeInputItem == null || itemTypes == null) {
|
|
462
|
+
return undefined;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
try {
|
|
466
|
+
const value = await encodeInputItem({
|
|
467
|
+
part,
|
|
468
|
+
tool: providerTool,
|
|
469
|
+
});
|
|
470
|
+
const items =
|
|
471
|
+
value == null ? undefined : Array.isArray(value) ? value : [value];
|
|
472
|
+
|
|
473
|
+
if (
|
|
474
|
+
items == null ||
|
|
475
|
+
items.length === 0 ||
|
|
476
|
+
!items.every(
|
|
477
|
+
item =>
|
|
478
|
+
isOpenResponsesExtensionItem(item) && itemTypes.includes(item.type),
|
|
479
|
+
)
|
|
480
|
+
) {
|
|
481
|
+
return undefined;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return items;
|
|
485
|
+
} catch {
|
|
486
|
+
return undefined;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function getExtensionReplay({
|
|
491
|
+
part,
|
|
492
|
+
providerOptionsName,
|
|
493
|
+
extensionRegistry,
|
|
494
|
+
}: {
|
|
495
|
+
part: {
|
|
496
|
+
providerOptions?: Record<string, unknown>;
|
|
497
|
+
};
|
|
498
|
+
providerOptionsName: string;
|
|
499
|
+
extensionRegistry: OpenResponsesExtensionRegistry | undefined;
|
|
500
|
+
}): { item?: OpenResponsesExtensionItem } | undefined {
|
|
501
|
+
const extensionData = getProviderData(
|
|
502
|
+
part,
|
|
503
|
+
providerOptionsName,
|
|
504
|
+
)?.openResponsesExtension;
|
|
505
|
+
|
|
506
|
+
if (
|
|
507
|
+
extensionData == null ||
|
|
508
|
+
typeof extensionData !== 'object' ||
|
|
509
|
+
Array.isArray(extensionData)
|
|
510
|
+
) {
|
|
511
|
+
return undefined;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const { id, item, itemId } = extensionData as {
|
|
515
|
+
id?: unknown;
|
|
516
|
+
item?: unknown;
|
|
517
|
+
itemId?: unknown;
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
if (typeof id !== 'string') {
|
|
521
|
+
return undefined;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const extension = extensionRegistry?.byExtensionId.get(
|
|
525
|
+
id as LanguageModelV4ProviderTool['id'],
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
if (extension == null) {
|
|
529
|
+
return undefined;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (
|
|
533
|
+
isOpenResponsesExtensionItem(item) &&
|
|
534
|
+
extension.itemTypes?.includes(item.type)
|
|
535
|
+
) {
|
|
536
|
+
return { item };
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return typeof itemId === 'string' ? {} : undefined;
|
|
540
|
+
}
|
|
541
|
+
|
|
349
542
|
function getProviderData(
|
|
350
543
|
part: {
|
|
351
544
|
providerOptions?: Record<string, unknown>;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { JSONSchema7 } from '@ai-sdk/provider';
|
|
2
2
|
import { lazySchema, zodSchema } from '@ai-sdk/provider-utils';
|
|
3
3
|
import { z } from 'zod/v4';
|
|
4
|
+
import type {
|
|
5
|
+
OpenResponsesExtensionEvent,
|
|
6
|
+
OpenResponsesExtensionItem,
|
|
7
|
+
OpenResponsesExtensionRecord,
|
|
8
|
+
} from '../open-responses-extension';
|
|
4
9
|
|
|
5
10
|
export const openResponsesErrorSchema = lazySchema(() =>
|
|
6
11
|
zodSchema(
|
|
@@ -263,7 +268,8 @@ export type AllowedToolsParam = {
|
|
|
263
268
|
export type ToolChoiceParam =
|
|
264
269
|
| ToolChoiceValueEnum
|
|
265
270
|
| SpecificFunctionParam
|
|
266
|
-
| AllowedToolsParam
|
|
271
|
+
| AllowedToolsParam
|
|
272
|
+
| OpenResponsesExtensionRecord;
|
|
267
273
|
|
|
268
274
|
// ============================================================================
|
|
269
275
|
// Configuration Types
|
|
@@ -483,7 +489,8 @@ export type OutputItem =
|
|
|
483
489
|
| FunctionCall
|
|
484
490
|
| FunctionCallOutput
|
|
485
491
|
| Message
|
|
486
|
-
| ReasoningBody
|
|
492
|
+
| ReasoningBody
|
|
493
|
+
| OpenResponsesExtensionItem;
|
|
487
494
|
|
|
488
495
|
/**
|
|
489
496
|
* Details about why the response was incomplete.
|
|
@@ -626,6 +633,7 @@ export type OpenResponsesRequestBody = {
|
|
|
626
633
|
| AssistantMessageItemParam
|
|
627
634
|
| FunctionCallItemParam
|
|
628
635
|
| FunctionCallOutputItemParam
|
|
636
|
+
| OpenResponsesExtensionRecord
|
|
629
637
|
>;
|
|
630
638
|
|
|
631
639
|
/**
|
|
@@ -643,7 +651,7 @@ export type OpenResponsesRequestBody = {
|
|
|
643
651
|
/**
|
|
644
652
|
* A list of tools that the model may call while generating the response.
|
|
645
653
|
*/
|
|
646
|
-
tools?: FunctionToolParam
|
|
654
|
+
tools?: Array<FunctionToolParam | OpenResponsesExtensionRecord>;
|
|
647
655
|
|
|
648
656
|
/**
|
|
649
657
|
* Controls which tool the model should use, if any.
|
|
@@ -1235,4 +1243,6 @@ export type OpenResponsesChunk =
|
|
|
1235
1243
|
| ResponseReasoningSummaryPartAddedEvent
|
|
1236
1244
|
| ResponseReasoningSummaryPartDoneEvent
|
|
1237
1245
|
// Error Event
|
|
1238
|
-
| ResponseErrorEvent
|
|
1246
|
+
| ResponseErrorEvent
|
|
1247
|
+
// Registered extension event
|
|
1248
|
+
| OpenResponsesExtensionEvent;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FetchFunction } from '@ai-sdk/provider-utils';
|
|
2
|
+
import type { OpenResponsesExtensionRegistry } from '../open-responses-extension';
|
|
2
3
|
|
|
3
4
|
export type OpenResponsesConfig = {
|
|
4
5
|
provider: string;
|
|
@@ -7,4 +8,5 @@ export type OpenResponsesConfig = {
|
|
|
7
8
|
headers?: () => Record<string, string | undefined>;
|
|
8
9
|
fetch?: FetchFunction;
|
|
9
10
|
generateId: () => string;
|
|
11
|
+
extensionRegistry?: OpenResponsesExtensionRegistry;
|
|
10
12
|
};
|