@ai-sdk/open-responses 2.0.54 → 2.0.55
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 +10 -0
- package/dist/index.d.ts +97 -40
- package/dist/index.js +154 -40
- package/dist/index.js.map +1 -1
- package/docs/06-open-responses.mdx +55 -4
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/open-responses-extension.ts +299 -67
- package/src/open-responses-provider.ts +2 -2
- package/src/responses/convert-to-open-responses-input.ts +12 -9
- package/src/responses/open-responses-language-model.ts +20 -16
|
@@ -386,6 +386,57 @@ const result = await generateText({
|
|
|
386
386
|
});
|
|
387
387
|
```
|
|
388
388
|
|
|
389
|
+
Some Responses-compatible implementations document non-namespaced extension
|
|
390
|
+
discriminators, such as `web_search`, `web_search_call`, or
|
|
391
|
+
`response.custom_tool_call_input.delta`. These are not portable Open Responses
|
|
392
|
+
types. Register each exact value through the dedicated bare fields to opt in:
|
|
393
|
+
|
|
394
|
+
```ts
|
|
395
|
+
import type { Experimental_OpenResponsesBareExtension } from '@ai-sdk/open-responses';
|
|
396
|
+
|
|
397
|
+
const webSearchExtension: Experimental_OpenResponsesBareExtension = {
|
|
398
|
+
id: 'acme.web_search',
|
|
399
|
+
allowBareTypes: true,
|
|
400
|
+
bareToolType: 'web_search',
|
|
401
|
+
bareItemTypes: ['web_search_call'],
|
|
402
|
+
bareEventTypes: ['response.web_search_call.completed'],
|
|
403
|
+
encodeTool: ({ name, args }) => ({
|
|
404
|
+
name,
|
|
405
|
+
search_context_size: args.searchContextSize,
|
|
406
|
+
}),
|
|
407
|
+
decodeItem: ({ item }) => {
|
|
408
|
+
if (
|
|
409
|
+
typeof item.call_id !== 'string' ||
|
|
410
|
+
typeof item.name !== 'string' ||
|
|
411
|
+
typeof item.query !== 'string'
|
|
412
|
+
) {
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return [
|
|
417
|
+
{
|
|
418
|
+
type: 'tool-call',
|
|
419
|
+
toolCallId: item.call_id,
|
|
420
|
+
toolName: item.name,
|
|
421
|
+
input: JSON.stringify({ query: item.query }),
|
|
422
|
+
providerExecuted: true,
|
|
423
|
+
},
|
|
424
|
+
];
|
|
425
|
+
},
|
|
426
|
+
decodeEvent: () => undefined,
|
|
427
|
+
};
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Set `allowBareTypes: true` when using `bareToolType`, `bareItemTypes`, or
|
|
431
|
+
`bareEventTypes`. These fields accept only non-namespaced values. They do not
|
|
432
|
+
enable unknown JSON passthrough: a bare discriminator is handled only when it
|
|
433
|
+
exactly matches a registered value. Core Open Responses discriminators and
|
|
434
|
+
duplicate registrations are rejected.
|
|
435
|
+
Use the normal namespaced fields whenever the implementation supports them.
|
|
436
|
+
`Experimental_OpenResponsesExtension` remains the namespaced interface;
|
|
437
|
+
use `Experimental_OpenResponsesBareExtension` only for this compatibility
|
|
438
|
+
mode.
|
|
439
|
+
|
|
389
440
|
Set `providerExecuted` on each decoded `tool-call` or `tool-input-start` part.
|
|
390
441
|
It can vary by item or event.
|
|
391
442
|
|
|
@@ -395,18 +446,18 @@ An extension codec can define:
|
|
|
395
446
|
with an `unsupported` warning. A `toolChoice` that selects the omitted tool is
|
|
396
447
|
also omitted.
|
|
397
448
|
- `encodeToolChoice`: Encodes a specific `toolChoice`. The default is
|
|
398
|
-
|
|
449
|
+
an object containing the registered `toolType` or `bareToolType`.
|
|
399
450
|
- `decodeItem`: Maps a completed item to AI SDK content parts. `mode` is either
|
|
400
451
|
`'generate'` or `'stream'`.
|
|
401
452
|
- `encodeInputItem`: Encodes tool-call or tool-result history when the original
|
|
402
453
|
wire item is unavailable. Returned items must include `id`, `status`, and a
|
|
403
|
-
registered
|
|
454
|
+
`type` registered through `itemTypes` or `bareItemTypes`.
|
|
404
455
|
- `decodeEvent`: Maps a streaming event to AI SDK stream parts. Its `state` map
|
|
405
456
|
lasts for one response stream.
|
|
406
457
|
|
|
407
458
|
Tool, item, and event capabilities are independent. An item-only extension, for
|
|
408
|
-
example, defines `itemTypes`
|
|
409
|
-
Capability fields must be provided in pairs.
|
|
459
|
+
example, defines `itemTypes` (or `bareItemTypes`) and `decodeItem` without a
|
|
460
|
+
tool type or `encodeTool`. Capability fields must be provided in pairs.
|
|
410
461
|
|
|
411
462
|
Each decoded item adds an `open-responses.extension-replay` custom part that
|
|
412
463
|
stores the original JSON in provider metadata. Other decoded parts reference
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/open-responses",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.55",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@ai-sdk/provider": "4.0.
|
|
33
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
32
|
+
"@ai-sdk/provider": "4.0.19",
|
|
33
|
+
"@ai-sdk/provider-utils": "5.0.50"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@ai-sdk/test-server": "2.0.2",
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ export type {
|
|
|
6
6
|
} from './open-responses-provider';
|
|
7
7
|
export type { OpenResponsesCustomToolOptions } from './open-responses-tools';
|
|
8
8
|
export type {
|
|
9
|
+
OpenResponsesBareExtension as Experimental_OpenResponsesBareExtension,
|
|
9
10
|
OpenResponsesExtension as Experimental_OpenResponsesExtension,
|
|
10
11
|
OpenResponsesExtensionContentPart as Experimental_OpenResponsesExtensionContentPart,
|
|
11
12
|
OpenResponsesExtensionEvent as Experimental_OpenResponsesExtensionEvent,
|
|
@@ -10,16 +10,22 @@ import type { MaybePromiseLike } from '@ai-sdk/provider-utils';
|
|
|
10
10
|
|
|
11
11
|
export type OpenResponsesNamespacedType = `${string}:${string}`;
|
|
12
12
|
|
|
13
|
-
export type OpenResponsesExtensionRecord
|
|
14
|
-
|
|
13
|
+
export type OpenResponsesExtensionRecord<
|
|
14
|
+
Type extends string = OpenResponsesNamespacedType,
|
|
15
|
+
> = JSONObject & {
|
|
16
|
+
type: Type;
|
|
15
17
|
};
|
|
16
18
|
|
|
17
|
-
export type OpenResponsesExtensionItem
|
|
19
|
+
export type OpenResponsesExtensionItem<
|
|
20
|
+
Type extends string = OpenResponsesNamespacedType,
|
|
21
|
+
> = OpenResponsesExtensionRecord<Type> & {
|
|
18
22
|
id: string;
|
|
19
23
|
status: string;
|
|
20
24
|
};
|
|
21
25
|
|
|
22
|
-
export type OpenResponsesExtensionEvent
|
|
26
|
+
export type OpenResponsesExtensionEvent<
|
|
27
|
+
Type extends string = OpenResponsesNamespacedType,
|
|
28
|
+
> = OpenResponsesExtensionRecord<Type> & {
|
|
23
29
|
sequence_number: number;
|
|
24
30
|
};
|
|
25
31
|
|
|
@@ -44,7 +50,7 @@ export type OpenResponsesExtensionStreamPart = Exclude<
|
|
|
44
50
|
*
|
|
45
51
|
* Tool, item, and event codecs can be registered independently.
|
|
46
52
|
*/
|
|
47
|
-
|
|
53
|
+
interface OpenResponsesExtensionBase {
|
|
48
54
|
/**
|
|
49
55
|
* Extension ID in `<implementor>.<extension>` format. For extensions that
|
|
50
56
|
* encode a provider tool, this is also the AI SDK provider-tool ID.
|
|
@@ -57,22 +63,10 @@ export interface OpenResponsesExtension {
|
|
|
57
63
|
*/
|
|
58
64
|
toolType?: OpenResponsesNamespacedType;
|
|
59
65
|
|
|
60
|
-
/**
|
|
61
|
-
* Namespaced item types decoded by this extension. Must be provided together
|
|
62
|
-
* with `decodeItem`.
|
|
63
|
-
*/
|
|
64
|
-
itemTypes?: readonly OpenResponsesNamespacedType[];
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Namespaced streaming event types decoded by this extension. Must be
|
|
68
|
-
* provided together with `decodeEvent`.
|
|
69
|
-
*/
|
|
70
|
-
eventTypes?: readonly OpenResponsesNamespacedType[];
|
|
71
|
-
|
|
72
66
|
/**
|
|
73
67
|
* Encodes an AI SDK provider tool. Return `undefined` when its arguments
|
|
74
|
-
* cannot be encoded. The adapter adds
|
|
75
|
-
* choice that selects the omitted tool.
|
|
68
|
+
* cannot be encoded. The adapter adds the registered tool type and omits a
|
|
69
|
+
* specific tool choice that selects the omitted tool.
|
|
76
70
|
*/
|
|
77
71
|
encodeTool?(options: {
|
|
78
72
|
name: string;
|
|
@@ -80,74 +74,196 @@ export interface OpenResponsesExtension {
|
|
|
80
74
|
}): MaybePromiseLike<JSONObject | undefined>;
|
|
81
75
|
|
|
82
76
|
/**
|
|
83
|
-
* Encodes a specific `toolChoice`. The adapter adds
|
|
84
|
-
* is an object containing only
|
|
77
|
+
* Encodes a specific `toolChoice`. The adapter adds the registered tool type.
|
|
78
|
+
* The default is an object containing only that type.
|
|
85
79
|
*/
|
|
86
80
|
encodeToolChoice?(options: {
|
|
87
81
|
name: string;
|
|
88
82
|
args: Record<string, unknown>;
|
|
89
83
|
}): MaybePromiseLike<JSONObject | undefined>;
|
|
84
|
+
}
|
|
90
85
|
|
|
86
|
+
type OpenResponsesExtensionItemCodec<Type extends string> = {
|
|
91
87
|
/**
|
|
92
|
-
*
|
|
88
|
+
* Namespaced item types decoded by this extension. Must be provided together
|
|
89
|
+
* with `decodeItem`.
|
|
90
|
+
*/
|
|
91
|
+
itemTypes?: readonly OpenResponsesNamespacedType[];
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Decodes a completed registered item into AI SDK content parts. The adapter
|
|
93
95
|
* adds a custom replay part containing the original item and reference
|
|
94
96
|
* metadata to the returned parts.
|
|
95
97
|
*/
|
|
96
98
|
decodeItem?(options: {
|
|
97
|
-
item: OpenResponsesExtensionItem
|
|
99
|
+
item: OpenResponsesExtensionItem<Type>;
|
|
98
100
|
mode: 'generate' | 'stream';
|
|
99
101
|
}): MaybePromiseLike<OpenResponsesExtensionContentPart[] | undefined>;
|
|
100
102
|
|
|
101
103
|
/**
|
|
102
104
|
* Encodes an AI SDK history part when no original wire item is available.
|
|
103
|
-
* Every returned item must use one of
|
|
105
|
+
* Every returned item must use one of the registered item types.
|
|
104
106
|
*/
|
|
105
107
|
encodeInputItem?(options: {
|
|
106
108
|
part: OpenResponsesExtensionInputPart;
|
|
107
109
|
tool: LanguageModelV4ProviderTool;
|
|
108
110
|
}): MaybePromiseLike<
|
|
109
|
-
|
|
111
|
+
| OpenResponsesExtensionItem<Type>
|
|
112
|
+
| OpenResponsesExtensionItem<Type>[]
|
|
113
|
+
| undefined
|
|
110
114
|
>;
|
|
115
|
+
};
|
|
111
116
|
|
|
117
|
+
type OpenResponsesExtensionEventCodec<Type extends string> = {
|
|
112
118
|
/**
|
|
113
|
-
*
|
|
119
|
+
* Namespaced streaming event types decoded by this extension. Must be
|
|
120
|
+
* provided together with `decodeEvent`.
|
|
121
|
+
*/
|
|
122
|
+
eventTypes?: readonly OpenResponsesNamespacedType[];
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Decodes a registered streaming event.
|
|
114
126
|
*
|
|
115
127
|
* `state` persists for the lifetime of one response stream.
|
|
116
128
|
*/
|
|
117
129
|
decodeEvent?(options: {
|
|
118
|
-
event: OpenResponsesExtensionEvent
|
|
130
|
+
event: OpenResponsesExtensionEvent<Type>;
|
|
119
131
|
state: Map<string, unknown>;
|
|
120
132
|
}): MaybePromiseLike<OpenResponsesExtensionStreamPart[] | undefined>;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Defines a portable Open Responses extension using namespaced wire types.
|
|
137
|
+
*
|
|
138
|
+
* This remains an interface so existing consumers can extend it from their own
|
|
139
|
+
* interfaces and implement it in classes.
|
|
140
|
+
*/
|
|
141
|
+
export interface OpenResponsesExtension
|
|
142
|
+
extends
|
|
143
|
+
OpenResponsesExtensionBase,
|
|
144
|
+
OpenResponsesExtensionItemCodec<OpenResponsesNamespacedType>,
|
|
145
|
+
OpenResponsesExtensionEventCodec<OpenResponsesNamespacedType> {
|
|
146
|
+
allowBareTypes?: false;
|
|
147
|
+
bareToolType?: undefined;
|
|
148
|
+
bareItemTypes?: undefined;
|
|
149
|
+
bareEventTypes?: undefined;
|
|
121
150
|
}
|
|
122
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Defines a non-portable Open Responses extension that explicitly registers
|
|
154
|
+
* exact bare wire types.
|
|
155
|
+
*/
|
|
156
|
+
export type OpenResponsesBareExtension = OpenResponsesExtensionBase &
|
|
157
|
+
OpenResponsesExtensionItemCodec<string> &
|
|
158
|
+
OpenResponsesExtensionEventCodec<string> & {
|
|
159
|
+
/**
|
|
160
|
+
* Explicitly enables non-portable bare discriminator registration for this
|
|
161
|
+
* extension.
|
|
162
|
+
*/
|
|
163
|
+
allowBareTypes: true;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Exact non-namespaced tool type used by a documented implementation
|
|
167
|
+
* extension. Must be provided together with `encodeTool` and cannot be used
|
|
168
|
+
* together with `toolType`.
|
|
169
|
+
*/
|
|
170
|
+
bareToolType?: string;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Exact non-namespaced item types decoded by a documented implementation
|
|
174
|
+
* extension. Must be provided together with `decodeItem`.
|
|
175
|
+
*/
|
|
176
|
+
bareItemTypes?: readonly string[];
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Exact non-namespaced streaming event types decoded by this extension.
|
|
180
|
+
* Must be provided together with `decodeEvent`.
|
|
181
|
+
*/
|
|
182
|
+
bareEventTypes?: readonly string[];
|
|
183
|
+
} & (
|
|
184
|
+
| { bareToolType: string }
|
|
185
|
+
| { bareItemTypes: readonly string[] }
|
|
186
|
+
| { bareEventTypes: readonly string[] }
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
export type OpenResponsesExtensionRegistration =
|
|
190
|
+
| OpenResponsesExtension
|
|
191
|
+
| OpenResponsesBareExtension;
|
|
192
|
+
|
|
123
193
|
export type OpenResponsesExtensionRegistry = {
|
|
124
|
-
byEventType: Map<
|
|
125
|
-
byExtensionId: Map<
|
|
126
|
-
|
|
194
|
+
byEventType: Map<string, OpenResponsesEventExtension>;
|
|
195
|
+
byExtensionId: Map<
|
|
196
|
+
LanguageModelV4ProviderTool['id'],
|
|
197
|
+
OpenResponsesExtensionRegistration
|
|
198
|
+
>;
|
|
199
|
+
byItemType: Map<string, OpenResponsesItemExtension>;
|
|
127
200
|
byProviderToolId: Map<
|
|
128
201
|
LanguageModelV4ProviderTool['id'],
|
|
129
202
|
OpenResponsesToolExtension
|
|
130
203
|
>;
|
|
131
|
-
byToolType: Map<
|
|
204
|
+
byToolType: Map<string, OpenResponsesToolExtension>;
|
|
132
205
|
};
|
|
133
206
|
|
|
134
|
-
type OpenResponsesToolExtension =
|
|
135
|
-
|
|
136
|
-
|
|
207
|
+
type OpenResponsesToolExtension = OpenResponsesExtensionRegistration & {
|
|
208
|
+
encodeTool: NonNullable<OpenResponsesExtensionRegistration['encodeTool']>;
|
|
209
|
+
encodeInputItem?: (options: {
|
|
210
|
+
part: OpenResponsesExtensionInputPart;
|
|
211
|
+
tool: LanguageModelV4ProviderTool;
|
|
212
|
+
}) => MaybePromiseLike<
|
|
213
|
+
| OpenResponsesExtensionItem<string>
|
|
214
|
+
| OpenResponsesExtensionItem<string>[]
|
|
215
|
+
| undefined
|
|
216
|
+
>;
|
|
137
217
|
};
|
|
138
218
|
|
|
139
|
-
type OpenResponsesItemExtension =
|
|
140
|
-
|
|
141
|
-
|
|
219
|
+
type OpenResponsesItemExtension = OpenResponsesExtensionRegistration & {
|
|
220
|
+
decodeItem(options: {
|
|
221
|
+
item: OpenResponsesExtensionItem<string>;
|
|
222
|
+
mode: 'generate' | 'stream';
|
|
223
|
+
}): MaybePromiseLike<OpenResponsesExtensionContentPart[] | undefined>;
|
|
142
224
|
};
|
|
143
225
|
|
|
144
|
-
type OpenResponsesEventExtension =
|
|
145
|
-
|
|
146
|
-
|
|
226
|
+
type OpenResponsesEventExtension = OpenResponsesExtensionRegistration & {
|
|
227
|
+
decodeEvent(options: {
|
|
228
|
+
event: OpenResponsesExtensionEvent<string>;
|
|
229
|
+
state: Map<string, unknown>;
|
|
230
|
+
}): MaybePromiseLike<OpenResponsesExtensionStreamPart[] | undefined>;
|
|
147
231
|
};
|
|
148
232
|
|
|
233
|
+
const coreToolTypes = new Set(['allowed_tools', 'function']);
|
|
234
|
+
const coreItemTypes = new Set([
|
|
235
|
+
'function_call',
|
|
236
|
+
'function_call_output',
|
|
237
|
+
'item_reference',
|
|
238
|
+
'message',
|
|
239
|
+
'reasoning',
|
|
240
|
+
]);
|
|
241
|
+
const coreEventTypes = new Set([
|
|
242
|
+
'error',
|
|
243
|
+
'response.completed',
|
|
244
|
+
'response.content_part.added',
|
|
245
|
+
'response.content_part.done',
|
|
246
|
+
'response.created',
|
|
247
|
+
'response.failed',
|
|
248
|
+
'response.function_call_arguments.delta',
|
|
249
|
+
'response.function_call_arguments.done',
|
|
250
|
+
'response.in_progress',
|
|
251
|
+
'response.incomplete',
|
|
252
|
+
'response.output_item.added',
|
|
253
|
+
'response.output_item.done',
|
|
254
|
+
'response.output_text.delta',
|
|
255
|
+
'response.output_text.done',
|
|
256
|
+
'response.reasoning_summary_part.added',
|
|
257
|
+
'response.reasoning_summary_part.done',
|
|
258
|
+
'response.reasoning_summary_text.delta',
|
|
259
|
+
'response.reasoning_summary_text.done',
|
|
260
|
+
'response.reasoning_text.delta',
|
|
261
|
+
'response.refusal.delta',
|
|
262
|
+
'response.refusal.done',
|
|
263
|
+
]);
|
|
264
|
+
|
|
149
265
|
export function createOpenResponsesExtensionRegistry(
|
|
150
|
-
extensions?: readonly
|
|
266
|
+
extensions?: readonly OpenResponsesExtensionRegistration[],
|
|
151
267
|
): OpenResponsesExtensionRegistry {
|
|
152
268
|
const registry: OpenResponsesExtensionRegistry = {
|
|
153
269
|
byEventType: new Map(),
|
|
@@ -173,28 +289,56 @@ export function createOpenResponsesExtensionRegistry(
|
|
|
173
289
|
field: 'id',
|
|
174
290
|
});
|
|
175
291
|
|
|
176
|
-
const
|
|
292
|
+
const hasBareTypes =
|
|
293
|
+
extension.bareToolType != null ||
|
|
294
|
+
extension.bareItemTypes != null ||
|
|
295
|
+
extension.bareEventTypes != null;
|
|
296
|
+
if (hasBareTypes !== (extension.allowBareTypes === true)) {
|
|
297
|
+
throw new Error(
|
|
298
|
+
`Open Responses extension ${extension.id} must set allowBareTypes to true exactly when registering bare types.`,
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (extension.toolType != null && extension.bareToolType != null) {
|
|
303
|
+
throw new Error(
|
|
304
|
+
`Open Responses extension ${extension.id} cannot provide toolType and bareToolType together.`,
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const toolType = extension.toolType ?? extension.bareToolType;
|
|
309
|
+
const hasToolType = toolType != null;
|
|
177
310
|
const hasToolEncoder = extension.encodeTool != null;
|
|
178
311
|
if (hasToolType !== hasToolEncoder) {
|
|
312
|
+
const typeField =
|
|
313
|
+
extension.bareToolType != null ? 'bareToolType' : 'toolType';
|
|
179
314
|
throw new Error(
|
|
180
|
-
`Open Responses extension ${extension.id} must provide
|
|
315
|
+
`Open Responses extension ${extension.id} must provide ${typeField} and encodeTool together.`,
|
|
181
316
|
);
|
|
182
317
|
}
|
|
183
318
|
|
|
184
319
|
if (extension.encodeToolChoice != null && !hasToolEncoder) {
|
|
185
320
|
throw new Error(
|
|
186
|
-
`Open Responses extension ${extension.id} cannot provide encodeToolChoice without
|
|
321
|
+
`Open Responses extension ${extension.id} cannot provide encodeToolChoice without a tool type and encodeTool.`,
|
|
187
322
|
);
|
|
188
323
|
}
|
|
189
324
|
|
|
190
325
|
if (hasToolType && hasToolEncoder) {
|
|
191
326
|
const toolExtension = extension as OpenResponsesToolExtension;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
327
|
+
if (extension.toolType != null) {
|
|
328
|
+
assertNamespacedType({
|
|
329
|
+
extensionId: extension.id,
|
|
330
|
+
namespace,
|
|
331
|
+
type: extension.toolType,
|
|
332
|
+
field: 'toolType',
|
|
333
|
+
});
|
|
334
|
+
} else {
|
|
335
|
+
assertBareType({
|
|
336
|
+
coreTypes: coreToolTypes,
|
|
337
|
+
extensionId: extension.id,
|
|
338
|
+
type: extension.bareToolType!,
|
|
339
|
+
field: 'bareToolType',
|
|
340
|
+
});
|
|
341
|
+
}
|
|
198
342
|
registerUnique({
|
|
199
343
|
map: registry.byProviderToolId,
|
|
200
344
|
key: extension.id,
|
|
@@ -203,34 +347,39 @@ export function createOpenResponsesExtensionRegistry(
|
|
|
203
347
|
});
|
|
204
348
|
registerUnique({
|
|
205
349
|
map: registry.byToolType,
|
|
206
|
-
key:
|
|
350
|
+
key: toolType,
|
|
207
351
|
extension: toolExtension,
|
|
208
|
-
field: '
|
|
352
|
+
field: 'tool type',
|
|
209
353
|
});
|
|
210
354
|
}
|
|
211
355
|
|
|
212
|
-
const
|
|
356
|
+
const itemTypes = [
|
|
357
|
+
...(extension.itemTypes ?? []),
|
|
358
|
+
...(extension.bareItemTypes ?? []),
|
|
359
|
+
];
|
|
360
|
+
const hasItemTypes =
|
|
361
|
+
extension.itemTypes != null || extension.bareItemTypes != null;
|
|
213
362
|
const hasItemDecoder = extension.decodeItem != null;
|
|
214
363
|
if (hasItemTypes !== hasItemDecoder) {
|
|
215
364
|
throw new Error(
|
|
216
|
-
`Open Responses extension ${extension.id} must provide itemTypes
|
|
365
|
+
`Open Responses extension ${extension.id} must provide itemTypes or bareItemTypes together with decodeItem.`,
|
|
217
366
|
);
|
|
218
367
|
}
|
|
219
368
|
|
|
220
369
|
if (extension.encodeInputItem != null && !hasItemDecoder) {
|
|
221
370
|
throw new Error(
|
|
222
|
-
`Open Responses extension ${extension.id} cannot provide encodeInputItem without itemTypes and decodeItem.`,
|
|
371
|
+
`Open Responses extension ${extension.id} cannot provide encodeInputItem without itemTypes or bareItemTypes and decodeItem.`,
|
|
223
372
|
);
|
|
224
373
|
}
|
|
225
374
|
|
|
226
375
|
if (hasItemTypes && hasItemDecoder) {
|
|
227
376
|
const itemExtension = extension as OpenResponsesItemExtension;
|
|
228
|
-
if (
|
|
377
|
+
if (itemTypes.length === 0) {
|
|
229
378
|
throw new Error(
|
|
230
379
|
`Open Responses extension ${extension.id} must register at least one item type.`,
|
|
231
380
|
);
|
|
232
381
|
}
|
|
233
|
-
for (const itemType of
|
|
382
|
+
for (const itemType of extension.itemTypes ?? []) {
|
|
234
383
|
assertNamespacedType({
|
|
235
384
|
extensionId: extension.id,
|
|
236
385
|
namespace,
|
|
@@ -244,24 +393,43 @@ export function createOpenResponsesExtensionRegistry(
|
|
|
244
393
|
field: 'item type',
|
|
245
394
|
});
|
|
246
395
|
}
|
|
396
|
+
for (const itemType of extension.bareItemTypes ?? []) {
|
|
397
|
+
assertBareType({
|
|
398
|
+
coreTypes: coreItemTypes,
|
|
399
|
+
extensionId: extension.id,
|
|
400
|
+
type: itemType,
|
|
401
|
+
field: 'bareItemTypes',
|
|
402
|
+
});
|
|
403
|
+
registerUnique({
|
|
404
|
+
map: registry.byItemType,
|
|
405
|
+
key: itemType,
|
|
406
|
+
extension: itemExtension,
|
|
407
|
+
field: 'item type',
|
|
408
|
+
});
|
|
409
|
+
}
|
|
247
410
|
}
|
|
248
411
|
|
|
249
|
-
const
|
|
412
|
+
const eventTypes = [
|
|
413
|
+
...(extension.eventTypes ?? []),
|
|
414
|
+
...(extension.bareEventTypes ?? []),
|
|
415
|
+
];
|
|
416
|
+
const hasEventTypes =
|
|
417
|
+
extension.eventTypes != null || extension.bareEventTypes != null;
|
|
250
418
|
const hasEventDecoder = extension.decodeEvent != null;
|
|
251
419
|
if (hasEventTypes !== hasEventDecoder) {
|
|
252
420
|
throw new Error(
|
|
253
|
-
`Open Responses extension ${extension.id} must provide eventTypes
|
|
421
|
+
`Open Responses extension ${extension.id} must provide eventTypes or bareEventTypes together with decodeEvent.`,
|
|
254
422
|
);
|
|
255
423
|
}
|
|
256
424
|
|
|
257
425
|
if (hasEventTypes && hasEventDecoder) {
|
|
258
426
|
const eventExtension = extension as OpenResponsesEventExtension;
|
|
259
|
-
if (
|
|
427
|
+
if (eventTypes.length === 0) {
|
|
260
428
|
throw new Error(
|
|
261
429
|
`Open Responses extension ${extension.id} must register at least one event type.`,
|
|
262
430
|
);
|
|
263
431
|
}
|
|
264
|
-
for (const eventType of
|
|
432
|
+
for (const eventType of extension.eventTypes ?? []) {
|
|
265
433
|
assertNamespacedType({
|
|
266
434
|
extensionId: extension.id,
|
|
267
435
|
namespace,
|
|
@@ -275,6 +443,20 @@ export function createOpenResponsesExtensionRegistry(
|
|
|
275
443
|
field: 'event type',
|
|
276
444
|
});
|
|
277
445
|
}
|
|
446
|
+
for (const eventType of extension.bareEventTypes ?? []) {
|
|
447
|
+
assertBareType({
|
|
448
|
+
coreTypes: coreEventTypes,
|
|
449
|
+
extensionId: extension.id,
|
|
450
|
+
type: eventType,
|
|
451
|
+
field: 'bareEventTypes',
|
|
452
|
+
});
|
|
453
|
+
registerUnique({
|
|
454
|
+
map: registry.byEventType,
|
|
455
|
+
key: eventType,
|
|
456
|
+
extension: eventExtension,
|
|
457
|
+
field: 'event type',
|
|
458
|
+
});
|
|
459
|
+
}
|
|
278
460
|
}
|
|
279
461
|
|
|
280
462
|
if (!hasToolEncoder && !hasItemDecoder && !hasEventDecoder) {
|
|
@@ -298,14 +480,64 @@ function assertNamespacedType({
|
|
|
298
480
|
type: string;
|
|
299
481
|
field: string;
|
|
300
482
|
}) {
|
|
301
|
-
|
|
483
|
+
const separatorIndex = type.indexOf(':');
|
|
484
|
+
if (separatorIndex <= 0 || type.slice(0, separatorIndex) !== namespace) {
|
|
302
485
|
throw new Error(
|
|
303
486
|
`Open Responses extension ${extensionId} has invalid ${field} value ${type}. Extension wire types must use the ${namespace}: namespace.`,
|
|
304
487
|
);
|
|
305
488
|
}
|
|
306
489
|
}
|
|
307
490
|
|
|
308
|
-
function
|
|
491
|
+
function assertBareType({
|
|
492
|
+
coreTypes,
|
|
493
|
+
extensionId,
|
|
494
|
+
type,
|
|
495
|
+
field,
|
|
496
|
+
}: {
|
|
497
|
+
coreTypes: Set<string>;
|
|
498
|
+
extensionId: string;
|
|
499
|
+
type: string;
|
|
500
|
+
field: string;
|
|
501
|
+
}) {
|
|
502
|
+
if (type.length === 0 || type.includes(':')) {
|
|
503
|
+
throw new Error(
|
|
504
|
+
`Open Responses extension ${extensionId} has invalid ${field} value ${type}. Bare extension wire types must be non-empty and must not contain a colon.`,
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (coreTypes.has(type)) {
|
|
509
|
+
throw new Error(
|
|
510
|
+
`Open Responses extension ${extensionId} cannot register core ${field} value ${type}.`,
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export function getOpenResponsesExtensionItemTypes(
|
|
516
|
+
extension: OpenResponsesExtensionRegistration,
|
|
517
|
+
): readonly string[] {
|
|
518
|
+
return [...(extension.itemTypes ?? []), ...(extension.bareItemTypes ?? [])];
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export function getOpenResponsesExtensionToolType(
|
|
522
|
+
extension: OpenResponsesExtensionRegistration,
|
|
523
|
+
): string | undefined {
|
|
524
|
+
return extension.toolType ?? extension.bareToolType;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Keeps the internal API unions narrow enough to discriminate core wire types.
|
|
529
|
+
* Bare records only reach this boundary after registry validation.
|
|
530
|
+
*/
|
|
531
|
+
export function asOpenResponsesExtensionRecord(
|
|
532
|
+
value: OpenResponsesExtensionRecord<string>,
|
|
533
|
+
): OpenResponsesExtensionRecord {
|
|
534
|
+
return value as OpenResponsesExtensionRecord;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function registerUnique<
|
|
538
|
+
K,
|
|
539
|
+
Extension extends OpenResponsesExtensionRegistration,
|
|
540
|
+
>({
|
|
309
541
|
map,
|
|
310
542
|
key,
|
|
311
543
|
extension,
|
|
@@ -333,10 +565,10 @@ export function isOpenResponsesNamespacedType(
|
|
|
333
565
|
|
|
334
566
|
export function isOpenResponsesExtensionRecord(
|
|
335
567
|
value: unknown,
|
|
336
|
-
): value is OpenResponsesExtensionRecord {
|
|
568
|
+
): value is OpenResponsesExtensionRecord<string> {
|
|
337
569
|
return (
|
|
338
570
|
isOpenResponsesJSONObject(value) &&
|
|
339
|
-
|
|
571
|
+
typeof (value as { type?: unknown }).type === 'string'
|
|
340
572
|
);
|
|
341
573
|
}
|
|
342
574
|
|
|
@@ -352,7 +584,7 @@ export function isOpenResponsesJSONObject(value: unknown): value is JSONObject {
|
|
|
352
584
|
|
|
353
585
|
export function isOpenResponsesExtensionItem(
|
|
354
586
|
value: unknown,
|
|
355
|
-
): value is OpenResponsesExtensionItem {
|
|
587
|
+
): value is OpenResponsesExtensionItem<string> {
|
|
356
588
|
return (
|
|
357
589
|
isOpenResponsesExtensionRecord(value) &&
|
|
358
590
|
typeof value.id === 'string' &&
|
|
@@ -362,7 +594,7 @@ export function isOpenResponsesExtensionItem(
|
|
|
362
594
|
|
|
363
595
|
export function isOpenResponsesExtensionEvent(
|
|
364
596
|
value: unknown,
|
|
365
|
-
): value is OpenResponsesExtensionEvent {
|
|
597
|
+
): value is OpenResponsesExtensionEvent<string> {
|
|
366
598
|
return (
|
|
367
599
|
isOpenResponsesExtensionRecord(value) &&
|
|
368
600
|
typeof value.sequence_number === 'number'
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from '@ai-sdk/provider-utils';
|
|
13
13
|
import {
|
|
14
14
|
createOpenResponsesExtensionRegistry,
|
|
15
|
-
type
|
|
15
|
+
type OpenResponsesExtensionRegistration,
|
|
16
16
|
} from './open-responses-extension';
|
|
17
17
|
import { createOpenResponsesTools } from './open-responses-tools';
|
|
18
18
|
import { OpenResponsesLanguageModel } from './responses/open-responses-language-model';
|
|
@@ -99,7 +99,7 @@ export interface OpenResponsesProviderSettings {
|
|
|
99
99
|
*
|
|
100
100
|
* @experimental This API may change in a future release.
|
|
101
101
|
*/
|
|
102
|
-
experimental_extensions?: readonly
|
|
102
|
+
experimental_extensions?: readonly OpenResponsesExtensionRegistration[];
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
export function createOpenResponses(
|