@ai-sdk/open-responses 2.0.27 → 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 +9 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +255 -55
- package/dist/index.js.map +1 -1
- package/docs/06-open-responses.mdx +14 -4
- package/package.json +1 -1
- package/src/responses/convert-to-open-responses-input.ts +201 -28
- package/src/responses/open-responses-api.ts +1 -1
- package/src/responses/open-responses-language-model-options.ts +6 -0
- package/src/responses/open-responses-language-model.ts +167 -34
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
resolveFullMediaType,
|
|
10
10
|
} from '@ai-sdk/provider-utils';
|
|
11
11
|
import type {
|
|
12
|
-
FunctionCallItemParam,
|
|
13
12
|
FunctionCallOutputItemParam,
|
|
14
13
|
InputFileContentParam,
|
|
15
14
|
InputImageContentParam,
|
|
@@ -22,8 +21,10 @@ import type {
|
|
|
22
21
|
|
|
23
22
|
export async function convertToOpenResponsesInput({
|
|
24
23
|
prompt,
|
|
24
|
+
providerOptionsName = 'open-responses',
|
|
25
25
|
}: {
|
|
26
26
|
prompt: LanguageModelV4Prompt;
|
|
27
|
+
providerOptionsName?: string;
|
|
27
28
|
}): Promise<{
|
|
28
29
|
input: OpenResponsesRequestBody['input'];
|
|
29
30
|
instructions: string | undefined;
|
|
@@ -103,33 +104,128 @@ export async function convertToOpenResponsesInput({
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
case 'assistant': {
|
|
106
|
-
|
|
107
|
+
let assistantContent: Array<
|
|
107
108
|
OutputTextContentParam | RefusalContentParam
|
|
108
109
|
> = [];
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
let assistantMessageId: string | undefined;
|
|
111
|
+
|
|
112
|
+
const flushAssistantContent = () => {
|
|
113
|
+
if (assistantContent.length === 0) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
input.push({
|
|
118
|
+
type: 'message',
|
|
119
|
+
role: 'assistant',
|
|
120
|
+
content: assistantContent,
|
|
121
|
+
...(assistantMessageId != null && { id: assistantMessageId }),
|
|
122
|
+
});
|
|
123
|
+
assistantContent = [];
|
|
124
|
+
assistantMessageId = undefined;
|
|
125
|
+
};
|
|
111
126
|
|
|
112
127
|
for (const part of content) {
|
|
113
128
|
switch (part.type) {
|
|
114
129
|
case 'reasoning': {
|
|
115
|
-
|
|
130
|
+
flushAssistantContent();
|
|
131
|
+
|
|
132
|
+
const providerData = getProviderData(part, providerOptionsName);
|
|
133
|
+
const itemId =
|
|
134
|
+
typeof providerData?.itemId === 'string'
|
|
135
|
+
? providerData.itemId
|
|
136
|
+
: undefined;
|
|
137
|
+
const summary = parseReasoningSummary(
|
|
138
|
+
providerData?.reasoningSummary,
|
|
139
|
+
);
|
|
140
|
+
const reasoningContent = parseReasoningContent(
|
|
141
|
+
providerData?.reasoningContent,
|
|
142
|
+
);
|
|
143
|
+
const hasReasoningContent =
|
|
144
|
+
providerData != null && 'reasoningContent' in providerData;
|
|
145
|
+
const encryptedContent =
|
|
146
|
+
typeof providerData?.reasoningEncryptedContent === 'string'
|
|
147
|
+
? providerData.reasoningEncryptedContent
|
|
148
|
+
: undefined;
|
|
149
|
+
|
|
150
|
+
const reasoningItem: ReasoningItemParam = {
|
|
116
151
|
type: 'reasoning',
|
|
117
|
-
summary: [],
|
|
118
|
-
|
|
119
|
-
|
|
152
|
+
summary: summary ?? [],
|
|
153
|
+
...(itemId != null && { id: itemId }),
|
|
154
|
+
...(reasoningContent != null
|
|
155
|
+
? { content: reasoningContent }
|
|
156
|
+
: !hasReasoningContent && part.text.length > 0
|
|
157
|
+
? {
|
|
158
|
+
content: [
|
|
159
|
+
{
|
|
160
|
+
type: 'reasoning_text' as const,
|
|
161
|
+
text: part.text,
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
}
|
|
165
|
+
: {}),
|
|
166
|
+
...(encryptedContent != null && {
|
|
167
|
+
encrypted_content: encryptedContent,
|
|
168
|
+
}),
|
|
169
|
+
};
|
|
170
|
+
const previousItem = input[input.length - 1];
|
|
171
|
+
|
|
172
|
+
if (
|
|
173
|
+
reasoningItem.id != null &&
|
|
174
|
+
previousItem?.type === 'reasoning' &&
|
|
175
|
+
previousItem.id === reasoningItem.id
|
|
176
|
+
) {
|
|
177
|
+
if (reasoningItem.content != null) {
|
|
178
|
+
previousItem.content = [
|
|
179
|
+
...(previousItem.content ?? []),
|
|
180
|
+
...reasoningItem.content,
|
|
181
|
+
];
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
input.push(reasoningItem);
|
|
185
|
+
}
|
|
120
186
|
break;
|
|
121
187
|
}
|
|
122
188
|
case 'text': {
|
|
123
|
-
|
|
189
|
+
const providerData = getProviderData(part, providerOptionsName);
|
|
190
|
+
const itemId =
|
|
191
|
+
typeof providerData?.itemId === 'string'
|
|
192
|
+
? providerData.itemId
|
|
193
|
+
: undefined;
|
|
194
|
+
const annotations = parseOutputTextAnnotations(
|
|
195
|
+
providerData?.annotations,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
if (
|
|
199
|
+
assistantContent.length > 0 &&
|
|
200
|
+
assistantMessageId !== itemId
|
|
201
|
+
) {
|
|
202
|
+
flushAssistantContent();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
assistantMessageId = itemId;
|
|
206
|
+
assistantContent.push({
|
|
207
|
+
type: 'output_text',
|
|
208
|
+
text: part.text,
|
|
209
|
+
...(annotations != null && { annotations }),
|
|
210
|
+
});
|
|
124
211
|
break;
|
|
125
212
|
}
|
|
126
213
|
case 'tool-call': {
|
|
214
|
+
flushAssistantContent();
|
|
215
|
+
|
|
127
216
|
const argumentsValue =
|
|
128
217
|
typeof part.input === 'string'
|
|
129
218
|
? part.input
|
|
130
219
|
: JSON.stringify(part.input);
|
|
131
|
-
|
|
220
|
+
const providerData = getProviderData(part, providerOptionsName);
|
|
221
|
+
const itemId =
|
|
222
|
+
typeof providerData?.itemId === 'string'
|
|
223
|
+
? providerData.itemId
|
|
224
|
+
: undefined;
|
|
225
|
+
|
|
226
|
+
input.push({
|
|
132
227
|
type: 'function_call',
|
|
228
|
+
...(itemId != null && { id: itemId }),
|
|
133
229
|
call_id: part.toolCallId,
|
|
134
230
|
name: part.toolName,
|
|
135
231
|
arguments: argumentsValue,
|
|
@@ -139,24 +235,7 @@ export async function convertToOpenResponsesInput({
|
|
|
139
235
|
}
|
|
140
236
|
}
|
|
141
237
|
|
|
142
|
-
|
|
143
|
-
for (const reasoningItem of reasoningItems) {
|
|
144
|
-
input.push(reasoningItem);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Push assistant message with text content if any
|
|
148
|
-
if (assistantContent.length > 0) {
|
|
149
|
-
input.push({
|
|
150
|
-
type: 'message',
|
|
151
|
-
role: 'assistant',
|
|
152
|
-
content: assistantContent,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Push function calls as separate items
|
|
157
|
-
for (const toolCall of toolCalls) {
|
|
158
|
-
input.push(toolCall);
|
|
159
|
-
}
|
|
238
|
+
flushAssistantContent();
|
|
160
239
|
|
|
161
240
|
break;
|
|
162
241
|
}
|
|
@@ -266,3 +345,97 @@ export async function convertToOpenResponsesInput({
|
|
|
266
345
|
warnings,
|
|
267
346
|
};
|
|
268
347
|
}
|
|
348
|
+
|
|
349
|
+
function getProviderData(
|
|
350
|
+
part: {
|
|
351
|
+
providerOptions?: Record<string, unknown>;
|
|
352
|
+
},
|
|
353
|
+
providerOptionsName: string,
|
|
354
|
+
): Record<string, unknown> | undefined {
|
|
355
|
+
const providerData =
|
|
356
|
+
part.providerOptions?.[providerOptionsName] ??
|
|
357
|
+
(
|
|
358
|
+
part as {
|
|
359
|
+
providerMetadata?: Record<string, unknown>;
|
|
360
|
+
}
|
|
361
|
+
).providerMetadata?.[providerOptionsName];
|
|
362
|
+
|
|
363
|
+
return providerData != null &&
|
|
364
|
+
typeof providerData === 'object' &&
|
|
365
|
+
!Array.isArray(providerData)
|
|
366
|
+
? (providerData as Record<string, unknown>)
|
|
367
|
+
: undefined;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function parseReasoningSummary(
|
|
371
|
+
value: unknown,
|
|
372
|
+
): ReasoningItemParam['summary'] | undefined {
|
|
373
|
+
if (
|
|
374
|
+
!Array.isArray(value) ||
|
|
375
|
+
!value.every(
|
|
376
|
+
part =>
|
|
377
|
+
part != null &&
|
|
378
|
+
typeof part === 'object' &&
|
|
379
|
+
(part as { type?: unknown }).type === 'summary_text' &&
|
|
380
|
+
typeof (part as { text?: unknown }).text === 'string',
|
|
381
|
+
)
|
|
382
|
+
) {
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return value.map(part => ({
|
|
387
|
+
type: 'summary_text',
|
|
388
|
+
text: (part as { text: string }).text,
|
|
389
|
+
}));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function parseReasoningContent(
|
|
393
|
+
value: unknown,
|
|
394
|
+
): ReasoningItemParam['content'] | undefined {
|
|
395
|
+
if (
|
|
396
|
+
!Array.isArray(value) ||
|
|
397
|
+
!value.every(
|
|
398
|
+
part =>
|
|
399
|
+
part != null &&
|
|
400
|
+
typeof part === 'object' &&
|
|
401
|
+
(part as { type?: unknown }).type === 'reasoning_text' &&
|
|
402
|
+
typeof (part as { text?: unknown }).text === 'string',
|
|
403
|
+
)
|
|
404
|
+
) {
|
|
405
|
+
return undefined;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return value.map(part => ({
|
|
409
|
+
type: 'reasoning_text',
|
|
410
|
+
text: (part as { text: string }).text,
|
|
411
|
+
}));
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function parseOutputTextAnnotations(
|
|
415
|
+
value: unknown,
|
|
416
|
+
): OutputTextContentParam['annotations'] | undefined {
|
|
417
|
+
if (
|
|
418
|
+
!Array.isArray(value) ||
|
|
419
|
+
!value.every(
|
|
420
|
+
annotation =>
|
|
421
|
+
annotation != null &&
|
|
422
|
+
typeof annotation === 'object' &&
|
|
423
|
+
(annotation as { type?: unknown }).type === 'url_citation' &&
|
|
424
|
+
typeof (annotation as { start_index?: unknown }).start_index ===
|
|
425
|
+
'number' &&
|
|
426
|
+
typeof (annotation as { end_index?: unknown }).end_index === 'number' &&
|
|
427
|
+
typeof (annotation as { url?: unknown }).url === 'string' &&
|
|
428
|
+
typeof (annotation as { title?: unknown }).title === 'string',
|
|
429
|
+
)
|
|
430
|
+
) {
|
|
431
|
+
return undefined;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return value.map(annotation => ({
|
|
435
|
+
type: 'url_citation',
|
|
436
|
+
start_index: (annotation as { start_index: number }).start_index,
|
|
437
|
+
end_index: (annotation as { end_index: number }).end_index,
|
|
438
|
+
url: (annotation as { url: string }).url,
|
|
439
|
+
title: (annotation as { title: string }).title,
|
|
440
|
+
}));
|
|
441
|
+
}
|
|
@@ -8,6 +8,12 @@ import { z } from 'zod/v4';
|
|
|
8
8
|
export const openResponsesLanguageModelOptions = lazySchema(() =>
|
|
9
9
|
zodSchema(
|
|
10
10
|
z.object({
|
|
11
|
+
/**
|
|
12
|
+
* Provider-native reasoning effort. The value is passed through to the
|
|
13
|
+
* endpoint and takes precedence over the top-level `reasoning` setting.
|
|
14
|
+
*/
|
|
15
|
+
reasoningEffort: z.string().nullish(),
|
|
16
|
+
|
|
11
17
|
/**
|
|
12
18
|
* Controls reasoning summary output from the model.
|
|
13
19
|
* Valid values: 'concise', 'detailed', 'auto'.
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type LanguageModelV4StreamPart,
|
|
9
9
|
type LanguageModelV4StreamResult,
|
|
10
10
|
type LanguageModelV4Usage,
|
|
11
|
+
type SharedV4ProviderMetadata,
|
|
11
12
|
type SharedV4Warning,
|
|
12
13
|
} from '@ai-sdk/provider';
|
|
13
14
|
import {
|
|
@@ -29,10 +30,12 @@ import { z } from 'zod/v4';
|
|
|
29
30
|
import { convertToOpenResponsesInput } from './convert-to-open-responses-input';
|
|
30
31
|
import {
|
|
31
32
|
openResponsesErrorSchema,
|
|
33
|
+
type Annotation,
|
|
32
34
|
type FunctionToolParam,
|
|
33
35
|
type OpenResponsesRequestBody,
|
|
34
36
|
type OpenResponsesResponseBody,
|
|
35
37
|
type OpenResponsesChunk,
|
|
38
|
+
type ReasoningBody,
|
|
36
39
|
type ToolChoiceParam,
|
|
37
40
|
} from './open-responses-api';
|
|
38
41
|
import { mapOpenResponsesFinishReason } from './map-open-responses-finish-reason';
|
|
@@ -112,20 +115,30 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
112
115
|
warnings: inputWarnings,
|
|
113
116
|
} = await convertToOpenResponsesInput({
|
|
114
117
|
prompt,
|
|
118
|
+
providerOptionsName: this.config.providerOptionsName,
|
|
115
119
|
});
|
|
116
120
|
|
|
117
121
|
warnings.push(...inputWarnings);
|
|
118
122
|
|
|
119
123
|
// Convert function tools to the Open Responses format
|
|
120
|
-
const functionTools: FunctionToolParam[]
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
124
|
+
const functionTools: FunctionToolParam[] = [];
|
|
125
|
+
|
|
126
|
+
for (const tool of tools ?? []) {
|
|
127
|
+
if (tool.type === 'provider') {
|
|
128
|
+
warnings.push({
|
|
129
|
+
type: 'unsupported',
|
|
130
|
+
feature: `provider-defined tool ${tool.id}`,
|
|
131
|
+
});
|
|
132
|
+
} else {
|
|
133
|
+
functionTools.push({
|
|
134
|
+
type: 'function',
|
|
135
|
+
name: tool.name,
|
|
136
|
+
description: tool.description,
|
|
137
|
+
parameters: tool.inputSchema,
|
|
138
|
+
...(tool.strict != null ? { strict: tool.strict } : {}),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
129
142
|
|
|
130
143
|
// Convert tool choice to the Open Responses format
|
|
131
144
|
const convertedToolChoice: ToolChoiceParam | undefined =
|
|
@@ -156,21 +169,23 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
156
169
|
schema: openResponsesLanguageModelOptions,
|
|
157
170
|
});
|
|
158
171
|
|
|
159
|
-
const resolvedReasoningEffort =
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
const resolvedReasoningEffort =
|
|
173
|
+
openResponsesOptions?.reasoningEffort ??
|
|
174
|
+
(isCustomReasoning(reasoning)
|
|
175
|
+
? reasoning === 'none'
|
|
176
|
+
? 'none'
|
|
177
|
+
: mapReasoningToProviderEffort({
|
|
178
|
+
reasoning,
|
|
179
|
+
effortMap: {
|
|
180
|
+
minimal: 'low',
|
|
181
|
+
low: 'low',
|
|
182
|
+
medium: 'medium',
|
|
183
|
+
high: 'high',
|
|
184
|
+
xhigh: 'xhigh',
|
|
185
|
+
},
|
|
186
|
+
warnings,
|
|
187
|
+
})
|
|
188
|
+
: undefined);
|
|
174
189
|
|
|
175
190
|
return {
|
|
176
191
|
body: {
|
|
@@ -194,7 +209,7 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
194
209
|
}),
|
|
195
210
|
}
|
|
196
211
|
: undefined,
|
|
197
|
-
tools: functionTools
|
|
212
|
+
tools: functionTools.length ? functionTools : undefined,
|
|
198
213
|
tool_choice: convertedToolChoice,
|
|
199
214
|
...(textFormat != null && { text: { format: textFormat } }),
|
|
200
215
|
},
|
|
@@ -263,10 +278,26 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
263
278
|
switch (part.type) {
|
|
264
279
|
// TODO AI SDK 7 adjust reasoning in the specification to better support the reasoning structure from open responses.
|
|
265
280
|
case 'reasoning': {
|
|
266
|
-
|
|
281
|
+
if ((part.content?.length ?? 0) > 0) {
|
|
282
|
+
for (const contentPart of part.content!) {
|
|
283
|
+
content.push({
|
|
284
|
+
type: 'reasoning',
|
|
285
|
+
text: contentPart.text,
|
|
286
|
+
providerMetadata: createReasoningProviderMetadata({
|
|
287
|
+
part,
|
|
288
|
+
providerOptionsName: this.config.providerOptionsName,
|
|
289
|
+
reasoningContent: [contentPart],
|
|
290
|
+
}),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
267
294
|
content.push({
|
|
268
295
|
type: 'reasoning',
|
|
269
|
-
text:
|
|
296
|
+
text: part.summary.map(summaryPart => summaryPart.text).join(''),
|
|
297
|
+
providerMetadata: createReasoningProviderMetadata({
|
|
298
|
+
part,
|
|
299
|
+
providerOptionsName: this.config.providerOptionsName,
|
|
300
|
+
}),
|
|
270
301
|
});
|
|
271
302
|
}
|
|
272
303
|
break;
|
|
@@ -274,9 +305,17 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
274
305
|
|
|
275
306
|
case 'message': {
|
|
276
307
|
for (const contentPart of part.content) {
|
|
308
|
+
const annotations = getOutputTextAnnotations(contentPart);
|
|
309
|
+
|
|
277
310
|
content.push({
|
|
278
311
|
type: 'text',
|
|
279
312
|
text: contentPart.text,
|
|
313
|
+
providerMetadata: {
|
|
314
|
+
[this.config.providerOptionsName]: {
|
|
315
|
+
itemId: part.id,
|
|
316
|
+
...(annotations.length > 0 && { annotations }),
|
|
317
|
+
},
|
|
318
|
+
},
|
|
280
319
|
});
|
|
281
320
|
}
|
|
282
321
|
|
|
@@ -290,6 +329,9 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
290
329
|
toolCallId: part.call_id,
|
|
291
330
|
toolName: part.name,
|
|
292
331
|
input: part.arguments,
|
|
332
|
+
providerMetadata: {
|
|
333
|
+
[this.config.providerOptionsName]: { itemId: part.id },
|
|
334
|
+
},
|
|
293
335
|
});
|
|
294
336
|
break;
|
|
295
337
|
}
|
|
@@ -401,7 +443,7 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
401
443
|
usage.raw = responseUsage;
|
|
402
444
|
};
|
|
403
445
|
|
|
404
|
-
let
|
|
446
|
+
let activeReasoningId: string | undefined;
|
|
405
447
|
let hasToolCalls = false;
|
|
406
448
|
let finishReason: LanguageModelV4FinishReason = {
|
|
407
449
|
unified: 'other',
|
|
@@ -411,6 +453,7 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
411
453
|
string,
|
|
412
454
|
{ toolName?: string; toolCallId?: string; arguments?: string }
|
|
413
455
|
>();
|
|
456
|
+
const providerOptionsName = this.config.providerOptionsName;
|
|
414
457
|
|
|
415
458
|
return {
|
|
416
459
|
stream: response.pipeThrough(
|
|
@@ -488,6 +531,11 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
488
531
|
toolCallId,
|
|
489
532
|
toolName,
|
|
490
533
|
input,
|
|
534
|
+
providerMetadata: {
|
|
535
|
+
[providerOptionsName]: {
|
|
536
|
+
itemId: chunk.item.id,
|
|
537
|
+
},
|
|
538
|
+
},
|
|
491
539
|
});
|
|
492
540
|
hasToolCalls = true;
|
|
493
541
|
|
|
@@ -503,7 +551,7 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
503
551
|
type: 'reasoning-start',
|
|
504
552
|
id: chunk.item.id,
|
|
505
553
|
});
|
|
506
|
-
|
|
554
|
+
activeReasoningId = chunk.item.id;
|
|
507
555
|
} else if (
|
|
508
556
|
(chunk as { type: string }).type ===
|
|
509
557
|
'response.reasoning_text.delta'
|
|
@@ -521,8 +569,17 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
521
569
|
chunk.type === 'response.output_item.done' &&
|
|
522
570
|
chunk.item.type === 'reasoning'
|
|
523
571
|
) {
|
|
524
|
-
controller.enqueue({
|
|
525
|
-
|
|
572
|
+
controller.enqueue({
|
|
573
|
+
type: 'reasoning-end',
|
|
574
|
+
id: chunk.item.id,
|
|
575
|
+
providerMetadata: createReasoningProviderMetadata({
|
|
576
|
+
part: chunk.item,
|
|
577
|
+
providerOptionsName,
|
|
578
|
+
}),
|
|
579
|
+
});
|
|
580
|
+
if (activeReasoningId === chunk.item.id) {
|
|
581
|
+
activeReasoningId = undefined;
|
|
582
|
+
}
|
|
526
583
|
}
|
|
527
584
|
|
|
528
585
|
// Text events
|
|
@@ -541,7 +598,20 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
541
598
|
chunk.type === 'response.output_item.done' &&
|
|
542
599
|
chunk.item.type === 'message'
|
|
543
600
|
) {
|
|
544
|
-
|
|
601
|
+
const annotations = chunk.item.content.flatMap(
|
|
602
|
+
getOutputTextAnnotations,
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
controller.enqueue({
|
|
606
|
+
type: 'text-end',
|
|
607
|
+
id: chunk.item.id,
|
|
608
|
+
providerMetadata: {
|
|
609
|
+
[providerOptionsName]: {
|
|
610
|
+
itemId: chunk.item.id,
|
|
611
|
+
...(annotations.length > 0 && { annotations }),
|
|
612
|
+
},
|
|
613
|
+
},
|
|
614
|
+
});
|
|
545
615
|
} else if (
|
|
546
616
|
chunk.type === 'response.completed' ||
|
|
547
617
|
chunk.type === 'response.incomplete'
|
|
@@ -565,8 +635,11 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
565
635
|
},
|
|
566
636
|
|
|
567
637
|
flush(controller) {
|
|
568
|
-
if (
|
|
569
|
-
controller.enqueue({
|
|
638
|
+
if (activeReasoningId != null) {
|
|
639
|
+
controller.enqueue({
|
|
640
|
+
type: 'reasoning-end',
|
|
641
|
+
id: activeReasoningId,
|
|
642
|
+
});
|
|
570
643
|
}
|
|
571
644
|
|
|
572
645
|
controller.enqueue({
|
|
@@ -583,3 +656,63 @@ export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
|
583
656
|
};
|
|
584
657
|
}
|
|
585
658
|
}
|
|
659
|
+
|
|
660
|
+
function createReasoningProviderMetadata({
|
|
661
|
+
part,
|
|
662
|
+
providerOptionsName,
|
|
663
|
+
reasoningContent = part.content,
|
|
664
|
+
}: {
|
|
665
|
+
part: ReasoningBody;
|
|
666
|
+
providerOptionsName: string;
|
|
667
|
+
reasoningContent?: ReasoningBody['content'];
|
|
668
|
+
}): SharedV4ProviderMetadata {
|
|
669
|
+
return {
|
|
670
|
+
[providerOptionsName]: {
|
|
671
|
+
itemId: part.id,
|
|
672
|
+
reasoningSummary: part.summary.map(summaryPart => ({
|
|
673
|
+
type: 'summary_text',
|
|
674
|
+
text: summaryPart.text,
|
|
675
|
+
})),
|
|
676
|
+
reasoningContent:
|
|
677
|
+
reasoningContent == null
|
|
678
|
+
? null
|
|
679
|
+
: reasoningContent.map(contentPart => ({
|
|
680
|
+
type: 'reasoning_text',
|
|
681
|
+
text: contentPart.text,
|
|
682
|
+
})),
|
|
683
|
+
...(part.encrypted_content != null && {
|
|
684
|
+
reasoningEncryptedContent: part.encrypted_content,
|
|
685
|
+
}),
|
|
686
|
+
},
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
function getOutputTextAnnotations(value: unknown): Annotation[] {
|
|
691
|
+
if (
|
|
692
|
+
value == null ||
|
|
693
|
+
typeof value !== 'object' ||
|
|
694
|
+
!('annotations' in value) ||
|
|
695
|
+
!Array.isArray(value.annotations) ||
|
|
696
|
+
!value.annotations.every(
|
|
697
|
+
annotation =>
|
|
698
|
+
annotation != null &&
|
|
699
|
+
typeof annotation === 'object' &&
|
|
700
|
+
(annotation as { type?: unknown }).type === 'url_citation' &&
|
|
701
|
+
typeof (annotation as { start_index?: unknown }).start_index ===
|
|
702
|
+
'number' &&
|
|
703
|
+
typeof (annotation as { end_index?: unknown }).end_index === 'number' &&
|
|
704
|
+
typeof (annotation as { url?: unknown }).url === 'string' &&
|
|
705
|
+
typeof (annotation as { title?: unknown }).title === 'string',
|
|
706
|
+
)
|
|
707
|
+
) {
|
|
708
|
+
return [];
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return value.annotations.map(annotation => ({
|
|
712
|
+
type: 'url_citation',
|
|
713
|
+
start_index: (annotation as { start_index: number }).start_index,
|
|
714
|
+
end_index: (annotation as { end_index: number }).end_index,
|
|
715
|
+
url: (annotation as { url: string }).url,
|
|
716
|
+
title: (annotation as { title: string }).title,
|
|
717
|
+
}));
|
|
718
|
+
}
|