@galaxy-yearn/codex-deepseek-gateway 0.2.0 → 0.2.2
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 +19 -10
- package/README.zh-CN.md +220 -0
- package/config/codex-model-catalog.json +18 -8
- package/config/codex-model-catalog.zh.json +22 -12
- package/package.json +2 -2
- package/src/codex-launch.js +30 -7
- package/src/codex-sessions.js +69 -13
- package/src/config.js +6 -5
- package/src/firecrawl.js +8 -3
- package/src/protocol.js +127 -155
- package/src/reasoning-cache.js +235 -0
- package/src/server.js +184 -129
- package/src/web-search-emulator.js +180 -106
- 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) {
|
|
@@ -1467,7 +1376,19 @@ function sanitizeMessagesForChatCompletion(messages, provider = 'generic') {
|
|
|
1467
1376
|
return result;
|
|
1468
1377
|
}
|
|
1469
1378
|
|
|
1470
|
-
const
|
|
1379
|
+
const DEEPSEEK_CONTEXT_WINDOW = 1000000;
|
|
1380
|
+
const DEEPSEEK_MAX_OUTPUT_TOKENS = 384000;
|
|
1381
|
+
const DEEPSEEK_AUTO_COMPACT_TOKEN_LIMIT = 900000;
|
|
1382
|
+
|
|
1383
|
+
function jointOutputTokenBudget(contextWindow, maxOutputTokens, inputTokenLimit) {
|
|
1384
|
+
return Math.max(1, Math.min(maxOutputTokens, contextWindow - inputTokenLimit));
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
const DEEPSEEK_DEFAULT_MAX_TOKENS = jointOutputTokenBudget(
|
|
1388
|
+
DEEPSEEK_CONTEXT_WINDOW,
|
|
1389
|
+
DEEPSEEK_MAX_OUTPUT_TOKENS,
|
|
1390
|
+
DEEPSEEK_AUTO_COMPACT_TOKEN_LIMIT,
|
|
1391
|
+
);
|
|
1471
1392
|
|
|
1472
1393
|
function deepseekDefaultMaxTokens(config = {}) {
|
|
1473
1394
|
const value = Number(config.upstreamMaxTokens);
|
|
@@ -1475,7 +1396,7 @@ function deepseekDefaultMaxTokens(config = {}) {
|
|
|
1475
1396
|
return DEEPSEEK_DEFAULT_MAX_TOKENS;
|
|
1476
1397
|
}
|
|
1477
1398
|
|
|
1478
|
-
const DEEPSEEK_JSON_SCHEMA_INSTRUCTIONS_MARKER = '
|
|
1399
|
+
const DEEPSEEK_JSON_SCHEMA_INSTRUCTIONS_MARKER = 'Return only one valid JSON object';
|
|
1479
1400
|
|
|
1480
1401
|
function jsonSchemaFromResponseFormat(responseFormat) {
|
|
1481
1402
|
if (!isObject(responseFormat)) return null;
|
|
@@ -1490,10 +1411,10 @@ function addDeepSeekJsonSchemaInstructions(messages, responseFormat) {
|
|
|
1490
1411
|
const schema = jsonSchemaFromResponseFormat(responseFormat);
|
|
1491
1412
|
const schemaText = schema ? truncateRawText(JSON.stringify(schema), 6000) : '';
|
|
1492
1413
|
const instructions = [
|
|
1493
|
-
`${DEEPSEEK_JSON_SCHEMA_INSTRUCTIONS_MARKER}
|
|
1414
|
+
`${DEEPSEEK_JSON_SCHEMA_INSTRUCTIONS_MARKER}, with no prose or Markdown.`,
|
|
1494
1415
|
schemaText
|
|
1495
|
-
? `
|
|
1496
|
-
: 'Use the
|
|
1416
|
+
? `It must match this JSON Schema:\n${schemaText}`
|
|
1417
|
+
: 'Use the object shape requested in the conversation.',
|
|
1497
1418
|
].join('\n');
|
|
1498
1419
|
if (!messages.length || messages[0]?.role !== 'system') {
|
|
1499
1420
|
return [{ role: 'system', content: instructions }, ...messages];
|
|
@@ -1656,12 +1577,14 @@ function normalizeInstructions(instructions) {
|
|
|
1656
1577
|
return toText(instructions);
|
|
1657
1578
|
}
|
|
1658
1579
|
|
|
1659
|
-
export function normalizeResponsesRequest(request) {
|
|
1580
|
+
export function normalizeResponsesRequest(request, { restoreDiscoveredTools = true } = {}) {
|
|
1660
1581
|
const messages = extractMessagesFromResponsesInput(request.input ?? request);
|
|
1661
1582
|
const model = request.model || request.model_id || request.upstream_model || '';
|
|
1662
1583
|
const instructions = normalizeInstructions(request.instructions);
|
|
1663
1584
|
const responseFormat = request.response_format || request.text?.format;
|
|
1664
|
-
const tools =
|
|
1585
|
+
const tools = restoreDiscoveredTools
|
|
1586
|
+
? mergeToolsWithToolSearchOutput(request.tools, request.input ?? request)
|
|
1587
|
+
: request.tools;
|
|
1665
1588
|
return {
|
|
1666
1589
|
model,
|
|
1667
1590
|
messages,
|
|
@@ -1683,10 +1606,8 @@ export function normalizeResponsesRequest(request) {
|
|
|
1683
1606
|
text: request.text,
|
|
1684
1607
|
truncation: request.truncation,
|
|
1685
1608
|
user: request.user,
|
|
1686
|
-
previous_response_id: request.previous_response_id,
|
|
1687
1609
|
store: request.store,
|
|
1688
1610
|
include: request.include,
|
|
1689
|
-
conversation: request.conversation,
|
|
1690
1611
|
};
|
|
1691
1612
|
}
|
|
1692
1613
|
|
|
@@ -1848,7 +1769,10 @@ export function toProviderChatCompletionsRequest(chatRequest, config = {}) {
|
|
|
1848
1769
|
if (provider === 'deepseek') {
|
|
1849
1770
|
delete request.reasoning_effort;
|
|
1850
1771
|
delete request.parallel_tool_calls;
|
|
1772
|
+
delete request.frequency_penalty;
|
|
1773
|
+
delete request.presence_penalty;
|
|
1851
1774
|
request.tools = addInternalCommentaryTool(adaptToolsForProvider(request.tools, provider, config), chatRequest.tool_choice);
|
|
1775
|
+
assertDeepSeekToolCapacity(request.tools);
|
|
1852
1776
|
request.messages = addDeepSeekToolInstructions(request.messages, request.tools);
|
|
1853
1777
|
if (request.user !== undefined) {
|
|
1854
1778
|
request.user_id = request.user;
|
|
@@ -1899,6 +1823,7 @@ function createBaseResponse({
|
|
|
1899
1823
|
normalized,
|
|
1900
1824
|
completedAt = null,
|
|
1901
1825
|
incompleteReason = null,
|
|
1826
|
+
error = null,
|
|
1902
1827
|
}) {
|
|
1903
1828
|
return {
|
|
1904
1829
|
id,
|
|
@@ -1907,7 +1832,7 @@ function createBaseResponse({
|
|
|
1907
1832
|
completed_at: completedAt == null ? null : Math.floor(completedAt),
|
|
1908
1833
|
status,
|
|
1909
1834
|
background: false,
|
|
1910
|
-
error
|
|
1835
|
+
error,
|
|
1911
1836
|
incomplete_details: incompleteReason ? { reason: incompleteReason } : null,
|
|
1912
1837
|
instructions: normalized?.instructions || null,
|
|
1913
1838
|
max_output_tokens: normalized?.max_tokens ?? null,
|
|
@@ -1930,6 +1855,37 @@ function createBaseResponse({
|
|
|
1930
1855
|
};
|
|
1931
1856
|
}
|
|
1932
1857
|
|
|
1858
|
+
function deepSeekFinishReasonOutcome(finishReason) {
|
|
1859
|
+
if (finishReason === 'stop' || finishReason === 'tool_calls') {
|
|
1860
|
+
return { status: 'completed', incompleteReason: null, error: null };
|
|
1861
|
+
}
|
|
1862
|
+
if (finishReason === 'length') {
|
|
1863
|
+
return { status: 'incomplete', incompleteReason: 'max_output_tokens', error: null };
|
|
1864
|
+
}
|
|
1865
|
+
if (finishReason === 'content_filter') {
|
|
1866
|
+
return { status: 'incomplete', incompleteReason: 'content_filter', error: null };
|
|
1867
|
+
}
|
|
1868
|
+
if (finishReason === 'insufficient_system_resource') {
|
|
1869
|
+
return {
|
|
1870
|
+
status: 'failed',
|
|
1871
|
+
incompleteReason: null,
|
|
1872
|
+
error: {
|
|
1873
|
+
code: 'server_is_overloaded',
|
|
1874
|
+
message: 'DeepSeek ended the response because upstream resources were insufficient.',
|
|
1875
|
+
},
|
|
1876
|
+
};
|
|
1877
|
+
}
|
|
1878
|
+
const label = finishReason == null || finishReason === '' ? 'missing' : String(finishReason);
|
|
1879
|
+
return {
|
|
1880
|
+
status: 'failed',
|
|
1881
|
+
incompleteReason: null,
|
|
1882
|
+
error: {
|
|
1883
|
+
code: 'upstream_error',
|
|
1884
|
+
message: `DeepSeek returned an unsupported finish_reason: ${label}.`,
|
|
1885
|
+
},
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1933
1889
|
function normalizeResponsesUsage(usage) {
|
|
1934
1890
|
if (!isObject(usage)) return null;
|
|
1935
1891
|
const inputTokens = usage.input_tokens ?? usage.prompt_tokens ?? 0;
|
|
@@ -1991,7 +1947,7 @@ export function createResponseEnvelope({
|
|
|
1991
1947
|
const PARALLEL_TOOL_WRAPPER_NAMES = new Set(['multi_tool_use.parallel', 'multi_tool_use_parallel']);
|
|
1992
1948
|
const EMITTED_TOOL_NAME_PREFIX = 'functions.';
|
|
1993
1949
|
|
|
1994
|
-
|
|
1950
|
+
function isParallelToolWrapperName(name) {
|
|
1995
1951
|
const raw = String(name || '').toLowerCase();
|
|
1996
1952
|
const stripped = raw.startsWith(EMITTED_TOOL_NAME_PREFIX) ? raw.slice(EMITTED_TOOL_NAME_PREFIX.length) : raw;
|
|
1997
1953
|
return PARALLEL_TOOL_WRAPPER_NAMES.has(stripped);
|
|
@@ -2001,7 +1957,7 @@ function toolNameMatchKey(name) {
|
|
|
2001
1957
|
return String(name || '').toLowerCase().replace(/\./g, '__');
|
|
2002
1958
|
}
|
|
2003
1959
|
|
|
2004
|
-
|
|
1960
|
+
function resolveEmittedToolName(name, knownNames) {
|
|
2005
1961
|
const raw = String(name || '');
|
|
2006
1962
|
if (!raw || !knownNames) return name;
|
|
2007
1963
|
const known = knownNames instanceof Set ? knownNames : new Set(knownNames);
|
|
@@ -2074,7 +2030,7 @@ function parallelToolUsesFromArguments(argumentsText) {
|
|
|
2074
2030
|
return calls;
|
|
2075
2031
|
}
|
|
2076
2032
|
|
|
2077
|
-
|
|
2033
|
+
function expandParallelToolCalls(toolCalls) {
|
|
2078
2034
|
if (!Array.isArray(toolCalls)) return toolCalls;
|
|
2079
2035
|
if (!toolCalls.some((toolCall) => isParallelToolWrapperName(toolCall?.function?.name))) return toolCalls;
|
|
2080
2036
|
const expanded = [];
|
|
@@ -2120,6 +2076,7 @@ export function convertChatCompletionToResponses({ completion: rawCompletion, mo
|
|
|
2120
2076
|
);
|
|
2121
2077
|
const createdAt = completion.created || Date.now() / 1000;
|
|
2122
2078
|
const choice = Array.isArray(completion.choices) ? completion.choices[0] : null;
|
|
2079
|
+
const outcome = deepSeekFinishReasonOutcome(choice?.finish_reason);
|
|
2123
2080
|
const message = choice?.message || {};
|
|
2124
2081
|
const content = chatContentToResponseOutputParts(message.content);
|
|
2125
2082
|
const messageHasToolCalls = hasChatToolCalls(message);
|
|
@@ -2133,7 +2090,7 @@ export function convertChatCompletionToResponses({ completion: rawCompletion, mo
|
|
|
2133
2090
|
id: generateId('msg'),
|
|
2134
2091
|
role: 'assistant',
|
|
2135
2092
|
content,
|
|
2136
|
-
status:
|
|
2093
|
+
status: outcome.status,
|
|
2137
2094
|
phase: message.phase || (messageHasToolCalls ? 'commentary' : 'final_answer'),
|
|
2138
2095
|
});
|
|
2139
2096
|
}
|
|
@@ -2156,6 +2113,7 @@ export function convertChatCompletionToResponses({ completion: rawCompletion, mo
|
|
|
2156
2113
|
continue;
|
|
2157
2114
|
}
|
|
2158
2115
|
const item = responseItemFromChatToolCall(toolCall, toolNames, customToolNames);
|
|
2116
|
+
item.status = outcome.status;
|
|
2159
2117
|
normalizeFunctionCallItemArguments(item, toolSchemas, config);
|
|
2160
2118
|
output.push(item);
|
|
2161
2119
|
}
|
|
@@ -2170,7 +2128,7 @@ export function convertChatCompletionToResponses({ completion: rawCompletion, mo
|
|
|
2170
2128
|
content: [],
|
|
2171
2129
|
reasoning_content: reasoningText,
|
|
2172
2130
|
encrypted_content: encodeGatewayReasoning(reasoningText),
|
|
2173
|
-
status:
|
|
2131
|
+
status: outcome.status,
|
|
2174
2132
|
});
|
|
2175
2133
|
}
|
|
2176
2134
|
|
|
@@ -2178,13 +2136,14 @@ export function convertChatCompletionToResponses({ completion: rawCompletion, mo
|
|
|
2178
2136
|
id: responseId,
|
|
2179
2137
|
model,
|
|
2180
2138
|
createdAt,
|
|
2181
|
-
status:
|
|
2139
|
+
status: outcome.status,
|
|
2182
2140
|
output,
|
|
2183
2141
|
previousResponseId: previousResponseId ?? null,
|
|
2184
2142
|
usage: normalizeResponsesUsage(completion.usage),
|
|
2185
2143
|
normalized,
|
|
2186
2144
|
completedAt: Date.now() / 1000,
|
|
2187
|
-
incompleteReason:
|
|
2145
|
+
incompleteReason: outcome.incompleteReason,
|
|
2146
|
+
error: outcome.error,
|
|
2188
2147
|
});
|
|
2189
2148
|
}
|
|
2190
2149
|
|
|
@@ -2224,6 +2183,7 @@ export class ResponsesStreamMapper {
|
|
|
2224
2183
|
this.pendingUsage = null;
|
|
2225
2184
|
this.completedAt = null;
|
|
2226
2185
|
this.finalized = false;
|
|
2186
|
+
this.terminalStatus = null;
|
|
2227
2187
|
this.messageItemAdded = false;
|
|
2228
2188
|
this.messageContentAdded = false;
|
|
2229
2189
|
this.messageItemClosed = false;
|
|
@@ -2253,6 +2213,7 @@ export class ResponsesStreamMapper {
|
|
|
2253
2213
|
}
|
|
2254
2214
|
|
|
2255
2215
|
response(status = 'in_progress') {
|
|
2216
|
+
const outcome = deepSeekFinishReasonOutcome(this.finishReason);
|
|
2256
2217
|
return createBaseResponse({
|
|
2257
2218
|
id: this.responseId,
|
|
2258
2219
|
model: this.model,
|
|
@@ -2263,7 +2224,8 @@ export class ResponsesStreamMapper {
|
|
|
2263
2224
|
usage: this.usage,
|
|
2264
2225
|
normalized: this.normalized,
|
|
2265
2226
|
completedAt: this.completedAt,
|
|
2266
|
-
incompleteReason:
|
|
2227
|
+
incompleteReason: status === 'in_progress' ? null : outcome.incompleteReason,
|
|
2228
|
+
error: status === 'failed' ? outcome.error : null,
|
|
2267
2229
|
});
|
|
2268
2230
|
}
|
|
2269
2231
|
|
|
@@ -2279,7 +2241,13 @@ export class ResponsesStreamMapper {
|
|
|
2279
2241
|
return {
|
|
2280
2242
|
type: 'response.in_progress',
|
|
2281
2243
|
sequence_number: this.nextSequence(),
|
|
2282
|
-
response:
|
|
2244
|
+
response: createBaseResponse({
|
|
2245
|
+
id: this.responseId,
|
|
2246
|
+
model: this.model,
|
|
2247
|
+
createdAt: this.createdAt,
|
|
2248
|
+
status: 'in_progress',
|
|
2249
|
+
previousResponseId: this.previousResponseId,
|
|
2250
|
+
}),
|
|
2283
2251
|
};
|
|
2284
2252
|
}
|
|
2285
2253
|
|
|
@@ -2761,7 +2729,9 @@ export class ResponsesStreamMapper {
|
|
|
2761
2729
|
if (this.finalized) return [];
|
|
2762
2730
|
this.finalized = true;
|
|
2763
2731
|
this.expandParallelToolItems();
|
|
2764
|
-
const
|
|
2732
|
+
const outcome = deepSeekFinishReasonOutcome(finishReason);
|
|
2733
|
+
const status = outcome.status;
|
|
2734
|
+
this.terminalStatus = status;
|
|
2765
2735
|
this.finishReason = finishReason;
|
|
2766
2736
|
this.usage = normalizeResponsesUsage(usage);
|
|
2767
2737
|
this.completedAt = Date.now() / 1000;
|
|
@@ -2876,10 +2846,11 @@ export class ResponsesStreamMapper {
|
|
|
2876
2846
|
usage: normalizeResponsesUsage(usage),
|
|
2877
2847
|
normalized: this.normalized,
|
|
2878
2848
|
completedAt: Date.now() / 1000,
|
|
2879
|
-
incompleteReason:
|
|
2849
|
+
incompleteReason: outcome.incompleteReason,
|
|
2850
|
+
error: outcome.error,
|
|
2880
2851
|
});
|
|
2881
2852
|
events.push({
|
|
2882
|
-
type: status === 'incomplete' ? 'response.incomplete' : 'response.
|
|
2853
|
+
type: status === 'completed' ? 'response.completed' : status === 'incomplete' ? 'response.incomplete' : 'response.failed',
|
|
2883
2854
|
sequence_number: this.nextSequence(),
|
|
2884
2855
|
response,
|
|
2885
2856
|
});
|
|
@@ -3060,6 +3031,7 @@ export class ResponsesStreamMapper {
|
|
|
3060
3031
|
streamFailed(message) {
|
|
3061
3032
|
if (this.finalized) return [];
|
|
3062
3033
|
this.finalized = true;
|
|
3034
|
+
this.terminalStatus = 'failed';
|
|
3063
3035
|
this.completedAt = Date.now() / 1000;
|
|
3064
3036
|
const response = this.response('failed');
|
|
3065
3037
|
response.error = { code: 'upstream_error', message: String(message || 'upstream stream failed') };
|