@galaxy-yearn/codex-deepseek-gateway 0.1.4 → 0.1.6
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/README.md +85 -199
- package/bin/codex-deepseek-gateway.js +52 -12
- package/config/codex-model-catalog.json +130 -0
- package/config/gateway.example.json +1 -17
- package/package.json +3 -2
- package/src/codex-launch.js +242 -39
- package/src/codex-sessions.js +72 -16
- package/src/config.js +1 -1
- package/src/firecrawl.js +2 -2
- package/src/protocol.js +640 -130
- package/src/server.js +128 -15
- package/src/tavily.js +3 -3
- package/src/web-search-emulator.js +104 -23
package/src/protocol.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { generateId, isObject, normalizeRole, toText } from './common.js';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_MODEL_ALIASES,
|
|
4
|
+
deepseekReasoningPayload,
|
|
5
|
+
isDeprecatedModel,
|
|
6
|
+
resolveModelAlias,
|
|
7
|
+
} from './model-map.js';
|
|
3
8
|
|
|
4
9
|
const TEXT_PART_TYPES = new Set(['input_text', 'output_text', 'text']);
|
|
5
10
|
const INPUT_CONTENT_PART_TYPES = new Set(['input_text', 'input_image', 'input_file', 'input_audio']);
|
|
@@ -10,6 +15,7 @@ const TOOL_CALL_TYPES = new Set([
|
|
|
10
15
|
'computer_call',
|
|
11
16
|
'mcp_call',
|
|
12
17
|
'web_search_call',
|
|
18
|
+
'tool_search_call',
|
|
13
19
|
'file_search_call',
|
|
14
20
|
'code_interpreter_call',
|
|
15
21
|
'image_generation_call',
|
|
@@ -21,18 +27,47 @@ const TOOL_OUTPUT_TYPES = new Set([
|
|
|
21
27
|
'computer_call_output',
|
|
22
28
|
'mcp_call_output',
|
|
23
29
|
'web_search_call_output',
|
|
30
|
+
'tool_search_output',
|
|
24
31
|
'file_search_call_output',
|
|
25
32
|
'code_interpreter_call_output',
|
|
26
33
|
'image_generation_call_output',
|
|
27
34
|
]);
|
|
28
|
-
const CHAT_HISTORY_IGNORED_TOOL_ITEM_TYPES = new Set([
|
|
35
|
+
const CHAT_HISTORY_IGNORED_TOOL_ITEM_TYPES = new Set([
|
|
36
|
+
'web_search_call',
|
|
37
|
+
'web_search_call_output',
|
|
38
|
+
'tool_search_call',
|
|
39
|
+
'tool_search_output',
|
|
40
|
+
]);
|
|
29
41
|
const EMULATED_HOSTED_TOOL_TYPES = new Set(['web_search', 'web_search_preview']);
|
|
42
|
+
const DEEPSEEK_TOOL_INSTRUCTIONS_MARKER = 'The tools in this request are real callable functions available now.';
|
|
43
|
+
const DEEPSEEK_TOOL_INSTRUCTIONS = [
|
|
44
|
+
DEEPSEEK_TOOL_INSTRUCTIONS_MARKER,
|
|
45
|
+
'When a tool is needed, respond with Chat Completions tool_calls and JSON arguments matching the schema.',
|
|
46
|
+
'Do not write pseudo XML, DSML, or JSON tool calls in assistant text.',
|
|
47
|
+
'Do not claim a listed function is unavailable because its name is unfamiliar.',
|
|
48
|
+
].join(' ');
|
|
49
|
+
const DEEPSEEK_TOOL_DESCRIPTION_CHARS = 220;
|
|
50
|
+
const DEEPSEEK_SCHEMA_DESCRIPTION_CHARS = 160;
|
|
51
|
+
const CODEX_REASONING_EFFORTS = ['low', 'medium', 'high', 'xhigh'];
|
|
52
|
+
const CODEX_SPAWN_AGENT_TOOL_NAME = 'multi_agent_v1__spawn_agent';
|
|
53
|
+
const CODEX_TOOL_SEARCH_TOOL_NAME = 'tool_search';
|
|
30
54
|
|
|
31
55
|
function jsonString(value) {
|
|
32
56
|
if (typeof value === 'string') return value;
|
|
33
57
|
return JSON.stringify(value ?? {});
|
|
34
58
|
}
|
|
35
59
|
|
|
60
|
+
function parseJsonObject(value) {
|
|
61
|
+
if (isObject(value)) return value;
|
|
62
|
+
if (typeof value !== 'string') return {};
|
|
63
|
+
try {
|
|
64
|
+
const parsed = JSON.parse(value);
|
|
65
|
+
return isObject(parsed) ? parsed : {};
|
|
66
|
+
} catch {
|
|
67
|
+
return {};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
36
71
|
function sanitizeFunctionName(name, fallback = 'tool_call') {
|
|
37
72
|
const candidate = String(name || fallback)
|
|
38
73
|
.replace(/[^a-zA-Z0-9_-]/g, '_')
|
|
@@ -41,6 +76,34 @@ function sanitizeFunctionName(name, fallback = 'tool_call') {
|
|
|
41
76
|
return candidate || fallback;
|
|
42
77
|
}
|
|
43
78
|
|
|
79
|
+
function toolNamespace(tool) {
|
|
80
|
+
if (!isObject(tool)) return '';
|
|
81
|
+
return String(tool.namespace || tool.function?.namespace || '').trim();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function toolBaseName(tool, fallback = 'tool_call') {
|
|
85
|
+
if (!isObject(tool)) return fallback;
|
|
86
|
+
if (isObject(tool.function) && tool.function.name) return tool.function.name;
|
|
87
|
+
return tool.name || tool.tool_name || tool.toolName || tool.server_label || fallback;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function encodeToolName(namespace, name, fallback = 'tool_call') {
|
|
91
|
+
const baseName = sanitizeFunctionName(name, fallback);
|
|
92
|
+
if (!namespace) return baseName;
|
|
93
|
+
const encodedNamespace = sanitizeFunctionName(namespace, 'namespace');
|
|
94
|
+
return sanitizeFunctionName(`${encodedNamespace}__${baseName}`, fallback);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function decodedToolName(name, toolNames) {
|
|
98
|
+
const value = String(name || '');
|
|
99
|
+
const known = toolNames?.get(value);
|
|
100
|
+
return known ? { ...known } : { name: value };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function isCodexToolSearchTool(name) {
|
|
104
|
+
return String(name || '') === CODEX_TOOL_SEARCH_TOOL_NAME;
|
|
105
|
+
}
|
|
106
|
+
|
|
44
107
|
function omitUndefined(value) {
|
|
45
108
|
if (!isObject(value)) return value;
|
|
46
109
|
const result = {};
|
|
@@ -50,6 +113,19 @@ function omitUndefined(value) {
|
|
|
50
113
|
return result;
|
|
51
114
|
}
|
|
52
115
|
|
|
116
|
+
function compactText(value) {
|
|
117
|
+
return String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function shortenText(value, maxChars) {
|
|
121
|
+
const text = compactText(value);
|
|
122
|
+
if (!text || text.length <= maxChars) return text;
|
|
123
|
+
const softLimit = Math.max(24, Math.floor(maxChars * 0.65));
|
|
124
|
+
const slice = text.slice(0, maxChars - 3);
|
|
125
|
+
const breakAt = Math.max(slice.lastIndexOf('. '), slice.lastIndexOf('; '), slice.lastIndexOf(', '), slice.lastIndexOf(' '));
|
|
126
|
+
return `${slice.slice(0, breakAt >= softLimit ? breakAt : slice.length).trimEnd()}...`;
|
|
127
|
+
}
|
|
128
|
+
|
|
53
129
|
function textPart(text) {
|
|
54
130
|
return { type: 'text', text: String(text ?? '') };
|
|
55
131
|
}
|
|
@@ -137,8 +213,9 @@ function toolCallId(item) {
|
|
|
137
213
|
}
|
|
138
214
|
|
|
139
215
|
function toolName(item) {
|
|
140
|
-
return
|
|
141
|
-
item
|
|
216
|
+
return encodeToolName(
|
|
217
|
+
toolNamespace(item),
|
|
218
|
+
item.name ||
|
|
142
219
|
item.tool_name ||
|
|
143
220
|
item.toolName ||
|
|
144
221
|
item.server_label ||
|
|
@@ -148,6 +225,7 @@ function toolName(item) {
|
|
|
148
225
|
|
|
149
226
|
function toolArguments(item) {
|
|
150
227
|
if (item.type === 'function_call') return jsonString(item.arguments ?? {});
|
|
228
|
+
if (item.type === 'tool_search_call') return jsonString(item.arguments ?? {});
|
|
151
229
|
if (item.arguments !== undefined) return jsonString(item.arguments);
|
|
152
230
|
if (item.input !== undefined) return jsonString({ input: item.input });
|
|
153
231
|
if (item.action !== undefined) return jsonString({ action: item.action });
|
|
@@ -170,35 +248,55 @@ function chatToolCallFromResponseItem(item) {
|
|
|
170
248
|
|
|
171
249
|
function reasoningTextFromItem(item) {
|
|
172
250
|
if (!isObject(item)) return '';
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (typeof item.text === 'string')
|
|
176
|
-
if (typeof item.content === 'string')
|
|
251
|
+
if (typeof item.reasoning_content === 'string') return normalizeReasoningContent(item.reasoning_content);
|
|
252
|
+
const rawParts = [];
|
|
253
|
+
if (typeof item.text === 'string') rawParts.push(item.text);
|
|
254
|
+
if (typeof item.content === 'string') rawParts.push(item.content);
|
|
177
255
|
if (Array.isArray(item.content)) {
|
|
178
256
|
for (const part of item.content) {
|
|
179
257
|
if (typeof part === 'string') {
|
|
180
|
-
|
|
258
|
+
rawParts.push(part);
|
|
181
259
|
continue;
|
|
182
260
|
}
|
|
183
261
|
if (!isObject(part)) continue;
|
|
184
262
|
if (part.type === 'reasoning_text' || part.type === 'text' || part.type === 'output_text') {
|
|
185
|
-
|
|
263
|
+
rawParts.push(String(part.text ?? part.content ?? ''));
|
|
186
264
|
}
|
|
187
265
|
}
|
|
188
266
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
267
|
+
const rawText = rawParts.filter(Boolean).join('');
|
|
268
|
+
return normalizeReasoningContent(rawText);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function normalizeReasoningContent(text) {
|
|
272
|
+
return String(text ?? '').replace(/\r\n?/g, '\n').trimEnd();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function normalizeReasoningDisplayText(text) {
|
|
276
|
+
const value = normalizeReasoningContent(text);
|
|
277
|
+
if (!value) return '';
|
|
278
|
+
const plain = value
|
|
279
|
+
.split('\n')
|
|
280
|
+
.map((line) => line
|
|
281
|
+
.replace(/^[ \t]{0,3}(?:`{3,}|~{3,}).*$/, '')
|
|
282
|
+
.replace(/^[ \t]{0,3}#{1,6}[ \t]+/, '')
|
|
283
|
+
.replace(/^[ \t]{0,3}>[ \t]?/, '')
|
|
284
|
+
.replace(/^[ \t]*[-*+][ \t]+(?:\[[ xX]\][ \t]+)?/, '\u2022 ')
|
|
285
|
+
.replace(/^[ \t]*(\d+)\.[ \t]+/, '$1) ')
|
|
286
|
+
.replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1')
|
|
287
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
|
|
288
|
+
.replace(/`([^`]*)`/g, '$1')
|
|
289
|
+
.replace(/(\*\*|__)([^*_]+)\1/g, '$2')
|
|
290
|
+
.replace(/(\*|_)([^*_]+)\1/g, '$2')
|
|
291
|
+
.replace(/[*_`~]/g, '')
|
|
292
|
+
.replace(/\\([\\`*_{}[\]()#+\-.!>])/g, '$1')
|
|
293
|
+
.trimEnd())
|
|
294
|
+
.join('\n');
|
|
295
|
+
return plain.replace(/^\n+/, '').trimEnd();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function reasoningDisplayText(text) {
|
|
299
|
+
return normalizeReasoningDisplayText(text);
|
|
202
300
|
}
|
|
203
301
|
|
|
204
302
|
function toolOutputContent(item) {
|
|
@@ -346,24 +444,28 @@ function extractMessagesFromResponsesInput(input) {
|
|
|
346
444
|
|
|
347
445
|
function normalizeTool(tool) {
|
|
348
446
|
if (!isObject(tool)) return tool;
|
|
447
|
+
if (tool.type === 'namespace') return null;
|
|
349
448
|
if (typeof tool.type === 'string' && EMULATED_HOSTED_TOOL_TYPES.has(tool.type)) {
|
|
350
449
|
return null;
|
|
351
450
|
}
|
|
352
451
|
if (tool.type === 'function' && isObject(tool.function)) {
|
|
452
|
+
const { namespace, ...fn } = tool.function;
|
|
353
453
|
return {
|
|
354
|
-
|
|
355
|
-
function: {
|
|
356
|
-
...
|
|
357
|
-
name:
|
|
358
|
-
|
|
359
|
-
|
|
454
|
+
type: 'function',
|
|
455
|
+
function: omitUndefined({
|
|
456
|
+
...fn,
|
|
457
|
+
name: encodeToolName(toolNamespace(tool), fn.name),
|
|
458
|
+
description: fn.description ?? tool.description,
|
|
459
|
+
parameters: normalizeJsonSchemaObject(fn.parameters ?? tool.parameters ?? tool.input_schema),
|
|
460
|
+
strict: fn.strict ?? tool.strict,
|
|
461
|
+
}),
|
|
360
462
|
};
|
|
361
463
|
}
|
|
362
464
|
if (tool.type === 'function' || tool.type === 'custom' || tool.name || tool.parameters || tool.input_schema) {
|
|
363
465
|
return {
|
|
364
466
|
type: 'function',
|
|
365
467
|
function: omitUndefined({
|
|
366
|
-
name:
|
|
468
|
+
name: encodeToolName(toolNamespace(tool), toolBaseName(tool, tool.type)),
|
|
367
469
|
description: tool.description,
|
|
368
470
|
parameters: normalizeJsonSchemaObject(tool.parameters || tool.input_schema),
|
|
369
471
|
strict: tool.strict,
|
|
@@ -373,6 +475,75 @@ function normalizeTool(tool) {
|
|
|
373
475
|
return null;
|
|
374
476
|
}
|
|
375
477
|
|
|
478
|
+
function expandNamespaceTool(tool) {
|
|
479
|
+
if (!isObject(tool) || tool.type !== 'namespace') return [tool];
|
|
480
|
+
const namespace = tool.name || tool.namespace || tool.server_label;
|
|
481
|
+
const children = Array.isArray(tool.tools) ? tool.tools : [];
|
|
482
|
+
return children
|
|
483
|
+
.filter(isObject)
|
|
484
|
+
.map((child) => {
|
|
485
|
+
if (child.type === 'function' && isObject(child.function)) {
|
|
486
|
+
return {
|
|
487
|
+
...child,
|
|
488
|
+
namespace: child.namespace || namespace,
|
|
489
|
+
function: {
|
|
490
|
+
...child.function,
|
|
491
|
+
namespace: child.function.namespace || child.namespace || namespace,
|
|
492
|
+
},
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
return {
|
|
496
|
+
...child,
|
|
497
|
+
namespace: child.namespace || namespace,
|
|
498
|
+
};
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function expandTools(tools) {
|
|
503
|
+
if (!Array.isArray(tools)) return [];
|
|
504
|
+
return tools.flatMap(expandNamespaceTool);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function normalizeTools(tools) {
|
|
508
|
+
const seen = new Set();
|
|
509
|
+
const normalized = [];
|
|
510
|
+
for (const tool of expandTools(tools).map(normalizeTool).filter(Boolean)) {
|
|
511
|
+
const name = tool?.type === 'function' ? tool.function?.name : '';
|
|
512
|
+
if (name) {
|
|
513
|
+
if (seen.has(name)) continue;
|
|
514
|
+
seen.add(name);
|
|
515
|
+
}
|
|
516
|
+
normalized.push(tool);
|
|
517
|
+
}
|
|
518
|
+
return normalized.length ? normalized : undefined;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function collectToolSearchOutputTools(input) {
|
|
522
|
+
const discovered = [];
|
|
523
|
+
const scan = (value) => {
|
|
524
|
+
if (Array.isArray(value)) {
|
|
525
|
+
for (const item of value) scan(item);
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
if (!isObject(value)) return;
|
|
529
|
+
if (value.type === 'tool_search_output' && Array.isArray(value.tools)) {
|
|
530
|
+
discovered.push(...value.tools.filter(isObject));
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
if (Array.isArray(value.input)) scan(value.input);
|
|
534
|
+
if (Array.isArray(value.content)) scan(value.content);
|
|
535
|
+
};
|
|
536
|
+
scan(input);
|
|
537
|
+
return discovered;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function mergeToolsWithToolSearchOutput(requestTools, input) {
|
|
541
|
+
const discovered = collectToolSearchOutputTools(input);
|
|
542
|
+
if (!discovered.length) return requestTools;
|
|
543
|
+
const base = Array.isArray(requestTools) ? requestTools : [];
|
|
544
|
+
return base.concat(discovered);
|
|
545
|
+
}
|
|
546
|
+
|
|
376
547
|
function normalizeJsonSchemaObject(schema) {
|
|
377
548
|
if (!isObject(schema)) {
|
|
378
549
|
return { type: 'object', properties: {}, additionalProperties: true };
|
|
@@ -405,7 +576,7 @@ function normalizeToolChoice(toolChoice) {
|
|
|
405
576
|
if (toolChoice.type === 'function' && toolChoice.name) {
|
|
406
577
|
return {
|
|
407
578
|
type: 'function',
|
|
408
|
-
function: { name:
|
|
579
|
+
function: { name: encodeToolName(toolNamespace(toolChoice), toolChoice.name) },
|
|
409
580
|
};
|
|
410
581
|
}
|
|
411
582
|
if (toolChoice.type === 'function' && isObject(toolChoice.function)) {
|
|
@@ -413,7 +584,7 @@ function normalizeToolChoice(toolChoice) {
|
|
|
413
584
|
...toolChoice,
|
|
414
585
|
function: {
|
|
415
586
|
...toolChoice.function,
|
|
416
|
-
name:
|
|
587
|
+
name: encodeToolName(toolNamespace(toolChoice), toolChoice.function.name),
|
|
417
588
|
},
|
|
418
589
|
};
|
|
419
590
|
}
|
|
@@ -424,7 +595,8 @@ function normalizeToolChoice(toolChoice) {
|
|
|
424
595
|
return {
|
|
425
596
|
type: 'function',
|
|
426
597
|
function: {
|
|
427
|
-
name:
|
|
598
|
+
name: encodeToolName(
|
|
599
|
+
toolNamespace(toolChoice),
|
|
428
600
|
toolChoice.name ||
|
|
429
601
|
toolChoice.tool_name ||
|
|
430
602
|
toolChoice.toolName ||
|
|
@@ -437,6 +609,45 @@ function normalizeToolChoice(toolChoice) {
|
|
|
437
609
|
return toolChoice;
|
|
438
610
|
}
|
|
439
611
|
|
|
612
|
+
function toolChoiceEntryName(entry) {
|
|
613
|
+
if (!isObject(entry)) return '';
|
|
614
|
+
if (entry.type === 'function' && entry.name) return encodeToolName(toolNamespace(entry), entry.name);
|
|
615
|
+
if (entry.type === 'function' && isObject(entry.function)) {
|
|
616
|
+
return encodeToolName(toolNamespace(entry), entry.function.name);
|
|
617
|
+
}
|
|
618
|
+
return encodeToolName(
|
|
619
|
+
toolNamespace(entry),
|
|
620
|
+
entry.name ||
|
|
621
|
+
entry.tool_name ||
|
|
622
|
+
entry.toolName ||
|
|
623
|
+
entry.server_label ||
|
|
624
|
+
entry.type,
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function allowedToolNames(toolChoice) {
|
|
629
|
+
if (!isObject(toolChoice) || toolChoice.type !== 'allowed_tools') return null;
|
|
630
|
+
const entries = Array.isArray(toolChoice.tools)
|
|
631
|
+
? toolChoice.tools
|
|
632
|
+
: Array.isArray(toolChoice.allowed_tools)
|
|
633
|
+
? toolChoice.allowed_tools
|
|
634
|
+
: Array.isArray(toolChoice.names)
|
|
635
|
+
? toolChoice.names.map((name) => ({ type: 'function', name }))
|
|
636
|
+
: [];
|
|
637
|
+
const names = entries.map(toolChoiceEntryName).filter(Boolean);
|
|
638
|
+
return names.length ? new Set(names) : null;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function applyAllowedTools(tools, toolChoice) {
|
|
642
|
+
const allowed = allowedToolNames(toolChoice);
|
|
643
|
+
if (!allowed || !Array.isArray(tools)) return tools;
|
|
644
|
+
const filtered = tools.filter((tool) => {
|
|
645
|
+
const name = tool?.type === 'function' ? tool.function?.name : '';
|
|
646
|
+
return !name || allowed.has(name);
|
|
647
|
+
});
|
|
648
|
+
return filtered.length ? filtered : undefined;
|
|
649
|
+
}
|
|
650
|
+
|
|
440
651
|
function unwrapJsonString(value) {
|
|
441
652
|
if (typeof value !== 'string') return value;
|
|
442
653
|
const trimmed = value.trim();
|
|
@@ -469,10 +680,63 @@ function coerceValueForSchema(value, schema) {
|
|
|
469
680
|
return value;
|
|
470
681
|
}
|
|
471
682
|
|
|
683
|
+
function toolSearchArgumentsFromText(argumentsText) {
|
|
684
|
+
const parsed = parseJsonObject(argumentsText);
|
|
685
|
+
const args = {};
|
|
686
|
+
if (parsed.limit !== undefined) {
|
|
687
|
+
const limit = Number(parsed.limit);
|
|
688
|
+
if (Number.isFinite(limit)) args.limit = limit;
|
|
689
|
+
}
|
|
690
|
+
if (parsed.query !== undefined) args.query = String(parsed.query);
|
|
691
|
+
return args;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function finalizeToolSearchCallItemArguments(item) {
|
|
695
|
+
if (!item || item.type !== 'tool_search_call') return;
|
|
696
|
+
if (typeof item.arguments === 'string') {
|
|
697
|
+
item.arguments = toolSearchArgumentsFromText(item.arguments);
|
|
698
|
+
} else if (!isObject(item.arguments)) {
|
|
699
|
+
item.arguments = {};
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function convertItemToToolSearchCall(item) {
|
|
704
|
+
if (!item || item.type === 'tool_search_call') return item;
|
|
705
|
+
item.type = 'tool_search_call';
|
|
706
|
+
item.id = generateId('tsc');
|
|
707
|
+
item.execution = 'client';
|
|
708
|
+
delete item.name;
|
|
709
|
+
delete item.namespace;
|
|
710
|
+
return item;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
function responseItemFromChatToolCall(toolCall, toolNames) {
|
|
714
|
+
const callId = toolCall.id || generateId('call');
|
|
715
|
+
if (isCodexToolSearchTool(toolCall.function?.name)) {
|
|
716
|
+
return {
|
|
717
|
+
type: 'tool_search_call',
|
|
718
|
+
id: generateId('tsc'),
|
|
719
|
+
call_id: callId,
|
|
720
|
+
status: 'completed',
|
|
721
|
+
execution: 'client',
|
|
722
|
+
arguments: toolSearchArgumentsFromText(toolCall.function?.arguments || ''),
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
const decoded = decodedToolName(toolCall.function?.name, toolNames);
|
|
726
|
+
return omitUndefined({
|
|
727
|
+
type: 'function_call',
|
|
728
|
+
id: callId,
|
|
729
|
+
call_id: callId,
|
|
730
|
+
name: decoded.name,
|
|
731
|
+
namespace: decoded.namespace,
|
|
732
|
+
arguments: toolCall.function?.arguments || '',
|
|
733
|
+
status: 'completed',
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
|
|
472
737
|
function buildToolSchemas(tools) {
|
|
473
738
|
const schemas = new Map();
|
|
474
|
-
|
|
475
|
-
for (const tool of tools) {
|
|
739
|
+
for (const tool of expandTools(tools)) {
|
|
476
740
|
const normalizedTool = normalizeTool(tool);
|
|
477
741
|
const fn = normalizedTool?.type === 'function' ? normalizedTool.function : null;
|
|
478
742
|
if (!fn?.name) continue;
|
|
@@ -481,6 +745,160 @@ function buildToolSchemas(tools) {
|
|
|
481
745
|
return schemas;
|
|
482
746
|
}
|
|
483
747
|
|
|
748
|
+
function buildToolNames(tools) {
|
|
749
|
+
const names = new Map();
|
|
750
|
+
for (const tool of expandTools(tools)) {
|
|
751
|
+
const namespace = toolNamespace(tool);
|
|
752
|
+
if (!namespace) continue;
|
|
753
|
+
const normalizedTool = normalizeTool(tool);
|
|
754
|
+
const fn = normalizedTool?.type === 'function' ? normalizedTool.function : null;
|
|
755
|
+
if (!fn?.name) continue;
|
|
756
|
+
names.set(fn.name, {
|
|
757
|
+
namespace: sanitizeFunctionName(namespace, 'namespace'),
|
|
758
|
+
name: sanitizeFunctionName(toolBaseName(tool, tool.type)),
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
return names;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function simplifyJsonSchemaDescriptions(schema) {
|
|
765
|
+
if (Array.isArray(schema)) return schema.map(simplifyJsonSchemaDescriptions);
|
|
766
|
+
if (!isObject(schema)) return schema;
|
|
767
|
+
const next = {};
|
|
768
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
769
|
+
if (key === 'description' && typeof value === 'string') {
|
|
770
|
+
const description = shortenText(value, DEEPSEEK_SCHEMA_DESCRIPTION_CHARS);
|
|
771
|
+
if (description) next.description = description;
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
next[key] = isObject(value) || Array.isArray(value) ? simplifyJsonSchemaDescriptions(value) : value;
|
|
775
|
+
}
|
|
776
|
+
return next;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
function readableToolName(name) {
|
|
780
|
+
return compactText(String(name || '').replace(/[_-]+/g, ' '));
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function requiredParametersText(parameters) {
|
|
784
|
+
const required = Array.isArray(parameters?.required) ? parameters.required.filter((name) => typeof name === 'string') : [];
|
|
785
|
+
return required.length ? ` Required: ${required.join(', ')}.` : '';
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function defaultDeepSeekToolDescription(name) {
|
|
789
|
+
const readable = readableToolName(name);
|
|
790
|
+
return compactText(`Call ${readable || name || 'this function'} when needed.`);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function deepSeekGatewayModelAliases(config = {}) {
|
|
794
|
+
const aliases = isObject(config.modelAliases) && Object.keys(config.modelAliases).length
|
|
795
|
+
? config.modelAliases
|
|
796
|
+
: DEFAULT_MODEL_ALIASES;
|
|
797
|
+
const spawnConfig = { ...config, upstreamProvider: 'deepseek', modelAliases: aliases };
|
|
798
|
+
const names = [];
|
|
799
|
+
for (const name of Object.keys(aliases)) {
|
|
800
|
+
if (!name || isDeprecatedModel(name)) continue;
|
|
801
|
+
const alias = resolveModelAlias(name, spawnConfig);
|
|
802
|
+
if (isDeprecatedModel(alias.upstreamModel)) continue;
|
|
803
|
+
names.push(name);
|
|
804
|
+
}
|
|
805
|
+
return [...new Set(names)];
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function enumStringProperty(property, values, description) {
|
|
809
|
+
const source = isObject(property) ? property : {};
|
|
810
|
+
const next = { ...source };
|
|
811
|
+
delete next.const;
|
|
812
|
+
delete next.oneOf;
|
|
813
|
+
delete next.anyOf;
|
|
814
|
+
delete next.allOf;
|
|
815
|
+
delete next.examples;
|
|
816
|
+
if (next.default !== undefined && !values.includes(next.default)) delete next.default;
|
|
817
|
+
next.type = 'string';
|
|
818
|
+
next.enum = values;
|
|
819
|
+
next.description = description;
|
|
820
|
+
return next;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function deepSeekSpawnAgentParameters(parameters, config = {}) {
|
|
824
|
+
const next = normalizeJsonSchemaObject(parameters);
|
|
825
|
+
const properties = isObject(next.properties) ? { ...next.properties } : {};
|
|
826
|
+
const modelAliases = deepSeekGatewayModelAliases(config);
|
|
827
|
+
properties.model = enumStringProperty(
|
|
828
|
+
properties.model,
|
|
829
|
+
modelAliases,
|
|
830
|
+
'Model for the sub-agent. Omit to inherit.',
|
|
831
|
+
);
|
|
832
|
+
properties.reasoning_effort = enumStringProperty(
|
|
833
|
+
properties.reasoning_effort,
|
|
834
|
+
CODEX_REASONING_EFFORTS,
|
|
835
|
+
'Codex reasoning effort for the sub-agent. Omit to inherit.',
|
|
836
|
+
);
|
|
837
|
+
return {
|
|
838
|
+
...next,
|
|
839
|
+
properties,
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
function deepSeekSpawnAgentDescription() {
|
|
844
|
+
const description = [
|
|
845
|
+
'Spawn a Codex-native sub-agent.',
|
|
846
|
+
'Omit model/reasoning_effort to inherit.',
|
|
847
|
+
'Valid values are enforced by the schema.',
|
|
848
|
+
].filter(Boolean).join(' ');
|
|
849
|
+
return shortenText(description, DEEPSEEK_TOOL_DESCRIPTION_CHARS);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
function isCodexSpawnAgentTool(name) {
|
|
853
|
+
return name === CODEX_SPAWN_AGENT_TOOL_NAME || name === 'spawn_agent';
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
function simplifyToolForDeepSeek(tool, config = {}) {
|
|
857
|
+
if (tool?.type !== 'function' || !isObject(tool.function)) return tool;
|
|
858
|
+
const fn = tool.function;
|
|
859
|
+
let parameters = simplifyJsonSchemaDescriptions(fn.parameters);
|
|
860
|
+
if (isCodexSpawnAgentTool(fn.name)) {
|
|
861
|
+
parameters = deepSeekSpawnAgentParameters(parameters, config);
|
|
862
|
+
}
|
|
863
|
+
const requiredText = requiredParametersText(parameters);
|
|
864
|
+
const descriptionBudget = Math.max(32, DEEPSEEK_TOOL_DESCRIPTION_CHARS - requiredText.length);
|
|
865
|
+
const baseDescription = isCodexSpawnAgentTool(fn.name)
|
|
866
|
+
? deepSeekSpawnAgentDescription()
|
|
867
|
+
: shortenText(fn.description, descriptionBudget) || defaultDeepSeekToolDescription(fn.name);
|
|
868
|
+
const description = shortenText(`${baseDescription}${requiredText}`, DEEPSEEK_TOOL_DESCRIPTION_CHARS);
|
|
869
|
+
return {
|
|
870
|
+
...tool,
|
|
871
|
+
function: omitUndefined({
|
|
872
|
+
...fn,
|
|
873
|
+
description,
|
|
874
|
+
parameters,
|
|
875
|
+
}),
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function hasDeepSeekToolInstructions(messages) {
|
|
880
|
+
return messages.some((message) => message?.role === 'system' && toText(message.content).includes(DEEPSEEK_TOOL_INSTRUCTIONS_MARKER));
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
function addDeepSeekToolInstructions(messages, tools) {
|
|
884
|
+
if (!Array.isArray(tools) || !tools.length || hasDeepSeekToolInstructions(messages)) return messages;
|
|
885
|
+
if (!messages.length || messages[0]?.role !== 'system') {
|
|
886
|
+
return [{ role: 'system', content: DEEPSEEK_TOOL_INSTRUCTIONS }, ...messages];
|
|
887
|
+
}
|
|
888
|
+
return [
|
|
889
|
+
{
|
|
890
|
+
...messages[0],
|
|
891
|
+
content: `${toText(messages[0].content)}\n\n${DEEPSEEK_TOOL_INSTRUCTIONS}`,
|
|
892
|
+
},
|
|
893
|
+
...messages.slice(1),
|
|
894
|
+
];
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
function adaptToolsForProvider(tools, provider, config = {}) {
|
|
898
|
+
if (provider !== 'deepseek' || !Array.isArray(tools)) return tools;
|
|
899
|
+
return tools.map((tool) => simplifyToolForDeepSeek(tool, config));
|
|
900
|
+
}
|
|
901
|
+
|
|
484
902
|
function normalizeToolCallArguments(name, argumentsText, toolSchema) {
|
|
485
903
|
if (!argumentsText) return '';
|
|
486
904
|
if (!isObject(toolSchema) || !isObject(toolSchema.properties)) return argumentsText;
|
|
@@ -503,9 +921,66 @@ function normalizeToolCallArguments(name, argumentsText, toolSchema) {
|
|
|
503
921
|
}
|
|
504
922
|
}
|
|
505
923
|
|
|
506
|
-
function
|
|
924
|
+
function normalizeReasoningEffortName(value) {
|
|
925
|
+
return value == null ? '' : String(value).toLowerCase().replaceAll('_', '-');
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
function normalizeCodexSpawnAgentArguments(encodedName, argumentsText, config = {}) {
|
|
929
|
+
if (!isCodexSpawnAgentTool(encodedName) || !argumentsText) return argumentsText;
|
|
930
|
+
let parsed;
|
|
931
|
+
try {
|
|
932
|
+
parsed = JSON.parse(argumentsText);
|
|
933
|
+
} catch {
|
|
934
|
+
return argumentsText;
|
|
935
|
+
}
|
|
936
|
+
if (!isObject(parsed)) return argumentsText;
|
|
937
|
+
const next = { ...parsed };
|
|
938
|
+
let changed = false;
|
|
939
|
+
const allowedModels = deepSeekGatewayModelAliases(config);
|
|
940
|
+
|
|
941
|
+
if (typeof next.model === 'string') {
|
|
942
|
+
const requestedModel = next.model;
|
|
943
|
+
if (allowedModels.length && !allowedModels.includes(requestedModel)) {
|
|
944
|
+
delete next.model;
|
|
945
|
+
changed = true;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
if (typeof next.reasoning_effort === 'string') {
|
|
950
|
+
const requestedEffort = normalizeReasoningEffortName(next.reasoning_effort);
|
|
951
|
+
if (!CODEX_REASONING_EFFORTS.includes(requestedEffort)) {
|
|
952
|
+
delete next.reasoning_effort;
|
|
953
|
+
changed = true;
|
|
954
|
+
return JSON.stringify(next);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
return changed ? JSON.stringify(next) : argumentsText;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
function normalizeFunctionCallItemArguments(item, toolSchemas, config = {}) {
|
|
507
962
|
if (!item || item.type !== 'function_call') return;
|
|
508
|
-
|
|
963
|
+
const encodedName = encodeToolName(item.namespace, item.name);
|
|
964
|
+
item.arguments = normalizeToolCallArguments(encodedName, item.arguments, toolSchemas?.get(encodedName));
|
|
965
|
+
item.arguments = normalizeCodexSpawnAgentArguments(encodedName, item.arguments, config);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function sanitizeToolCallsForChatCompletion(toolCalls) {
|
|
969
|
+
return toolCalls.map((toolCall) => {
|
|
970
|
+
if (!isObject(toolCall)) return toolCall;
|
|
971
|
+
const fn = isObject(toolCall.function) ? toolCall.function : {};
|
|
972
|
+
const { namespace, ...functionFields } = fn;
|
|
973
|
+
const name = fn.name || toolCall.name || 'tool_call';
|
|
974
|
+
const toolCallNamespace = toolCall.namespace || namespace || '';
|
|
975
|
+
return {
|
|
976
|
+
id: toolCall.id,
|
|
977
|
+
type: toolCall.type || 'function',
|
|
978
|
+
function: {
|
|
979
|
+
...functionFields,
|
|
980
|
+
name: encodeToolName(toolCallNamespace, name),
|
|
981
|
+
},
|
|
982
|
+
};
|
|
983
|
+
});
|
|
509
984
|
}
|
|
510
985
|
|
|
511
986
|
function sanitizeMessageForChatCompletion(message, provider = 'generic') {
|
|
@@ -515,7 +990,9 @@ function sanitizeMessageForChatCompletion(message, provider = 'generic') {
|
|
|
515
990
|
content: contentToChatContent(message.content),
|
|
516
991
|
};
|
|
517
992
|
if (message.name !== undefined) sanitized.name = message.name;
|
|
518
|
-
if (Array.isArray(message.tool_calls))
|
|
993
|
+
if (Array.isArray(message.tool_calls)) {
|
|
994
|
+
sanitized.tool_calls = sanitizeToolCallsForChatCompletion(message.tool_calls);
|
|
995
|
+
}
|
|
519
996
|
if (message.tool_call_id !== undefined) sanitized.tool_call_id = message.tool_call_id;
|
|
520
997
|
if (provider === 'deepseek' && message.role === 'assistant' && typeof message.reasoning_content === 'string') {
|
|
521
998
|
sanitized.reasoning_content = message.reasoning_content;
|
|
@@ -543,32 +1020,8 @@ function normalizeOutputTextPart(text, annotations = []) {
|
|
|
543
1020
|
};
|
|
544
1021
|
}
|
|
545
1022
|
|
|
546
|
-
function normalizeReasoningSummaryText(text) {
|
|
547
|
-
const value = String(text ?? '').replace(/\r\n?/g, '\n');
|
|
548
|
-
if (!value) return '';
|
|
549
|
-
const plain = value
|
|
550
|
-
.split('\n')
|
|
551
|
-
.map((line) => line
|
|
552
|
-
.replace(/^[ \t]{0,3}(?:`{3,}|~{3,}).*$/, '')
|
|
553
|
-
.replace(/^[ \t]{0,3}#{1,6}[ \t]+/, '')
|
|
554
|
-
.replace(/^[ \t]{0,3}>[ \t]?/, '')
|
|
555
|
-
.replace(/^[ \t]*[-*+][ \t]+(?:\[[ xX]\][ \t]+)?/, '\u2022 ')
|
|
556
|
-
.replace(/^[ \t]*(\d+)\.[ \t]+/, '$1) ')
|
|
557
|
-
.replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1')
|
|
558
|
-
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
|
|
559
|
-
.replace(/`([^`]*)`/g, '$1')
|
|
560
|
-
.replace(/(\*\*|__)([^*_]+)\1/g, '$2')
|
|
561
|
-
.replace(/(\*|_)([^*_]+)\1/g, '$2')
|
|
562
|
-
.replace(/[*_`~]/g, '')
|
|
563
|
-
.replace(/\\([\\`*_{}[\]()#+\-.!>])/g, '$1')
|
|
564
|
-
.trimEnd())
|
|
565
|
-
.join('\n');
|
|
566
|
-
return plain.replace(/^\n+/, '').trimEnd();
|
|
567
|
-
}
|
|
568
|
-
|
|
569
1023
|
function reasoningSummaryText(text) {
|
|
570
|
-
|
|
571
|
-
return summary ? `**Reasoning**\n\n${summary}` : '';
|
|
1024
|
+
return reasoningDisplayText(text);
|
|
572
1025
|
}
|
|
573
1026
|
|
|
574
1027
|
function normalizeSummaryTextPart(text) {
|
|
@@ -611,6 +1064,16 @@ function snapshotResponsePart(part) {
|
|
|
611
1064
|
|
|
612
1065
|
function snapshotResponseItem(item) {
|
|
613
1066
|
if (!isObject(item)) return item;
|
|
1067
|
+
if (item.type === 'tool_search_call') {
|
|
1068
|
+
return omitUndefined({
|
|
1069
|
+
type: item.type,
|
|
1070
|
+
id: item.id,
|
|
1071
|
+
call_id: item.call_id,
|
|
1072
|
+
status: item.status,
|
|
1073
|
+
execution: item.execution,
|
|
1074
|
+
arguments: isObject(item.arguments) ? { ...item.arguments } : toolSearchArgumentsFromText(item.arguments),
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
614
1077
|
return {
|
|
615
1078
|
...item,
|
|
616
1079
|
content: Array.isArray(item.content) ? item.content.map(snapshotResponsePart) : item.content,
|
|
@@ -676,6 +1139,7 @@ export function normalizeResponsesRequest(request) {
|
|
|
676
1139
|
const model = request.model || request.model_id || request.upstream_model || '';
|
|
677
1140
|
const instructions = normalizeInstructions(request.instructions);
|
|
678
1141
|
const responseFormat = request.response_format || request.text?.format;
|
|
1142
|
+
const tools = mergeToolsWithToolSearchOutput(request.tools, request.input ?? request);
|
|
679
1143
|
return {
|
|
680
1144
|
model,
|
|
681
1145
|
messages,
|
|
@@ -685,7 +1149,7 @@ export function normalizeResponsesRequest(request) {
|
|
|
685
1149
|
max_tokens: request.max_output_tokens ?? request.max_tokens,
|
|
686
1150
|
stop: request.stop,
|
|
687
1151
|
stream: Boolean(request.stream),
|
|
688
|
-
tools
|
|
1152
|
+
tools,
|
|
689
1153
|
tool_choice: request.tool_choice,
|
|
690
1154
|
parallel_tool_calls: request.parallel_tool_calls,
|
|
691
1155
|
presence_penalty: request.presence_penalty,
|
|
@@ -710,7 +1174,7 @@ export function toChatCompletionsRequest(normalized, overrides = {}) {
|
|
|
710
1174
|
messages.push({ role: 'system', content: normalized.instructions });
|
|
711
1175
|
}
|
|
712
1176
|
messages.push(...normalized.messages);
|
|
713
|
-
const tools =
|
|
1177
|
+
const tools = applyAllowedTools(normalizeTools(normalized.tools), normalized.tool_choice);
|
|
714
1178
|
const request = {
|
|
715
1179
|
model: overrides.model || normalized.model,
|
|
716
1180
|
messages,
|
|
@@ -757,16 +1221,14 @@ export function assistantMessageFromResponseOutput(output) {
|
|
|
757
1221
|
id: item.call_id || item.id,
|
|
758
1222
|
type: 'function',
|
|
759
1223
|
function: {
|
|
760
|
-
name: item.name,
|
|
1224
|
+
name: encodeToolName(item.namespace, item.name),
|
|
761
1225
|
arguments: item.arguments || '',
|
|
762
1226
|
},
|
|
763
1227
|
}));
|
|
764
1228
|
if (toolCalls.length) assistant.tool_calls = toolCalls;
|
|
765
1229
|
const reasoningContent = reasoningItems
|
|
766
|
-
.map((item) => item
|
|
767
|
-
.
|
|
768
|
-
.filter((part) => part.type === 'reasoning_text')
|
|
769
|
-
.map((part) => part.text)
|
|
1230
|
+
.map((item) => reasoningTextFromItem(item))
|
|
1231
|
+
.filter(Boolean)
|
|
770
1232
|
.join('');
|
|
771
1233
|
if (reasoningContent) assistant.reasoning_content = reasoningContent;
|
|
772
1234
|
return assistant;
|
|
@@ -820,6 +1282,8 @@ export function toProviderChatCompletionsRequest(chatRequest, config = {}) {
|
|
|
820
1282
|
if (provider === 'deepseek') {
|
|
821
1283
|
delete request.reasoning_effort;
|
|
822
1284
|
delete request.parallel_tool_calls;
|
|
1285
|
+
request.tools = adaptToolsForProvider(request.tools, provider, config);
|
|
1286
|
+
request.messages = addDeepSeekToolInstructions(request.messages, request.tools);
|
|
823
1287
|
if (request.user !== undefined) {
|
|
824
1288
|
request.user_id = request.user;
|
|
825
1289
|
delete request.user;
|
|
@@ -949,12 +1413,13 @@ export function createResponseEnvelope({
|
|
|
949
1413
|
});
|
|
950
1414
|
}
|
|
951
1415
|
|
|
952
|
-
export function convertChatCompletionToResponses({ completion, model, previousResponseId, normalized, responseId = generateId('resp') }) {
|
|
1416
|
+
export function convertChatCompletionToResponses({ completion, model, previousResponseId, normalized, responseId = generateId('resp'), config = {} }) {
|
|
953
1417
|
const createdAt = completion.created || Date.now() / 1000;
|
|
954
1418
|
const choice = Array.isArray(completion.choices) ? completion.choices[0] : null;
|
|
955
1419
|
const message = choice?.message || {};
|
|
956
1420
|
const content = chatContentToResponseOutputParts(message.content);
|
|
957
1421
|
const toolSchemas = buildToolSchemas(normalized?.tools);
|
|
1422
|
+
const toolNames = buildToolNames(normalized?.tools);
|
|
958
1423
|
const output = [];
|
|
959
1424
|
|
|
960
1425
|
if (content.length || message.tool_calls?.length) {
|
|
@@ -970,27 +1435,20 @@ export function convertChatCompletionToResponses({ completion, model, previousRe
|
|
|
970
1435
|
|
|
971
1436
|
if (Array.isArray(message.tool_calls)) {
|
|
972
1437
|
for (const toolCall of message.tool_calls) {
|
|
973
|
-
const
|
|
974
|
-
|
|
975
|
-
type: 'function_call',
|
|
976
|
-
id: callId,
|
|
977
|
-
call_id: callId,
|
|
978
|
-
name: toolCall.function?.name,
|
|
979
|
-
arguments: toolCall.function?.arguments || '',
|
|
980
|
-
status: 'completed',
|
|
981
|
-
};
|
|
982
|
-
normalizeFunctionCallItemArguments(item, toolSchemas);
|
|
1438
|
+
const item = responseItemFromChatToolCall(toolCall, toolNames);
|
|
1439
|
+
normalizeFunctionCallItemArguments(item, toolSchemas, config);
|
|
983
1440
|
output.push(item);
|
|
984
1441
|
}
|
|
985
1442
|
}
|
|
986
1443
|
|
|
987
1444
|
if (message.reasoning_content) {
|
|
988
|
-
const reasoningText =
|
|
1445
|
+
const reasoningText = normalizeReasoningContent(message.reasoning_content);
|
|
989
1446
|
output.unshift({
|
|
990
1447
|
type: 'reasoning',
|
|
991
1448
|
id: generateId('rs'),
|
|
992
1449
|
summary: [normalizeSummaryTextPart(reasoningSummaryText(reasoningText))],
|
|
993
|
-
content: [
|
|
1450
|
+
content: [],
|
|
1451
|
+
reasoning_content: reasoningText,
|
|
994
1452
|
encrypted_content: null,
|
|
995
1453
|
status: 'completed',
|
|
996
1454
|
});
|
|
@@ -1020,6 +1478,7 @@ export class ResponsesStreamMapper {
|
|
|
1020
1478
|
bufferOutputUntilDone = false,
|
|
1021
1479
|
emitReasoningSummary = true,
|
|
1022
1480
|
emitReasoningText = false,
|
|
1481
|
+
config = {},
|
|
1023
1482
|
} = {}) {
|
|
1024
1483
|
this.responseId = responseId;
|
|
1025
1484
|
this.model = model;
|
|
@@ -1048,6 +1507,8 @@ export class ResponsesStreamMapper {
|
|
|
1048
1507
|
this.reasoningItemAdded = false;
|
|
1049
1508
|
this.streamedReasoningSummaryText = '';
|
|
1050
1509
|
this.toolSchemas = buildToolSchemas(normalized?.tools);
|
|
1510
|
+
this.toolNames = buildToolNames(normalized?.tools);
|
|
1511
|
+
this.config = config;
|
|
1051
1512
|
}
|
|
1052
1513
|
|
|
1053
1514
|
nextSequence() {
|
|
@@ -1114,7 +1575,7 @@ export class ResponsesStreamMapper {
|
|
|
1114
1575
|
type: 'reasoning',
|
|
1115
1576
|
status: 'in_progress',
|
|
1116
1577
|
summary: [],
|
|
1117
|
-
content: [normalizeReasoningTextPart('')],
|
|
1578
|
+
content: this.emitReasoningText ? [normalizeReasoningTextPart('')] : [],
|
|
1118
1579
|
encrypted_content: null,
|
|
1119
1580
|
};
|
|
1120
1581
|
this.output.push(this.reasoningItem);
|
|
@@ -1133,6 +1594,7 @@ export class ResponsesStreamMapper {
|
|
|
1133
1594
|
|
|
1134
1595
|
ensureReasoningContentPart(events) {
|
|
1135
1596
|
if (!this.emitReasoningText || this.reasoningContentAdded || !this.reasoningItem) return;
|
|
1597
|
+
if (!this.reasoningItem.content[0]) this.reasoningItem.content[0] = normalizeReasoningTextPart('');
|
|
1136
1598
|
this.reasoningContentAdded = true;
|
|
1137
1599
|
events.push({
|
|
1138
1600
|
type: 'response.content_part.added',
|
|
@@ -1144,6 +1606,19 @@ export class ResponsesStreamMapper {
|
|
|
1144
1606
|
});
|
|
1145
1607
|
}
|
|
1146
1608
|
|
|
1609
|
+
syncReasoningItemContent() {
|
|
1610
|
+
if (!this.reasoningItem) return;
|
|
1611
|
+
const reasoningText = normalizeReasoningContent(this.reasoningText);
|
|
1612
|
+
if (this.emitReasoningText) {
|
|
1613
|
+
if (!this.reasoningItem.content[0]) this.reasoningItem.content[0] = normalizeReasoningTextPart('');
|
|
1614
|
+
this.reasoningItem.content[0].text = reasoningText;
|
|
1615
|
+
} else {
|
|
1616
|
+
this.reasoningItem.content = [];
|
|
1617
|
+
}
|
|
1618
|
+
if (reasoningText) this.reasoningItem.reasoning_content = reasoningText;
|
|
1619
|
+
else delete this.reasoningItem.reasoning_content;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1147
1622
|
appendReasoningSummaryDelta(events) {
|
|
1148
1623
|
if (!this.emitReasoningSummary || !this.reasoningItem) return;
|
|
1149
1624
|
if (!this.reasoningSummaryAdded) {
|
|
@@ -1234,7 +1709,7 @@ export class ResponsesStreamMapper {
|
|
|
1234
1709
|
const events = [];
|
|
1235
1710
|
const added = this.ensureReasoningItem();
|
|
1236
1711
|
if (added) events.push(added);
|
|
1237
|
-
this.
|
|
1712
|
+
this.syncReasoningItemContent();
|
|
1238
1713
|
this.appendReasoningSummaryDelta(events);
|
|
1239
1714
|
return events;
|
|
1240
1715
|
}
|
|
@@ -1245,12 +1720,12 @@ export class ResponsesStreamMapper {
|
|
|
1245
1720
|
type: 'reasoning',
|
|
1246
1721
|
status: 'in_progress',
|
|
1247
1722
|
summary: [],
|
|
1248
|
-
content: [normalizeReasoningTextPart('')],
|
|
1723
|
+
content: this.emitReasoningText ? [normalizeReasoningTextPart('')] : [],
|
|
1249
1724
|
encrypted_content: null,
|
|
1250
1725
|
};
|
|
1251
1726
|
this.output.push(this.reasoningItem);
|
|
1252
1727
|
}
|
|
1253
|
-
this.
|
|
1728
|
+
this.syncReasoningItemContent();
|
|
1254
1729
|
return [];
|
|
1255
1730
|
}
|
|
1256
1731
|
|
|
@@ -1258,7 +1733,7 @@ export class ResponsesStreamMapper {
|
|
|
1258
1733
|
const added = this.ensureReasoningItem();
|
|
1259
1734
|
if (added) events.push(added);
|
|
1260
1735
|
this.ensureReasoningContentPart(events);
|
|
1261
|
-
this.
|
|
1736
|
+
this.syncReasoningItemContent();
|
|
1262
1737
|
this.appendReasoningSummaryDelta(events);
|
|
1263
1738
|
if (this.emitReasoningText) {
|
|
1264
1739
|
events.push({
|
|
@@ -1279,18 +1754,31 @@ export class ResponsesStreamMapper {
|
|
|
1279
1754
|
let item = this.toolItems.get(index);
|
|
1280
1755
|
if (!item) {
|
|
1281
1756
|
const callId = toolCall.id || generateId('call');
|
|
1282
|
-
item =
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1757
|
+
item = isCodexToolSearchTool(toolCall.function?.name)
|
|
1758
|
+
? {
|
|
1759
|
+
id: generateId('tsc'),
|
|
1760
|
+
type: 'tool_search_call',
|
|
1761
|
+
status: 'in_progress',
|
|
1762
|
+
call_id: callId,
|
|
1763
|
+
execution: 'client',
|
|
1764
|
+
arguments: '',
|
|
1765
|
+
}
|
|
1766
|
+
: {
|
|
1767
|
+
id: callId,
|
|
1768
|
+
type: 'function_call',
|
|
1769
|
+
status: 'in_progress',
|
|
1770
|
+
call_id: callId,
|
|
1771
|
+
...decodedToolName(toolCall.function?.name, this.toolNames),
|
|
1772
|
+
arguments: '',
|
|
1773
|
+
};
|
|
1290
1774
|
this.toolItems.set(index, item);
|
|
1291
1775
|
this.output.push(item);
|
|
1292
1776
|
}
|
|
1293
|
-
if (toolCall.function?.name)
|
|
1777
|
+
if (isCodexToolSearchTool(toolCall.function?.name)) {
|
|
1778
|
+
item = convertItemToToolSearchCall(item);
|
|
1779
|
+
} else if (toolCall.function?.name && item.type !== 'tool_search_call') {
|
|
1780
|
+
Object.assign(item, decodedToolName(toolCall.function.name, this.toolNames));
|
|
1781
|
+
}
|
|
1294
1782
|
item.arguments += toolCall.function?.arguments || '';
|
|
1295
1783
|
return [];
|
|
1296
1784
|
}
|
|
@@ -1303,14 +1791,23 @@ export class ResponsesStreamMapper {
|
|
|
1303
1791
|
let item = this.toolItems.get(index);
|
|
1304
1792
|
if (!item) {
|
|
1305
1793
|
const callId = toolCall.id || generateId('call');
|
|
1306
|
-
item =
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1794
|
+
item = isCodexToolSearchTool(toolCall.function?.name)
|
|
1795
|
+
? {
|
|
1796
|
+
id: generateId('tsc'),
|
|
1797
|
+
type: 'tool_search_call',
|
|
1798
|
+
status: 'in_progress',
|
|
1799
|
+
call_id: callId,
|
|
1800
|
+
execution: 'client',
|
|
1801
|
+
arguments: '',
|
|
1802
|
+
}
|
|
1803
|
+
: {
|
|
1804
|
+
id: callId,
|
|
1805
|
+
type: 'function_call',
|
|
1806
|
+
status: 'in_progress',
|
|
1807
|
+
call_id: callId,
|
|
1808
|
+
...decodedToolName(toolCall.function?.name, this.toolNames),
|
|
1809
|
+
arguments: '',
|
|
1810
|
+
};
|
|
1314
1811
|
this.toolItems.set(index, item);
|
|
1315
1812
|
this.output.push(item);
|
|
1316
1813
|
events.push({
|
|
@@ -1320,10 +1817,14 @@ export class ResponsesStreamMapper {
|
|
|
1320
1817
|
item: snapshotResponseItem(item),
|
|
1321
1818
|
});
|
|
1322
1819
|
}
|
|
1323
|
-
if (toolCall.function?.name)
|
|
1820
|
+
if (isCodexToolSearchTool(toolCall.function?.name)) {
|
|
1821
|
+
item = convertItemToToolSearchCall(item);
|
|
1822
|
+
} else if (toolCall.function?.name && item.type !== 'tool_search_call') {
|
|
1823
|
+
Object.assign(item, decodedToolName(toolCall.function.name, this.toolNames));
|
|
1824
|
+
}
|
|
1324
1825
|
const delta = toolCall.function?.arguments || '';
|
|
1325
1826
|
item.arguments += delta;
|
|
1326
|
-
if (delta) {
|
|
1827
|
+
if (delta && item.type === 'function_call') {
|
|
1327
1828
|
events.push({
|
|
1328
1829
|
type: 'response.function_call_arguments.delta',
|
|
1329
1830
|
sequence_number: this.nextSequence(),
|
|
@@ -1347,7 +1848,7 @@ export class ResponsesStreamMapper {
|
|
|
1347
1848
|
|
|
1348
1849
|
if (this.reasoningItem) {
|
|
1349
1850
|
this.reasoningItem.status = status;
|
|
1350
|
-
this.
|
|
1851
|
+
this.syncReasoningItemContent();
|
|
1351
1852
|
}
|
|
1352
1853
|
if (this.messageItem) {
|
|
1353
1854
|
this.messageItem.status = status;
|
|
@@ -1421,19 +1922,23 @@ export class ResponsesStreamMapper {
|
|
|
1421
1922
|
|
|
1422
1923
|
for (const item of this.toolItems.values()) {
|
|
1423
1924
|
item.status = status;
|
|
1424
|
-
|
|
1925
|
+
if (item.type === 'tool_search_call') finalizeToolSearchCallItemArguments(item);
|
|
1926
|
+
else normalizeFunctionCallItemArguments(item, this.toolSchemas, this.config);
|
|
1425
1927
|
const outputIndex = this.output.indexOf(item);
|
|
1426
1928
|
events.push({
|
|
1427
1929
|
type: 'response.output_item.added',
|
|
1428
1930
|
sequence_number: this.nextSequence(),
|
|
1429
1931
|
output_index: outputIndex,
|
|
1430
|
-
item: snapshotResponseItem({
|
|
1932
|
+
item: snapshotResponseItem(item.type === 'tool_search_call' ? {
|
|
1933
|
+
...item,
|
|
1934
|
+
status: 'in_progress',
|
|
1935
|
+
} : {
|
|
1431
1936
|
...item,
|
|
1432
1937
|
status: 'in_progress',
|
|
1433
1938
|
arguments: '',
|
|
1434
1939
|
}),
|
|
1435
1940
|
});
|
|
1436
|
-
if (item.arguments) {
|
|
1941
|
+
if (item.type === 'function_call' && item.arguments) {
|
|
1437
1942
|
events.push({
|
|
1438
1943
|
type: 'response.function_call_arguments.delta',
|
|
1439
1944
|
sequence_number: this.nextSequence(),
|
|
@@ -1442,13 +1947,15 @@ export class ResponsesStreamMapper {
|
|
|
1442
1947
|
delta: item.arguments,
|
|
1443
1948
|
});
|
|
1444
1949
|
}
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1950
|
+
if (item.type === 'function_call') {
|
|
1951
|
+
events.push({
|
|
1952
|
+
type: 'response.function_call_arguments.done',
|
|
1953
|
+
sequence_number: this.nextSequence(),
|
|
1954
|
+
output_index: outputIndex,
|
|
1955
|
+
item_id: item.id,
|
|
1956
|
+
arguments: item.arguments,
|
|
1957
|
+
});
|
|
1958
|
+
}
|
|
1452
1959
|
events.push({
|
|
1453
1960
|
type: 'response.output_item.done',
|
|
1454
1961
|
sequence_number: this.nextSequence(),
|
|
@@ -1487,7 +1994,7 @@ export class ResponsesStreamMapper {
|
|
|
1487
1994
|
this.reasoningSummaryAdded = true;
|
|
1488
1995
|
this.reasoningItem.summary.push(normalizeSummaryTextPart(reasoningSummaryText(this.reasoningText)));
|
|
1489
1996
|
}
|
|
1490
|
-
this.
|
|
1997
|
+
this.syncReasoningItemContent();
|
|
1491
1998
|
if (this.reasoningItem.summary[0]) {
|
|
1492
1999
|
this.reasoningItem.summary[0].text = reasoningSummaryText(this.reasoningText);
|
|
1493
2000
|
}
|
|
@@ -1533,14 +2040,17 @@ export class ResponsesStreamMapper {
|
|
|
1533
2040
|
events.push(...this.closeReasoningItem(finishReason === 'length' ? 'incomplete' : 'completed'));
|
|
1534
2041
|
for (const item of this.toolItems.values()) {
|
|
1535
2042
|
item.status = finishReason === 'length' ? 'incomplete' : 'completed';
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
2043
|
+
if (item.type === 'tool_search_call') finalizeToolSearchCallItemArguments(item);
|
|
2044
|
+
else normalizeFunctionCallItemArguments(item, this.toolSchemas, this.config);
|
|
2045
|
+
if (item.type === 'function_call') {
|
|
2046
|
+
events.push({
|
|
2047
|
+
type: 'response.function_call_arguments.done',
|
|
2048
|
+
sequence_number: this.nextSequence(),
|
|
2049
|
+
output_index: this.output.indexOf(item),
|
|
2050
|
+
item_id: item.id,
|
|
2051
|
+
arguments: item.arguments,
|
|
2052
|
+
});
|
|
2053
|
+
}
|
|
1544
2054
|
events.push({
|
|
1545
2055
|
type: 'response.output_item.done',
|
|
1546
2056
|
sequence_number: this.nextSequence(),
|
|
@@ -1587,7 +2097,7 @@ export class ResponsesStreamMapper {
|
|
|
1587
2097
|
...this.reasoningItem,
|
|
1588
2098
|
status: 'in_progress',
|
|
1589
2099
|
summary: [],
|
|
1590
|
-
content: [normalizeReasoningTextPart('')],
|
|
2100
|
+
content: this.emitReasoningText ? [normalizeReasoningTextPart('')] : [],
|
|
1591
2101
|
}),
|
|
1592
2102
|
});
|
|
1593
2103
|
}
|
|
@@ -1640,7 +2150,7 @@ export class ResponsesStreamMapper {
|
|
|
1640
2150
|
this.reasoningItem.summary[0].text = summaryText;
|
|
1641
2151
|
}
|
|
1642
2152
|
this.reasoningItem.status = status;
|
|
1643
|
-
this.
|
|
2153
|
+
this.syncReasoningItemContent();
|
|
1644
2154
|
return events;
|
|
1645
2155
|
}
|
|
1646
2156
|
|
|
@@ -1648,7 +2158,7 @@ export class ResponsesStreamMapper {
|
|
|
1648
2158
|
if (!this.reasoningItem || this.reasoningItemDone) return [];
|
|
1649
2159
|
this.reasoningItemDone = true;
|
|
1650
2160
|
this.reasoningItem.status = status;
|
|
1651
|
-
this.
|
|
2161
|
+
this.syncReasoningItemContent();
|
|
1652
2162
|
const summaryText = reasoningSummaryText(this.reasoningText);
|
|
1653
2163
|
if (this.emitReasoningSummary && this.reasoningText && !this.reasoningSummaryAdded) {
|
|
1654
2164
|
this.reasoningSummaryAdded = true;
|