@ai-sdk/open-responses 0.0.0-6b196531-20260710185421
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 +606 -0
- package/LICENSE +13 -0
- package/README.md +57 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +729 -0
- package/dist/index.js.map +1 -0
- package/docs/06-open-responses.mdx +238 -0
- package/package.json +76 -0
- package/src/index.ts +11 -0
- package/src/open-responses-provider.ts +100 -0
- package/src/responses/convert-to-open-responses-input.ts +253 -0
- package/src/responses/map-open-responses-finish-reason.ts +21 -0
- package/src/responses/open-responses-api.ts +1238 -0
- package/src/responses/open-responses-config.ts +10 -0
- package/src/responses/open-responses-language-model-options.ts +22 -0
- package/src/responses/open-responses-language-model.ts +557 -0
- package/src/version.ts +6 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FetchFunction } from '@ai-sdk/provider-utils';
|
|
2
|
+
|
|
3
|
+
export type OpenResponsesConfig = {
|
|
4
|
+
provider: string;
|
|
5
|
+
providerOptionsName: string;
|
|
6
|
+
url: string;
|
|
7
|
+
headers?: () => Record<string, string | undefined>;
|
|
8
|
+
fetch?: FetchFunction;
|
|
9
|
+
generateId: () => string;
|
|
10
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
lazySchema,
|
|
3
|
+
zodSchema,
|
|
4
|
+
type InferSchema,
|
|
5
|
+
} from '@ai-sdk/provider-utils';
|
|
6
|
+
import { z } from 'zod/v4';
|
|
7
|
+
|
|
8
|
+
export const openResponsesLanguageModelOptions = lazySchema(() =>
|
|
9
|
+
zodSchema(
|
|
10
|
+
z.object({
|
|
11
|
+
/**
|
|
12
|
+
* Controls reasoning summary output from the model.
|
|
13
|
+
* Valid values: 'concise', 'detailed', 'auto'.
|
|
14
|
+
*/
|
|
15
|
+
reasoningSummary: z.enum(['concise', 'detailed', 'auto']).nullish(),
|
|
16
|
+
}),
|
|
17
|
+
),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export type OpenResponsesLanguageModelOptions = InferSchema<
|
|
21
|
+
typeof openResponsesLanguageModelOptions
|
|
22
|
+
>;
|
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV4,
|
|
3
|
+
LanguageModelV4CallOptions,
|
|
4
|
+
LanguageModelV4Content,
|
|
5
|
+
LanguageModelV4FinishReason,
|
|
6
|
+
LanguageModelV4GenerateResult,
|
|
7
|
+
LanguageModelV4StreamPart,
|
|
8
|
+
LanguageModelV4StreamResult,
|
|
9
|
+
LanguageModelV4Usage,
|
|
10
|
+
SharedV4Warning,
|
|
11
|
+
} from '@ai-sdk/provider';
|
|
12
|
+
import {
|
|
13
|
+
combineHeaders,
|
|
14
|
+
createEventSourceResponseHandler,
|
|
15
|
+
createJsonErrorResponseHandler,
|
|
16
|
+
createJsonResponseHandler,
|
|
17
|
+
isCustomReasoning,
|
|
18
|
+
jsonSchema,
|
|
19
|
+
mapReasoningToProviderEffort,
|
|
20
|
+
parseProviderOptions,
|
|
21
|
+
postJsonToApi,
|
|
22
|
+
serializeModelOptions,
|
|
23
|
+
WORKFLOW_SERIALIZE,
|
|
24
|
+
WORKFLOW_DESERIALIZE,
|
|
25
|
+
type ParseResult,
|
|
26
|
+
} from '@ai-sdk/provider-utils';
|
|
27
|
+
import { z } from 'zod/v4';
|
|
28
|
+
import { convertToOpenResponsesInput } from './convert-to-open-responses-input';
|
|
29
|
+
import {
|
|
30
|
+
openResponsesErrorSchema,
|
|
31
|
+
type FunctionToolParam,
|
|
32
|
+
type OpenResponsesRequestBody,
|
|
33
|
+
type OpenResponsesResponseBody,
|
|
34
|
+
type OpenResponsesChunk,
|
|
35
|
+
type ToolChoiceParam,
|
|
36
|
+
} from './open-responses-api';
|
|
37
|
+
import { mapOpenResponsesFinishReason } from './map-open-responses-finish-reason';
|
|
38
|
+
import type { OpenResponsesConfig } from './open-responses-config';
|
|
39
|
+
import { openResponsesLanguageModelOptions } from './open-responses-language-model-options';
|
|
40
|
+
|
|
41
|
+
export class OpenResponsesLanguageModel implements LanguageModelV4 {
|
|
42
|
+
readonly specificationVersion = 'v4';
|
|
43
|
+
|
|
44
|
+
readonly modelId: string;
|
|
45
|
+
|
|
46
|
+
private readonly config: OpenResponsesConfig;
|
|
47
|
+
|
|
48
|
+
static [WORKFLOW_SERIALIZE](model: OpenResponsesLanguageModel) {
|
|
49
|
+
return serializeModelOptions({
|
|
50
|
+
modelId: model.modelId,
|
|
51
|
+
config: model.config,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static [WORKFLOW_DESERIALIZE](options: {
|
|
56
|
+
modelId: string;
|
|
57
|
+
config: OpenResponsesConfig;
|
|
58
|
+
}) {
|
|
59
|
+
return new OpenResponsesLanguageModel(options.modelId, options.config);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
constructor(modelId: string, config: OpenResponsesConfig) {
|
|
63
|
+
this.modelId = modelId;
|
|
64
|
+
this.config = config;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
readonly supportedUrls: Record<string, RegExp[]> = {
|
|
68
|
+
'image/*': [/^https?:\/\/.*$/],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
get provider(): string {
|
|
72
|
+
return this.config.provider;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private async getArgs({
|
|
76
|
+
maxOutputTokens,
|
|
77
|
+
temperature,
|
|
78
|
+
stopSequences,
|
|
79
|
+
topP,
|
|
80
|
+
topK,
|
|
81
|
+
presencePenalty,
|
|
82
|
+
frequencyPenalty,
|
|
83
|
+
seed,
|
|
84
|
+
reasoning,
|
|
85
|
+
prompt,
|
|
86
|
+
providerOptions,
|
|
87
|
+
tools,
|
|
88
|
+
toolChoice,
|
|
89
|
+
responseFormat,
|
|
90
|
+
}: LanguageModelV4CallOptions): Promise<{
|
|
91
|
+
body: Omit<OpenResponsesRequestBody, 'stream' | 'stream_options'>;
|
|
92
|
+
warnings: SharedV4Warning[];
|
|
93
|
+
}> {
|
|
94
|
+
const warnings: SharedV4Warning[] = [];
|
|
95
|
+
|
|
96
|
+
if (stopSequences != null) {
|
|
97
|
+
warnings.push({ type: 'unsupported', feature: 'stopSequences' });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (topK != null) {
|
|
101
|
+
warnings.push({ type: 'unsupported', feature: 'topK' });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (seed != null) {
|
|
105
|
+
warnings.push({ type: 'unsupported', feature: 'seed' });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const {
|
|
109
|
+
input,
|
|
110
|
+
instructions,
|
|
111
|
+
warnings: inputWarnings,
|
|
112
|
+
} = await convertToOpenResponsesInput({
|
|
113
|
+
prompt,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
warnings.push(...inputWarnings);
|
|
117
|
+
|
|
118
|
+
// Convert function tools to the Open Responses format
|
|
119
|
+
const functionTools: FunctionToolParam[] | undefined = tools
|
|
120
|
+
?.filter(tool => tool.type === 'function')
|
|
121
|
+
.map(tool => ({
|
|
122
|
+
type: 'function' as const,
|
|
123
|
+
name: tool.name,
|
|
124
|
+
description: tool.description,
|
|
125
|
+
parameters: tool.inputSchema,
|
|
126
|
+
...(tool.strict != null ? { strict: tool.strict } : {}),
|
|
127
|
+
}));
|
|
128
|
+
|
|
129
|
+
// Convert tool choice to the Open Responses format
|
|
130
|
+
const convertedToolChoice: ToolChoiceParam | undefined =
|
|
131
|
+
toolChoice == null
|
|
132
|
+
? undefined
|
|
133
|
+
: toolChoice.type === 'tool'
|
|
134
|
+
? { type: 'function', name: toolChoice.toolName }
|
|
135
|
+
: toolChoice.type; // 'auto' | 'none' | 'required'
|
|
136
|
+
|
|
137
|
+
const textFormat =
|
|
138
|
+
responseFormat?.type === 'json'
|
|
139
|
+
? {
|
|
140
|
+
type: 'json_schema' as const,
|
|
141
|
+
...(responseFormat.schema != null
|
|
142
|
+
? {
|
|
143
|
+
name: responseFormat.name ?? 'response',
|
|
144
|
+
description: responseFormat.description,
|
|
145
|
+
schema: responseFormat.schema,
|
|
146
|
+
strict: true,
|
|
147
|
+
}
|
|
148
|
+
: {}),
|
|
149
|
+
}
|
|
150
|
+
: undefined;
|
|
151
|
+
|
|
152
|
+
const openResponsesOptions = await parseProviderOptions({
|
|
153
|
+
provider: this.config.providerOptionsName,
|
|
154
|
+
providerOptions,
|
|
155
|
+
schema: openResponsesLanguageModelOptions,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const resolvedReasoningEffort = isCustomReasoning(reasoning)
|
|
159
|
+
? reasoning === 'none'
|
|
160
|
+
? 'none'
|
|
161
|
+
: mapReasoningToProviderEffort({
|
|
162
|
+
reasoning,
|
|
163
|
+
effortMap: {
|
|
164
|
+
minimal: 'low',
|
|
165
|
+
low: 'low',
|
|
166
|
+
medium: 'medium',
|
|
167
|
+
high: 'high',
|
|
168
|
+
xhigh: 'xhigh',
|
|
169
|
+
},
|
|
170
|
+
warnings,
|
|
171
|
+
})
|
|
172
|
+
: undefined;
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
body: {
|
|
176
|
+
model: this.modelId,
|
|
177
|
+
input,
|
|
178
|
+
instructions,
|
|
179
|
+
max_output_tokens: maxOutputTokens,
|
|
180
|
+
temperature,
|
|
181
|
+
top_p: topP,
|
|
182
|
+
presence_penalty: presencePenalty,
|
|
183
|
+
frequency_penalty: frequencyPenalty,
|
|
184
|
+
reasoning:
|
|
185
|
+
resolvedReasoningEffort != null ||
|
|
186
|
+
openResponsesOptions?.reasoningSummary != null
|
|
187
|
+
? {
|
|
188
|
+
...(resolvedReasoningEffort != null && {
|
|
189
|
+
effort: resolvedReasoningEffort,
|
|
190
|
+
}),
|
|
191
|
+
...(openResponsesOptions?.reasoningSummary != null && {
|
|
192
|
+
summary: openResponsesOptions.reasoningSummary,
|
|
193
|
+
}),
|
|
194
|
+
}
|
|
195
|
+
: undefined,
|
|
196
|
+
tools: functionTools?.length ? functionTools : undefined,
|
|
197
|
+
tool_choice: convertedToolChoice,
|
|
198
|
+
...(textFormat != null && { text: { format: textFormat } }),
|
|
199
|
+
},
|
|
200
|
+
warnings,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async doGenerate(
|
|
205
|
+
options: LanguageModelV4CallOptions,
|
|
206
|
+
): Promise<LanguageModelV4GenerateResult> {
|
|
207
|
+
const { body, warnings } = await this.getArgs(options);
|
|
208
|
+
|
|
209
|
+
const {
|
|
210
|
+
responseHeaders,
|
|
211
|
+
value: response,
|
|
212
|
+
rawValue: rawResponse,
|
|
213
|
+
} = await postJsonToApi({
|
|
214
|
+
url: this.config.url,
|
|
215
|
+
headers: combineHeaders(this.config.headers?.(), options.headers),
|
|
216
|
+
body,
|
|
217
|
+
failedResponseHandler: createJsonErrorResponseHandler({
|
|
218
|
+
errorSchema: openResponsesErrorSchema,
|
|
219
|
+
errorToMessage: error => error.error.message,
|
|
220
|
+
}),
|
|
221
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
222
|
+
// do not validate the response body, only apply types to the response body
|
|
223
|
+
jsonSchema<OpenResponsesResponseBody>(() => {
|
|
224
|
+
throw new Error('json schema not implemented');
|
|
225
|
+
}),
|
|
226
|
+
),
|
|
227
|
+
abortSignal: options.abortSignal,
|
|
228
|
+
fetch: this.config.fetch,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const content: Array<LanguageModelV4Content> = [];
|
|
232
|
+
let hasToolCalls = false;
|
|
233
|
+
|
|
234
|
+
for (const part of response.output!) {
|
|
235
|
+
switch (part.type) {
|
|
236
|
+
// TODO AI SDK 7 adjust reasoning in the specification to better support the reasoning structure from open responses.
|
|
237
|
+
case 'reasoning': {
|
|
238
|
+
for (const contentPart of part.content ?? []) {
|
|
239
|
+
content.push({
|
|
240
|
+
type: 'reasoning',
|
|
241
|
+
text: contentPart.text,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
case 'message': {
|
|
248
|
+
for (const contentPart of part.content) {
|
|
249
|
+
content.push({
|
|
250
|
+
type: 'text',
|
|
251
|
+
text: contentPart.text,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
case 'function_call': {
|
|
259
|
+
hasToolCalls = true;
|
|
260
|
+
content.push({
|
|
261
|
+
type: 'tool-call',
|
|
262
|
+
toolCallId: part.call_id,
|
|
263
|
+
toolName: part.name,
|
|
264
|
+
input: part.arguments,
|
|
265
|
+
});
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const usage = response.usage;
|
|
272
|
+
const inputTokens = usage?.input_tokens;
|
|
273
|
+
const cachedInputTokens = usage?.input_tokens_details?.cached_tokens;
|
|
274
|
+
const outputTokens = usage?.output_tokens;
|
|
275
|
+
const reasoningTokens = usage?.output_tokens_details?.reasoning_tokens;
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
content,
|
|
279
|
+
finishReason: {
|
|
280
|
+
unified: mapOpenResponsesFinishReason({
|
|
281
|
+
finishReason: response.incomplete_details?.reason,
|
|
282
|
+
hasToolCalls,
|
|
283
|
+
}),
|
|
284
|
+
raw: response.incomplete_details?.reason ?? undefined,
|
|
285
|
+
},
|
|
286
|
+
usage: {
|
|
287
|
+
inputTokens: {
|
|
288
|
+
total: inputTokens,
|
|
289
|
+
noCache: (inputTokens ?? 0) - (cachedInputTokens ?? 0),
|
|
290
|
+
cacheRead: cachedInputTokens,
|
|
291
|
+
cacheWrite: undefined,
|
|
292
|
+
},
|
|
293
|
+
outputTokens: {
|
|
294
|
+
total: outputTokens,
|
|
295
|
+
text: (outputTokens ?? 0) - (reasoningTokens ?? 0),
|
|
296
|
+
reasoning: reasoningTokens,
|
|
297
|
+
},
|
|
298
|
+
raw: response.usage,
|
|
299
|
+
},
|
|
300
|
+
request: { body },
|
|
301
|
+
response: {
|
|
302
|
+
id: response.id,
|
|
303
|
+
timestamp: new Date(response.created_at! * 1000),
|
|
304
|
+
modelId: response.model,
|
|
305
|
+
headers: responseHeaders,
|
|
306
|
+
body: rawResponse,
|
|
307
|
+
},
|
|
308
|
+
providerMetadata: undefined,
|
|
309
|
+
warnings,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async doStream(
|
|
314
|
+
options: LanguageModelV4CallOptions,
|
|
315
|
+
): Promise<LanguageModelV4StreamResult> {
|
|
316
|
+
const { body, warnings } = await this.getArgs(options);
|
|
317
|
+
|
|
318
|
+
const { responseHeaders, value: response } = await postJsonToApi({
|
|
319
|
+
url: this.config.url,
|
|
320
|
+
headers: combineHeaders(this.config.headers?.(), options.headers),
|
|
321
|
+
body: {
|
|
322
|
+
...body,
|
|
323
|
+
stream: true,
|
|
324
|
+
} satisfies OpenResponsesRequestBody,
|
|
325
|
+
failedResponseHandler: createJsonErrorResponseHandler({
|
|
326
|
+
errorSchema: openResponsesErrorSchema,
|
|
327
|
+
errorToMessage: error => error.error.message,
|
|
328
|
+
}),
|
|
329
|
+
successfulResponseHandler: createEventSourceResponseHandler(z.any()),
|
|
330
|
+
abortSignal: options.abortSignal,
|
|
331
|
+
fetch: this.config.fetch,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const usage: LanguageModelV4Usage = {
|
|
335
|
+
inputTokens: {
|
|
336
|
+
total: undefined,
|
|
337
|
+
noCache: undefined,
|
|
338
|
+
cacheRead: undefined,
|
|
339
|
+
cacheWrite: undefined,
|
|
340
|
+
},
|
|
341
|
+
outputTokens: {
|
|
342
|
+
total: undefined,
|
|
343
|
+
text: undefined,
|
|
344
|
+
reasoning: undefined,
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const updateUsage = (
|
|
349
|
+
responseUsage?: OpenResponsesResponseBody['usage'],
|
|
350
|
+
) => {
|
|
351
|
+
if (!responseUsage) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const inputTokens = responseUsage.input_tokens;
|
|
356
|
+
const cachedInputTokens =
|
|
357
|
+
responseUsage.input_tokens_details?.cached_tokens;
|
|
358
|
+
const outputTokens = responseUsage.output_tokens;
|
|
359
|
+
const reasoningTokens =
|
|
360
|
+
responseUsage.output_tokens_details?.reasoning_tokens;
|
|
361
|
+
|
|
362
|
+
usage.inputTokens = {
|
|
363
|
+
total: inputTokens,
|
|
364
|
+
noCache: (inputTokens ?? 0) - (cachedInputTokens ?? 0),
|
|
365
|
+
cacheRead: cachedInputTokens,
|
|
366
|
+
cacheWrite: undefined,
|
|
367
|
+
};
|
|
368
|
+
usage.outputTokens = {
|
|
369
|
+
total: outputTokens,
|
|
370
|
+
text: (outputTokens ?? 0) - (reasoningTokens ?? 0),
|
|
371
|
+
reasoning: reasoningTokens,
|
|
372
|
+
};
|
|
373
|
+
usage.raw = responseUsage;
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
let isActiveReasoning = false;
|
|
377
|
+
let hasToolCalls = false;
|
|
378
|
+
let finishReason: LanguageModelV4FinishReason = {
|
|
379
|
+
unified: 'other',
|
|
380
|
+
raw: undefined,
|
|
381
|
+
};
|
|
382
|
+
const toolCallsByItemId = new Map<
|
|
383
|
+
string,
|
|
384
|
+
{ toolName?: string; toolCallId?: string; arguments?: string }
|
|
385
|
+
>();
|
|
386
|
+
|
|
387
|
+
return {
|
|
388
|
+
stream: response.pipeThrough(
|
|
389
|
+
new TransformStream<
|
|
390
|
+
ParseResult<OpenResponsesChunk>,
|
|
391
|
+
LanguageModelV4StreamPart
|
|
392
|
+
>({
|
|
393
|
+
start(controller) {
|
|
394
|
+
controller.enqueue({ type: 'stream-start', warnings });
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
transform(parseResult, controller) {
|
|
398
|
+
if (options.includeRawChunks) {
|
|
399
|
+
controller.enqueue({
|
|
400
|
+
type: 'raw',
|
|
401
|
+
rawValue: parseResult.rawValue,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (!parseResult.success) {
|
|
406
|
+
controller.enqueue({ type: 'error', error: parseResult.error });
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const chunk = parseResult.value;
|
|
411
|
+
|
|
412
|
+
// Tool call events (single-shot tool-call when complete)
|
|
413
|
+
if (
|
|
414
|
+
chunk.type === 'response.output_item.added' &&
|
|
415
|
+
chunk.item.type === 'function_call'
|
|
416
|
+
) {
|
|
417
|
+
toolCallsByItemId.set(chunk.item.id, {
|
|
418
|
+
toolName: chunk.item.name,
|
|
419
|
+
toolCallId: chunk.item.call_id,
|
|
420
|
+
arguments: chunk.item.arguments,
|
|
421
|
+
});
|
|
422
|
+
} else if (
|
|
423
|
+
chunk.type === 'response.function_call_arguments.delta'
|
|
424
|
+
) {
|
|
425
|
+
const functionCallChunk = chunk;
|
|
426
|
+
const toolCall = toolCallsByItemId.get(functionCallChunk.item_id);
|
|
427
|
+
|
|
428
|
+
if (toolCall == null) {
|
|
429
|
+
toolCallsByItemId.set(functionCallChunk.item_id, {
|
|
430
|
+
arguments: functionCallChunk.delta,
|
|
431
|
+
});
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
toolCall.arguments =
|
|
436
|
+
(toolCall.arguments ?? '') + functionCallChunk.delta;
|
|
437
|
+
} else if (chunk.type === 'response.function_call_arguments.done') {
|
|
438
|
+
const functionCallChunk = chunk;
|
|
439
|
+
const toolCall = toolCallsByItemId.get(functionCallChunk.item_id);
|
|
440
|
+
|
|
441
|
+
if (toolCall == null) {
|
|
442
|
+
toolCallsByItemId.set(functionCallChunk.item_id, {
|
|
443
|
+
arguments: functionCallChunk.arguments,
|
|
444
|
+
});
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
toolCall.arguments = functionCallChunk.arguments;
|
|
449
|
+
} else if (
|
|
450
|
+
chunk.type === 'response.output_item.done' &&
|
|
451
|
+
chunk.item.type === 'function_call'
|
|
452
|
+
) {
|
|
453
|
+
const toolCall = toolCallsByItemId.get(chunk.item.id);
|
|
454
|
+
const toolName = toolCall?.toolName ?? chunk.item.name;
|
|
455
|
+
const toolCallId = toolCall?.toolCallId ?? chunk.item.call_id;
|
|
456
|
+
const input = toolCall?.arguments ?? chunk.item.arguments ?? '';
|
|
457
|
+
|
|
458
|
+
controller.enqueue({
|
|
459
|
+
type: 'tool-call',
|
|
460
|
+
toolCallId,
|
|
461
|
+
toolName,
|
|
462
|
+
input,
|
|
463
|
+
});
|
|
464
|
+
hasToolCalls = true;
|
|
465
|
+
|
|
466
|
+
toolCallsByItemId.delete(chunk.item.id);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Reasoning events (note: response.reasoning_text.delta is an LM Studio extension, not in official spec)
|
|
470
|
+
else if (
|
|
471
|
+
chunk.type === 'response.output_item.added' &&
|
|
472
|
+
chunk.item.type === 'reasoning'
|
|
473
|
+
) {
|
|
474
|
+
controller.enqueue({
|
|
475
|
+
type: 'reasoning-start',
|
|
476
|
+
id: chunk.item.id,
|
|
477
|
+
});
|
|
478
|
+
isActiveReasoning = true;
|
|
479
|
+
} else if (
|
|
480
|
+
(chunk as { type: string }).type ===
|
|
481
|
+
'response.reasoning_text.delta'
|
|
482
|
+
) {
|
|
483
|
+
const reasoningChunk = chunk as {
|
|
484
|
+
item_id: string;
|
|
485
|
+
delta: string;
|
|
486
|
+
};
|
|
487
|
+
controller.enqueue({
|
|
488
|
+
type: 'reasoning-delta',
|
|
489
|
+
id: reasoningChunk.item_id,
|
|
490
|
+
delta: reasoningChunk.delta,
|
|
491
|
+
});
|
|
492
|
+
} else if (
|
|
493
|
+
chunk.type === 'response.output_item.done' &&
|
|
494
|
+
chunk.item.type === 'reasoning'
|
|
495
|
+
) {
|
|
496
|
+
controller.enqueue({ type: 'reasoning-end', id: chunk.item.id });
|
|
497
|
+
isActiveReasoning = false;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Text events
|
|
501
|
+
else if (
|
|
502
|
+
chunk.type === 'response.output_item.added' &&
|
|
503
|
+
chunk.item.type === 'message'
|
|
504
|
+
) {
|
|
505
|
+
controller.enqueue({ type: 'text-start', id: chunk.item.id });
|
|
506
|
+
} else if (chunk.type === 'response.output_text.delta') {
|
|
507
|
+
controller.enqueue({
|
|
508
|
+
type: 'text-delta',
|
|
509
|
+
id: chunk.item_id,
|
|
510
|
+
delta: chunk.delta,
|
|
511
|
+
});
|
|
512
|
+
} else if (
|
|
513
|
+
chunk.type === 'response.output_item.done' &&
|
|
514
|
+
chunk.item.type === 'message'
|
|
515
|
+
) {
|
|
516
|
+
controller.enqueue({ type: 'text-end', id: chunk.item.id });
|
|
517
|
+
} else if (
|
|
518
|
+
chunk.type === 'response.completed' ||
|
|
519
|
+
chunk.type === 'response.incomplete'
|
|
520
|
+
) {
|
|
521
|
+
const reason = chunk.response.incomplete_details?.reason;
|
|
522
|
+
finishReason = {
|
|
523
|
+
unified: mapOpenResponsesFinishReason({
|
|
524
|
+
finishReason: reason,
|
|
525
|
+
hasToolCalls,
|
|
526
|
+
}),
|
|
527
|
+
raw: reason ?? undefined,
|
|
528
|
+
};
|
|
529
|
+
updateUsage(chunk.response.usage);
|
|
530
|
+
} else if (chunk.type === 'response.failed') {
|
|
531
|
+
finishReason = {
|
|
532
|
+
unified: 'error',
|
|
533
|
+
raw: chunk.response.error?.code ?? chunk.response.status,
|
|
534
|
+
};
|
|
535
|
+
updateUsage(chunk.response.usage);
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
|
|
539
|
+
flush(controller) {
|
|
540
|
+
if (isActiveReasoning) {
|
|
541
|
+
controller.enqueue({ type: 'reasoning-end', id: 'reasoning-0' });
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
controller.enqueue({
|
|
545
|
+
type: 'finish',
|
|
546
|
+
finishReason,
|
|
547
|
+
usage,
|
|
548
|
+
providerMetadata: undefined,
|
|
549
|
+
});
|
|
550
|
+
},
|
|
551
|
+
}),
|
|
552
|
+
),
|
|
553
|
+
request: { body },
|
|
554
|
+
response: { headers: responseHeaders },
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
}
|