@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 +17 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +259 -47
- package/dist/index.js.map +1 -1
- package/docs/06-open-responses.mdx +14 -4
- package/package.json +3 -3
- package/src/responses/convert-to-open-responses-input.ts +206 -18
- package/src/responses/open-responses-api.ts +2 -2
- package/src/responses/open-responses-language-model-options.ts +6 -0
- package/src/responses/open-responses-language-model.ts +167 -34
|
@@ -9,20 +9,22 @@ 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,
|
|
16
15
|
InputTextContentParam,
|
|
17
16
|
OpenResponsesRequestBody,
|
|
18
17
|
OutputTextContentParam,
|
|
18
|
+
ReasoningItemParam,
|
|
19
19
|
RefusalContentParam,
|
|
20
20
|
} from './open-responses-api';
|
|
21
21
|
|
|
22
22
|
export async function convertToOpenResponsesInput({
|
|
23
23
|
prompt,
|
|
24
|
+
providerOptionsName = 'open-responses',
|
|
24
25
|
}: {
|
|
25
26
|
prompt: LanguageModelV4Prompt;
|
|
27
|
+
providerOptionsName?: string;
|
|
26
28
|
}): Promise<{
|
|
27
29
|
input: OpenResponsesRequestBody['input'];
|
|
28
30
|
instructions: string | undefined;
|
|
@@ -102,24 +104,128 @@ export async function convertToOpenResponsesInput({
|
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
case 'assistant': {
|
|
105
|
-
|
|
107
|
+
let assistantContent: Array<
|
|
106
108
|
OutputTextContentParam | RefusalContentParam
|
|
107
109
|
> = [];
|
|
108
|
-
|
|
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
|
+
};
|
|
109
126
|
|
|
110
127
|
for (const part of content) {
|
|
111
128
|
switch (part.type) {
|
|
129
|
+
case 'reasoning': {
|
|
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 = {
|
|
151
|
+
type: 'reasoning',
|
|
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
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
112
188
|
case 'text': {
|
|
113
|
-
|
|
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
|
+
});
|
|
114
211
|
break;
|
|
115
212
|
}
|
|
116
213
|
case 'tool-call': {
|
|
214
|
+
flushAssistantContent();
|
|
215
|
+
|
|
117
216
|
const argumentsValue =
|
|
118
217
|
typeof part.input === 'string'
|
|
119
218
|
? part.input
|
|
120
219
|
: JSON.stringify(part.input);
|
|
121
|
-
|
|
220
|
+
const providerData = getProviderData(part, providerOptionsName);
|
|
221
|
+
const itemId =
|
|
222
|
+
typeof providerData?.itemId === 'string'
|
|
223
|
+
? providerData.itemId
|
|
224
|
+
: undefined;
|
|
225
|
+
|
|
226
|
+
input.push({
|
|
122
227
|
type: 'function_call',
|
|
228
|
+
...(itemId != null && { id: itemId }),
|
|
123
229
|
call_id: part.toolCallId,
|
|
124
230
|
name: part.toolName,
|
|
125
231
|
arguments: argumentsValue,
|
|
@@ -129,19 +235,7 @@ export async function convertToOpenResponsesInput({
|
|
|
129
235
|
}
|
|
130
236
|
}
|
|
131
237
|
|
|
132
|
-
|
|
133
|
-
if (assistantContent.length > 0) {
|
|
134
|
-
input.push({
|
|
135
|
-
type: 'message',
|
|
136
|
-
role: 'assistant',
|
|
137
|
-
content: assistantContent,
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Push function calls as separate items
|
|
142
|
-
for (const toolCall of toolCalls) {
|
|
143
|
-
input.push(toolCall);
|
|
144
|
-
}
|
|
238
|
+
flushAssistantContent();
|
|
145
239
|
|
|
146
240
|
break;
|
|
147
241
|
}
|
|
@@ -251,3 +345,97 @@ export async function convertToOpenResponsesInput({
|
|
|
251
345
|
warnings,
|
|
252
346
|
};
|
|
253
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
|
+
}
|
|
@@ -143,7 +143,7 @@ export type ReasoningItemParam = {
|
|
|
143
143
|
id?: string;
|
|
144
144
|
type: 'reasoning';
|
|
145
145
|
summary: ReasoningSummaryContentParam[];
|
|
146
|
-
content?:
|
|
146
|
+
content?: ReasoningTextContent[];
|
|
147
147
|
encrypted_content?: string;
|
|
148
148
|
};
|
|
149
149
|
|
|
@@ -311,7 +311,7 @@ export type StreamOptionsParam = {
|
|
|
311
311
|
* Configuration options for reasoning behavior.
|
|
312
312
|
*/
|
|
313
313
|
export type ReasoningParam = {
|
|
314
|
-
effort?:
|
|
314
|
+
effort?: string;
|
|
315
315
|
summary?: ReasoningSummaryEnum;
|
|
316
316
|
};
|
|
317
317
|
|
|
@@ -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
|
+
}
|