@galaxy-yearn/codex-deepseek-gateway 0.1.0

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.
@@ -0,0 +1,1740 @@
1
+ import { generateId, isObject, normalizeRole, toText } from './common.js';
2
+ import { deepseekReasoningPayload, resolveModelAlias } from './model-map.js';
3
+
4
+ const TEXT_PART_TYPES = new Set(['input_text', 'output_text', 'text']);
5
+ const INPUT_CONTENT_PART_TYPES = new Set(['input_text', 'input_image', 'input_file', 'input_audio']);
6
+ const TOOL_CALL_TYPES = new Set([
7
+ 'function_call',
8
+ 'custom_tool_call',
9
+ 'local_shell_call',
10
+ 'computer_call',
11
+ 'mcp_call',
12
+ 'web_search_call',
13
+ 'file_search_call',
14
+ 'code_interpreter_call',
15
+ 'image_generation_call',
16
+ ]);
17
+ const TOOL_OUTPUT_TYPES = new Set([
18
+ 'function_call_output',
19
+ 'custom_tool_call_output',
20
+ 'local_shell_call_output',
21
+ 'computer_call_output',
22
+ 'mcp_call_output',
23
+ 'web_search_call_output',
24
+ 'file_search_call_output',
25
+ 'code_interpreter_call_output',
26
+ 'image_generation_call_output',
27
+ ]);
28
+
29
+ function jsonString(value) {
30
+ if (typeof value === 'string') return value;
31
+ return JSON.stringify(value ?? {});
32
+ }
33
+
34
+ function sanitizeFunctionName(name, fallback = 'tool_call') {
35
+ const candidate = String(name || fallback)
36
+ .replace(/[^a-zA-Z0-9_-]/g, '_')
37
+ .replace(/^_+/, '')
38
+ .slice(0, 64);
39
+ return candidate || fallback;
40
+ }
41
+
42
+ function omitUndefined(value) {
43
+ if (!isObject(value)) return value;
44
+ const result = {};
45
+ for (const [key, child] of Object.entries(value)) {
46
+ if (child !== undefined) result[key] = child;
47
+ }
48
+ return result;
49
+ }
50
+
51
+ function textPart(text) {
52
+ return { type: 'text', text: String(text ?? '') };
53
+ }
54
+
55
+ function normalizeImageUrl(value, source = {}) {
56
+ const imageUrl = {};
57
+ if (typeof value === 'string') {
58
+ imageUrl.url = value;
59
+ } else if (isObject(value)) {
60
+ Object.assign(imageUrl, value);
61
+ }
62
+ if (source.url !== undefined && imageUrl.url === undefined) imageUrl.url = source.url;
63
+ if (source.image_url !== undefined && imageUrl.url === undefined && typeof source.image_url === 'string') {
64
+ imageUrl.url = source.image_url;
65
+ }
66
+ if (source.file_id !== undefined && imageUrl.file_id === undefined) imageUrl.file_id = source.file_id;
67
+ if (source.detail !== undefined && imageUrl.detail === undefined) imageUrl.detail = source.detail;
68
+ return Object.keys(imageUrl).length ? imageUrl : null;
69
+ }
70
+
71
+ function normalizeFilePart(value, source = {}) {
72
+ const file = isObject(value) ? { ...value } : {};
73
+ for (const key of ['file_id', 'file_data', 'file_url', 'filename', 'mime_type']) {
74
+ if (source[key] !== undefined && file[key] === undefined) file[key] = source[key];
75
+ }
76
+ if (source.url !== undefined && file.file_url === undefined) file.file_url = source.url;
77
+ return Object.keys(file).length ? file : null;
78
+ }
79
+
80
+ function normalizeAudioPart(value, source = {}) {
81
+ const inputAudio = isObject(value) ? { ...value } : {};
82
+ for (const key of ['data', 'format', 'file_id', 'mime_type']) {
83
+ if (source[key] !== undefined && inputAudio[key] === undefined) inputAudio[key] = source[key];
84
+ }
85
+ return Object.keys(inputAudio).length ? inputAudio : null;
86
+ }
87
+
88
+ function contentPartToChatPart(part) {
89
+ if (typeof part === 'string') return textPart(part);
90
+ if (!isObject(part)) return null;
91
+
92
+ if (TEXT_PART_TYPES.has(part.type) || (part.type === undefined && typeof part.text === 'string')) {
93
+ return textPart(part.text ?? part.content ?? '');
94
+ }
95
+
96
+ if (part.type === 'input_image' || part.type === 'output_image' || part.type === 'image_url') {
97
+ const imageUrl = normalizeImageUrl(part.image_url, part);
98
+ return imageUrl ? { type: 'image_url', image_url: imageUrl } : null;
99
+ }
100
+
101
+ if (part.type === 'input_file' || part.type === 'output_file' || part.type === 'file') {
102
+ const file = normalizeFilePart(part.file, part);
103
+ return file ? { type: 'file', file } : null;
104
+ }
105
+
106
+ if (part.type === 'input_audio') {
107
+ const inputAudio = normalizeAudioPart(part.input_audio, part);
108
+ return inputAudio ? { type: 'input_audio', input_audio: inputAudio } : null;
109
+ }
110
+
111
+ if (part.type === 'refusal') {
112
+ return { type: 'text', text: String(part.refusal ?? part.text ?? '') };
113
+ }
114
+
115
+ return null;
116
+ }
117
+
118
+ function contentToChatContent(content) {
119
+ if (content == null) return '';
120
+ if (typeof content === 'string') return content;
121
+ const parts = Array.isArray(content) ? content.map(contentPartToChatPart).filter(Boolean) : [contentPartToChatPart(content)].filter(Boolean);
122
+ if (!parts.length) return toText(content);
123
+ if (parts.every((part) => part.type === 'text')) {
124
+ return parts.map((part) => part.text).join('');
125
+ }
126
+ return parts;
127
+ }
128
+
129
+ function isInputContentPart(item) {
130
+ return isObject(item) && INPUT_CONTENT_PART_TYPES.has(item.type);
131
+ }
132
+
133
+ function toolCallId(item) {
134
+ return item.call_id || item.callId || item.id || generateId('call');
135
+ }
136
+
137
+ function toolName(item) {
138
+ return sanitizeFunctionName(
139
+ item.name ||
140
+ item.tool_name ||
141
+ item.toolName ||
142
+ item.server_label ||
143
+ String(item.type || 'tool_call').replace(/_call$/, ''),
144
+ );
145
+ }
146
+
147
+ function toolArguments(item) {
148
+ if (item.type === 'function_call') return jsonString(item.arguments ?? {});
149
+ if (item.arguments !== undefined) return jsonString(item.arguments);
150
+ if (item.input !== undefined) return jsonString({ input: item.input });
151
+ if (item.action !== undefined) return jsonString({ action: item.action });
152
+ if (item.query !== undefined) return jsonString({ query: item.query });
153
+ if (item.code !== undefined) return jsonString({ code: item.code });
154
+ if (item.command !== undefined) return jsonString({ command: item.command });
155
+ return jsonString({ type: item.type });
156
+ }
157
+
158
+ function chatToolCallFromResponseItem(item) {
159
+ return {
160
+ id: toolCallId(item),
161
+ type: 'function',
162
+ function: {
163
+ name: toolName(item),
164
+ arguments: toolArguments(item),
165
+ },
166
+ };
167
+ }
168
+
169
+ function reasoningTextFromItem(item) {
170
+ if (!isObject(item)) return '';
171
+ const parts = [];
172
+ if (typeof item.reasoning_content === 'string') parts.push(item.reasoning_content);
173
+ if (typeof item.text === 'string') parts.push(item.text);
174
+ if (typeof item.content === 'string') parts.push(item.content);
175
+ if (Array.isArray(item.content)) {
176
+ for (const part of item.content) {
177
+ if (typeof part === 'string') {
178
+ parts.push(part);
179
+ continue;
180
+ }
181
+ if (!isObject(part)) continue;
182
+ if (part.type === 'reasoning_text' || part.type === 'text' || part.type === 'output_text') {
183
+ parts.push(String(part.text ?? part.content ?? ''));
184
+ }
185
+ }
186
+ }
187
+ if (Array.isArray(item.summary)) {
188
+ for (const part of item.summary) {
189
+ if (typeof part === 'string') {
190
+ parts.push(part);
191
+ continue;
192
+ }
193
+ if (!isObject(part)) continue;
194
+ if (part.type === 'summary_text' || part.type === 'text') {
195
+ parts.push(String(part.text ?? part.content ?? ''));
196
+ }
197
+ }
198
+ }
199
+ return parts.filter(Boolean).join('');
200
+ }
201
+
202
+ function toolOutputContent(item) {
203
+ const output = item.output ?? item.content ?? item.result ?? item.error ?? '';
204
+ if (typeof output === 'string') return output;
205
+ if (Array.isArray(output)) return contentToChatContent(output);
206
+ if (isObject(output) && (output.type || output.content)) return contentToChatContent(output.content ?? output);
207
+ return JSON.stringify(output ?? '');
208
+ }
209
+
210
+ function responseToolOutputToMessage(item) {
211
+ return {
212
+ role: 'tool',
213
+ content: toolOutputContent(item),
214
+ tool_call_id: toolCallId(item),
215
+ };
216
+ }
217
+
218
+ function normalizeMessage(message) {
219
+ if (!isObject(message)) return null;
220
+ const role = message.role || 'user';
221
+ const content = contentToChatContent(message.content);
222
+ const normalized = {
223
+ role,
224
+ content,
225
+ tool_calls: Array.isArray(message.tool_calls) ? message.tool_calls : undefined,
226
+ tool_call_id: message.tool_call_id,
227
+ name: message.name,
228
+ };
229
+ if (typeof message.reasoning_content === 'string') normalized.reasoning_content = message.reasoning_content;
230
+ return normalized;
231
+ }
232
+
233
+ function extractMessagesFromResponsesInput(input) {
234
+ if (Array.isArray(input)) {
235
+ const messages = [];
236
+ let pendingUserContent = [];
237
+ let pendingReasoningContent = '';
238
+ let pendingAssistantToolMessage = null;
239
+ const flushPendingUserContent = () => {
240
+ if (!pendingUserContent.length) return;
241
+ messages.push({ role: 'user', content: contentToChatContent(pendingUserContent) });
242
+ pendingUserContent = [];
243
+ };
244
+ const flushPendingAssistantToolMessage = () => {
245
+ if (!pendingAssistantToolMessage) return;
246
+ messages.push(pendingAssistantToolMessage);
247
+ pendingAssistantToolMessage = null;
248
+ pendingReasoningContent = '';
249
+ };
250
+ const ensurePendingAssistantToolMessage = () => {
251
+ if (!pendingAssistantToolMessage) {
252
+ pendingAssistantToolMessage = {
253
+ role: 'assistant',
254
+ content: '',
255
+ tool_calls: [],
256
+ };
257
+ if (pendingReasoningContent) {
258
+ pendingAssistantToolMessage.reasoning_content = pendingReasoningContent;
259
+ }
260
+ }
261
+ return pendingAssistantToolMessage;
262
+ };
263
+
264
+ for (const item of input) {
265
+ if (typeof item === 'string') {
266
+ flushPendingAssistantToolMessage();
267
+ pendingUserContent.push({ type: 'input_text', text: item });
268
+ continue;
269
+ }
270
+ if (!isObject(item)) continue;
271
+ if (isInputContentPart(item)) {
272
+ flushPendingAssistantToolMessage();
273
+ pendingUserContent.push(item);
274
+ continue;
275
+ }
276
+ if (item.type === 'reasoning') {
277
+ pendingReasoningContent += reasoningTextFromItem(item);
278
+ continue;
279
+ }
280
+ if (item.type === 'message' && item.role) {
281
+ flushPendingUserContent();
282
+ flushPendingAssistantToolMessage();
283
+ const normalized = normalizeMessage(item);
284
+ if (normalized?.role === 'assistant' && !normalized.reasoning_content && pendingReasoningContent) {
285
+ normalized.reasoning_content = pendingReasoningContent;
286
+ pendingReasoningContent = '';
287
+ }
288
+ if (normalized) messages.push(normalized);
289
+ continue;
290
+ }
291
+ if (TOOL_OUTPUT_TYPES.has(item.type) || String(item.type || '').endsWith('_call_output')) {
292
+ flushPendingUserContent();
293
+ flushPendingAssistantToolMessage();
294
+ messages.push(responseToolOutputToMessage(item));
295
+ continue;
296
+ }
297
+ if (TOOL_CALL_TYPES.has(item.type) || String(item.type || '').endsWith('_call')) {
298
+ flushPendingUserContent();
299
+ ensurePendingAssistantToolMessage().tool_calls.push(chatToolCallFromResponseItem(item));
300
+ continue;
301
+ }
302
+ if (item.role) {
303
+ flushPendingUserContent();
304
+ flushPendingAssistantToolMessage();
305
+ const normalized = normalizeMessage(item);
306
+ if (normalized?.role === 'assistant' && !normalized.reasoning_content && pendingReasoningContent) {
307
+ normalized.reasoning_content = pendingReasoningContent;
308
+ pendingReasoningContent = '';
309
+ }
310
+ if (normalized) messages.push(normalized);
311
+ }
312
+ }
313
+ flushPendingUserContent();
314
+ flushPendingAssistantToolMessage();
315
+ return messages;
316
+ }
317
+
318
+ if (isObject(input)) {
319
+ if (Array.isArray(input.messages)) {
320
+ return input.messages.map(normalizeMessage).filter(Boolean);
321
+ }
322
+ if (Array.isArray(input.input)) {
323
+ return extractMessagesFromResponsesInput(input.input);
324
+ }
325
+ if (typeof input.input === 'string') {
326
+ return [{ role: 'user', content: input.input }];
327
+ }
328
+ if (isInputContentPart(input.input)) {
329
+ return [{ role: 'user', content: contentToChatContent(input.input) }];
330
+ }
331
+ }
332
+
333
+ if (typeof input === 'string') {
334
+ return [{ role: 'user', content: input }];
335
+ }
336
+
337
+ return [];
338
+ }
339
+
340
+ function normalizeTool(tool) {
341
+ if (!isObject(tool)) return tool;
342
+ if (tool.type === 'function' && isObject(tool.function)) {
343
+ return {
344
+ ...tool,
345
+ function: {
346
+ ...tool.function,
347
+ name: sanitizeFunctionName(tool.function.name),
348
+ parameters: normalizeJsonSchemaObject(tool.function.parameters),
349
+ },
350
+ };
351
+ }
352
+ if (tool.type === 'function' || tool.type === 'custom' || tool.name || tool.parameters || tool.input_schema) {
353
+ return {
354
+ type: 'function',
355
+ function: omitUndefined({
356
+ name: sanitizeFunctionName(tool.name || tool.type),
357
+ description: tool.description,
358
+ parameters: normalizeJsonSchemaObject(tool.parameters || tool.input_schema),
359
+ strict: tool.strict,
360
+ }),
361
+ };
362
+ }
363
+ if (typeof tool.type === 'string') {
364
+ return {
365
+ type: 'function',
366
+ function: omitUndefined({
367
+ name: sanitizeFunctionName(tool.name || tool.type),
368
+ description: tool.description || `Gateway shim for Responses tool type ${tool.type}.`,
369
+ parameters: normalizeJsonSchemaObject(tool.parameters || tool.input_schema),
370
+ strict: tool.strict,
371
+ }),
372
+ };
373
+ }
374
+ return null;
375
+ }
376
+
377
+ function normalizeJsonSchemaObject(schema) {
378
+ if (!isObject(schema)) {
379
+ return { type: 'object', properties: {}, additionalProperties: true };
380
+ }
381
+ if (schema.type === 'object') {
382
+ return {
383
+ ...schema,
384
+ properties: isObject(schema.properties) ? schema.properties : {},
385
+ };
386
+ }
387
+ if (schema.type == null) {
388
+ return {
389
+ ...schema,
390
+ type: 'object',
391
+ properties: isObject(schema.properties) ? schema.properties : {},
392
+ };
393
+ }
394
+ return {
395
+ type: 'object',
396
+ properties: {
397
+ value: schema,
398
+ },
399
+ required: ['value'],
400
+ additionalProperties: false,
401
+ };
402
+ }
403
+
404
+ function normalizeToolChoice(toolChoice) {
405
+ if (!isObject(toolChoice)) return toolChoice;
406
+ if (toolChoice.type === 'function' && toolChoice.name) {
407
+ return {
408
+ type: 'function',
409
+ function: { name: sanitizeFunctionName(toolChoice.name) },
410
+ };
411
+ }
412
+ if (toolChoice.type === 'function' && isObject(toolChoice.function)) {
413
+ return {
414
+ ...toolChoice,
415
+ function: {
416
+ ...toolChoice.function,
417
+ name: sanitizeFunctionName(toolChoice.function.name),
418
+ },
419
+ };
420
+ }
421
+ if (toolChoice.type === 'allowed_tools') {
422
+ return toolChoice.mode === 'required' ? 'required' : 'auto';
423
+ }
424
+ if (typeof toolChoice.type === 'string') {
425
+ return {
426
+ type: 'function',
427
+ function: {
428
+ name: sanitizeFunctionName(
429
+ toolChoice.name ||
430
+ toolChoice.tool_name ||
431
+ toolChoice.toolName ||
432
+ toolChoice.server_label ||
433
+ toolChoice.type,
434
+ ),
435
+ },
436
+ };
437
+ }
438
+ return toolChoice;
439
+ }
440
+
441
+ function unwrapJsonString(value) {
442
+ if (typeof value !== 'string') return value;
443
+ const trimmed = value.trim();
444
+ if (!trimmed) return value;
445
+ const first = trimmed[0];
446
+ if (!['{', '[', '"', 't', 'f', 'n', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].includes(first)) {
447
+ return value;
448
+ }
449
+ try {
450
+ return JSON.parse(trimmed);
451
+ } catch {
452
+ return value;
453
+ }
454
+ }
455
+
456
+ function schemaTypes(schema) {
457
+ if (!isObject(schema)) return [];
458
+ return Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
459
+ }
460
+
461
+ function coerceValueForSchema(value, schema) {
462
+ const types = schemaTypes(schema);
463
+ if (!types.length || typeof value !== 'string') return value;
464
+ const unwrapped = unwrapJsonString(value);
465
+ if (types.includes('array') && Array.isArray(unwrapped)) return unwrapped;
466
+ if (types.includes('object') && isObject(unwrapped)) return unwrapped;
467
+ if (types.includes('boolean') && typeof unwrapped === 'boolean') return unwrapped;
468
+ if (types.includes('number') && typeof unwrapped === 'number') return unwrapped;
469
+ if (types.includes('integer') && Number.isInteger(unwrapped)) return unwrapped;
470
+ return value;
471
+ }
472
+
473
+ function buildToolSchemas(tools) {
474
+ const schemas = new Map();
475
+ if (!Array.isArray(tools)) return schemas;
476
+ for (const tool of tools) {
477
+ const normalizedTool = normalizeTool(tool);
478
+ const fn = normalizedTool?.type === 'function' ? normalizedTool.function : null;
479
+ if (!fn?.name) continue;
480
+ schemas.set(fn.name, normalizeJsonSchemaObject(fn.parameters));
481
+ }
482
+ return schemas;
483
+ }
484
+
485
+ function normalizeToolCallArguments(name, argumentsText, toolSchema) {
486
+ if (!argumentsText) return '';
487
+ if (!isObject(toolSchema) || !isObject(toolSchema.properties)) return argumentsText;
488
+ try {
489
+ const parsed = JSON.parse(argumentsText);
490
+ if (!isObject(parsed)) return argumentsText;
491
+ let changed = false;
492
+ const next = { ...parsed };
493
+ for (const [key, schema] of Object.entries(toolSchema.properties)) {
494
+ if (!(key in next)) continue;
495
+ const value = coerceValueForSchema(next[key], schema);
496
+ if (value !== next[key]) {
497
+ next[key] = value;
498
+ changed = true;
499
+ }
500
+ }
501
+ return changed ? JSON.stringify(next) : argumentsText;
502
+ } catch {
503
+ return argumentsText;
504
+ }
505
+ }
506
+
507
+ function normalizeFunctionCallItemArguments(item, toolSchemas) {
508
+ if (!item || item.type !== 'function_call') return;
509
+ item.arguments = normalizeToolCallArguments(item.name, item.arguments, toolSchemas?.get(item.name));
510
+ }
511
+
512
+ function sanitizeMessageForChatCompletion(message, provider = 'generic') {
513
+ if (!isObject(message)) return message;
514
+ const sanitized = {
515
+ role: normalizeRole(message.role, provider),
516
+ content: contentToChatContent(message.content),
517
+ };
518
+ if (message.name !== undefined) sanitized.name = message.name;
519
+ if (Array.isArray(message.tool_calls)) sanitized.tool_calls = message.tool_calls;
520
+ if (message.tool_call_id !== undefined) sanitized.tool_call_id = message.tool_call_id;
521
+ if (provider === 'deepseek' && message.role === 'assistant' && typeof message.reasoning_content === 'string') {
522
+ sanitized.reasoning_content = message.reasoning_content;
523
+ }
524
+ return sanitized;
525
+ }
526
+
527
+ function patchDeepSeekThinkingHistory(messages, request) {
528
+ if (request.thinking?.type !== 'enabled' && !request.reasoning_effort) return messages;
529
+ return messages.map((message) => {
530
+ if (message?.role !== 'assistant') return message;
531
+ if (typeof message.reasoning_content === 'string') return message;
532
+ return {
533
+ ...message,
534
+ reasoning_content: '',
535
+ };
536
+ });
537
+ }
538
+
539
+ function normalizeOutputTextPart(text, annotations = []) {
540
+ return {
541
+ type: 'output_text',
542
+ text: String(text ?? ''),
543
+ annotations: Array.isArray(annotations) ? annotations : [],
544
+ };
545
+ }
546
+
547
+ function normalizeReasoningSummaryText(text) {
548
+ const value = String(text ?? '').replace(/\r\n?/g, '\n');
549
+ if (!value) return '';
550
+ const plain = value
551
+ .split('\n')
552
+ .map((line) => line
553
+ .replace(/^[ \t]{0,3}(?:`{3,}|~{3,}).*$/, '')
554
+ .replace(/^[ \t]{0,3}#{1,6}[ \t]+/, '')
555
+ .replace(/^[ \t]{0,3}>[ \t]?/, '')
556
+ .replace(/^[ \t]*[-*+][ \t]+(?:\[[ xX]\][ \t]+)?/, '\u2022 ')
557
+ .replace(/^[ \t]*(\d+)\.[ \t]+/, '$1) ')
558
+ .replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1')
559
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
560
+ .replace(/`([^`]*)`/g, '$1')
561
+ .replace(/(\*\*|__)([^*_]+)\1/g, '$2')
562
+ .replace(/(\*|_)([^*_]+)\1/g, '$2')
563
+ .replace(/[*_`~]/g, '')
564
+ .replace(/\\([\\`*_{}[\]()#+\-.!>])/g, '$1')
565
+ .trimEnd())
566
+ .join('\n');
567
+ return plain.replace(/^\n+/, '').trimEnd();
568
+ }
569
+
570
+ function reasoningSummaryText(text) {
571
+ const summary = normalizeReasoningSummaryText(text);
572
+ return summary ? `**Reasoning**\n\n${summary}` : '';
573
+ }
574
+
575
+ function normalizeSummaryTextPart(text) {
576
+ return {
577
+ type: 'summary_text',
578
+ text: String(text ?? ''),
579
+ };
580
+ }
581
+
582
+ function normalizeReasoningTextPart(text) {
583
+ return {
584
+ type: 'reasoning_text',
585
+ text: String(text ?? ''),
586
+ };
587
+ }
588
+
589
+ const BUFFERED_REASONING_DELTA_CHARS = 1200;
590
+
591
+ function splitTextChunks(text, maxChars) {
592
+ const value = String(text ?? '');
593
+ if (!value) return [];
594
+ const chunks = [];
595
+ for (let index = 0; index < value.length; index += maxChars) {
596
+ chunks.push(value.slice(index, index + maxChars));
597
+ }
598
+ return chunks;
599
+ }
600
+
601
+ function splitBufferedReasoningDeltas(text) {
602
+ return splitTextChunks(text, BUFFERED_REASONING_DELTA_CHARS);
603
+ }
604
+
605
+ function snapshotResponsePart(part) {
606
+ if (!isObject(part)) return part;
607
+ return {
608
+ ...part,
609
+ annotations: Array.isArray(part.annotations) ? [...part.annotations] : part.annotations,
610
+ };
611
+ }
612
+
613
+ function snapshotResponseItem(item) {
614
+ if (!isObject(item)) return item;
615
+ return {
616
+ ...item,
617
+ content: Array.isArray(item.content) ? item.content.map(snapshotResponsePart) : item.content,
618
+ summary: Array.isArray(item.summary) ? item.summary.map(snapshotResponsePart) : item.summary,
619
+ };
620
+ }
621
+
622
+ function chatContentToResponseOutputParts(content) {
623
+ if (content == null || content === '') return [];
624
+ if (typeof content === 'string') return [normalizeOutputTextPart(content)];
625
+ if (!Array.isArray(content)) {
626
+ const text = toText(content);
627
+ return text ? [normalizeOutputTextPart(text)] : [];
628
+ }
629
+
630
+ const output = [];
631
+ for (const part of content) {
632
+ if (typeof part === 'string') {
633
+ output.push(normalizeOutputTextPart(part));
634
+ continue;
635
+ }
636
+ if (!isObject(part)) continue;
637
+ if (part.type === 'text' || part.type === 'output_text') {
638
+ output.push(normalizeOutputTextPart(part.text ?? part.content ?? '', part.annotations));
639
+ continue;
640
+ }
641
+ if (part.type === 'image_url' || part.type === 'output_image') {
642
+ output.push(omitUndefined({
643
+ type: 'output_image',
644
+ image_url: part.image_url,
645
+ url: part.url,
646
+ file_id: part.file_id,
647
+ }));
648
+ continue;
649
+ }
650
+ if (part.type === 'file' || part.type === 'output_file') {
651
+ output.push(omitUndefined({
652
+ type: 'output_file',
653
+ file: part.file,
654
+ file_id: part.file_id,
655
+ filename: part.filename,
656
+ }));
657
+ continue;
658
+ }
659
+ if (part.type === 'refusal') {
660
+ output.push({ type: 'refusal', refusal: String(part.refusal ?? part.text ?? '') });
661
+ }
662
+ }
663
+ return output;
664
+ }
665
+
666
+ function normalizeInstructions(instructions) {
667
+ if (instructions == null) return '';
668
+ if (typeof instructions === 'string') return instructions;
669
+ if (Array.isArray(instructions)) {
670
+ return instructions.map((item) => toText(item)).filter(Boolean).join('\n');
671
+ }
672
+ return toText(instructions);
673
+ }
674
+
675
+ export function normalizeResponsesRequest(request) {
676
+ const messages = extractMessagesFromResponsesInput(request.input ?? request);
677
+ const model = request.model || request.model_id || request.upstream_model || '';
678
+ const instructions = normalizeInstructions(request.instructions);
679
+ const responseFormat = request.response_format || request.text?.format;
680
+ return {
681
+ model,
682
+ messages,
683
+ instructions,
684
+ temperature: request.temperature,
685
+ top_p: request.top_p,
686
+ max_tokens: request.max_output_tokens ?? request.max_tokens,
687
+ stop: request.stop,
688
+ stream: Boolean(request.stream),
689
+ tools: request.tools,
690
+ tool_choice: request.tool_choice,
691
+ parallel_tool_calls: request.parallel_tool_calls,
692
+ presence_penalty: request.presence_penalty,
693
+ frequency_penalty: request.frequency_penalty,
694
+ metadata: request.metadata,
695
+ reasoning: request.reasoning,
696
+ response_format: responseFormat,
697
+ stream_options: request.stream_options,
698
+ text: request.text,
699
+ truncation: request.truncation,
700
+ user: request.user,
701
+ previous_response_id: request.previous_response_id,
702
+ store: request.store,
703
+ include: request.include,
704
+ conversation: request.conversation,
705
+ };
706
+ }
707
+
708
+ export function toChatCompletionsRequest(normalized, overrides = {}) {
709
+ const messages = [];
710
+ if (normalized.instructions) {
711
+ messages.push({ role: 'system', content: normalized.instructions });
712
+ }
713
+ messages.push(...normalized.messages);
714
+ const tools = Array.isArray(normalized.tools) ? normalized.tools.map(normalizeTool).filter(Boolean) : undefined;
715
+ const request = {
716
+ model: overrides.model || normalized.model,
717
+ messages,
718
+ temperature: normalized.temperature,
719
+ top_p: normalized.top_p,
720
+ max_tokens: normalized.max_tokens,
721
+ stop: normalized.stop,
722
+ stream: normalized.stream,
723
+ tools,
724
+ tool_choice: normalizeToolChoice(normalized.tool_choice),
725
+ parallel_tool_calls: normalized.parallel_tool_calls,
726
+ presence_penalty: normalized.presence_penalty,
727
+ frequency_penalty: normalized.frequency_penalty,
728
+ response_format: normalized.response_format,
729
+ metadata: normalized.metadata,
730
+ user: normalized.user,
731
+ reasoning: normalized.reasoning,
732
+ stream_options: normalized.stream_options,
733
+ };
734
+
735
+ for (const key of Object.keys(request)) {
736
+ if (request[key] === undefined) delete request[key];
737
+ }
738
+
739
+ return request;
740
+ }
741
+
742
+ export function assistantMessageFromResponseOutput(output) {
743
+ const messageItems = Array.isArray(output) ? output.filter((item) => item.type === 'message') : [];
744
+ const functionItems = Array.isArray(output) ? output.filter((item) => item.type === 'function_call') : [];
745
+ const reasoningItems = Array.isArray(output) ? output.filter((item) => item.type === 'reasoning') : [];
746
+ const contentParts = messageItems.map((item) => item.content || []).flat();
747
+ const chatParts = contentParts.map(contentPartToChatPart).filter(Boolean);
748
+ const content = !chatParts.length
749
+ ? ''
750
+ : chatParts.every((part) => part.type === 'text')
751
+ ? chatParts.map((part) => part.text).join('')
752
+ : chatParts;
753
+ const assistant = {
754
+ role: 'assistant',
755
+ content,
756
+ };
757
+ const toolCalls = functionItems.map((item) => ({
758
+ id: item.call_id || item.id,
759
+ type: 'function',
760
+ function: {
761
+ name: item.name,
762
+ arguments: item.arguments || '',
763
+ },
764
+ }));
765
+ if (toolCalls.length) assistant.tool_calls = toolCalls;
766
+ const reasoningContent = reasoningItems
767
+ .map((item) => item.content || [])
768
+ .flat()
769
+ .filter((part) => part.type === 'reasoning_text')
770
+ .map((part) => part.text)
771
+ .join('');
772
+ if (reasoningContent) assistant.reasoning_content = reasoningContent;
773
+ return assistant;
774
+ }
775
+
776
+ export function extractToolCallIdsFromMessages(messages) {
777
+ const ids = [];
778
+ for (const message of Array.isArray(messages) ? messages : []) {
779
+ if (message?.role !== 'assistant' || !Array.isArray(message.tool_calls)) continue;
780
+ for (const toolCall of message.tool_calls) {
781
+ if (toolCall?.id) ids.push(toolCall.id);
782
+ }
783
+ }
784
+ return ids;
785
+ }
786
+
787
+ export function toProviderChatCompletionsRequest(chatRequest, config = {}) {
788
+ const provider = config.upstreamProvider || 'generic';
789
+ const modelAlias = resolveModelAlias(chatRequest.model, config);
790
+ const fallbackReasoning = !chatRequest.reasoning && config.codexReasoningEffort
791
+ ? { effort: config.codexReasoningEffort }
792
+ : chatRequest.reasoning;
793
+ const messages = Array.isArray(chatRequest.messages)
794
+ ? chatRequest.messages.map((message) => sanitizeMessageForChatCompletion(message, provider))
795
+ : [];
796
+ const request = {
797
+ model: modelAlias.upstreamModel || config.upstreamModel || chatRequest.model,
798
+ messages,
799
+ temperature: chatRequest.temperature,
800
+ top_p: chatRequest.top_p,
801
+ max_tokens: chatRequest.max_tokens,
802
+ stop: chatRequest.stop,
803
+ stream: chatRequest.stream,
804
+ tools: chatRequest.tools,
805
+ tool_choice: chatRequest.tool_choice,
806
+ parallel_tool_calls: chatRequest.parallel_tool_calls,
807
+ presence_penalty: chatRequest.presence_penalty,
808
+ frequency_penalty: chatRequest.frequency_penalty,
809
+ response_format: chatRequest.response_format,
810
+ user: chatRequest.user,
811
+ stream_options: chatRequest.stream_options,
812
+ };
813
+
814
+ if (fallbackReasoning && isObject(fallbackReasoning)) {
815
+ const effort = fallbackReasoning.effort;
816
+ if (effort !== undefined) {
817
+ request.reasoning_effort = effort;
818
+ }
819
+ }
820
+
821
+ if (provider === 'deepseek') {
822
+ delete request.reasoning_effort;
823
+ delete request.parallel_tool_calls;
824
+ if (request.user !== undefined) {
825
+ request.user_id = request.user;
826
+ delete request.user;
827
+ }
828
+ if (request.stream) {
829
+ request.stream_options = { include_usage: true };
830
+ } else {
831
+ delete request.stream_options;
832
+ }
833
+ if (request.response_format?.type === 'json_schema') {
834
+ request.response_format = { type: 'json_object' };
835
+ }
836
+ Object.assign(request, deepseekReasoningPayload({ alias: modelAlias, reasoning: fallbackReasoning }));
837
+ request.messages = patchDeepSeekThinkingHistory(request.messages, request);
838
+ }
839
+
840
+ Object.assign(request, modelAlias.extraBody);
841
+
842
+ for (const key of Object.keys(request)) {
843
+ if (request[key] === undefined) delete request[key];
844
+ }
845
+
846
+ return request;
847
+ }
848
+
849
+ function extractAssistantTextFromMessage(message) {
850
+ if (!isObject(message)) return '';
851
+ return toText(message.content);
852
+ }
853
+
854
+ function createBaseResponse({
855
+ id,
856
+ model,
857
+ createdAt,
858
+ status = 'in_progress',
859
+ output = [],
860
+ previousResponseId = null,
861
+ usage = null,
862
+ normalized,
863
+ completedAt = null,
864
+ incompleteReason = null,
865
+ }) {
866
+ return {
867
+ id,
868
+ object: 'response',
869
+ created_at: Math.floor(createdAt),
870
+ completed_at: completedAt == null ? null : Math.floor(completedAt),
871
+ status,
872
+ background: false,
873
+ error: null,
874
+ incomplete_details: incompleteReason ? { reason: incompleteReason } : null,
875
+ instructions: normalized?.instructions || null,
876
+ max_output_tokens: normalized?.max_tokens ?? null,
877
+ model,
878
+ output,
879
+ output_text: outputTextFromOutput(output),
880
+ parallel_tool_calls: normalized?.parallel_tool_calls ?? false,
881
+ previous_response_id: previousResponseId,
882
+ reasoning: normalized?.reasoning ?? null,
883
+ store: normalized?.store ?? false,
884
+ temperature: normalized?.temperature ?? null,
885
+ text: normalized?.text ?? null,
886
+ tool_choice: normalized?.tool_choice ?? 'auto',
887
+ tools: normalized?.tools ?? [],
888
+ top_p: normalized?.top_p ?? null,
889
+ truncation: normalized?.truncation ?? 'disabled',
890
+ usage,
891
+ user: normalized?.user ?? null,
892
+ metadata: normalized?.metadata ?? null,
893
+ };
894
+ }
895
+
896
+ function normalizeResponsesUsage(usage) {
897
+ if (!isObject(usage)) return null;
898
+ const inputTokens = usage.input_tokens ?? usage.prompt_tokens ?? 0;
899
+ const outputTokens = usage.output_tokens ?? usage.completion_tokens ?? 0;
900
+ return {
901
+ input_tokens: inputTokens,
902
+ input_tokens_details: usage.input_tokens_details ?? {
903
+ cached_tokens: usage.prompt_tokens_details?.cached_tokens ?? 0,
904
+ },
905
+ output_tokens: outputTokens,
906
+ output_tokens_details: usage.output_tokens_details ?? {
907
+ reasoning_tokens: usage.completion_tokens_details?.reasoning_tokens ?? usage.reasoning_tokens ?? 0,
908
+ },
909
+ total_tokens: usage.total_tokens ?? inputTokens + outputTokens,
910
+ };
911
+ }
912
+
913
+ function outputTextFromOutput(output) {
914
+ if (!Array.isArray(output)) return '';
915
+ const chunks = [];
916
+ for (const item of output) {
917
+ if (item?.type !== 'message' || !Array.isArray(item.content)) continue;
918
+ for (const part of item.content) {
919
+ if (part?.type === 'output_text' && typeof part.text === 'string') {
920
+ chunks.push(part.text);
921
+ }
922
+ }
923
+ }
924
+ return chunks.join('');
925
+ }
926
+
927
+ export function createResponseEnvelope({
928
+ id = generateId('resp'),
929
+ model,
930
+ createdAt = Date.now() / 1000,
931
+ status = 'in_progress',
932
+ output = [],
933
+ previousResponseId = null,
934
+ usage = null,
935
+ normalized,
936
+ completedAt = null,
937
+ incompleteReason = null,
938
+ }) {
939
+ return createBaseResponse({
940
+ id,
941
+ model,
942
+ createdAt,
943
+ status,
944
+ output,
945
+ previousResponseId,
946
+ usage,
947
+ normalized,
948
+ completedAt,
949
+ incompleteReason,
950
+ });
951
+ }
952
+
953
+ export function convertChatCompletionToResponses({ completion, model, previousResponseId, normalized, responseId = generateId('resp') }) {
954
+ const createdAt = completion.created || Date.now() / 1000;
955
+ const choice = Array.isArray(completion.choices) ? completion.choices[0] : null;
956
+ const message = choice?.message || {};
957
+ const content = chatContentToResponseOutputParts(message.content);
958
+ const toolSchemas = buildToolSchemas(normalized?.tools);
959
+ const output = [];
960
+
961
+ if (content.length || message.tool_calls?.length) {
962
+ output.push({
963
+ type: 'message',
964
+ id: generateId('msg'),
965
+ role: 'assistant',
966
+ content,
967
+ status: 'completed',
968
+ phase: message.phase || 'final_answer',
969
+ });
970
+ }
971
+
972
+ if (Array.isArray(message.tool_calls)) {
973
+ for (const toolCall of message.tool_calls) {
974
+ const callId = toolCall.id || generateId('call');
975
+ const item = {
976
+ type: 'function_call',
977
+ id: callId,
978
+ call_id: callId,
979
+ name: toolCall.function?.name,
980
+ arguments: toolCall.function?.arguments || '',
981
+ status: 'completed',
982
+ };
983
+ normalizeFunctionCallItemArguments(item, toolSchemas);
984
+ output.push(item);
985
+ }
986
+ }
987
+
988
+ if (message.reasoning_content) {
989
+ const reasoningText = String(message.reasoning_content);
990
+ output.unshift({
991
+ type: 'reasoning',
992
+ id: generateId('rs'),
993
+ summary: [normalizeSummaryTextPart(reasoningSummaryText(reasoningText))],
994
+ content: [normalizeReasoningTextPart(reasoningText)],
995
+ encrypted_content: null,
996
+ status: 'completed',
997
+ });
998
+ }
999
+
1000
+ return createBaseResponse({
1001
+ id: responseId,
1002
+ model,
1003
+ createdAt,
1004
+ status: choice?.finish_reason === 'length' ? 'incomplete' : 'completed',
1005
+ output,
1006
+ previousResponseId: previousResponseId ?? null,
1007
+ usage: normalizeResponsesUsage(completion.usage),
1008
+ normalized,
1009
+ completedAt: Date.now() / 1000,
1010
+ incompleteReason: choice?.finish_reason === 'length' ? 'max_output_tokens' : null,
1011
+ });
1012
+ }
1013
+
1014
+ export class ResponsesStreamMapper {
1015
+ constructor({
1016
+ responseId = generateId('resp'),
1017
+ model,
1018
+ createdAt = Date.now() / 1000,
1019
+ previousResponseId = null,
1020
+ normalized,
1021
+ bufferOutputUntilDone = false,
1022
+ emitReasoningSummary = true,
1023
+ emitReasoningText = false,
1024
+ } = {}) {
1025
+ this.responseId = responseId;
1026
+ this.model = model;
1027
+ this.createdAt = createdAt;
1028
+ this.previousResponseId = previousResponseId;
1029
+ this.normalized = normalized;
1030
+ this.bufferOutputUntilDone = Boolean(bufferOutputUntilDone);
1031
+ this.emitReasoningSummary = Boolean(emitReasoningSummary);
1032
+ this.emitReasoningText = Boolean(emitReasoningText);
1033
+ this.sequenceNumber = 0;
1034
+ this.output = [];
1035
+ this.messageItem = null;
1036
+ this.reasoningItem = null;
1037
+ this.toolItems = new Map();
1038
+ this.text = '';
1039
+ this.reasoningText = '';
1040
+ this.streamReasoningLive = true;
1041
+ this.reasoningItemDone = false;
1042
+ this.finishReason = null;
1043
+ this.usage = null;
1044
+ this.completedAt = null;
1045
+ this.finalized = false;
1046
+ this.messageContentAdded = false;
1047
+ this.reasoningContentAdded = false;
1048
+ this.reasoningSummaryAdded = false;
1049
+ this.reasoningItemAdded = false;
1050
+ this.streamedReasoningSummaryText = '';
1051
+ this.toolSchemas = buildToolSchemas(normalized?.tools);
1052
+ }
1053
+
1054
+ nextSequence() {
1055
+ this.sequenceNumber += 1;
1056
+ return this.sequenceNumber;
1057
+ }
1058
+
1059
+ response(status = 'in_progress') {
1060
+ return createBaseResponse({
1061
+ id: this.responseId,
1062
+ model: this.model,
1063
+ createdAt: this.createdAt,
1064
+ status,
1065
+ output: this.output,
1066
+ previousResponseId: this.previousResponseId,
1067
+ usage: this.usage,
1068
+ normalized: this.normalized,
1069
+ completedAt: this.completedAt,
1070
+ incompleteReason: this.finishReason === 'length' ? 'max_output_tokens' : null,
1071
+ });
1072
+ }
1073
+
1074
+ createdEvent() {
1075
+ return {
1076
+ type: 'response.created',
1077
+ sequence_number: this.nextSequence(),
1078
+ response: this.response('in_progress'),
1079
+ };
1080
+ }
1081
+
1082
+ inProgressEvent() {
1083
+ return {
1084
+ type: 'response.in_progress',
1085
+ sequence_number: this.nextSequence(),
1086
+ response: this.response('in_progress'),
1087
+ };
1088
+ }
1089
+
1090
+ ensureMessageItem() {
1091
+ if (!this.messageItem) {
1092
+ this.messageItem = {
1093
+ id: generateId('msg'),
1094
+ type: 'message',
1095
+ status: 'in_progress',
1096
+ role: 'assistant',
1097
+ phase: 'final_answer',
1098
+ content: [],
1099
+ };
1100
+ this.output.push(this.messageItem);
1101
+ return {
1102
+ type: 'response.output_item.added',
1103
+ sequence_number: this.nextSequence(),
1104
+ output_index: this.output.length - 1,
1105
+ item: snapshotResponseItem(this.messageItem),
1106
+ };
1107
+ }
1108
+ return null;
1109
+ }
1110
+
1111
+ ensureReasoningItem() {
1112
+ if (!this.reasoningItem) {
1113
+ this.reasoningItem = {
1114
+ id: generateId('rs'),
1115
+ type: 'reasoning',
1116
+ status: 'in_progress',
1117
+ summary: [],
1118
+ content: [normalizeReasoningTextPart('')],
1119
+ encrypted_content: null,
1120
+ };
1121
+ this.output.push(this.reasoningItem);
1122
+ }
1123
+ if (!this.reasoningItemAdded) {
1124
+ this.reasoningItemAdded = true;
1125
+ return {
1126
+ type: 'response.output_item.added',
1127
+ sequence_number: this.nextSequence(),
1128
+ output_index: this.output.indexOf(this.reasoningItem),
1129
+ item: snapshotResponseItem(this.reasoningItem),
1130
+ };
1131
+ }
1132
+ return null;
1133
+ }
1134
+
1135
+ ensureReasoningContentPart(events) {
1136
+ if (!this.emitReasoningText || this.reasoningContentAdded || !this.reasoningItem) return;
1137
+ this.reasoningContentAdded = true;
1138
+ events.push({
1139
+ type: 'response.content_part.added',
1140
+ sequence_number: this.nextSequence(),
1141
+ output_index: this.output.indexOf(this.reasoningItem),
1142
+ content_index: 0,
1143
+ item_id: this.reasoningItem.id,
1144
+ part: snapshotResponsePart(this.reasoningItem.content[0]),
1145
+ });
1146
+ }
1147
+
1148
+ appendReasoningSummaryDelta(events) {
1149
+ if (!this.emitReasoningSummary || !this.reasoningItem) return;
1150
+ if (!this.reasoningSummaryAdded) {
1151
+ this.reasoningSummaryAdded = true;
1152
+ this.reasoningItem.summary.push(normalizeSummaryTextPart(''));
1153
+ events.push({
1154
+ type: 'response.reasoning_summary_part.added',
1155
+ sequence_number: this.nextSequence(),
1156
+ output_index: this.output.indexOf(this.reasoningItem),
1157
+ item_id: this.reasoningItem.id,
1158
+ summary_index: 0,
1159
+ part: snapshotResponsePart(this.reasoningItem.summary[0]),
1160
+ });
1161
+ }
1162
+ const nextSummaryText = reasoningSummaryText(this.reasoningText);
1163
+ this.reasoningItem.summary[0].text = nextSummaryText;
1164
+ if (!nextSummaryText.startsWith(this.streamedReasoningSummaryText)) {
1165
+ this.streamedReasoningSummaryText = nextSummaryText;
1166
+ return;
1167
+ }
1168
+ const deltaText = nextSummaryText.slice(this.streamedReasoningSummaryText.length);
1169
+ this.streamedReasoningSummaryText = nextSummaryText;
1170
+ for (const delta of splitBufferedReasoningDeltas(deltaText)) {
1171
+ events.push({
1172
+ type: 'response.reasoning_summary_text.delta',
1173
+ sequence_number: this.nextSequence(),
1174
+ output_index: this.output.indexOf(this.reasoningItem),
1175
+ item_id: this.reasoningItem.id,
1176
+ summary_index: 0,
1177
+ delta,
1178
+ });
1179
+ }
1180
+ }
1181
+
1182
+ textDelta(delta) {
1183
+ if (this.bufferOutputUntilDone) {
1184
+ if (!this.messageItem) {
1185
+ this.messageItem = {
1186
+ id: generateId('msg'),
1187
+ type: 'message',
1188
+ status: 'in_progress',
1189
+ role: 'assistant',
1190
+ phase: 'final_answer',
1191
+ content: [normalizeOutputTextPart('')],
1192
+ };
1193
+ this.output.push(this.messageItem);
1194
+ }
1195
+ this.text += delta;
1196
+ this.messageItem.content[0].text = this.text;
1197
+ return [];
1198
+ }
1199
+ const events = [];
1200
+ if (this.streamReasoningLive) {
1201
+ events.push(...this.closeReasoningItem('completed'));
1202
+ }
1203
+ this.streamReasoningLive = false;
1204
+ const added = this.ensureMessageItem();
1205
+ if (added) events.push(added);
1206
+ if (!this.messageContentAdded) {
1207
+ this.messageContentAdded = true;
1208
+ this.messageItem.content.push(normalizeOutputTextPart(''));
1209
+ events.push({
1210
+ type: 'response.content_part.added',
1211
+ sequence_number: this.nextSequence(),
1212
+ output_index: this.output.indexOf(this.messageItem),
1213
+ content_index: 0,
1214
+ item_id: this.messageItem.id,
1215
+ part: snapshotResponsePart(this.messageItem.content[0]),
1216
+ });
1217
+ }
1218
+ this.text += delta;
1219
+ this.messageItem.content[0].text = this.text;
1220
+ events.push({
1221
+ type: 'response.output_text.delta',
1222
+ sequence_number: this.nextSequence(),
1223
+ output_index: this.output.indexOf(this.messageItem),
1224
+ content_index: 0,
1225
+ item_id: this.messageItem.id,
1226
+ delta,
1227
+ logprobs: [],
1228
+ });
1229
+ return events;
1230
+ }
1231
+
1232
+ reasoningDelta(delta) {
1233
+ this.reasoningText += delta;
1234
+ if (this.bufferOutputUntilDone) {
1235
+ const events = [];
1236
+ const added = this.ensureReasoningItem();
1237
+ if (added) events.push(added);
1238
+ this.reasoningItem.content[0].text = this.reasoningText;
1239
+ this.appendReasoningSummaryDelta(events);
1240
+ return events;
1241
+ }
1242
+ if (!this.streamReasoningLive) {
1243
+ if (!this.reasoningItem) {
1244
+ this.reasoningItem = {
1245
+ id: generateId('rs'),
1246
+ type: 'reasoning',
1247
+ status: 'in_progress',
1248
+ summary: [],
1249
+ content: [normalizeReasoningTextPart('')],
1250
+ encrypted_content: null,
1251
+ };
1252
+ this.output.push(this.reasoningItem);
1253
+ }
1254
+ this.reasoningItem.content[0].text = this.reasoningText;
1255
+ return [];
1256
+ }
1257
+
1258
+ const events = [];
1259
+ const added = this.ensureReasoningItem();
1260
+ if (added) events.push(added);
1261
+ this.ensureReasoningContentPart(events);
1262
+ this.reasoningItem.content[0].text = this.reasoningText;
1263
+ this.appendReasoningSummaryDelta(events);
1264
+ if (this.emitReasoningText) {
1265
+ events.push({
1266
+ type: 'response.reasoning_text.delta',
1267
+ sequence_number: this.nextSequence(),
1268
+ output_index: this.output.indexOf(this.reasoningItem),
1269
+ item_id: this.reasoningItem.id,
1270
+ content_index: 0,
1271
+ delta,
1272
+ });
1273
+ }
1274
+ return events;
1275
+ }
1276
+
1277
+ functionDelta(toolCall) {
1278
+ if (this.bufferOutputUntilDone) {
1279
+ const index = toolCall.index ?? 0;
1280
+ let item = this.toolItems.get(index);
1281
+ if (!item) {
1282
+ const callId = toolCall.id || generateId('call');
1283
+ item = {
1284
+ id: callId,
1285
+ type: 'function_call',
1286
+ status: 'in_progress',
1287
+ call_id: callId,
1288
+ name: toolCall.function?.name || '',
1289
+ arguments: '',
1290
+ };
1291
+ this.toolItems.set(index, item);
1292
+ this.output.push(item);
1293
+ }
1294
+ if (toolCall.function?.name) item.name = toolCall.function.name;
1295
+ item.arguments += toolCall.function?.arguments || '';
1296
+ return [];
1297
+ }
1298
+ const events = [];
1299
+ if (this.streamReasoningLive) {
1300
+ events.push(...this.closeReasoningItem('completed'));
1301
+ }
1302
+ this.streamReasoningLive = false;
1303
+ const index = toolCall.index ?? 0;
1304
+ let item = this.toolItems.get(index);
1305
+ if (!item) {
1306
+ const callId = toolCall.id || generateId('call');
1307
+ item = {
1308
+ id: callId,
1309
+ type: 'function_call',
1310
+ status: 'in_progress',
1311
+ call_id: callId,
1312
+ name: toolCall.function?.name || '',
1313
+ arguments: '',
1314
+ };
1315
+ this.toolItems.set(index, item);
1316
+ this.output.push(item);
1317
+ events.push({
1318
+ type: 'response.output_item.added',
1319
+ sequence_number: this.nextSequence(),
1320
+ output_index: this.output.length - 1,
1321
+ item: snapshotResponseItem(item),
1322
+ });
1323
+ }
1324
+ if (toolCall.function?.name) item.name = toolCall.function.name;
1325
+ const delta = toolCall.function?.arguments || '';
1326
+ item.arguments += delta;
1327
+ if (delta) {
1328
+ events.push({
1329
+ type: 'response.function_call_arguments.delta',
1330
+ sequence_number: this.nextSequence(),
1331
+ output_index: this.output.indexOf(item),
1332
+ item_id: item.id,
1333
+ delta,
1334
+ });
1335
+ }
1336
+ return events;
1337
+ }
1338
+
1339
+ finalize(finishReason = 'stop', usage = null) {
1340
+ if (this.finalized) return [];
1341
+ this.finalized = true;
1342
+ const status = finishReason === 'length' ? 'incomplete' : 'completed';
1343
+
1344
+ if (this.bufferOutputUntilDone) {
1345
+ this.finishReason = finishReason;
1346
+ this.usage = normalizeResponsesUsage(usage);
1347
+ this.completedAt = Date.now() / 1000;
1348
+
1349
+ if (this.reasoningItem) {
1350
+ this.reasoningItem.status = status;
1351
+ this.reasoningItem.content[0].text = this.reasoningText;
1352
+ }
1353
+ if (this.messageItem) {
1354
+ this.messageItem.status = status;
1355
+ if (!this.messageItem.content.length) {
1356
+ this.messageItem.content.push(normalizeOutputTextPart(''));
1357
+ }
1358
+ this.messageItem.content[0].text = this.text;
1359
+ }
1360
+
1361
+ const events = [];
1362
+ if (this.reasoningItem && this.bufferOutputUntilDone) {
1363
+ events.push(...this.flushBufferedReasoning(status));
1364
+ }
1365
+ events.push(...this.closeReasoningItem(status));
1366
+
1367
+ if (this.messageItem) {
1368
+ const outputIndex = this.output.indexOf(this.messageItem);
1369
+ events.push({
1370
+ type: 'response.output_item.added',
1371
+ sequence_number: this.nextSequence(),
1372
+ output_index: outputIndex,
1373
+ item: snapshotResponseItem({
1374
+ ...this.messageItem,
1375
+ status: 'in_progress',
1376
+ content: [],
1377
+ }),
1378
+ });
1379
+ events.push({
1380
+ type: 'response.content_part.added',
1381
+ sequence_number: this.nextSequence(),
1382
+ output_index: outputIndex,
1383
+ content_index: 0,
1384
+ item_id: this.messageItem.id,
1385
+ part: snapshotResponsePart(normalizeOutputTextPart('')),
1386
+ });
1387
+ if (this.text) {
1388
+ events.push({
1389
+ type: 'response.output_text.delta',
1390
+ sequence_number: this.nextSequence(),
1391
+ output_index: outputIndex,
1392
+ content_index: 0,
1393
+ item_id: this.messageItem.id,
1394
+ delta: this.text,
1395
+ logprobs: [],
1396
+ });
1397
+ }
1398
+ events.push({
1399
+ type: 'response.output_text.done',
1400
+ sequence_number: this.nextSequence(),
1401
+ output_index: outputIndex,
1402
+ content_index: 0,
1403
+ item_id: this.messageItem.id,
1404
+ text: this.text,
1405
+ logprobs: [],
1406
+ });
1407
+ events.push({
1408
+ type: 'response.content_part.done',
1409
+ sequence_number: this.nextSequence(),
1410
+ output_index: outputIndex,
1411
+ content_index: 0,
1412
+ item_id: this.messageItem.id,
1413
+ part: snapshotResponsePart(this.messageItem.content[0]),
1414
+ });
1415
+ events.push({
1416
+ type: 'response.output_item.done',
1417
+ sequence_number: this.nextSequence(),
1418
+ output_index: outputIndex,
1419
+ item: snapshotResponseItem(this.messageItem),
1420
+ });
1421
+ }
1422
+
1423
+ for (const item of this.toolItems.values()) {
1424
+ item.status = status;
1425
+ item.arguments = normalizeToolCallArguments(item.name, item.arguments, this.toolSchemas.get(item.name));
1426
+ const outputIndex = this.output.indexOf(item);
1427
+ events.push({
1428
+ type: 'response.output_item.added',
1429
+ sequence_number: this.nextSequence(),
1430
+ output_index: outputIndex,
1431
+ item: snapshotResponseItem({
1432
+ ...item,
1433
+ status: 'in_progress',
1434
+ arguments: '',
1435
+ }),
1436
+ });
1437
+ if (item.arguments) {
1438
+ events.push({
1439
+ type: 'response.function_call_arguments.delta',
1440
+ sequence_number: this.nextSequence(),
1441
+ output_index: outputIndex,
1442
+ item_id: item.id,
1443
+ delta: item.arguments,
1444
+ });
1445
+ }
1446
+ events.push({
1447
+ type: 'response.function_call_arguments.done',
1448
+ sequence_number: this.nextSequence(),
1449
+ output_index: outputIndex,
1450
+ item_id: item.id,
1451
+ arguments: item.arguments,
1452
+ });
1453
+ events.push({
1454
+ type: 'response.output_item.done',
1455
+ sequence_number: this.nextSequence(),
1456
+ output_index: outputIndex,
1457
+ item: snapshotResponseItem(item),
1458
+ });
1459
+ }
1460
+
1461
+ const response = createBaseResponse({
1462
+ id: this.responseId,
1463
+ model: this.model,
1464
+ createdAt: this.createdAt,
1465
+ status,
1466
+ output: this.output,
1467
+ previousResponseId: this.previousResponseId,
1468
+ usage: normalizeResponsesUsage(usage),
1469
+ normalized: this.normalized,
1470
+ completedAt: Date.now() / 1000,
1471
+ incompleteReason: finishReason === 'length' ? 'max_output_tokens' : null,
1472
+ });
1473
+ events.push({
1474
+ type: status === 'incomplete' ? 'response.incomplete' : 'response.completed',
1475
+ sequence_number: this.nextSequence(),
1476
+ response,
1477
+ });
1478
+ return events;
1479
+ }
1480
+
1481
+ const events = [];
1482
+ this.finishReason = finishReason;
1483
+ this.usage = normalizeResponsesUsage(usage);
1484
+ this.completedAt = Date.now() / 1000;
1485
+ if (this.reasoningItem) {
1486
+ this.reasoningItem.status = finishReason === 'length' ? 'incomplete' : 'completed';
1487
+ if (this.emitReasoningSummary && this.reasoningText && !this.reasoningSummaryAdded) {
1488
+ this.reasoningSummaryAdded = true;
1489
+ this.reasoningItem.summary.push(normalizeSummaryTextPart(reasoningSummaryText(this.reasoningText)));
1490
+ }
1491
+ this.reasoningItem.content[0].text = this.reasoningText;
1492
+ if (this.reasoningItem.summary[0]) {
1493
+ this.reasoningItem.summary[0].text = reasoningSummaryText(this.reasoningText);
1494
+ }
1495
+ }
1496
+ if (this.messageItem) {
1497
+ this.messageItem.status = finishReason === 'length' ? 'incomplete' : 'completed';
1498
+ this.messageItem.content[0].text = this.text;
1499
+ if (!this.messageContentAdded) {
1500
+ this.messageContentAdded = true;
1501
+ events.push({
1502
+ type: 'response.content_part.added',
1503
+ sequence_number: this.nextSequence(),
1504
+ output_index: this.output.indexOf(this.messageItem),
1505
+ content_index: 0,
1506
+ item_id: this.messageItem.id,
1507
+ part: snapshotResponsePart(this.messageItem.content[0]),
1508
+ });
1509
+ }
1510
+ events.push({
1511
+ type: 'response.output_text.done',
1512
+ sequence_number: this.nextSequence(),
1513
+ output_index: this.output.indexOf(this.messageItem),
1514
+ content_index: 0,
1515
+ item_id: this.messageItem.id,
1516
+ text: this.text,
1517
+ logprobs: [],
1518
+ });
1519
+ events.push({
1520
+ type: 'response.content_part.done',
1521
+ sequence_number: this.nextSequence(),
1522
+ output_index: this.output.indexOf(this.messageItem),
1523
+ content_index: 0,
1524
+ item_id: this.messageItem.id,
1525
+ part: snapshotResponsePart(this.messageItem.content[0]),
1526
+ });
1527
+ events.push({
1528
+ type: 'response.output_item.done',
1529
+ sequence_number: this.nextSequence(),
1530
+ output_index: this.output.indexOf(this.messageItem),
1531
+ item: snapshotResponseItem(this.messageItem),
1532
+ });
1533
+ }
1534
+ events.push(...this.closeReasoningItem(finishReason === 'length' ? 'incomplete' : 'completed'));
1535
+ for (const item of this.toolItems.values()) {
1536
+ item.status = finishReason === 'length' ? 'incomplete' : 'completed';
1537
+ normalizeFunctionCallItemArguments(item, this.toolSchemas);
1538
+ events.push({
1539
+ type: 'response.function_call_arguments.done',
1540
+ sequence_number: this.nextSequence(),
1541
+ output_index: this.output.indexOf(item),
1542
+ item_id: item.id,
1543
+ arguments: item.arguments,
1544
+ });
1545
+ events.push({
1546
+ type: 'response.output_item.done',
1547
+ sequence_number: this.nextSequence(),
1548
+ output_index: this.output.indexOf(item),
1549
+ item: snapshotResponseItem(item),
1550
+ });
1551
+ }
1552
+ const response = createBaseResponse({
1553
+ id: this.responseId,
1554
+ model: this.model,
1555
+ createdAt: this.createdAt,
1556
+ status,
1557
+ output: this.output,
1558
+ previousResponseId: this.previousResponseId,
1559
+ usage: normalizeResponsesUsage(usage),
1560
+ normalized: this.normalized,
1561
+ completedAt: Date.now() / 1000,
1562
+ incompleteReason: finishReason === 'length' ? 'max_output_tokens' : null,
1563
+ });
1564
+ events.push({
1565
+ type: status === 'incomplete' ? 'response.incomplete' : 'response.completed',
1566
+ sequence_number: this.nextSequence(),
1567
+ response,
1568
+ });
1569
+ return events;
1570
+ }
1571
+
1572
+ assistantMessage() {
1573
+ return assistantMessageFromResponseOutput(this.output);
1574
+ }
1575
+
1576
+ flushBufferedReasoning(status = 'completed') {
1577
+ if (!this.reasoningItem || this.reasoningItemDone) return [];
1578
+ const events = [];
1579
+ const outputIndex = this.output.indexOf(this.reasoningItem);
1580
+ const summaryText = reasoningSummaryText(this.reasoningText);
1581
+ if (!this.reasoningItemAdded) {
1582
+ this.reasoningItemAdded = true;
1583
+ events.push({
1584
+ type: 'response.output_item.added',
1585
+ sequence_number: this.nextSequence(),
1586
+ output_index: outputIndex,
1587
+ item: snapshotResponseItem({
1588
+ ...this.reasoningItem,
1589
+ status: 'in_progress',
1590
+ summary: [],
1591
+ content: [normalizeReasoningTextPart('')],
1592
+ }),
1593
+ });
1594
+ }
1595
+ if (this.emitReasoningText) {
1596
+ this.reasoningContentAdded = true;
1597
+ events.push({
1598
+ type: 'response.content_part.added',
1599
+ sequence_number: this.nextSequence(),
1600
+ output_index: outputIndex,
1601
+ content_index: 0,
1602
+ item_id: this.reasoningItem.id,
1603
+ part: snapshotResponsePart(normalizeReasoningTextPart('')),
1604
+ });
1605
+ if (this.reasoningText) {
1606
+ for (const delta of splitBufferedReasoningDeltas(this.reasoningText)) {
1607
+ events.push({
1608
+ type: 'response.reasoning_text.delta',
1609
+ sequence_number: this.nextSequence(),
1610
+ output_index: outputIndex,
1611
+ item_id: this.reasoningItem.id,
1612
+ content_index: 0,
1613
+ delta,
1614
+ });
1615
+ }
1616
+ }
1617
+ }
1618
+ if (this.emitReasoningSummary && !this.reasoningSummaryAdded) {
1619
+ this.reasoningSummaryAdded = true;
1620
+ this.reasoningItem.summary = [normalizeSummaryTextPart(summaryText)];
1621
+ events.push({
1622
+ type: 'response.reasoning_summary_part.added',
1623
+ sequence_number: this.nextSequence(),
1624
+ output_index: outputIndex,
1625
+ item_id: this.reasoningItem.id,
1626
+ summary_index: 0,
1627
+ part: snapshotResponsePart(normalizeSummaryTextPart('')),
1628
+ });
1629
+ for (const delta of splitBufferedReasoningDeltas(summaryText)) {
1630
+ events.push({
1631
+ type: 'response.reasoning_summary_text.delta',
1632
+ sequence_number: this.nextSequence(),
1633
+ output_index: outputIndex,
1634
+ item_id: this.reasoningItem.id,
1635
+ summary_index: 0,
1636
+ delta,
1637
+ });
1638
+ }
1639
+ this.streamedReasoningSummaryText = summaryText;
1640
+ } else if (this.emitReasoningSummary && this.reasoningItem.summary[0]) {
1641
+ this.reasoningItem.summary[0].text = summaryText;
1642
+ }
1643
+ this.reasoningItem.status = status;
1644
+ this.reasoningItem.content[0].text = this.reasoningText;
1645
+ return events;
1646
+ }
1647
+
1648
+ closeReasoningItem(status = 'completed') {
1649
+ if (!this.reasoningItem || this.reasoningItemDone) return [];
1650
+ this.reasoningItemDone = true;
1651
+ this.reasoningItem.status = status;
1652
+ this.reasoningItem.content[0].text = this.reasoningText;
1653
+ const summaryText = reasoningSummaryText(this.reasoningText);
1654
+ if (this.emitReasoningSummary && this.reasoningText && !this.reasoningSummaryAdded) {
1655
+ this.reasoningSummaryAdded = true;
1656
+ this.reasoningItem.summary.push(normalizeSummaryTextPart(summaryText));
1657
+ }
1658
+ const events = [];
1659
+ if (this.reasoningSummaryAdded) {
1660
+ for (const [summaryIndex, part] of this.reasoningItem.summary.entries()) {
1661
+ events.push({
1662
+ type: 'response.reasoning_summary_text.done',
1663
+ sequence_number: this.nextSequence(),
1664
+ output_index: this.output.indexOf(this.reasoningItem),
1665
+ item_id: this.reasoningItem.id,
1666
+ summary_index: summaryIndex,
1667
+ text: part.text,
1668
+ });
1669
+ events.push({
1670
+ type: 'response.reasoning_summary_part.done',
1671
+ sequence_number: this.nextSequence(),
1672
+ output_index: this.output.indexOf(this.reasoningItem),
1673
+ item_id: this.reasoningItem.id,
1674
+ summary_index: summaryIndex,
1675
+ part: snapshotResponsePart(part),
1676
+ });
1677
+ }
1678
+ }
1679
+ if (this.reasoningContentAdded) {
1680
+ events.push({
1681
+ type: 'response.reasoning_text.done',
1682
+ sequence_number: this.nextSequence(),
1683
+ output_index: this.output.indexOf(this.reasoningItem),
1684
+ item_id: this.reasoningItem.id,
1685
+ content_index: 0,
1686
+ text: this.reasoningText,
1687
+ });
1688
+ events.push({
1689
+ type: 'response.content_part.done',
1690
+ sequence_number: this.nextSequence(),
1691
+ output_index: this.output.indexOf(this.reasoningItem),
1692
+ content_index: 0,
1693
+ item_id: this.reasoningItem.id,
1694
+ part: snapshotResponsePart(this.reasoningItem.content[0]),
1695
+ });
1696
+ }
1697
+ events.push({
1698
+ type: 'response.output_item.done',
1699
+ sequence_number: this.nextSequence(),
1700
+ output_index: this.output.indexOf(this.reasoningItem),
1701
+ item: snapshotResponseItem(this.reasoningItem),
1702
+ });
1703
+ return events;
1704
+ }
1705
+
1706
+ mapChatEvent(event) {
1707
+ if (!event) return [];
1708
+ if (event.done) return this.finalize(this.finishReason || 'stop', this.usage);
1709
+ const payload = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
1710
+ if (!isObject(payload)) return [];
1711
+ const choice = Array.isArray(payload.choices) ? payload.choices[0] : null;
1712
+ if (!choice) return [];
1713
+ const delta = choice.delta || choice.message || {};
1714
+ const events = [];
1715
+ if (typeof delta.reasoning_content === 'string' && delta.reasoning_content) {
1716
+ events.push(...this.reasoningDelta(delta.reasoning_content));
1717
+ }
1718
+ if (typeof delta.content === 'string' && delta.content) {
1719
+ events.push(...this.textDelta(delta.content));
1720
+ } else if (Array.isArray(delta.content)) {
1721
+ const text = toText(delta.content);
1722
+ if (text) events.push(...this.textDelta(text));
1723
+ }
1724
+ if (Array.isArray(delta.tool_calls)) {
1725
+ for (const [position, toolCall] of delta.tool_calls.entries()) {
1726
+ events.push(...this.functionDelta({ ...toolCall, index: toolCall.index ?? position }));
1727
+ }
1728
+ }
1729
+ if (choice.finish_reason) {
1730
+ events.push(...this.finalize(choice.finish_reason, payload.usage ?? null));
1731
+ }
1732
+ return events;
1733
+ }
1734
+ }
1735
+
1736
+ export function serializeResponsesSseEvent(event) {
1737
+ if (!event) return '';
1738
+ if (event.done) return 'data: [DONE]\n\n';
1739
+ return `event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`;
1740
+ }