@galaxy-yearn/codex-deepseek-gateway 0.1.5 → 0.1.7
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 +42 -29
- package/bin/codex-deepseek-gateway.js +47 -11
- package/config/codex-model-catalog.json +130 -0
- package/config/codex-model-catalog.zh.json +130 -0
- package/config/frontend-design-guidance/en.md +33 -0
- package/config/frontend-design-guidance/zh.md +33 -0
- package/config/gateway.example.json +1 -0
- package/package.json +4 -1
- package/src/codex-launch.js +250 -45
- package/src/codex-sessions.js +19 -7
- package/src/config.js +4 -1
- package/src/local-config.js +6 -2
- package/src/prompt-language.js +13 -0
- package/src/protocol.js +328 -63
- package/src/server.js +3 -0
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,11 +27,17 @@ 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']);
|
|
30
42
|
const DEEPSEEK_TOOL_INSTRUCTIONS_MARKER = 'The tools in this request are real callable functions available now.';
|
|
31
43
|
const DEEPSEEK_TOOL_INSTRUCTIONS = [
|
|
@@ -36,12 +48,26 @@ const DEEPSEEK_TOOL_INSTRUCTIONS = [
|
|
|
36
48
|
].join(' ');
|
|
37
49
|
const DEEPSEEK_TOOL_DESCRIPTION_CHARS = 220;
|
|
38
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';
|
|
39
54
|
|
|
40
55
|
function jsonString(value) {
|
|
41
56
|
if (typeof value === 'string') return value;
|
|
42
57
|
return JSON.stringify(value ?? {});
|
|
43
58
|
}
|
|
44
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
|
+
|
|
45
71
|
function sanitizeFunctionName(name, fallback = 'tool_call') {
|
|
46
72
|
const candidate = String(name || fallback)
|
|
47
73
|
.replace(/[^a-zA-Z0-9_-]/g, '_')
|
|
@@ -74,6 +100,10 @@ function decodedToolName(name, toolNames) {
|
|
|
74
100
|
return known ? { ...known } : { name: value };
|
|
75
101
|
}
|
|
76
102
|
|
|
103
|
+
function isCodexToolSearchTool(name) {
|
|
104
|
+
return String(name || '') === CODEX_TOOL_SEARCH_TOOL_NAME;
|
|
105
|
+
}
|
|
106
|
+
|
|
77
107
|
function omitUndefined(value) {
|
|
78
108
|
if (!isObject(value)) return value;
|
|
79
109
|
const result = {};
|
|
@@ -195,6 +225,7 @@ function toolName(item) {
|
|
|
195
225
|
|
|
196
226
|
function toolArguments(item) {
|
|
197
227
|
if (item.type === 'function_call') return jsonString(item.arguments ?? {});
|
|
228
|
+
if (item.type === 'tool_search_call') return jsonString(item.arguments ?? {});
|
|
198
229
|
if (item.arguments !== undefined) return jsonString(item.arguments);
|
|
199
230
|
if (item.input !== undefined) return jsonString({ input: item.input });
|
|
200
231
|
if (item.action !== undefined) return jsonString({ action: item.action });
|
|
@@ -474,10 +505,45 @@ function expandTools(tools) {
|
|
|
474
505
|
}
|
|
475
506
|
|
|
476
507
|
function normalizeTools(tools) {
|
|
477
|
-
const
|
|
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
|
+
}
|
|
478
518
|
return normalized.length ? normalized : undefined;
|
|
479
519
|
}
|
|
480
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
|
+
|
|
481
547
|
function normalizeJsonSchemaObject(schema) {
|
|
482
548
|
if (!isObject(schema)) {
|
|
483
549
|
return { type: 'object', properties: {}, additionalProperties: true };
|
|
@@ -614,6 +680,60 @@ function coerceValueForSchema(value, schema) {
|
|
|
614
680
|
return value;
|
|
615
681
|
}
|
|
616
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
|
+
|
|
617
737
|
function buildToolSchemas(tools) {
|
|
618
738
|
const schemas = new Map();
|
|
619
739
|
for (const tool of expandTools(tools)) {
|
|
@@ -670,13 +790,81 @@ function defaultDeepSeekToolDescription(name) {
|
|
|
670
790
|
return compactText(`Call ${readable || name || 'this function'} when needed.`);
|
|
671
791
|
}
|
|
672
792
|
|
|
673
|
-
function
|
|
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 = {}) {
|
|
674
857
|
if (tool?.type !== 'function' || !isObject(tool.function)) return tool;
|
|
675
858
|
const fn = tool.function;
|
|
676
|
-
|
|
859
|
+
let parameters = simplifyJsonSchemaDescriptions(fn.parameters);
|
|
860
|
+
if (isCodexSpawnAgentTool(fn.name)) {
|
|
861
|
+
parameters = deepSeekSpawnAgentParameters(parameters, config);
|
|
862
|
+
}
|
|
677
863
|
const requiredText = requiredParametersText(parameters);
|
|
678
864
|
const descriptionBudget = Math.max(32, DEEPSEEK_TOOL_DESCRIPTION_CHARS - requiredText.length);
|
|
679
|
-
const baseDescription =
|
|
865
|
+
const baseDescription = isCodexSpawnAgentTool(fn.name)
|
|
866
|
+
? deepSeekSpawnAgentDescription()
|
|
867
|
+
: shortenText(fn.description, descriptionBudget) || defaultDeepSeekToolDescription(fn.name);
|
|
680
868
|
const description = shortenText(`${baseDescription}${requiredText}`, DEEPSEEK_TOOL_DESCRIPTION_CHARS);
|
|
681
869
|
return {
|
|
682
870
|
...tool,
|
|
@@ -706,9 +894,9 @@ function addDeepSeekToolInstructions(messages, tools) {
|
|
|
706
894
|
];
|
|
707
895
|
}
|
|
708
896
|
|
|
709
|
-
function adaptToolsForProvider(tools, provider) {
|
|
897
|
+
function adaptToolsForProvider(tools, provider, config = {}) {
|
|
710
898
|
if (provider !== 'deepseek' || !Array.isArray(tools)) return tools;
|
|
711
|
-
return tools.map(simplifyToolForDeepSeek);
|
|
899
|
+
return tools.map((tool) => simplifyToolForDeepSeek(tool, config));
|
|
712
900
|
}
|
|
713
901
|
|
|
714
902
|
function normalizeToolCallArguments(name, argumentsText, toolSchema) {
|
|
@@ -733,10 +921,48 @@ function normalizeToolCallArguments(name, argumentsText, toolSchema) {
|
|
|
733
921
|
}
|
|
734
922
|
}
|
|
735
923
|
|
|
736
|
-
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 = {}) {
|
|
737
962
|
if (!item || item.type !== 'function_call') return;
|
|
738
963
|
const encodedName = encodeToolName(item.namespace, item.name);
|
|
739
964
|
item.arguments = normalizeToolCallArguments(encodedName, item.arguments, toolSchemas?.get(encodedName));
|
|
965
|
+
item.arguments = normalizeCodexSpawnAgentArguments(encodedName, item.arguments, config);
|
|
740
966
|
}
|
|
741
967
|
|
|
742
968
|
function sanitizeToolCallsForChatCompletion(toolCalls) {
|
|
@@ -764,7 +990,9 @@ function sanitizeMessageForChatCompletion(message, provider = 'generic') {
|
|
|
764
990
|
content: contentToChatContent(message.content),
|
|
765
991
|
};
|
|
766
992
|
if (message.name !== undefined) sanitized.name = message.name;
|
|
767
|
-
if (Array.isArray(message.tool_calls))
|
|
993
|
+
if (Array.isArray(message.tool_calls)) {
|
|
994
|
+
sanitized.tool_calls = sanitizeToolCallsForChatCompletion(message.tool_calls);
|
|
995
|
+
}
|
|
768
996
|
if (message.tool_call_id !== undefined) sanitized.tool_call_id = message.tool_call_id;
|
|
769
997
|
if (provider === 'deepseek' && message.role === 'assistant' && typeof message.reasoning_content === 'string') {
|
|
770
998
|
sanitized.reasoning_content = message.reasoning_content;
|
|
@@ -836,6 +1064,16 @@ function snapshotResponsePart(part) {
|
|
|
836
1064
|
|
|
837
1065
|
function snapshotResponseItem(item) {
|
|
838
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
|
+
}
|
|
839
1077
|
return {
|
|
840
1078
|
...item,
|
|
841
1079
|
content: Array.isArray(item.content) ? item.content.map(snapshotResponsePart) : item.content,
|
|
@@ -901,6 +1139,7 @@ export function normalizeResponsesRequest(request) {
|
|
|
901
1139
|
const model = request.model || request.model_id || request.upstream_model || '';
|
|
902
1140
|
const instructions = normalizeInstructions(request.instructions);
|
|
903
1141
|
const responseFormat = request.response_format || request.text?.format;
|
|
1142
|
+
const tools = mergeToolsWithToolSearchOutput(request.tools, request.input ?? request);
|
|
904
1143
|
return {
|
|
905
1144
|
model,
|
|
906
1145
|
messages,
|
|
@@ -910,7 +1149,7 @@ export function normalizeResponsesRequest(request) {
|
|
|
910
1149
|
max_tokens: request.max_output_tokens ?? request.max_tokens,
|
|
911
1150
|
stop: request.stop,
|
|
912
1151
|
stream: Boolean(request.stream),
|
|
913
|
-
tools
|
|
1152
|
+
tools,
|
|
914
1153
|
tool_choice: request.tool_choice,
|
|
915
1154
|
parallel_tool_calls: request.parallel_tool_calls,
|
|
916
1155
|
presence_penalty: request.presence_penalty,
|
|
@@ -1043,7 +1282,7 @@ export function toProviderChatCompletionsRequest(chatRequest, config = {}) {
|
|
|
1043
1282
|
if (provider === 'deepseek') {
|
|
1044
1283
|
delete request.reasoning_effort;
|
|
1045
1284
|
delete request.parallel_tool_calls;
|
|
1046
|
-
request.tools = adaptToolsForProvider(request.tools, provider);
|
|
1285
|
+
request.tools = adaptToolsForProvider(request.tools, provider, config);
|
|
1047
1286
|
request.messages = addDeepSeekToolInstructions(request.messages, request.tools);
|
|
1048
1287
|
if (request.user !== undefined) {
|
|
1049
1288
|
request.user_id = request.user;
|
|
@@ -1174,7 +1413,7 @@ export function createResponseEnvelope({
|
|
|
1174
1413
|
});
|
|
1175
1414
|
}
|
|
1176
1415
|
|
|
1177
|
-
export function convertChatCompletionToResponses({ completion, model, previousResponseId, normalized, responseId = generateId('resp') }) {
|
|
1416
|
+
export function convertChatCompletionToResponses({ completion, model, previousResponseId, normalized, responseId = generateId('resp'), config = {} }) {
|
|
1178
1417
|
const createdAt = completion.created || Date.now() / 1000;
|
|
1179
1418
|
const choice = Array.isArray(completion.choices) ? completion.choices[0] : null;
|
|
1180
1419
|
const message = choice?.message || {};
|
|
@@ -1196,18 +1435,8 @@ export function convertChatCompletionToResponses({ completion, model, previousRe
|
|
|
1196
1435
|
|
|
1197
1436
|
if (Array.isArray(message.tool_calls)) {
|
|
1198
1437
|
for (const toolCall of message.tool_calls) {
|
|
1199
|
-
const
|
|
1200
|
-
|
|
1201
|
-
const item = omitUndefined({
|
|
1202
|
-
type: 'function_call',
|
|
1203
|
-
id: callId,
|
|
1204
|
-
call_id: callId,
|
|
1205
|
-
name: decoded.name,
|
|
1206
|
-
namespace: decoded.namespace,
|
|
1207
|
-
arguments: toolCall.function?.arguments || '',
|
|
1208
|
-
status: 'completed',
|
|
1209
|
-
});
|
|
1210
|
-
normalizeFunctionCallItemArguments(item, toolSchemas);
|
|
1438
|
+
const item = responseItemFromChatToolCall(toolCall, toolNames);
|
|
1439
|
+
normalizeFunctionCallItemArguments(item, toolSchemas, config);
|
|
1211
1440
|
output.push(item);
|
|
1212
1441
|
}
|
|
1213
1442
|
}
|
|
@@ -1249,6 +1478,7 @@ export class ResponsesStreamMapper {
|
|
|
1249
1478
|
bufferOutputUntilDone = false,
|
|
1250
1479
|
emitReasoningSummary = true,
|
|
1251
1480
|
emitReasoningText = false,
|
|
1481
|
+
config = {},
|
|
1252
1482
|
} = {}) {
|
|
1253
1483
|
this.responseId = responseId;
|
|
1254
1484
|
this.model = model;
|
|
@@ -1278,6 +1508,7 @@ export class ResponsesStreamMapper {
|
|
|
1278
1508
|
this.streamedReasoningSummaryText = '';
|
|
1279
1509
|
this.toolSchemas = buildToolSchemas(normalized?.tools);
|
|
1280
1510
|
this.toolNames = buildToolNames(normalized?.tools);
|
|
1511
|
+
this.config = config;
|
|
1281
1512
|
}
|
|
1282
1513
|
|
|
1283
1514
|
nextSequence() {
|
|
@@ -1523,18 +1754,31 @@ export class ResponsesStreamMapper {
|
|
|
1523
1754
|
let item = this.toolItems.get(index);
|
|
1524
1755
|
if (!item) {
|
|
1525
1756
|
const callId = toolCall.id || generateId('call');
|
|
1526
|
-
item =
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
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
|
+
};
|
|
1534
1774
|
this.toolItems.set(index, item);
|
|
1535
1775
|
this.output.push(item);
|
|
1536
1776
|
}
|
|
1537
|
-
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
|
+
}
|
|
1538
1782
|
item.arguments += toolCall.function?.arguments || '';
|
|
1539
1783
|
return [];
|
|
1540
1784
|
}
|
|
@@ -1547,14 +1791,23 @@ export class ResponsesStreamMapper {
|
|
|
1547
1791
|
let item = this.toolItems.get(index);
|
|
1548
1792
|
if (!item) {
|
|
1549
1793
|
const callId = toolCall.id || generateId('call');
|
|
1550
|
-
item =
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
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
|
+
};
|
|
1558
1811
|
this.toolItems.set(index, item);
|
|
1559
1812
|
this.output.push(item);
|
|
1560
1813
|
events.push({
|
|
@@ -1564,10 +1817,14 @@ export class ResponsesStreamMapper {
|
|
|
1564
1817
|
item: snapshotResponseItem(item),
|
|
1565
1818
|
});
|
|
1566
1819
|
}
|
|
1567
|
-
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
|
+
}
|
|
1568
1825
|
const delta = toolCall.function?.arguments || '';
|
|
1569
1826
|
item.arguments += delta;
|
|
1570
|
-
if (delta) {
|
|
1827
|
+
if (delta && item.type === 'function_call') {
|
|
1571
1828
|
events.push({
|
|
1572
1829
|
type: 'response.function_call_arguments.delta',
|
|
1573
1830
|
sequence_number: this.nextSequence(),
|
|
@@ -1665,20 +1922,23 @@ export class ResponsesStreamMapper {
|
|
|
1665
1922
|
|
|
1666
1923
|
for (const item of this.toolItems.values()) {
|
|
1667
1924
|
item.status = status;
|
|
1668
|
-
|
|
1669
|
-
|
|
1925
|
+
if (item.type === 'tool_search_call') finalizeToolSearchCallItemArguments(item);
|
|
1926
|
+
else normalizeFunctionCallItemArguments(item, this.toolSchemas, this.config);
|
|
1670
1927
|
const outputIndex = this.output.indexOf(item);
|
|
1671
1928
|
events.push({
|
|
1672
1929
|
type: 'response.output_item.added',
|
|
1673
1930
|
sequence_number: this.nextSequence(),
|
|
1674
1931
|
output_index: outputIndex,
|
|
1675
|
-
item: snapshotResponseItem({
|
|
1932
|
+
item: snapshotResponseItem(item.type === 'tool_search_call' ? {
|
|
1933
|
+
...item,
|
|
1934
|
+
status: 'in_progress',
|
|
1935
|
+
} : {
|
|
1676
1936
|
...item,
|
|
1677
1937
|
status: 'in_progress',
|
|
1678
1938
|
arguments: '',
|
|
1679
1939
|
}),
|
|
1680
1940
|
});
|
|
1681
|
-
if (item.arguments) {
|
|
1941
|
+
if (item.type === 'function_call' && item.arguments) {
|
|
1682
1942
|
events.push({
|
|
1683
1943
|
type: 'response.function_call_arguments.delta',
|
|
1684
1944
|
sequence_number: this.nextSequence(),
|
|
@@ -1687,13 +1947,15 @@ export class ResponsesStreamMapper {
|
|
|
1687
1947
|
delta: item.arguments,
|
|
1688
1948
|
});
|
|
1689
1949
|
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
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
|
+
}
|
|
1697
1959
|
events.push({
|
|
1698
1960
|
type: 'response.output_item.done',
|
|
1699
1961
|
sequence_number: this.nextSequence(),
|
|
@@ -1778,14 +2040,17 @@ export class ResponsesStreamMapper {
|
|
|
1778
2040
|
events.push(...this.closeReasoningItem(finishReason === 'length' ? 'incomplete' : 'completed'));
|
|
1779
2041
|
for (const item of this.toolItems.values()) {
|
|
1780
2042
|
item.status = finishReason === 'length' ? 'incomplete' : 'completed';
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
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
|
+
}
|
|
1789
2054
|
events.push({
|
|
1790
2055
|
type: 'response.output_item.done',
|
|
1791
2056
|
sequence_number: this.nextSequence(),
|
package/src/server.js
CHANGED
|
@@ -747,6 +747,7 @@ export function createProxyServer({ config = loadConfig(), sessions = new Sessio
|
|
|
747
747
|
previousResponseId,
|
|
748
748
|
normalized,
|
|
749
749
|
responseId,
|
|
750
|
+
config,
|
|
750
751
|
}), loop.searches, normalized, loop.openedPages);
|
|
751
752
|
if (loop.incompleteReason) markPayloadIncomplete(payload, loop.incompleteReason);
|
|
752
753
|
const assistantMessage = assistantMessageFromResponseOutput(payload.output);
|
|
@@ -802,6 +803,7 @@ export function createProxyServer({ config = loadConfig(), sessions = new Sessio
|
|
|
802
803
|
previousResponseId,
|
|
803
804
|
normalized,
|
|
804
805
|
responseId,
|
|
806
|
+
config,
|
|
805
807
|
});
|
|
806
808
|
nextSession.history.push({
|
|
807
809
|
request: normalized,
|
|
@@ -830,6 +832,7 @@ export function createProxyServer({ config = loadConfig(), sessions = new Sessio
|
|
|
830
832
|
createdAt: Math.floor(Date.now() / 1000),
|
|
831
833
|
previousResponseId,
|
|
832
834
|
normalized,
|
|
835
|
+
config,
|
|
833
836
|
...(() => {
|
|
834
837
|
const reasoningMode = resolveReasoningStreamMode(config, upstreamRequest);
|
|
835
838
|
return {
|