@galaxy-yearn/codex-deepseek-gateway 0.2.0 → 0.2.1
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 +10 -8
- package/README.zh-CN.md +213 -0
- package/config/codex-model-catalog.json +16 -8
- package/config/codex-model-catalog.zh.json +20 -12
- package/package.json +2 -2
- package/src/codex-launch.js +30 -7
- package/src/codex-sessions.js +69 -13
- package/src/config.js +5 -4
- package/src/firecrawl.js +8 -3
- package/src/protocol.js +107 -153
- package/src/reasoning-cache.js +235 -0
- package/src/server.js +60 -73
- package/src/web-search-emulator.js +3 -5
- package/state/reasoning-cache.example.jsonl +1 -0
- package/src/session-store.js +0 -282
- package/state/sessions.example.json +0 -72
package/src/protocol.js
CHANGED
|
@@ -2,9 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
|
|
3
3
|
import { generateId, isObject, normalizeRole, parseJsonObject, safeJsonParse, toText } from './common.js';
|
|
4
4
|
import {
|
|
5
|
-
DEFAULT_MODEL_ALIASES,
|
|
6
5
|
deepseekReasoningPayload,
|
|
7
|
-
isDeprecatedModel,
|
|
8
6
|
resolveModelAlias,
|
|
9
7
|
} from './model-map.js';
|
|
10
8
|
|
|
@@ -51,17 +49,16 @@ const UNSUPPORTED_HOSTED_TOOL_TYPES = new Set([
|
|
|
51
49
|
const DEEPSEEK_TOOL_INSTRUCTIONS_MARKER = 'The tools in this request are real callable functions available now.';
|
|
52
50
|
const DEEPSEEK_TOOL_INSTRUCTIONS = [
|
|
53
51
|
DEEPSEEK_TOOL_INSTRUCTIONS_MARKER,
|
|
54
|
-
'When
|
|
55
|
-
'
|
|
56
|
-
'Do not claim a listed function is unavailable because its name is unfamiliar.',
|
|
52
|
+
'When needed, use Chat Completions tool_calls with JSON arguments that match the schema.',
|
|
53
|
+
'Never write tool calls in assistant text, including as XML, DSML, or JSON, or claim a listed function is unavailable merely because its name is unfamiliar.',
|
|
57
54
|
].join(' ');
|
|
58
55
|
export const INTERNAL_COMMENTARY_TOOL = 'commentary';
|
|
59
|
-
const DEEPSEEK_COMMENTARY_INSTRUCTIONS = 'The user cannot see your thinking; commentary is the
|
|
56
|
+
const DEEPSEEK_COMMENTARY_INSTRUCTIONS = 'The user cannot see your thinking; commentary is the progress they see during tool work. In every tool-calling reply, include one commentary call first in the tool_calls array with a one- or two-sentence update. Never call it alone or use it for the final answer.';
|
|
60
57
|
const INTERNAL_COMMENTARY_TOOL_DEFINITION = {
|
|
61
58
|
type: 'function',
|
|
62
59
|
function: {
|
|
63
60
|
name: INTERNAL_COMMENTARY_TOOL,
|
|
64
|
-
description:
|
|
61
|
+
description: 'Post the user-visible progress update. Include this function first in every tool_calls array with other tool calls. One or two sentences; never call it alone or use it for the final answer.',
|
|
65
62
|
parameters: {
|
|
66
63
|
type: 'object',
|
|
67
64
|
properties: {
|
|
@@ -77,10 +74,9 @@ const INTERNAL_COMMENTARY_TOOL_DEFINITION = {
|
|
|
77
74
|
};
|
|
78
75
|
const CHAT_FUNCTION_NAME_MAX_CHARS = 64;
|
|
79
76
|
const TOOL_NAME_HASH_CHARS = 8;
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
const CODEX_SPAWN_AGENT_TOOL_NAME = 'multi_agent_v1__spawn_agent';
|
|
77
|
+
const DEEPSEEK_TOOL_DESCRIPTION_SOFT_CHARS = 900;
|
|
78
|
+
const DEEPSEEK_SCHEMA_DESCRIPTION_SOFT_CHARS = 480;
|
|
79
|
+
const DEEPSEEK_MAX_FUNCTION_TOOLS = 128;
|
|
84
80
|
const CODEX_TOOL_SEARCH_TOOL_NAME = 'tool_search';
|
|
85
81
|
|
|
86
82
|
function jsonString(value) {
|
|
@@ -172,6 +168,16 @@ function compactText(value) {
|
|
|
172
168
|
return String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
173
169
|
}
|
|
174
170
|
|
|
171
|
+
function compactStructuredText(value, softChars) {
|
|
172
|
+
const lines = String(value ?? '').replace(/\r\n?/g, '\n').split('\n');
|
|
173
|
+
while (lines.length && !lines[0].trim()) lines.shift();
|
|
174
|
+
while (lines.length && !lines.at(-1).trim()) lines.pop();
|
|
175
|
+
const structured = lines.join('\n').replace(/\n(?:[ \t]*\n){2,}/g, '\n\n');
|
|
176
|
+
if (!structured) return '';
|
|
177
|
+
const compact = compactText(structured);
|
|
178
|
+
return compact.length <= softChars ? compact : structured;
|
|
179
|
+
}
|
|
180
|
+
|
|
175
181
|
function shortenText(value, maxChars) {
|
|
176
182
|
const text = compactText(value);
|
|
177
183
|
if (!text || text.length <= maxChars) return text;
|
|
@@ -631,8 +637,6 @@ function extractMessagesFromResponsesInput(input) {
|
|
|
631
637
|
}
|
|
632
638
|
|
|
633
639
|
const CUSTOM_TOOL_INPUT_HINT = 'Pass the complete raw tool input as the "input" string argument. Do not wrap it in extra JSON, quotes, or markdown fences.';
|
|
634
|
-
const CUSTOM_TOOL_GRAMMAR_CHARS = 1600;
|
|
635
|
-
const CUSTOM_TOOL_DESCRIPTION_CHARS = 3600;
|
|
636
640
|
|
|
637
641
|
function truncateRawText(value, maxChars) {
|
|
638
642
|
const text = String(value ?? '');
|
|
@@ -658,25 +662,25 @@ function customToolShim(tool) {
|
|
|
658
662
|
const format = isObject(tool.format) ? tool.format : {};
|
|
659
663
|
const baseName = toolBaseName(tool, 'custom_tool');
|
|
660
664
|
const chunks = [
|
|
661
|
-
|
|
665
|
+
compactStructuredText(tool.description, DEEPSEEK_TOOL_DESCRIPTION_SOFT_CHARS) || `Freeform tool ${baseName}.`,
|
|
662
666
|
CUSTOM_TOOL_INPUT_HINT,
|
|
663
667
|
];
|
|
664
668
|
if (format.syntax) chunks.push(`Input syntax: ${format.syntax}.`);
|
|
665
669
|
if (typeof format.definition === 'string' && format.definition.trim()) {
|
|
666
|
-
chunks.push(`Input grammar:\n${
|
|
670
|
+
chunks.push(`Input grammar:\n${format.definition.trim()}`);
|
|
667
671
|
}
|
|
668
672
|
const parameters = customToolShimParameters();
|
|
669
673
|
const sourceSchema = isObject(tool.input_schema) ? tool.input_schema : isObject(tool.parameters) ? tool.parameters : null;
|
|
670
674
|
const sourceInputDescription = sourceSchema?.properties?.input?.description;
|
|
671
675
|
if (typeof sourceInputDescription === 'string' && sourceInputDescription) {
|
|
672
|
-
parameters.properties.input.description = sourceInputDescription;
|
|
676
|
+
parameters.properties.input.description = compactStructuredText(sourceInputDescription, DEEPSEEK_SCHEMA_DESCRIPTION_SOFT_CHARS);
|
|
673
677
|
}
|
|
674
678
|
return {
|
|
675
679
|
type: 'function',
|
|
676
680
|
gateway_custom_tool: true,
|
|
677
681
|
function: {
|
|
678
682
|
name: encodeToolName(toolNamespace(tool), baseName),
|
|
679
|
-
description:
|
|
683
|
+
description: chunks.join('\n'),
|
|
680
684
|
parameters,
|
|
681
685
|
},
|
|
682
686
|
};
|
|
@@ -1127,7 +1131,7 @@ function simplifyJsonSchemaDescriptions(schema) {
|
|
|
1127
1131
|
const next = {};
|
|
1128
1132
|
for (const [key, value] of Object.entries(schema)) {
|
|
1129
1133
|
if (key === 'description' && typeof value === 'string') {
|
|
1130
|
-
const description =
|
|
1134
|
+
const description = compactStructuredText(value, DEEPSEEK_SCHEMA_DESCRIPTION_SOFT_CHARS);
|
|
1131
1135
|
if (description) next.description = description;
|
|
1132
1136
|
continue;
|
|
1133
1137
|
}
|
|
@@ -1150,83 +1154,15 @@ function defaultDeepSeekToolDescription(name) {
|
|
|
1150
1154
|
return compactText(`Call ${readable || name || 'this function'} when needed.`);
|
|
1151
1155
|
}
|
|
1152
1156
|
|
|
1153
|
-
function
|
|
1154
|
-
const aliases = isObject(config.modelAliases) && Object.keys(config.modelAliases).length
|
|
1155
|
-
? config.modelAliases
|
|
1156
|
-
: DEFAULT_MODEL_ALIASES;
|
|
1157
|
-
const spawnConfig = { ...config, upstreamProvider: 'deepseek', modelAliases: aliases };
|
|
1158
|
-
const names = [];
|
|
1159
|
-
for (const name of Object.keys(aliases)) {
|
|
1160
|
-
if (!name || isDeprecatedModel(name)) continue;
|
|
1161
|
-
const alias = resolveModelAlias(name, spawnConfig);
|
|
1162
|
-
if (isDeprecatedModel(alias.upstreamModel)) continue;
|
|
1163
|
-
names.push(name);
|
|
1164
|
-
}
|
|
1165
|
-
return [...new Set(names)];
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
function enumStringProperty(property, values, description) {
|
|
1169
|
-
const source = isObject(property) ? property : {};
|
|
1170
|
-
const next = { ...source };
|
|
1171
|
-
delete next.const;
|
|
1172
|
-
delete next.oneOf;
|
|
1173
|
-
delete next.anyOf;
|
|
1174
|
-
delete next.allOf;
|
|
1175
|
-
delete next.examples;
|
|
1176
|
-
if (next.default !== undefined && !values.includes(next.default)) delete next.default;
|
|
1177
|
-
next.type = 'string';
|
|
1178
|
-
next.enum = values;
|
|
1179
|
-
next.description = description;
|
|
1180
|
-
return next;
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
function deepSeekSpawnAgentParameters(parameters, config = {}) {
|
|
1184
|
-
const next = normalizeJsonSchemaObject(parameters);
|
|
1185
|
-
const properties = isObject(next.properties) ? { ...next.properties } : {};
|
|
1186
|
-
const modelAliases = deepSeekGatewayModelAliases(config);
|
|
1187
|
-
properties.model = enumStringProperty(
|
|
1188
|
-
properties.model,
|
|
1189
|
-
modelAliases,
|
|
1190
|
-
'Model for the sub-agent. Omit to inherit.',
|
|
1191
|
-
);
|
|
1192
|
-
properties.reasoning_effort = enumStringProperty(
|
|
1193
|
-
properties.reasoning_effort,
|
|
1194
|
-
CODEX_REASONING_EFFORTS,
|
|
1195
|
-
'Codex reasoning effort for the sub-agent. Omit to inherit.',
|
|
1196
|
-
);
|
|
1197
|
-
return {
|
|
1198
|
-
...next,
|
|
1199
|
-
properties,
|
|
1200
|
-
};
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
function deepSeekSpawnAgentDescription() {
|
|
1204
|
-
const description = [
|
|
1205
|
-
'Spawn a Codex-native sub-agent.',
|
|
1206
|
-
'Omit model/reasoning_effort to inherit.',
|
|
1207
|
-
'Valid values are enforced by the schema.',
|
|
1208
|
-
].filter(Boolean).join(' ');
|
|
1209
|
-
return shortenText(description, DEEPSEEK_TOOL_DESCRIPTION_CHARS);
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
function isCodexSpawnAgentTool(name) {
|
|
1213
|
-
return name === CODEX_SPAWN_AGENT_TOOL_NAME;
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
function simplifyToolForDeepSeek(tool, config = {}) {
|
|
1157
|
+
function simplifyToolForDeepSeek(tool) {
|
|
1217
1158
|
if (tool?.type !== 'function' || !isObject(tool.function)) return tool;
|
|
1218
1159
|
if (tool.gateway_custom_tool) return tool;
|
|
1219
1160
|
const fn = tool.function;
|
|
1220
|
-
|
|
1221
|
-
if (isCodexSpawnAgentTool(fn.name)) {
|
|
1222
|
-
parameters = deepSeekSpawnAgentParameters(parameters, config);
|
|
1223
|
-
}
|
|
1161
|
+
const parameters = simplifyJsonSchemaDescriptions(fn.parameters);
|
|
1224
1162
|
const requiredText = requiredParametersText(parameters);
|
|
1225
|
-
const
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
: shortenText(fn.description, descriptionBudget) || defaultDeepSeekToolDescription(fn.name);
|
|
1229
|
-
const description = shortenText(`${baseDescription}${requiredText}`, DEEPSEEK_TOOL_DESCRIPTION_CHARS);
|
|
1163
|
+
const baseDescription = compactStructuredText(fn.description, DEEPSEEK_TOOL_DESCRIPTION_SOFT_CHARS)
|
|
1164
|
+
|| defaultDeepSeekToolDescription(fn.name);
|
|
1165
|
+
const description = `${baseDescription}${requiredText}`;
|
|
1230
1166
|
return {
|
|
1231
1167
|
...tool,
|
|
1232
1168
|
function: omitUndefined({
|
|
@@ -1266,7 +1202,7 @@ function adaptToolsForProvider(tools, provider, config = {}) {
|
|
|
1266
1202
|
if (provider !== 'deepseek' || !Array.isArray(tools)) return tools;
|
|
1267
1203
|
const keepStrict = isDeepSeekBetaBaseUrl(config.upstreamBaseUrl);
|
|
1268
1204
|
return tools.map((tool) => {
|
|
1269
|
-
const simplified = simplifyToolForDeepSeek(tool
|
|
1205
|
+
const simplified = simplifyToolForDeepSeek(tool);
|
|
1270
1206
|
if (!keepStrict && simplified?.type === 'function' && isObject(simplified.function) && simplified.function.strict !== undefined) {
|
|
1271
1207
|
const { strict, ...fn } = simplified.function;
|
|
1272
1208
|
return { ...simplified, function: fn };
|
|
@@ -1275,6 +1211,17 @@ function adaptToolsForProvider(tools, provider, config = {}) {
|
|
|
1275
1211
|
});
|
|
1276
1212
|
}
|
|
1277
1213
|
|
|
1214
|
+
function assertDeepSeekToolCapacity(tools) {
|
|
1215
|
+
const count = Array.isArray(tools)
|
|
1216
|
+
? tools.filter((tool) => tool?.type === 'function' && isObject(tool.function)).length
|
|
1217
|
+
: 0;
|
|
1218
|
+
if (count <= DEEPSEEK_MAX_FUNCTION_TOOLS) return;
|
|
1219
|
+
const error = new RangeError(`DeepSeek supports at most ${DEEPSEEK_MAX_FUNCTION_TOOLS} function tools; received ${count} after gateway adaptation.`);
|
|
1220
|
+
error.statusCode = 400;
|
|
1221
|
+
error.code = 'too_many_tools';
|
|
1222
|
+
throw error;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1278
1225
|
function normalizeToolCallArguments(name, argumentsText, toolSchema) {
|
|
1279
1226
|
if (!argumentsText) return '';
|
|
1280
1227
|
if (!isObject(toolSchema) || !isObject(toolSchema.properties)) return argumentsText;
|
|
@@ -1297,54 +1244,16 @@ function normalizeToolCallArguments(name, argumentsText, toolSchema) {
|
|
|
1297
1244
|
}
|
|
1298
1245
|
}
|
|
1299
1246
|
|
|
1300
|
-
function
|
|
1301
|
-
return value == null ? '' : String(value).toLowerCase().replaceAll('_', '-');
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
function normalizeCodexSpawnAgentArguments(encodedName, argumentsText, config = {}) {
|
|
1305
|
-
if (!isCodexSpawnAgentTool(encodedName) || !argumentsText) return argumentsText;
|
|
1306
|
-
let parsed;
|
|
1307
|
-
try {
|
|
1308
|
-
parsed = JSON.parse(argumentsText);
|
|
1309
|
-
} catch {
|
|
1310
|
-
return argumentsText;
|
|
1311
|
-
}
|
|
1312
|
-
if (!isObject(parsed)) return argumentsText;
|
|
1313
|
-
const next = { ...parsed };
|
|
1314
|
-
let changed = false;
|
|
1315
|
-
const allowedModels = deepSeekGatewayModelAliases(config);
|
|
1316
|
-
|
|
1317
|
-
if (typeof next.model === 'string') {
|
|
1318
|
-
const requestedModel = next.model;
|
|
1319
|
-
if (allowedModels.length && !allowedModels.includes(requestedModel)) {
|
|
1320
|
-
delete next.model;
|
|
1321
|
-
changed = true;
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
if (typeof next.reasoning_effort === 'string') {
|
|
1326
|
-
const requestedEffort = normalizeReasoningEffortName(next.reasoning_effort);
|
|
1327
|
-
if (!CODEX_REASONING_EFFORTS.includes(requestedEffort)) {
|
|
1328
|
-
delete next.reasoning_effort;
|
|
1329
|
-
changed = true;
|
|
1330
|
-
return JSON.stringify(next);
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
|
|
1334
|
-
return changed ? JSON.stringify(next) : argumentsText;
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
function normalizeFunctionCallItemArguments(item, toolSchemas, config = {}) {
|
|
1247
|
+
function normalizeFunctionCallItemArguments(item, toolSchemas) {
|
|
1338
1248
|
if (!item || item.type !== 'function_call') return;
|
|
1339
1249
|
const encodedName = encodeToolName(item.namespace, item.name);
|
|
1340
1250
|
item.arguments = normalizeToolCallArguments(encodedName, item.arguments, toolSchemas?.get(encodedName));
|
|
1341
|
-
item.arguments = normalizeCodexSpawnAgentArguments(encodedName, item.arguments, config);
|
|
1342
1251
|
}
|
|
1343
1252
|
|
|
1344
|
-
function functionCallItemNeedsArgumentNormalization(item, toolSchemas
|
|
1253
|
+
function functionCallItemNeedsArgumentNormalization(item, toolSchemas) {
|
|
1345
1254
|
if (!item || item.type !== 'function_call') return false;
|
|
1346
1255
|
const encodedName = encodeToolName(item.namespace, item.name);
|
|
1347
|
-
return Boolean(toolSchemas?.has(encodedName)
|
|
1256
|
+
return Boolean(toolSchemas?.has(encodedName));
|
|
1348
1257
|
}
|
|
1349
1258
|
|
|
1350
1259
|
function hasToolCallFunctionName(toolCall) {
|
|
@@ -1475,7 +1384,7 @@ function deepseekDefaultMaxTokens(config = {}) {
|
|
|
1475
1384
|
return DEEPSEEK_DEFAULT_MAX_TOKENS;
|
|
1476
1385
|
}
|
|
1477
1386
|
|
|
1478
|
-
const DEEPSEEK_JSON_SCHEMA_INSTRUCTIONS_MARKER = '
|
|
1387
|
+
const DEEPSEEK_JSON_SCHEMA_INSTRUCTIONS_MARKER = 'Return only one valid JSON object';
|
|
1479
1388
|
|
|
1480
1389
|
function jsonSchemaFromResponseFormat(responseFormat) {
|
|
1481
1390
|
if (!isObject(responseFormat)) return null;
|
|
@@ -1490,10 +1399,10 @@ function addDeepSeekJsonSchemaInstructions(messages, responseFormat) {
|
|
|
1490
1399
|
const schema = jsonSchemaFromResponseFormat(responseFormat);
|
|
1491
1400
|
const schemaText = schema ? truncateRawText(JSON.stringify(schema), 6000) : '';
|
|
1492
1401
|
const instructions = [
|
|
1493
|
-
`${DEEPSEEK_JSON_SCHEMA_INSTRUCTIONS_MARKER}
|
|
1402
|
+
`${DEEPSEEK_JSON_SCHEMA_INSTRUCTIONS_MARKER}, with no prose or Markdown.`,
|
|
1494
1403
|
schemaText
|
|
1495
|
-
? `
|
|
1496
|
-
: 'Use the
|
|
1404
|
+
? `It must match this JSON Schema:\n${schemaText}`
|
|
1405
|
+
: 'Use the object shape requested in the conversation.',
|
|
1497
1406
|
].join('\n');
|
|
1498
1407
|
if (!messages.length || messages[0]?.role !== 'system') {
|
|
1499
1408
|
return [{ role: 'system', content: instructions }, ...messages];
|
|
@@ -1656,12 +1565,14 @@ function normalizeInstructions(instructions) {
|
|
|
1656
1565
|
return toText(instructions);
|
|
1657
1566
|
}
|
|
1658
1567
|
|
|
1659
|
-
export function normalizeResponsesRequest(request) {
|
|
1568
|
+
export function normalizeResponsesRequest(request, { restoreDiscoveredTools = true } = {}) {
|
|
1660
1569
|
const messages = extractMessagesFromResponsesInput(request.input ?? request);
|
|
1661
1570
|
const model = request.model || request.model_id || request.upstream_model || '';
|
|
1662
1571
|
const instructions = normalizeInstructions(request.instructions);
|
|
1663
1572
|
const responseFormat = request.response_format || request.text?.format;
|
|
1664
|
-
const tools =
|
|
1573
|
+
const tools = restoreDiscoveredTools
|
|
1574
|
+
? mergeToolsWithToolSearchOutput(request.tools, request.input ?? request)
|
|
1575
|
+
: request.tools;
|
|
1665
1576
|
return {
|
|
1666
1577
|
model,
|
|
1667
1578
|
messages,
|
|
@@ -1683,10 +1594,8 @@ export function normalizeResponsesRequest(request) {
|
|
|
1683
1594
|
text: request.text,
|
|
1684
1595
|
truncation: request.truncation,
|
|
1685
1596
|
user: request.user,
|
|
1686
|
-
previous_response_id: request.previous_response_id,
|
|
1687
1597
|
store: request.store,
|
|
1688
1598
|
include: request.include,
|
|
1689
|
-
conversation: request.conversation,
|
|
1690
1599
|
};
|
|
1691
1600
|
}
|
|
1692
1601
|
|
|
@@ -1848,7 +1757,10 @@ export function toProviderChatCompletionsRequest(chatRequest, config = {}) {
|
|
|
1848
1757
|
if (provider === 'deepseek') {
|
|
1849
1758
|
delete request.reasoning_effort;
|
|
1850
1759
|
delete request.parallel_tool_calls;
|
|
1760
|
+
delete request.frequency_penalty;
|
|
1761
|
+
delete request.presence_penalty;
|
|
1851
1762
|
request.tools = addInternalCommentaryTool(adaptToolsForProvider(request.tools, provider, config), chatRequest.tool_choice);
|
|
1763
|
+
assertDeepSeekToolCapacity(request.tools);
|
|
1852
1764
|
request.messages = addDeepSeekToolInstructions(request.messages, request.tools);
|
|
1853
1765
|
if (request.user !== undefined) {
|
|
1854
1766
|
request.user_id = request.user;
|
|
@@ -1899,6 +1811,7 @@ function createBaseResponse({
|
|
|
1899
1811
|
normalized,
|
|
1900
1812
|
completedAt = null,
|
|
1901
1813
|
incompleteReason = null,
|
|
1814
|
+
error = null,
|
|
1902
1815
|
}) {
|
|
1903
1816
|
return {
|
|
1904
1817
|
id,
|
|
@@ -1907,7 +1820,7 @@ function createBaseResponse({
|
|
|
1907
1820
|
completed_at: completedAt == null ? null : Math.floor(completedAt),
|
|
1908
1821
|
status,
|
|
1909
1822
|
background: false,
|
|
1910
|
-
error
|
|
1823
|
+
error,
|
|
1911
1824
|
incomplete_details: incompleteReason ? { reason: incompleteReason } : null,
|
|
1912
1825
|
instructions: normalized?.instructions || null,
|
|
1913
1826
|
max_output_tokens: normalized?.max_tokens ?? null,
|
|
@@ -1930,6 +1843,37 @@ function createBaseResponse({
|
|
|
1930
1843
|
};
|
|
1931
1844
|
}
|
|
1932
1845
|
|
|
1846
|
+
function deepSeekFinishReasonOutcome(finishReason) {
|
|
1847
|
+
if (finishReason === 'stop' || finishReason === 'tool_calls') {
|
|
1848
|
+
return { status: 'completed', incompleteReason: null, error: null };
|
|
1849
|
+
}
|
|
1850
|
+
if (finishReason === 'length') {
|
|
1851
|
+
return { status: 'incomplete', incompleteReason: 'max_output_tokens', error: null };
|
|
1852
|
+
}
|
|
1853
|
+
if (finishReason === 'content_filter') {
|
|
1854
|
+
return { status: 'incomplete', incompleteReason: 'content_filter', error: null };
|
|
1855
|
+
}
|
|
1856
|
+
if (finishReason === 'insufficient_system_resource') {
|
|
1857
|
+
return {
|
|
1858
|
+
status: 'failed',
|
|
1859
|
+
incompleteReason: null,
|
|
1860
|
+
error: {
|
|
1861
|
+
code: 'server_is_overloaded',
|
|
1862
|
+
message: 'DeepSeek ended the response because upstream resources were insufficient.',
|
|
1863
|
+
},
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
const label = finishReason == null || finishReason === '' ? 'missing' : String(finishReason);
|
|
1867
|
+
return {
|
|
1868
|
+
status: 'failed',
|
|
1869
|
+
incompleteReason: null,
|
|
1870
|
+
error: {
|
|
1871
|
+
code: 'upstream_error',
|
|
1872
|
+
message: `DeepSeek returned an unsupported finish_reason: ${label}.`,
|
|
1873
|
+
},
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1933
1877
|
function normalizeResponsesUsage(usage) {
|
|
1934
1878
|
if (!isObject(usage)) return null;
|
|
1935
1879
|
const inputTokens = usage.input_tokens ?? usage.prompt_tokens ?? 0;
|
|
@@ -1991,7 +1935,7 @@ export function createResponseEnvelope({
|
|
|
1991
1935
|
const PARALLEL_TOOL_WRAPPER_NAMES = new Set(['multi_tool_use.parallel', 'multi_tool_use_parallel']);
|
|
1992
1936
|
const EMITTED_TOOL_NAME_PREFIX = 'functions.';
|
|
1993
1937
|
|
|
1994
|
-
|
|
1938
|
+
function isParallelToolWrapperName(name) {
|
|
1995
1939
|
const raw = String(name || '').toLowerCase();
|
|
1996
1940
|
const stripped = raw.startsWith(EMITTED_TOOL_NAME_PREFIX) ? raw.slice(EMITTED_TOOL_NAME_PREFIX.length) : raw;
|
|
1997
1941
|
return PARALLEL_TOOL_WRAPPER_NAMES.has(stripped);
|
|
@@ -2001,7 +1945,7 @@ function toolNameMatchKey(name) {
|
|
|
2001
1945
|
return String(name || '').toLowerCase().replace(/\./g, '__');
|
|
2002
1946
|
}
|
|
2003
1947
|
|
|
2004
|
-
|
|
1948
|
+
function resolveEmittedToolName(name, knownNames) {
|
|
2005
1949
|
const raw = String(name || '');
|
|
2006
1950
|
if (!raw || !knownNames) return name;
|
|
2007
1951
|
const known = knownNames instanceof Set ? knownNames : new Set(knownNames);
|
|
@@ -2074,7 +2018,7 @@ function parallelToolUsesFromArguments(argumentsText) {
|
|
|
2074
2018
|
return calls;
|
|
2075
2019
|
}
|
|
2076
2020
|
|
|
2077
|
-
|
|
2021
|
+
function expandParallelToolCalls(toolCalls) {
|
|
2078
2022
|
if (!Array.isArray(toolCalls)) return toolCalls;
|
|
2079
2023
|
if (!toolCalls.some((toolCall) => isParallelToolWrapperName(toolCall?.function?.name))) return toolCalls;
|
|
2080
2024
|
const expanded = [];
|
|
@@ -2120,6 +2064,7 @@ export function convertChatCompletionToResponses({ completion: rawCompletion, mo
|
|
|
2120
2064
|
);
|
|
2121
2065
|
const createdAt = completion.created || Date.now() / 1000;
|
|
2122
2066
|
const choice = Array.isArray(completion.choices) ? completion.choices[0] : null;
|
|
2067
|
+
const outcome = deepSeekFinishReasonOutcome(choice?.finish_reason);
|
|
2123
2068
|
const message = choice?.message || {};
|
|
2124
2069
|
const content = chatContentToResponseOutputParts(message.content);
|
|
2125
2070
|
const messageHasToolCalls = hasChatToolCalls(message);
|
|
@@ -2133,7 +2078,7 @@ export function convertChatCompletionToResponses({ completion: rawCompletion, mo
|
|
|
2133
2078
|
id: generateId('msg'),
|
|
2134
2079
|
role: 'assistant',
|
|
2135
2080
|
content,
|
|
2136
|
-
status:
|
|
2081
|
+
status: outcome.status,
|
|
2137
2082
|
phase: message.phase || (messageHasToolCalls ? 'commentary' : 'final_answer'),
|
|
2138
2083
|
});
|
|
2139
2084
|
}
|
|
@@ -2156,6 +2101,7 @@ export function convertChatCompletionToResponses({ completion: rawCompletion, mo
|
|
|
2156
2101
|
continue;
|
|
2157
2102
|
}
|
|
2158
2103
|
const item = responseItemFromChatToolCall(toolCall, toolNames, customToolNames);
|
|
2104
|
+
item.status = outcome.status;
|
|
2159
2105
|
normalizeFunctionCallItemArguments(item, toolSchemas, config);
|
|
2160
2106
|
output.push(item);
|
|
2161
2107
|
}
|
|
@@ -2170,7 +2116,7 @@ export function convertChatCompletionToResponses({ completion: rawCompletion, mo
|
|
|
2170
2116
|
content: [],
|
|
2171
2117
|
reasoning_content: reasoningText,
|
|
2172
2118
|
encrypted_content: encodeGatewayReasoning(reasoningText),
|
|
2173
|
-
status:
|
|
2119
|
+
status: outcome.status,
|
|
2174
2120
|
});
|
|
2175
2121
|
}
|
|
2176
2122
|
|
|
@@ -2178,13 +2124,14 @@ export function convertChatCompletionToResponses({ completion: rawCompletion, mo
|
|
|
2178
2124
|
id: responseId,
|
|
2179
2125
|
model,
|
|
2180
2126
|
createdAt,
|
|
2181
|
-
status:
|
|
2127
|
+
status: outcome.status,
|
|
2182
2128
|
output,
|
|
2183
2129
|
previousResponseId: previousResponseId ?? null,
|
|
2184
2130
|
usage: normalizeResponsesUsage(completion.usage),
|
|
2185
2131
|
normalized,
|
|
2186
2132
|
completedAt: Date.now() / 1000,
|
|
2187
|
-
incompleteReason:
|
|
2133
|
+
incompleteReason: outcome.incompleteReason,
|
|
2134
|
+
error: outcome.error,
|
|
2188
2135
|
});
|
|
2189
2136
|
}
|
|
2190
2137
|
|
|
@@ -2224,6 +2171,7 @@ export class ResponsesStreamMapper {
|
|
|
2224
2171
|
this.pendingUsage = null;
|
|
2225
2172
|
this.completedAt = null;
|
|
2226
2173
|
this.finalized = false;
|
|
2174
|
+
this.terminalStatus = null;
|
|
2227
2175
|
this.messageItemAdded = false;
|
|
2228
2176
|
this.messageContentAdded = false;
|
|
2229
2177
|
this.messageItemClosed = false;
|
|
@@ -2253,6 +2201,7 @@ export class ResponsesStreamMapper {
|
|
|
2253
2201
|
}
|
|
2254
2202
|
|
|
2255
2203
|
response(status = 'in_progress') {
|
|
2204
|
+
const outcome = deepSeekFinishReasonOutcome(this.finishReason);
|
|
2256
2205
|
return createBaseResponse({
|
|
2257
2206
|
id: this.responseId,
|
|
2258
2207
|
model: this.model,
|
|
@@ -2263,7 +2212,8 @@ export class ResponsesStreamMapper {
|
|
|
2263
2212
|
usage: this.usage,
|
|
2264
2213
|
normalized: this.normalized,
|
|
2265
2214
|
completedAt: this.completedAt,
|
|
2266
|
-
incompleteReason:
|
|
2215
|
+
incompleteReason: status === 'in_progress' ? null : outcome.incompleteReason,
|
|
2216
|
+
error: status === 'failed' ? outcome.error : null,
|
|
2267
2217
|
});
|
|
2268
2218
|
}
|
|
2269
2219
|
|
|
@@ -2761,7 +2711,9 @@ export class ResponsesStreamMapper {
|
|
|
2761
2711
|
if (this.finalized) return [];
|
|
2762
2712
|
this.finalized = true;
|
|
2763
2713
|
this.expandParallelToolItems();
|
|
2764
|
-
const
|
|
2714
|
+
const outcome = deepSeekFinishReasonOutcome(finishReason);
|
|
2715
|
+
const status = outcome.status;
|
|
2716
|
+
this.terminalStatus = status;
|
|
2765
2717
|
this.finishReason = finishReason;
|
|
2766
2718
|
this.usage = normalizeResponsesUsage(usage);
|
|
2767
2719
|
this.completedAt = Date.now() / 1000;
|
|
@@ -2876,10 +2828,11 @@ export class ResponsesStreamMapper {
|
|
|
2876
2828
|
usage: normalizeResponsesUsage(usage),
|
|
2877
2829
|
normalized: this.normalized,
|
|
2878
2830
|
completedAt: Date.now() / 1000,
|
|
2879
|
-
incompleteReason:
|
|
2831
|
+
incompleteReason: outcome.incompleteReason,
|
|
2832
|
+
error: outcome.error,
|
|
2880
2833
|
});
|
|
2881
2834
|
events.push({
|
|
2882
|
-
type: status === 'incomplete' ? 'response.incomplete' : 'response.
|
|
2835
|
+
type: status === 'completed' ? 'response.completed' : status === 'incomplete' ? 'response.incomplete' : 'response.failed',
|
|
2883
2836
|
sequence_number: this.nextSequence(),
|
|
2884
2837
|
response,
|
|
2885
2838
|
});
|
|
@@ -3060,6 +3013,7 @@ export class ResponsesStreamMapper {
|
|
|
3060
3013
|
streamFailed(message) {
|
|
3061
3014
|
if (this.finalized) return [];
|
|
3062
3015
|
this.finalized = true;
|
|
3016
|
+
this.terminalStatus = 'failed';
|
|
3063
3017
|
this.completedAt = Date.now() / 1000;
|
|
3064
3018
|
const response = this.response('failed');
|
|
3065
3019
|
response.error = { code: 'upstream_error', message: String(message || 'upstream stream failed') };
|