@galaxy-yearn/codex-deepseek-gateway 0.1.7 → 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 +57 -37
- package/README.zh-CN.md +213 -0
- package/config/codex-model-catalog.json +32 -28
- package/config/codex-model-catalog.zh.json +36 -32
- package/package.json +2 -1
- package/src/codex-launch.js +30 -8
- package/src/codex-sessions.js +69 -13
- package/src/common.js +18 -3
- package/src/config.js +13 -2
- package/src/firecrawl.js +15 -19
- package/src/model-map.js +6 -20
- package/src/protocol.js +1395 -572
- package/src/reasoning-cache.js +235 -0
- package/src/server.js +525 -378
- package/src/tavily.js +2 -11
- package/src/upstream.js +14 -12
- package/src/web-search-emulator.js +15 -16
- package/state/reasoning-cache.example.jsonl +1 -0
- package/src/session-store.js +0 -67
package/src/protocol.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
import { generateId, isObject, normalizeRole, parseJsonObject, safeJsonParse, toText } from './common.js';
|
|
2
4
|
import {
|
|
3
|
-
DEFAULT_MODEL_ALIASES,
|
|
4
5
|
deepseekReasoningPayload,
|
|
5
|
-
isDeprecatedModel,
|
|
6
6
|
resolveModelAlias,
|
|
7
7
|
} from './model-map.js';
|
|
8
8
|
|
|
@@ -33,23 +33,50 @@ const TOOL_OUTPUT_TYPES = new Set([
|
|
|
33
33
|
'image_generation_call_output',
|
|
34
34
|
]);
|
|
35
35
|
const CHAT_HISTORY_IGNORED_TOOL_ITEM_TYPES = new Set([
|
|
36
|
-
'web_search_call',
|
|
37
36
|
'web_search_call_output',
|
|
38
|
-
'tool_search_call',
|
|
39
|
-
'tool_search_output',
|
|
40
37
|
]);
|
|
41
38
|
const EMULATED_HOSTED_TOOL_TYPES = new Set(['web_search', 'web_search_preview']);
|
|
39
|
+
const BRIDGED_RESPONSES_TOOL_TYPES = new Set(['tool_search']);
|
|
40
|
+
const UNSUPPORTED_HOSTED_TOOL_TYPES = new Set([
|
|
41
|
+
'file_search',
|
|
42
|
+
'code_interpreter',
|
|
43
|
+
'image_generation',
|
|
44
|
+
'computer',
|
|
45
|
+
'computer_use',
|
|
46
|
+
'mcp',
|
|
47
|
+
'local_shell',
|
|
48
|
+
]);
|
|
42
49
|
const DEEPSEEK_TOOL_INSTRUCTIONS_MARKER = 'The tools in this request are real callable functions available now.';
|
|
43
50
|
const DEEPSEEK_TOOL_INSTRUCTIONS = [
|
|
44
51
|
DEEPSEEK_TOOL_INSTRUCTIONS_MARKER,
|
|
45
|
-
'When
|
|
46
|
-
'
|
|
47
|
-
'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.',
|
|
48
54
|
].join(' ');
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
|
|
55
|
+
export const INTERNAL_COMMENTARY_TOOL = 'commentary';
|
|
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.';
|
|
57
|
+
const INTERNAL_COMMENTARY_TOOL_DEFINITION = {
|
|
58
|
+
type: 'function',
|
|
59
|
+
function: {
|
|
60
|
+
name: INTERNAL_COMMENTARY_TOOL,
|
|
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.',
|
|
62
|
+
parameters: {
|
|
63
|
+
type: 'object',
|
|
64
|
+
properties: {
|
|
65
|
+
text: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
description: 'One or two short sentences on what you are doing or just found.',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
required: ['text'],
|
|
71
|
+
additionalProperties: false,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
const CHAT_FUNCTION_NAME_MAX_CHARS = 64;
|
|
76
|
+
const TOOL_NAME_HASH_CHARS = 8;
|
|
77
|
+
const DEEPSEEK_TOOL_DESCRIPTION_SOFT_CHARS = 900;
|
|
78
|
+
const DEEPSEEK_SCHEMA_DESCRIPTION_SOFT_CHARS = 480;
|
|
79
|
+
const DEEPSEEK_MAX_FUNCTION_TOOLS = 128;
|
|
53
80
|
const CODEX_TOOL_SEARCH_TOOL_NAME = 'tool_search';
|
|
54
81
|
|
|
55
82
|
function jsonString(value) {
|
|
@@ -57,23 +84,27 @@ function jsonString(value) {
|
|
|
57
84
|
return JSON.stringify(value ?? {});
|
|
58
85
|
}
|
|
59
86
|
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
} catch {
|
|
67
|
-
return {};
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function sanitizeFunctionName(name, fallback = 'tool_call') {
|
|
72
|
-
const candidate = String(name || fallback)
|
|
87
|
+
function sanitizeFunctionName(name, fallback = 'tool_call', maxChars = CHAT_FUNCTION_NAME_MAX_CHARS) {
|
|
88
|
+
const fallbackName = String(fallback || 'tool_call')
|
|
89
|
+
.replace(/[^a-zA-Z0-9_-]/g, '_')
|
|
90
|
+
.replace(/^_+/, '')
|
|
91
|
+
.slice(0, maxChars) || 'tool_call';
|
|
92
|
+
const candidate = String(name || fallbackName)
|
|
73
93
|
.replace(/[^a-zA-Z0-9_-]/g, '_')
|
|
74
94
|
.replace(/^_+/, '')
|
|
75
|
-
.slice(0,
|
|
76
|
-
return candidate ||
|
|
95
|
+
.slice(0, maxChars);
|
|
96
|
+
return candidate || fallbackName;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function stableToolNameHash(parts) {
|
|
100
|
+
const hashInput = parts.map((part) => String(part ?? '')).join('\0');
|
|
101
|
+
return createHash('sha256').update(hashInput).digest('hex').slice(0, TOOL_NAME_HASH_CHARS);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function appendToolNameHash(name, hash, fallback = 'tool_call') {
|
|
105
|
+
const suffix = `__${hash}`;
|
|
106
|
+
const headChars = Math.max(1, CHAT_FUNCTION_NAME_MAX_CHARS - suffix.length);
|
|
107
|
+
return `${sanitizeFunctionName(name, fallback, headChars)}${suffix}`;
|
|
77
108
|
}
|
|
78
109
|
|
|
79
110
|
function toolNamespace(tool) {
|
|
@@ -88,22 +119,42 @@ function toolBaseName(tool, fallback = 'tool_call') {
|
|
|
88
119
|
}
|
|
89
120
|
|
|
90
121
|
function encodeToolName(namespace, name, fallback = 'tool_call') {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
122
|
+
const rawNamespace = String(namespace || '').trim();
|
|
123
|
+
const rawName = String(name || fallback);
|
|
124
|
+
const baseName = sanitizeFunctionName(rawName, fallback);
|
|
125
|
+
const encodedNamespace = rawNamespace ? sanitizeFunctionName(rawNamespace, 'namespace') : '';
|
|
126
|
+
const candidate = encodedNamespace ? `${encodedNamespace}__${baseName}` : baseName;
|
|
127
|
+
const encoded = sanitizeFunctionName(candidate, fallback);
|
|
128
|
+
const rawCandidate = rawNamespace ? `${rawNamespace}__${rawName}` : rawName;
|
|
129
|
+
if (encoded === rawCandidate && rawCandidate.length <= CHAT_FUNCTION_NAME_MAX_CHARS) {
|
|
130
|
+
return encoded;
|
|
131
|
+
}
|
|
132
|
+
return appendToolNameHash(candidate, stableToolNameHash([rawNamespace, rawName, fallback]), fallback);
|
|
95
133
|
}
|
|
96
134
|
|
|
97
135
|
function decodedToolName(name, toolNames) {
|
|
98
136
|
const value = String(name || '');
|
|
99
137
|
const known = toolNames?.get(value);
|
|
100
|
-
|
|
138
|
+
if (!known) return { name: value };
|
|
139
|
+
return omitUndefined({
|
|
140
|
+
namespace: known.namespace,
|
|
141
|
+
name: known.original_name || known.name,
|
|
142
|
+
});
|
|
101
143
|
}
|
|
102
144
|
|
|
103
145
|
function isCodexToolSearchTool(name) {
|
|
104
146
|
return String(name || '') === CODEX_TOOL_SEARCH_TOOL_NAME;
|
|
105
147
|
}
|
|
106
148
|
|
|
149
|
+
function toolChoiceDisablesTools(toolChoice) {
|
|
150
|
+
if (String(toolChoice || '').toLowerCase() === 'none') return true;
|
|
151
|
+
return isObject(toolChoice) && String(toolChoice.type || '').toLowerCase() === 'none';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function hasChatToolCalls(message) {
|
|
155
|
+
return Array.isArray(message?.tool_calls) && message.tool_calls.length > 0;
|
|
156
|
+
}
|
|
157
|
+
|
|
107
158
|
function omitUndefined(value) {
|
|
108
159
|
if (!isObject(value)) return value;
|
|
109
160
|
const result = {};
|
|
@@ -117,13 +168,31 @@ function compactText(value) {
|
|
|
117
168
|
return String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
118
169
|
}
|
|
119
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
|
+
|
|
120
181
|
function shortenText(value, maxChars) {
|
|
121
182
|
const text = compactText(value);
|
|
122
183
|
if (!text || text.length <= maxChars) return text;
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
const
|
|
126
|
-
|
|
184
|
+
if (maxChars <= 16) return `${text.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
|
|
185
|
+
const marker = ' ... ';
|
|
186
|
+
const contentChars = maxChars - marker.length;
|
|
187
|
+
const headChars = Math.max(24, Math.floor(contentChars * 0.72));
|
|
188
|
+
const tailChars = Math.max(12, contentChars - headChars);
|
|
189
|
+
const headSlice = text.slice(0, headChars);
|
|
190
|
+
const headBreak = Math.max(headSlice.lastIndexOf('. '), headSlice.lastIndexOf('; '), headSlice.lastIndexOf(', '), headSlice.lastIndexOf(' '));
|
|
191
|
+
const head = headSlice.slice(0, headBreak >= Math.floor(headChars * 0.55) ? headBreak : headSlice.length).trimEnd();
|
|
192
|
+
const tailSlice = text.slice(-tailChars);
|
|
193
|
+
const tailBreak = Math.max(tailSlice.indexOf('. '), tailSlice.indexOf('; '), tailSlice.indexOf(', '), tailSlice.indexOf(' '));
|
|
194
|
+
const tail = tailSlice.slice(tailBreak >= 0 && tailBreak <= Math.floor(tailChars * 0.45) ? tailBreak + 1 : 0).trimStart();
|
|
195
|
+
return `${head}${marker}${tail}`.slice(0, maxChars);
|
|
127
196
|
}
|
|
128
197
|
|
|
129
198
|
function textPart(text) {
|
|
@@ -246,9 +315,29 @@ function chatToolCallFromResponseItem(item) {
|
|
|
246
315
|
};
|
|
247
316
|
}
|
|
248
317
|
|
|
318
|
+
const GATEWAY_ENCRYPTED_REASONING_PREFIX = 'dsgw1:';
|
|
319
|
+
|
|
320
|
+
function encodeGatewayReasoning(text) {
|
|
321
|
+
const value = String(text ?? '');
|
|
322
|
+
if (!value) return null;
|
|
323
|
+
return `${GATEWAY_ENCRYPTED_REASONING_PREFIX}${Buffer.from(value, 'utf8').toString('base64')}`;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function decodeGatewayReasoning(encryptedContent) {
|
|
327
|
+
if (typeof encryptedContent !== 'string') return '';
|
|
328
|
+
if (!encryptedContent.startsWith(GATEWAY_ENCRYPTED_REASONING_PREFIX)) return '';
|
|
329
|
+
try {
|
|
330
|
+
return Buffer.from(encryptedContent.slice(GATEWAY_ENCRYPTED_REASONING_PREFIX.length), 'base64').toString('utf8');
|
|
331
|
+
} catch {
|
|
332
|
+
return '';
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
249
336
|
function reasoningTextFromItem(item) {
|
|
250
337
|
if (!isObject(item)) return '';
|
|
251
338
|
if (typeof item.reasoning_content === 'string') return normalizeReasoningContent(item.reasoning_content);
|
|
339
|
+
const decoded = decodeGatewayReasoning(item.encrypted_content);
|
|
340
|
+
if (decoded) return normalizeReasoningContent(decoded);
|
|
252
341
|
const rawParts = [];
|
|
253
342
|
if (typeof item.text === 'string') rawParts.push(item.text);
|
|
254
343
|
if (typeof item.content === 'string') rawParts.push(item.content);
|
|
@@ -281,7 +370,7 @@ function normalizeReasoningDisplayText(text) {
|
|
|
281
370
|
.replace(/^[ \t]{0,3}(?:`{3,}|~{3,}).*$/, '')
|
|
282
371
|
.replace(/^[ \t]{0,3}#{1,6}[ \t]+/, '')
|
|
283
372
|
.replace(/^[ \t]{0,3}>[ \t]?/, '')
|
|
284
|
-
.replace(/^[ \t]*[-*+][ \t]+(?:\[[ xX]\][ \t]+)?/, '
|
|
373
|
+
.replace(/^[ \t]*[-*+][ \t]+(?:\[[ xX]\][ \t]+)?/, '• ')
|
|
285
374
|
.replace(/^[ \t]*(\d+)\.[ \t]+/, '$1) ')
|
|
286
375
|
.replace(/!\[([^\]]*)\]\([^)]+\)/g, '$1')
|
|
287
376
|
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1')
|
|
@@ -299,7 +388,36 @@ function reasoningDisplayText(text) {
|
|
|
299
388
|
return normalizeReasoningDisplayText(text);
|
|
300
389
|
}
|
|
301
390
|
|
|
391
|
+
function compactToolSearchOutputTool(tool) {
|
|
392
|
+
if (!isObject(tool)) return null;
|
|
393
|
+
const namespace = toolNamespace(tool);
|
|
394
|
+
const name = toolBaseName(tool, '');
|
|
395
|
+
if (!name) return null;
|
|
396
|
+
const schema = normalizeJsonSchemaObject(tool.function?.parameters ?? tool.parameters ?? tool.input_schema);
|
|
397
|
+
const properties = Object.keys(isObject(schema.properties) ? schema.properties : {});
|
|
398
|
+
const required = Array.isArray(schema.required) ? schema.required.map(String) : [];
|
|
399
|
+
const description = tool.function?.description ?? tool.description ?? '';
|
|
400
|
+
return omitUndefined({
|
|
401
|
+
name: encodeToolName(namespace, name),
|
|
402
|
+
description: description ? shortenText(description, 220) : undefined,
|
|
403
|
+
required: required.length ? required : undefined,
|
|
404
|
+
properties: properties.length ? properties : undefined,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
302
408
|
function toolOutputContent(item) {
|
|
409
|
+
if (item?.type === 'tool_search_output' && Array.isArray(item.tools)) {
|
|
410
|
+
const tools = expandTools(item.tools)
|
|
411
|
+
.map(compactToolSearchOutputTool)
|
|
412
|
+
.filter(Boolean);
|
|
413
|
+
return JSON.stringify(omitUndefined({
|
|
414
|
+
status: item.status || 'completed',
|
|
415
|
+
note: tools.length
|
|
416
|
+
? 'Discovered tools are loaded into the current tool list and can be called directly by these names.'
|
|
417
|
+
: undefined,
|
|
418
|
+
discovered_tools: tools,
|
|
419
|
+
}));
|
|
420
|
+
}
|
|
303
421
|
const output = item.output ?? item.content ?? item.result ?? item.error ?? '';
|
|
304
422
|
if (typeof output === 'string') return output;
|
|
305
423
|
if (Array.isArray(output)) return contentToChatContent(output);
|
|
@@ -330,46 +448,127 @@ function normalizeMessage(message) {
|
|
|
330
448
|
return normalized;
|
|
331
449
|
}
|
|
332
450
|
|
|
451
|
+
function assistantMessageIsEmpty(message) {
|
|
452
|
+
if (hasChatToolCalls(message)) return false;
|
|
453
|
+
const content = message?.content;
|
|
454
|
+
if (typeof content === 'string') return content.length === 0;
|
|
455
|
+
if (Array.isArray(content)) return content.length === 0;
|
|
456
|
+
return content == null;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function webSearchCallNote(item) {
|
|
460
|
+
const action = isObject(item?.action) ? item.action : {};
|
|
461
|
+
const actionType = action.type || 'search';
|
|
462
|
+
if (actionType === 'open_page') {
|
|
463
|
+
return action.url ? `Opened page ${action.url}.` : 'Opened a web page.';
|
|
464
|
+
}
|
|
465
|
+
if (actionType === 'find_in_page') {
|
|
466
|
+
const target = action.url || 'a web page';
|
|
467
|
+
return action.query ? `Searched within ${target} for "${action.query}".` : `Searched within ${target}.`;
|
|
468
|
+
}
|
|
469
|
+
const query = action.query ? `"${action.query}"` : 'the web';
|
|
470
|
+
const sources = (Array.isArray(action.sources) ? action.sources : [])
|
|
471
|
+
.filter(isObject)
|
|
472
|
+
.slice(0, 3)
|
|
473
|
+
.map((source) => (source.title && source.url ? `${source.title} <${source.url}>` : source.url || source.title))
|
|
474
|
+
.filter(Boolean)
|
|
475
|
+
.join('; ');
|
|
476
|
+
return `Searched the web for ${query}${sources ? ` (sources: ${sources})` : ''}.`;
|
|
477
|
+
}
|
|
478
|
+
|
|
333
479
|
function extractMessagesFromResponsesInput(input) {
|
|
334
480
|
if (Array.isArray(input)) {
|
|
335
481
|
const messages = [];
|
|
336
482
|
let pendingUserContent = [];
|
|
337
483
|
let pendingReasoningContent = '';
|
|
338
484
|
let pendingAssistantToolMessage = null;
|
|
485
|
+
let pendingAssistantMessage = null;
|
|
486
|
+
let pendingWebSearchNotes = [];
|
|
339
487
|
const flushPendingUserContent = () => {
|
|
340
488
|
if (!pendingUserContent.length) return;
|
|
341
489
|
messages.push({ role: 'user', content: contentToChatContent(pendingUserContent) });
|
|
342
490
|
pendingUserContent = [];
|
|
343
491
|
};
|
|
492
|
+
const flushPendingWebSearchNotes = () => {
|
|
493
|
+
if (!pendingWebSearchNotes.length) return;
|
|
494
|
+
messages.push({
|
|
495
|
+
role: 'assistant',
|
|
496
|
+
content: `[Earlier web activity] ${pendingWebSearchNotes.join(' ')}`,
|
|
497
|
+
});
|
|
498
|
+
pendingWebSearchNotes = [];
|
|
499
|
+
};
|
|
344
500
|
const flushPendingAssistantToolMessage = () => {
|
|
345
501
|
if (!pendingAssistantToolMessage) return;
|
|
346
502
|
messages.push(pendingAssistantToolMessage);
|
|
347
503
|
pendingAssistantToolMessage = null;
|
|
348
504
|
pendingReasoningContent = '';
|
|
349
505
|
};
|
|
506
|
+
const flushPendingAssistantMessage = () => {
|
|
507
|
+
if (!pendingAssistantMessage) return;
|
|
508
|
+
const message = pendingAssistantMessage;
|
|
509
|
+
pendingAssistantMessage = null;
|
|
510
|
+
if (assistantMessageIsEmpty(message)) {
|
|
511
|
+
if (message.reasoning_content && !pendingReasoningContent) {
|
|
512
|
+
pendingReasoningContent = message.reasoning_content;
|
|
513
|
+
}
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
messages.push(message);
|
|
517
|
+
};
|
|
518
|
+
const flushAssistantState = () => {
|
|
519
|
+
flushPendingAssistantToolMessage();
|
|
520
|
+
flushPendingAssistantMessage();
|
|
521
|
+
};
|
|
350
522
|
const ensurePendingAssistantToolMessage = () => {
|
|
351
523
|
if (!pendingAssistantToolMessage) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
tool_calls
|
|
356
|
-
|
|
357
|
-
|
|
524
|
+
if (pendingAssistantMessage) {
|
|
525
|
+
pendingAssistantToolMessage = pendingAssistantMessage;
|
|
526
|
+
pendingAssistantMessage = null;
|
|
527
|
+
if (!Array.isArray(pendingAssistantToolMessage.tool_calls)) {
|
|
528
|
+
pendingAssistantToolMessage.tool_calls = [];
|
|
529
|
+
}
|
|
530
|
+
} else {
|
|
531
|
+
pendingAssistantToolMessage = {
|
|
532
|
+
role: 'assistant',
|
|
533
|
+
content: '',
|
|
534
|
+
tool_calls: [],
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
if (!pendingAssistantToolMessage.reasoning_content && pendingReasoningContent) {
|
|
358
538
|
pendingAssistantToolMessage.reasoning_content = pendingReasoningContent;
|
|
359
539
|
}
|
|
360
540
|
}
|
|
361
541
|
return pendingAssistantToolMessage;
|
|
362
542
|
};
|
|
543
|
+
const pushRoleMessage = (item) => {
|
|
544
|
+
flushPendingUserContent();
|
|
545
|
+
flushPendingAssistantToolMessage();
|
|
546
|
+
flushPendingWebSearchNotes();
|
|
547
|
+
flushPendingAssistantMessage();
|
|
548
|
+
const normalized = normalizeMessage(item);
|
|
549
|
+
if (!normalized) return;
|
|
550
|
+
if (normalized.role === 'assistant' && !normalized.reasoning_content && pendingReasoningContent) {
|
|
551
|
+
normalized.reasoning_content = pendingReasoningContent;
|
|
552
|
+
pendingReasoningContent = '';
|
|
553
|
+
}
|
|
554
|
+
if (normalized.role === 'assistant' && !hasChatToolCalls(normalized)) {
|
|
555
|
+
pendingAssistantMessage = normalized;
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
messages.push(normalized);
|
|
559
|
+
};
|
|
363
560
|
|
|
364
561
|
for (const item of input) {
|
|
365
562
|
if (typeof item === 'string') {
|
|
366
|
-
|
|
563
|
+
flushAssistantState();
|
|
564
|
+
flushPendingWebSearchNotes();
|
|
367
565
|
pendingUserContent.push({ type: 'input_text', text: item });
|
|
368
566
|
continue;
|
|
369
567
|
}
|
|
370
568
|
if (!isObject(item)) continue;
|
|
371
569
|
if (isInputContentPart(item)) {
|
|
372
|
-
|
|
570
|
+
flushAssistantState();
|
|
571
|
+
flushPendingWebSearchNotes();
|
|
373
572
|
pendingUserContent.push(item);
|
|
374
573
|
continue;
|
|
375
574
|
}
|
|
@@ -378,45 +577,40 @@ function extractMessagesFromResponsesInput(input) {
|
|
|
378
577
|
continue;
|
|
379
578
|
}
|
|
380
579
|
if (item.type === 'message' && item.role) {
|
|
580
|
+
pushRoleMessage(item);
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
if (item.type === 'web_search_call') {
|
|
381
584
|
flushPendingUserContent();
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (normalized?.role === 'assistant' && !normalized.reasoning_content && pendingReasoningContent) {
|
|
385
|
-
normalized.reasoning_content = pendingReasoningContent;
|
|
386
|
-
pendingReasoningContent = '';
|
|
387
|
-
}
|
|
388
|
-
if (normalized) messages.push(normalized);
|
|
585
|
+
flushAssistantState();
|
|
586
|
+
pendingWebSearchNotes.push(webSearchCallNote(item));
|
|
389
587
|
continue;
|
|
390
588
|
}
|
|
391
589
|
if (CHAT_HISTORY_IGNORED_TOOL_ITEM_TYPES.has(item.type)) {
|
|
392
590
|
flushPendingUserContent();
|
|
393
|
-
|
|
591
|
+
flushAssistantState();
|
|
394
592
|
continue;
|
|
395
593
|
}
|
|
396
594
|
if (TOOL_OUTPUT_TYPES.has(item.type) || String(item.type || '').endsWith('_call_output')) {
|
|
397
595
|
flushPendingUserContent();
|
|
398
|
-
|
|
596
|
+
flushPendingWebSearchNotes();
|
|
597
|
+
flushAssistantState();
|
|
399
598
|
messages.push(responseToolOutputToMessage(item));
|
|
400
599
|
continue;
|
|
401
600
|
}
|
|
402
601
|
if (TOOL_CALL_TYPES.has(item.type) || String(item.type || '').endsWith('_call')) {
|
|
403
602
|
flushPendingUserContent();
|
|
603
|
+
flushPendingWebSearchNotes();
|
|
404
604
|
ensurePendingAssistantToolMessage().tool_calls.push(chatToolCallFromResponseItem(item));
|
|
405
605
|
continue;
|
|
406
606
|
}
|
|
407
607
|
if (item.role) {
|
|
408
|
-
|
|
409
|
-
flushPendingAssistantToolMessage();
|
|
410
|
-
const normalized = normalizeMessage(item);
|
|
411
|
-
if (normalized?.role === 'assistant' && !normalized.reasoning_content && pendingReasoningContent) {
|
|
412
|
-
normalized.reasoning_content = pendingReasoningContent;
|
|
413
|
-
pendingReasoningContent = '';
|
|
414
|
-
}
|
|
415
|
-
if (normalized) messages.push(normalized);
|
|
608
|
+
pushRoleMessage(item);
|
|
416
609
|
}
|
|
417
610
|
}
|
|
418
611
|
flushPendingUserContent();
|
|
419
|
-
|
|
612
|
+
flushAssistantState();
|
|
613
|
+
flushPendingWebSearchNotes();
|
|
420
614
|
return messages;
|
|
421
615
|
}
|
|
422
616
|
|
|
@@ -442,14 +636,145 @@ function extractMessagesFromResponsesInput(input) {
|
|
|
442
636
|
return [];
|
|
443
637
|
}
|
|
444
638
|
|
|
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.';
|
|
640
|
+
|
|
641
|
+
function truncateRawText(value, maxChars) {
|
|
642
|
+
const text = String(value ?? '');
|
|
643
|
+
if (text.length <= maxChars) return text;
|
|
644
|
+
return `${text.slice(0, Math.max(0, maxChars - 4))}\n...`;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function customToolShimParameters() {
|
|
648
|
+
return {
|
|
649
|
+
type: 'object',
|
|
650
|
+
properties: {
|
|
651
|
+
input: {
|
|
652
|
+
type: 'string',
|
|
653
|
+
description: 'Complete raw input text for this tool.',
|
|
654
|
+
},
|
|
655
|
+
},
|
|
656
|
+
required: ['input'],
|
|
657
|
+
additionalProperties: false,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function customToolShim(tool) {
|
|
662
|
+
const format = isObject(tool.format) ? tool.format : {};
|
|
663
|
+
const baseName = toolBaseName(tool, 'custom_tool');
|
|
664
|
+
const chunks = [
|
|
665
|
+
compactStructuredText(tool.description, DEEPSEEK_TOOL_DESCRIPTION_SOFT_CHARS) || `Freeform tool ${baseName}.`,
|
|
666
|
+
CUSTOM_TOOL_INPUT_HINT,
|
|
667
|
+
];
|
|
668
|
+
if (format.syntax) chunks.push(`Input syntax: ${format.syntax}.`);
|
|
669
|
+
if (typeof format.definition === 'string' && format.definition.trim()) {
|
|
670
|
+
chunks.push(`Input grammar:\n${format.definition.trim()}`);
|
|
671
|
+
}
|
|
672
|
+
const parameters = customToolShimParameters();
|
|
673
|
+
const sourceSchema = isObject(tool.input_schema) ? tool.input_schema : isObject(tool.parameters) ? tool.parameters : null;
|
|
674
|
+
const sourceInputDescription = sourceSchema?.properties?.input?.description;
|
|
675
|
+
if (typeof sourceInputDescription === 'string' && sourceInputDescription) {
|
|
676
|
+
parameters.properties.input.description = compactStructuredText(sourceInputDescription, DEEPSEEK_SCHEMA_DESCRIPTION_SOFT_CHARS);
|
|
677
|
+
}
|
|
678
|
+
return {
|
|
679
|
+
type: 'function',
|
|
680
|
+
gateway_custom_tool: true,
|
|
681
|
+
function: {
|
|
682
|
+
name: encodeToolName(toolNamespace(tool), baseName),
|
|
683
|
+
description: chunks.join('\n'),
|
|
684
|
+
parameters,
|
|
685
|
+
},
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function buildCustomToolNames(tools) {
|
|
690
|
+
const names = new Set();
|
|
691
|
+
for (const tool of expandTools(tools)) {
|
|
692
|
+
if (!isObject(tool) || tool.type !== 'custom') continue;
|
|
693
|
+
const normalizedTool = normalizeTool(tool);
|
|
694
|
+
const fn = normalizedTool?.type === 'function' ? normalizedTool.function : null;
|
|
695
|
+
if (fn?.name) names.add(fn.name);
|
|
696
|
+
}
|
|
697
|
+
return names;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function customToolInputFromArguments(argumentsText) {
|
|
701
|
+
if (typeof argumentsText !== 'string' || !argumentsText) return '';
|
|
702
|
+
const parsed = safeJsonParse(argumentsText);
|
|
703
|
+
if (parsed.ok && typeof parsed.value === 'string') return parsed.value;
|
|
704
|
+
if (parsed.ok && isObject(parsed.value)) {
|
|
705
|
+
const input = parsed.value.input;
|
|
706
|
+
if (typeof input === 'string') return input;
|
|
707
|
+
if (input !== undefined) return jsonString(input);
|
|
708
|
+
return argumentsText;
|
|
709
|
+
}
|
|
710
|
+
return argumentsText;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
function stripGatewayToolMarkers(tools) {
|
|
714
|
+
if (!Array.isArray(tools)) return tools;
|
|
715
|
+
return tools.map((tool) => {
|
|
716
|
+
if (!isObject(tool) || tool.gateway_custom_tool === undefined) return tool;
|
|
717
|
+
const { gateway_custom_tool, ...rest } = tool;
|
|
718
|
+
return rest;
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const UNAVAILABLE_TOOL_GUIDANCE = 'Do not call this tool; if the task depends on it, tell the user it is unavailable.';
|
|
723
|
+
|
|
724
|
+
function unavailableHostedToolShim(tool, reason) {
|
|
725
|
+
const capability = String(tool.type || 'tool');
|
|
726
|
+
const detail = reason || `Unavailable capability: the client requested the hosted ${capability} tool, but this gateway cannot execute it.`;
|
|
727
|
+
return {
|
|
728
|
+
type: 'function',
|
|
729
|
+
function: {
|
|
730
|
+
name: encodeToolName(toolNamespace(tool), toolBaseName(tool, capability)),
|
|
731
|
+
description: `${detail} ${UNAVAILABLE_TOOL_GUIDANCE}`,
|
|
732
|
+
parameters: { type: 'object', properties: {}, additionalProperties: false },
|
|
733
|
+
},
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
export function unavailableWebSearchToolShims(tools) {
|
|
738
|
+
const shims = [];
|
|
739
|
+
const seen = new Set();
|
|
740
|
+
for (const tool of expandTools(tools)) {
|
|
741
|
+
if (!isObject(tool) || typeof tool.type !== 'string' || !EMULATED_HOSTED_TOOL_TYPES.has(tool.type)) continue;
|
|
742
|
+
const shim = unavailableHostedToolShim(
|
|
743
|
+
tool,
|
|
744
|
+
'Unavailable capability: web search was requested, but the gateway has no search provider configured.',
|
|
745
|
+
);
|
|
746
|
+
if (seen.has(shim.function.name)) continue;
|
|
747
|
+
seen.add(shim.function.name);
|
|
748
|
+
shims.push(shim);
|
|
749
|
+
}
|
|
750
|
+
return shims;
|
|
751
|
+
}
|
|
752
|
+
|
|
445
753
|
function normalizeTool(tool) {
|
|
446
754
|
if (!isObject(tool)) return tool;
|
|
447
755
|
if (tool.type === 'namespace') return null;
|
|
448
756
|
if (typeof tool.type === 'string' && EMULATED_HOSTED_TOOL_TYPES.has(tool.type)) {
|
|
449
757
|
return null;
|
|
450
758
|
}
|
|
759
|
+
if (typeof tool.type === 'string' && UNSUPPORTED_HOSTED_TOOL_TYPES.has(tool.type)) {
|
|
760
|
+
return unavailableHostedToolShim(tool);
|
|
761
|
+
}
|
|
762
|
+
if (tool.type === 'custom') {
|
|
763
|
+
return customToolShim(tool);
|
|
764
|
+
}
|
|
765
|
+
if (typeof tool.type === 'string' && BRIDGED_RESPONSES_TOOL_TYPES.has(tool.type)) {
|
|
766
|
+
return {
|
|
767
|
+
type: 'function',
|
|
768
|
+
function: omitUndefined({
|
|
769
|
+
name: encodeToolName(toolNamespace(tool), tool.type),
|
|
770
|
+
description: tool.description,
|
|
771
|
+
parameters: normalizeJsonSchemaObject(tool.parameters || tool.input_schema),
|
|
772
|
+
strict: tool.strict,
|
|
773
|
+
}),
|
|
774
|
+
};
|
|
775
|
+
}
|
|
451
776
|
if (tool.type === 'function' && isObject(tool.function)) {
|
|
452
|
-
const { namespace, ...fn } = tool.function;
|
|
777
|
+
const { namespace, defer_loading, ...fn } = tool.function;
|
|
453
778
|
return {
|
|
454
779
|
type: 'function',
|
|
455
780
|
function: omitUndefined({
|
|
@@ -461,7 +786,7 @@ function normalizeTool(tool) {
|
|
|
461
786
|
}),
|
|
462
787
|
};
|
|
463
788
|
}
|
|
464
|
-
if (tool.type === 'function'
|
|
789
|
+
if (tool.type === 'function') {
|
|
465
790
|
return {
|
|
466
791
|
type: 'function',
|
|
467
792
|
function: omitUndefined({
|
|
@@ -472,6 +797,17 @@ function normalizeTool(tool) {
|
|
|
472
797
|
}),
|
|
473
798
|
};
|
|
474
799
|
}
|
|
800
|
+
if (!tool.type && (tool.name || tool.parameters || tool.input_schema)) {
|
|
801
|
+
return {
|
|
802
|
+
type: 'function',
|
|
803
|
+
function: omitUndefined({
|
|
804
|
+
name: encodeToolName(toolNamespace(tool), toolBaseName(tool, 'tool_call')),
|
|
805
|
+
description: tool.description,
|
|
806
|
+
parameters: normalizeJsonSchemaObject(tool.parameters || tool.input_schema),
|
|
807
|
+
strict: tool.strict,
|
|
808
|
+
}),
|
|
809
|
+
};
|
|
810
|
+
}
|
|
475
811
|
return null;
|
|
476
812
|
}
|
|
477
813
|
|
|
@@ -518,8 +854,19 @@ function normalizeTools(tools) {
|
|
|
518
854
|
return normalized.length ? normalized : undefined;
|
|
519
855
|
}
|
|
520
856
|
|
|
857
|
+
function hasRunnableChatTools(normalized) {
|
|
858
|
+
return Array.isArray(applyAllowedTools(normalizeTools(normalized?.tools), normalized?.tool_choice));
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
function toolSearchDiscoveryKey(tool) {
|
|
862
|
+
if (tool.type === 'namespace') {
|
|
863
|
+
return `namespace:${tool.name || tool.namespace || tool.server_label || ''}`;
|
|
864
|
+
}
|
|
865
|
+
return `${tool.type || 'function'}:${toolNamespace(tool)}:${toolBaseName(tool, '')}`;
|
|
866
|
+
}
|
|
867
|
+
|
|
521
868
|
function collectToolSearchOutputTools(input) {
|
|
522
|
-
const discovered =
|
|
869
|
+
const discovered = new Map();
|
|
523
870
|
const scan = (value) => {
|
|
524
871
|
if (Array.isArray(value)) {
|
|
525
872
|
for (const item of value) scan(item);
|
|
@@ -527,14 +874,16 @@ function collectToolSearchOutputTools(input) {
|
|
|
527
874
|
}
|
|
528
875
|
if (!isObject(value)) return;
|
|
529
876
|
if (value.type === 'tool_search_output' && Array.isArray(value.tools)) {
|
|
530
|
-
|
|
877
|
+
for (const tool of value.tools) {
|
|
878
|
+
if (isObject(tool)) discovered.set(toolSearchDiscoveryKey(tool), tool);
|
|
879
|
+
}
|
|
531
880
|
return;
|
|
532
881
|
}
|
|
533
882
|
if (Array.isArray(value.input)) scan(value.input);
|
|
534
883
|
if (Array.isArray(value.content)) scan(value.content);
|
|
535
884
|
};
|
|
536
885
|
scan(input);
|
|
537
|
-
return discovered;
|
|
886
|
+
return [...discovered.values()];
|
|
538
887
|
}
|
|
539
888
|
|
|
540
889
|
function mergeToolsWithToolSearchOutput(requestTools, input) {
|
|
@@ -572,6 +921,7 @@ function normalizeJsonSchemaObject(schema) {
|
|
|
572
921
|
}
|
|
573
922
|
|
|
574
923
|
function normalizeToolChoice(toolChoice) {
|
|
924
|
+
if (toolChoiceDisablesTools(toolChoice)) return 'none';
|
|
575
925
|
if (!isObject(toolChoice)) return toolChoice;
|
|
576
926
|
if (toolChoice.type === 'function' && toolChoice.name) {
|
|
577
927
|
return {
|
|
@@ -639,6 +989,7 @@ function allowedToolNames(toolChoice) {
|
|
|
639
989
|
}
|
|
640
990
|
|
|
641
991
|
function applyAllowedTools(tools, toolChoice) {
|
|
992
|
+
if (toolChoiceDisablesTools(toolChoice)) return undefined;
|
|
642
993
|
const allowed = allowedToolNames(toolChoice);
|
|
643
994
|
if (!allowed || !Array.isArray(tools)) return tools;
|
|
644
995
|
const filtered = tools.filter((tool) => {
|
|
@@ -710,7 +1061,7 @@ function convertItemToToolSearchCall(item) {
|
|
|
710
1061
|
return item;
|
|
711
1062
|
}
|
|
712
1063
|
|
|
713
|
-
function responseItemFromChatToolCall(toolCall, toolNames) {
|
|
1064
|
+
function responseItemFromChatToolCall(toolCall, toolNames, customToolNames) {
|
|
714
1065
|
const callId = toolCall.id || generateId('call');
|
|
715
1066
|
if (isCodexToolSearchTool(toolCall.function?.name)) {
|
|
716
1067
|
return {
|
|
@@ -723,6 +1074,16 @@ function responseItemFromChatToolCall(toolCall, toolNames) {
|
|
|
723
1074
|
};
|
|
724
1075
|
}
|
|
725
1076
|
const decoded = decodedToolName(toolCall.function?.name, toolNames);
|
|
1077
|
+
if (customToolNames?.has(toolCall.function?.name)) {
|
|
1078
|
+
return omitUndefined({
|
|
1079
|
+
type: 'custom_tool_call',
|
|
1080
|
+
id: callId,
|
|
1081
|
+
call_id: callId,
|
|
1082
|
+
name: decoded.name,
|
|
1083
|
+
input: customToolInputFromArguments(toolCall.function?.arguments || ''),
|
|
1084
|
+
status: 'completed',
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
726
1087
|
return omitUndefined({
|
|
727
1088
|
type: 'function_call',
|
|
728
1089
|
id: callId,
|
|
@@ -749,14 +1110,17 @@ function buildToolNames(tools) {
|
|
|
749
1110
|
const names = new Map();
|
|
750
1111
|
for (const tool of expandTools(tools)) {
|
|
751
1112
|
const namespace = toolNamespace(tool);
|
|
752
|
-
if (!namespace) continue;
|
|
753
1113
|
const normalizedTool = normalizeTool(tool);
|
|
754
1114
|
const fn = normalizedTool?.type === 'function' ? normalizedTool.function : null;
|
|
755
1115
|
if (!fn?.name) continue;
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
1116
|
+
const baseName = String(toolBaseName(tool, tool.type));
|
|
1117
|
+
const sanitizedBaseName = sanitizeFunctionName(baseName);
|
|
1118
|
+
if (!namespace && fn.name === baseName) continue;
|
|
1119
|
+
names.set(fn.name, omitUndefined({
|
|
1120
|
+
namespace: namespace || undefined,
|
|
1121
|
+
name: sanitizedBaseName,
|
|
1122
|
+
original_name: baseName,
|
|
1123
|
+
}));
|
|
760
1124
|
}
|
|
761
1125
|
return names;
|
|
762
1126
|
}
|
|
@@ -767,7 +1131,7 @@ function simplifyJsonSchemaDescriptions(schema) {
|
|
|
767
1131
|
const next = {};
|
|
768
1132
|
for (const [key, value] of Object.entries(schema)) {
|
|
769
1133
|
if (key === 'description' && typeof value === 'string') {
|
|
770
|
-
const description =
|
|
1134
|
+
const description = compactStructuredText(value, DEEPSEEK_SCHEMA_DESCRIPTION_SOFT_CHARS);
|
|
771
1135
|
if (description) next.description = description;
|
|
772
1136
|
continue;
|
|
773
1137
|
}
|
|
@@ -790,82 +1154,15 @@ function defaultDeepSeekToolDescription(name) {
|
|
|
790
1154
|
return compactText(`Call ${readable || name || 'this function'} when needed.`);
|
|
791
1155
|
}
|
|
792
1156
|
|
|
793
|
-
function
|
|
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 = {}) {
|
|
1157
|
+
function simplifyToolForDeepSeek(tool) {
|
|
857
1158
|
if (tool?.type !== 'function' || !isObject(tool.function)) return tool;
|
|
1159
|
+
if (tool.gateway_custom_tool) return tool;
|
|
858
1160
|
const fn = tool.function;
|
|
859
|
-
|
|
860
|
-
if (isCodexSpawnAgentTool(fn.name)) {
|
|
861
|
-
parameters = deepSeekSpawnAgentParameters(parameters, config);
|
|
862
|
-
}
|
|
1161
|
+
const parameters = simplifyJsonSchemaDescriptions(fn.parameters);
|
|
863
1162
|
const requiredText = requiredParametersText(parameters);
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
: shortenText(fn.description, descriptionBudget) || defaultDeepSeekToolDescription(fn.name);
|
|
868
|
-
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}`;
|
|
869
1166
|
return {
|
|
870
1167
|
...tool,
|
|
871
1168
|
function: omitUndefined({
|
|
@@ -882,21 +1179,47 @@ function hasDeepSeekToolInstructions(messages) {
|
|
|
882
1179
|
|
|
883
1180
|
function addDeepSeekToolInstructions(messages, tools) {
|
|
884
1181
|
if (!Array.isArray(tools) || !tools.length || hasDeepSeekToolInstructions(messages)) return messages;
|
|
1182
|
+
const instructions = requestToolsIncludeCommentary(tools)
|
|
1183
|
+
? `${DEEPSEEK_TOOL_INSTRUCTIONS} ${DEEPSEEK_COMMENTARY_INSTRUCTIONS}`
|
|
1184
|
+
: DEEPSEEK_TOOL_INSTRUCTIONS;
|
|
885
1185
|
if (!messages.length || messages[0]?.role !== 'system') {
|
|
886
|
-
return [{ role: 'system', content:
|
|
1186
|
+
return [{ role: 'system', content: instructions }, ...messages];
|
|
887
1187
|
}
|
|
888
1188
|
return [
|
|
889
1189
|
{
|
|
890
1190
|
...messages[0],
|
|
891
|
-
content: `${toText(messages[0].content)}\n\n${
|
|
1191
|
+
content: `${toText(messages[0].content)}\n\n${instructions}`,
|
|
892
1192
|
},
|
|
893
1193
|
...messages.slice(1),
|
|
894
1194
|
];
|
|
895
1195
|
}
|
|
896
1196
|
|
|
1197
|
+
function isDeepSeekBetaBaseUrl(baseUrl) {
|
|
1198
|
+
return /\/beta\/?$/.test(String(baseUrl || ''));
|
|
1199
|
+
}
|
|
1200
|
+
|
|
897
1201
|
function adaptToolsForProvider(tools, provider, config = {}) {
|
|
898
1202
|
if (provider !== 'deepseek' || !Array.isArray(tools)) return tools;
|
|
899
|
-
|
|
1203
|
+
const keepStrict = isDeepSeekBetaBaseUrl(config.upstreamBaseUrl);
|
|
1204
|
+
return tools.map((tool) => {
|
|
1205
|
+
const simplified = simplifyToolForDeepSeek(tool);
|
|
1206
|
+
if (!keepStrict && simplified?.type === 'function' && isObject(simplified.function) && simplified.function.strict !== undefined) {
|
|
1207
|
+
const { strict, ...fn } = simplified.function;
|
|
1208
|
+
return { ...simplified, function: fn };
|
|
1209
|
+
}
|
|
1210
|
+
return simplified;
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
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;
|
|
900
1223
|
}
|
|
901
1224
|
|
|
902
1225
|
function normalizeToolCallArguments(name, argumentsText, toolSchema) {
|
|
@@ -921,48 +1244,20 @@ function normalizeToolCallArguments(name, argumentsText, toolSchema) {
|
|
|
921
1244
|
}
|
|
922
1245
|
}
|
|
923
1246
|
|
|
924
|
-
function
|
|
925
|
-
|
|
1247
|
+
function normalizeFunctionCallItemArguments(item, toolSchemas) {
|
|
1248
|
+
if (!item || item.type !== 'function_call') return;
|
|
1249
|
+
const encodedName = encodeToolName(item.namespace, item.name);
|
|
1250
|
+
item.arguments = normalizeToolCallArguments(encodedName, item.arguments, toolSchemas?.get(encodedName));
|
|
926
1251
|
}
|
|
927
1252
|
|
|
928
|
-
function
|
|
929
|
-
if (!
|
|
930
|
-
|
|
931
|
-
|
|
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;
|
|
1253
|
+
function functionCallItemNeedsArgumentNormalization(item, toolSchemas) {
|
|
1254
|
+
if (!item || item.type !== 'function_call') return false;
|
|
1255
|
+
const encodedName = encodeToolName(item.namespace, item.name);
|
|
1256
|
+
return Boolean(toolSchemas?.has(encodedName));
|
|
959
1257
|
}
|
|
960
1258
|
|
|
961
|
-
function
|
|
962
|
-
|
|
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);
|
|
1259
|
+
function hasToolCallFunctionName(toolCall) {
|
|
1260
|
+
return typeof toolCall?.function?.name === 'string' && toolCall.function.name.length > 0;
|
|
966
1261
|
}
|
|
967
1262
|
|
|
968
1263
|
function sanitizeToolCallsForChatCompletion(toolCalls) {
|
|
@@ -983,11 +1278,51 @@ function sanitizeToolCallsForChatCompletion(toolCalls) {
|
|
|
983
1278
|
});
|
|
984
1279
|
}
|
|
985
1280
|
|
|
1281
|
+
function multimodalPlaceholder(kind, hint) {
|
|
1282
|
+
const cleanHint = compactText(hint);
|
|
1283
|
+
const shortHint = cleanHint && !cleanHint.startsWith('data:') ? shortenText(cleanHint, 120) : '';
|
|
1284
|
+
return `[${kind} omitted: DeepSeek accepts text input only${shortHint ? `; source: ${shortHint}` : ''}]`;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
function deepseekTextOnlyContent(content) {
|
|
1288
|
+
if (content == null) return '';
|
|
1289
|
+
if (typeof content === 'string') return content;
|
|
1290
|
+
const parts = Array.isArray(content) ? content : [content];
|
|
1291
|
+
const chunks = [];
|
|
1292
|
+
for (const part of parts) {
|
|
1293
|
+
if (typeof part === 'string') {
|
|
1294
|
+
chunks.push(part);
|
|
1295
|
+
continue;
|
|
1296
|
+
}
|
|
1297
|
+
if (!isObject(part)) continue;
|
|
1298
|
+
if (part.type === 'text') {
|
|
1299
|
+
chunks.push(String(part.text ?? ''));
|
|
1300
|
+
continue;
|
|
1301
|
+
}
|
|
1302
|
+
if (part.type === 'image_url') {
|
|
1303
|
+
chunks.push(multimodalPlaceholder('image', part.image_url?.url || part.image_url?.file_id));
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1306
|
+
if (part.type === 'file') {
|
|
1307
|
+
chunks.push(multimodalPlaceholder('file', part.file?.filename || part.file?.file_url || part.file?.file_id));
|
|
1308
|
+
continue;
|
|
1309
|
+
}
|
|
1310
|
+
if (part.type === 'input_audio') {
|
|
1311
|
+
chunks.push(multimodalPlaceholder('audio', part.input_audio?.file_id));
|
|
1312
|
+
continue;
|
|
1313
|
+
}
|
|
1314
|
+
const text = toText(part);
|
|
1315
|
+
if (text) chunks.push(text);
|
|
1316
|
+
}
|
|
1317
|
+
return chunks.filter((chunk) => chunk !== '').join('\n');
|
|
1318
|
+
}
|
|
1319
|
+
|
|
986
1320
|
function sanitizeMessageForChatCompletion(message, provider = 'generic') {
|
|
987
1321
|
if (!isObject(message)) return message;
|
|
1322
|
+
const chatContent = contentToChatContent(message.content);
|
|
988
1323
|
const sanitized = {
|
|
989
1324
|
role: normalizeRole(message.role, provider),
|
|
990
|
-
content:
|
|
1325
|
+
content: provider === 'deepseek' ? deepseekTextOnlyContent(chatContent) : chatContent,
|
|
991
1326
|
};
|
|
992
1327
|
if (message.name !== undefined) sanitized.name = message.name;
|
|
993
1328
|
if (Array.isArray(message.tool_calls)) {
|
|
@@ -1000,11 +1335,93 @@ function sanitizeMessageForChatCompletion(message, provider = 'generic') {
|
|
|
1000
1335
|
return sanitized;
|
|
1001
1336
|
}
|
|
1002
1337
|
|
|
1338
|
+
function deepSeekInstructionBlock(message, includePriorityLabels) {
|
|
1339
|
+
const content = toText(message?.content).trim();
|
|
1340
|
+
if (!content) return '';
|
|
1341
|
+
if (!includePriorityLabels) return content;
|
|
1342
|
+
const label = message.role === 'developer'
|
|
1343
|
+
? 'Developer instructions (priority below system):'
|
|
1344
|
+
: 'System instructions (highest priority):';
|
|
1345
|
+
return `${label}\n${content}`;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
function sanitizeMessagesForChatCompletion(messages, provider = 'generic') {
|
|
1349
|
+
const source = Array.isArray(messages) ? messages : [];
|
|
1350
|
+
if (provider !== 'deepseek') {
|
|
1351
|
+
return source.map((message) => sanitizeMessageForChatCompletion(message, provider));
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
const result = [];
|
|
1355
|
+
let instructionBlock = [];
|
|
1356
|
+
const flushInstructionBlock = () => {
|
|
1357
|
+
if (!instructionBlock.length) return;
|
|
1358
|
+
const hasDeveloper = instructionBlock.some((message) => message?.role === 'developer');
|
|
1359
|
+
const content = instructionBlock
|
|
1360
|
+
.map((message) => deepSeekInstructionBlock(message, hasDeveloper))
|
|
1361
|
+
.filter(Boolean)
|
|
1362
|
+
.join('\n\n');
|
|
1363
|
+
if (content) result.push({ role: 'system', content });
|
|
1364
|
+
instructionBlock = [];
|
|
1365
|
+
};
|
|
1366
|
+
|
|
1367
|
+
for (const message of source) {
|
|
1368
|
+
if (message?.role === 'system' || message?.role === 'developer') {
|
|
1369
|
+
instructionBlock.push(message);
|
|
1370
|
+
continue;
|
|
1371
|
+
}
|
|
1372
|
+
flushInstructionBlock();
|
|
1373
|
+
result.push(sanitizeMessageForChatCompletion(message, provider));
|
|
1374
|
+
}
|
|
1375
|
+
flushInstructionBlock();
|
|
1376
|
+
return result;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
const DEEPSEEK_DEFAULT_MAX_TOKENS = 65536;
|
|
1380
|
+
|
|
1381
|
+
function deepseekDefaultMaxTokens(config = {}) {
|
|
1382
|
+
const value = Number(config.upstreamMaxTokens);
|
|
1383
|
+
if (Number.isFinite(value) && value > 0) return Math.floor(value);
|
|
1384
|
+
return DEEPSEEK_DEFAULT_MAX_TOKENS;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
const DEEPSEEK_JSON_SCHEMA_INSTRUCTIONS_MARKER = 'Return only one valid JSON object';
|
|
1388
|
+
|
|
1389
|
+
function jsonSchemaFromResponseFormat(responseFormat) {
|
|
1390
|
+
if (!isObject(responseFormat)) return null;
|
|
1391
|
+
const container = isObject(responseFormat.json_schema) ? responseFormat.json_schema : responseFormat;
|
|
1392
|
+
return isObject(container.schema) ? container.schema : null;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
function addDeepSeekJsonSchemaInstructions(messages, responseFormat) {
|
|
1396
|
+
if (messages.some((message) => message?.role === 'system' && toText(message.content).includes(DEEPSEEK_JSON_SCHEMA_INSTRUCTIONS_MARKER))) {
|
|
1397
|
+
return messages;
|
|
1398
|
+
}
|
|
1399
|
+
const schema = jsonSchemaFromResponseFormat(responseFormat);
|
|
1400
|
+
const schemaText = schema ? truncateRawText(JSON.stringify(schema), 6000) : '';
|
|
1401
|
+
const instructions = [
|
|
1402
|
+
`${DEEPSEEK_JSON_SCHEMA_INSTRUCTIONS_MARKER}, with no prose or Markdown.`,
|
|
1403
|
+
schemaText
|
|
1404
|
+
? `It must match this JSON Schema:\n${schemaText}`
|
|
1405
|
+
: 'Use the object shape requested in the conversation.',
|
|
1406
|
+
].join('\n');
|
|
1407
|
+
if (!messages.length || messages[0]?.role !== 'system') {
|
|
1408
|
+
return [{ role: 'system', content: instructions }, ...messages];
|
|
1409
|
+
}
|
|
1410
|
+
return [
|
|
1411
|
+
{
|
|
1412
|
+
...messages[0],
|
|
1413
|
+
content: `${toText(messages[0].content)}\n\n${instructions}`,
|
|
1414
|
+
},
|
|
1415
|
+
...messages.slice(1),
|
|
1416
|
+
];
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1003
1419
|
function patchDeepSeekThinkingHistory(messages, request) {
|
|
1004
1420
|
if (request.thinking?.type !== 'enabled' && !request.reasoning_effort) return messages;
|
|
1005
1421
|
return messages.map((message) => {
|
|
1006
1422
|
if (message?.role !== 'assistant') return message;
|
|
1007
1423
|
if (typeof message.reasoning_content === 'string') return message;
|
|
1424
|
+
if (!hasChatToolCalls(message)) return message;
|
|
1008
1425
|
return {
|
|
1009
1426
|
...message,
|
|
1010
1427
|
reasoning_content: '',
|
|
@@ -1020,8 +1437,12 @@ function normalizeOutputTextPart(text, annotations = []) {
|
|
|
1020
1437
|
};
|
|
1021
1438
|
}
|
|
1022
1439
|
|
|
1440
|
+
const REASONING_SUMMARY_HEADER = '**Reasoning**\n\n';
|
|
1441
|
+
|
|
1023
1442
|
function reasoningSummaryText(text) {
|
|
1024
|
-
|
|
1443
|
+
const display = reasoningDisplayText(text);
|
|
1444
|
+
if (!display) return '';
|
|
1445
|
+
return `${REASONING_SUMMARY_HEADER}${display}`;
|
|
1025
1446
|
}
|
|
1026
1447
|
|
|
1027
1448
|
function normalizeSummaryTextPart(text) {
|
|
@@ -1074,6 +1495,16 @@ function snapshotResponseItem(item) {
|
|
|
1074
1495
|
arguments: isObject(item.arguments) ? { ...item.arguments } : toolSearchArgumentsFromText(item.arguments),
|
|
1075
1496
|
});
|
|
1076
1497
|
}
|
|
1498
|
+
if (item.type === 'custom_tool_call') {
|
|
1499
|
+
return omitUndefined({
|
|
1500
|
+
type: item.type,
|
|
1501
|
+
id: item.id,
|
|
1502
|
+
call_id: item.call_id,
|
|
1503
|
+
status: item.status,
|
|
1504
|
+
name: item.name,
|
|
1505
|
+
input: typeof item.input === 'string' ? item.input : '',
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1077
1508
|
return {
|
|
1078
1509
|
...item,
|
|
1079
1510
|
content: Array.isArray(item.content) ? item.content.map(snapshotResponsePart) : item.content,
|
|
@@ -1134,12 +1565,14 @@ function normalizeInstructions(instructions) {
|
|
|
1134
1565
|
return toText(instructions);
|
|
1135
1566
|
}
|
|
1136
1567
|
|
|
1137
|
-
export function normalizeResponsesRequest(request) {
|
|
1568
|
+
export function normalizeResponsesRequest(request, { restoreDiscoveredTools = true } = {}) {
|
|
1138
1569
|
const messages = extractMessagesFromResponsesInput(request.input ?? request);
|
|
1139
1570
|
const model = request.model || request.model_id || request.upstream_model || '';
|
|
1140
1571
|
const instructions = normalizeInstructions(request.instructions);
|
|
1141
1572
|
const responseFormat = request.response_format || request.text?.format;
|
|
1142
|
-
const tools =
|
|
1573
|
+
const tools = restoreDiscoveredTools
|
|
1574
|
+
? mergeToolsWithToolSearchOutput(request.tools, request.input ?? request)
|
|
1575
|
+
: request.tools;
|
|
1143
1576
|
return {
|
|
1144
1577
|
model,
|
|
1145
1578
|
messages,
|
|
@@ -1161,10 +1594,8 @@ export function normalizeResponsesRequest(request) {
|
|
|
1161
1594
|
text: request.text,
|
|
1162
1595
|
truncation: request.truncation,
|
|
1163
1596
|
user: request.user,
|
|
1164
|
-
previous_response_id: request.previous_response_id,
|
|
1165
1597
|
store: request.store,
|
|
1166
1598
|
include: request.include,
|
|
1167
|
-
conversation: request.conversation,
|
|
1168
1599
|
};
|
|
1169
1600
|
}
|
|
1170
1601
|
|
|
@@ -1204,7 +1635,9 @@ export function toChatCompletionsRequest(normalized, overrides = {}) {
|
|
|
1204
1635
|
|
|
1205
1636
|
export function assistantMessageFromResponseOutput(output) {
|
|
1206
1637
|
const messageItems = Array.isArray(output) ? output.filter((item) => item.type === 'message') : [];
|
|
1207
|
-
const functionItems = Array.isArray(output)
|
|
1638
|
+
const functionItems = Array.isArray(output)
|
|
1639
|
+
? output.filter((item) => item.type === 'function_call' || item.type === 'custom_tool_call' || item.type === 'tool_search_call')
|
|
1640
|
+
: [];
|
|
1208
1641
|
const reasoningItems = Array.isArray(output) ? output.filter((item) => item.type === 'reasoning') : [];
|
|
1209
1642
|
const contentParts = messageItems.map((item) => item.content || []).flat();
|
|
1210
1643
|
const chatParts = contentParts.map(contentPartToChatPart).filter(Boolean);
|
|
@@ -1217,14 +1650,7 @@ export function assistantMessageFromResponseOutput(output) {
|
|
|
1217
1650
|
role: 'assistant',
|
|
1218
1651
|
content,
|
|
1219
1652
|
};
|
|
1220
|
-
const toolCalls = functionItems.map(
|
|
1221
|
-
id: item.call_id || item.id,
|
|
1222
|
-
type: 'function',
|
|
1223
|
-
function: {
|
|
1224
|
-
name: encodeToolName(item.namespace, item.name),
|
|
1225
|
-
arguments: item.arguments || '',
|
|
1226
|
-
},
|
|
1227
|
-
}));
|
|
1653
|
+
const toolCalls = functionItems.map(chatToolCallFromResponseItem);
|
|
1228
1654
|
if (toolCalls.length) assistant.tool_calls = toolCalls;
|
|
1229
1655
|
const reasoningContent = reasoningItems
|
|
1230
1656
|
.map((item) => reasoningTextFromItem(item))
|
|
@@ -1245,15 +1671,64 @@ export function extractToolCallIdsFromMessages(messages) {
|
|
|
1245
1671
|
return ids;
|
|
1246
1672
|
}
|
|
1247
1673
|
|
|
1248
|
-
export function
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1674
|
+
export function requestToolsIncludeCommentary(tools) {
|
|
1675
|
+
for (const tool of Array.isArray(tools) ? tools : []) {
|
|
1676
|
+
if (!isObject(tool)) continue;
|
|
1677
|
+
const name = isObject(tool.function) ? tool.function.name : tool.name;
|
|
1678
|
+
if (String(name || '') === INTERNAL_COMMENTARY_TOOL) return true;
|
|
1679
|
+
}
|
|
1680
|
+
return false;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
export function bridgedCommentaryToolCallsFromMessage(message, tools) {
|
|
1684
|
+
if (requestToolsIncludeCommentary(tools)) return [];
|
|
1685
|
+
const toolCalls = Array.isArray(message?.tool_calls) ? message.tool_calls : [];
|
|
1686
|
+
return toolCalls.filter(
|
|
1687
|
+
(toolCall) => toolCall?.type === 'function' && String(toolCall?.function?.name || '') === INTERNAL_COMMENTARY_TOOL,
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
export function stripBridgedCommentaryFromCompletion(completion, tools) {
|
|
1692
|
+
if (requestToolsIncludeCommentary(tools)) return completion;
|
|
1693
|
+
const choice = Array.isArray(completion?.choices) ? completion.choices[0] : null;
|
|
1694
|
+
const message = choice?.message;
|
|
1695
|
+
if (!Array.isArray(message?.tool_calls)) return completion;
|
|
1696
|
+
const kept = message.tool_calls.filter(
|
|
1697
|
+
(toolCall) => !(toolCall?.type === 'function' && String(toolCall?.function?.name || '') === INTERNAL_COMMENTARY_TOOL),
|
|
1698
|
+
);
|
|
1699
|
+
if (kept.length === message.tool_calls.length) return completion;
|
|
1700
|
+
const nextMessage = { ...message };
|
|
1701
|
+
if (kept.length) nextMessage.tool_calls = kept;
|
|
1702
|
+
else delete nextMessage.tool_calls;
|
|
1703
|
+
return {
|
|
1704
|
+
...completion,
|
|
1705
|
+
choices: [{ ...choice, message: nextMessage }, ...completion.choices.slice(1)],
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
function commentaryTextFromArguments(argumentsText) {
|
|
1710
|
+
const args = parseJsonObject(argumentsText);
|
|
1711
|
+
const text = String(args.text ?? args.message ?? args.update ?? args.content ?? '').trim();
|
|
1712
|
+
if (text) return text;
|
|
1713
|
+
const raw = String(argumentsText ?? '').trim();
|
|
1714
|
+
if (!raw || raw.startsWith('{') || raw === 'null') return '';
|
|
1715
|
+
return raw;
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
function addInternalCommentaryTool(tools, toolChoice) {
|
|
1719
|
+
if (!Array.isArray(tools) || !tools.length) return tools;
|
|
1720
|
+
if (toolChoiceDisablesTools(toolChoice)) return tools;
|
|
1721
|
+
if (requestToolsIncludeCommentary(tools)) return tools;
|
|
1722
|
+
return [...tools, JSON.parse(JSON.stringify(INTERNAL_COMMENTARY_TOOL_DEFINITION))];
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
export function toProviderChatCompletionsRequest(chatRequest, config = {}) {
|
|
1726
|
+
const provider = config.upstreamProvider || 'generic';
|
|
1727
|
+
const modelAlias = resolveModelAlias(chatRequest.model, config);
|
|
1728
|
+
const fallbackReasoning = !chatRequest.reasoning && config.codexReasoningEffort
|
|
1729
|
+
? { effort: config.codexReasoningEffort }
|
|
1730
|
+
: chatRequest.reasoning;
|
|
1731
|
+
const messages = sanitizeMessagesForChatCompletion(chatRequest.messages, provider);
|
|
1257
1732
|
const request = {
|
|
1258
1733
|
model: modelAlias.upstreamModel || config.upstreamModel || chatRequest.model,
|
|
1259
1734
|
messages,
|
|
@@ -1282,7 +1757,10 @@ export function toProviderChatCompletionsRequest(chatRequest, config = {}) {
|
|
|
1282
1757
|
if (provider === 'deepseek') {
|
|
1283
1758
|
delete request.reasoning_effort;
|
|
1284
1759
|
delete request.parallel_tool_calls;
|
|
1285
|
-
|
|
1760
|
+
delete request.frequency_penalty;
|
|
1761
|
+
delete request.presence_penalty;
|
|
1762
|
+
request.tools = addInternalCommentaryTool(adaptToolsForProvider(request.tools, provider, config), chatRequest.tool_choice);
|
|
1763
|
+
assertDeepSeekToolCapacity(request.tools);
|
|
1286
1764
|
request.messages = addDeepSeekToolInstructions(request.messages, request.tools);
|
|
1287
1765
|
if (request.user !== undefined) {
|
|
1288
1766
|
request.user_id = request.user;
|
|
@@ -1293,15 +1771,23 @@ export function toProviderChatCompletionsRequest(chatRequest, config = {}) {
|
|
|
1293
1771
|
} else {
|
|
1294
1772
|
delete request.stream_options;
|
|
1295
1773
|
}
|
|
1774
|
+
if (request.max_tokens == null) {
|
|
1775
|
+
request.max_tokens = deepseekDefaultMaxTokens(config);
|
|
1776
|
+
}
|
|
1296
1777
|
if (request.response_format?.type === 'json_schema') {
|
|
1778
|
+
request.messages = addDeepSeekJsonSchemaInstructions(request.messages, request.response_format);
|
|
1297
1779
|
request.response_format = { type: 'json_object' };
|
|
1298
1780
|
}
|
|
1299
1781
|
Object.assign(request, deepseekReasoningPayload({ alias: modelAlias, reasoning: fallbackReasoning }));
|
|
1300
|
-
request.messages = patchDeepSeekThinkingHistory(request.messages, request);
|
|
1301
1782
|
}
|
|
1302
1783
|
|
|
1303
1784
|
Object.assign(request, modelAlias.extraBody);
|
|
1304
1785
|
|
|
1786
|
+
if (provider === 'deepseek') {
|
|
1787
|
+
request.messages = patchDeepSeekThinkingHistory(request.messages, request);
|
|
1788
|
+
}
|
|
1789
|
+
request.tools = stripGatewayToolMarkers(request.tools);
|
|
1790
|
+
|
|
1305
1791
|
for (const key of Object.keys(request)) {
|
|
1306
1792
|
if (request[key] === undefined) delete request[key];
|
|
1307
1793
|
}
|
|
@@ -1325,6 +1811,7 @@ function createBaseResponse({
|
|
|
1325
1811
|
normalized,
|
|
1326
1812
|
completedAt = null,
|
|
1327
1813
|
incompleteReason = null,
|
|
1814
|
+
error = null,
|
|
1328
1815
|
}) {
|
|
1329
1816
|
return {
|
|
1330
1817
|
id,
|
|
@@ -1333,7 +1820,7 @@ function createBaseResponse({
|
|
|
1333
1820
|
completed_at: completedAt == null ? null : Math.floor(completedAt),
|
|
1334
1821
|
status,
|
|
1335
1822
|
background: false,
|
|
1336
|
-
error
|
|
1823
|
+
error,
|
|
1337
1824
|
incomplete_details: incompleteReason ? { reason: incompleteReason } : null,
|
|
1338
1825
|
instructions: normalized?.instructions || null,
|
|
1339
1826
|
max_output_tokens: normalized?.max_tokens ?? null,
|
|
@@ -1356,6 +1843,37 @@ function createBaseResponse({
|
|
|
1356
1843
|
};
|
|
1357
1844
|
}
|
|
1358
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
|
+
|
|
1359
1877
|
function normalizeResponsesUsage(usage) {
|
|
1360
1878
|
if (!isObject(usage)) return null;
|
|
1361
1879
|
const inputTokens = usage.input_tokens ?? usage.prompt_tokens ?? 0;
|
|
@@ -1363,7 +1881,7 @@ function normalizeResponsesUsage(usage) {
|
|
|
1363
1881
|
return {
|
|
1364
1882
|
input_tokens: inputTokens,
|
|
1365
1883
|
input_tokens_details: usage.input_tokens_details ?? {
|
|
1366
|
-
cached_tokens: usage.prompt_tokens_details?.cached_tokens ?? 0,
|
|
1884
|
+
cached_tokens: usage.prompt_tokens_details?.cached_tokens ?? usage.prompt_cache_hit_tokens ?? 0,
|
|
1367
1885
|
},
|
|
1368
1886
|
output_tokens: outputTokens,
|
|
1369
1887
|
output_tokens_details: usage.output_tokens_details ?? {
|
|
@@ -1378,6 +1896,7 @@ function outputTextFromOutput(output) {
|
|
|
1378
1896
|
const chunks = [];
|
|
1379
1897
|
for (const item of output) {
|
|
1380
1898
|
if (item?.type !== 'message' || !Array.isArray(item.content)) continue;
|
|
1899
|
+
if (item.phase === 'commentary') continue;
|
|
1381
1900
|
for (const part of item.content) {
|
|
1382
1901
|
if (part?.type === 'output_text' && typeof part.text === 'string') {
|
|
1383
1902
|
chunks.push(part.text);
|
|
@@ -1413,29 +1932,176 @@ export function createResponseEnvelope({
|
|
|
1413
1932
|
});
|
|
1414
1933
|
}
|
|
1415
1934
|
|
|
1416
|
-
|
|
1935
|
+
const PARALLEL_TOOL_WRAPPER_NAMES = new Set(['multi_tool_use.parallel', 'multi_tool_use_parallel']);
|
|
1936
|
+
const EMITTED_TOOL_NAME_PREFIX = 'functions.';
|
|
1937
|
+
|
|
1938
|
+
function isParallelToolWrapperName(name) {
|
|
1939
|
+
const raw = String(name || '').toLowerCase();
|
|
1940
|
+
const stripped = raw.startsWith(EMITTED_TOOL_NAME_PREFIX) ? raw.slice(EMITTED_TOOL_NAME_PREFIX.length) : raw;
|
|
1941
|
+
return PARALLEL_TOOL_WRAPPER_NAMES.has(stripped);
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
function toolNameMatchKey(name) {
|
|
1945
|
+
return String(name || '').toLowerCase().replace(/\./g, '__');
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
function resolveEmittedToolName(name, knownNames) {
|
|
1949
|
+
const raw = String(name || '');
|
|
1950
|
+
if (!raw || !knownNames) return name;
|
|
1951
|
+
const known = knownNames instanceof Set ? knownNames : new Set(knownNames);
|
|
1952
|
+
if (!known.size || known.has(raw)) return name;
|
|
1953
|
+
const candidates = [raw];
|
|
1954
|
+
if (raw.startsWith(EMITTED_TOOL_NAME_PREFIX)) candidates.push(raw.slice(EMITTED_TOOL_NAME_PREFIX.length));
|
|
1955
|
+
for (const candidate of candidates) {
|
|
1956
|
+
if (known.has(candidate)) return candidate;
|
|
1957
|
+
}
|
|
1958
|
+
const knownByKey = new Map();
|
|
1959
|
+
for (const knownName of known) {
|
|
1960
|
+
const key = toolNameMatchKey(knownName);
|
|
1961
|
+
if (!knownByKey.has(key)) knownByKey.set(key, knownName);
|
|
1962
|
+
}
|
|
1963
|
+
for (const candidate of candidates) {
|
|
1964
|
+
const match = knownByKey.get(toolNameMatchKey(candidate));
|
|
1965
|
+
if (match) return match;
|
|
1966
|
+
}
|
|
1967
|
+
return name;
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
export function chatToolNamesFromTools(tools) {
|
|
1971
|
+
const names = [];
|
|
1972
|
+
for (const tool of Array.isArray(tools) ? tools : []) {
|
|
1973
|
+
if (!isObject(tool)) continue;
|
|
1974
|
+
const name = isObject(tool.function) ? tool.function.name : tool.name;
|
|
1975
|
+
if (name) names.push(String(name));
|
|
1976
|
+
}
|
|
1977
|
+
return names;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
export function resolveEmittedToolCallNamesInCompletion(completion, knownNames) {
|
|
1981
|
+
const choice = Array.isArray(completion?.choices) ? completion.choices[0] : null;
|
|
1982
|
+
const toolCalls = choice?.message?.tool_calls;
|
|
1983
|
+
if (!Array.isArray(toolCalls) || !toolCalls.length) return completion;
|
|
1984
|
+
let changed = false;
|
|
1985
|
+
const resolved = toolCalls.map((toolCall) => {
|
|
1986
|
+
const name = toolCall?.function?.name;
|
|
1987
|
+
if (!name) return toolCall;
|
|
1988
|
+
const resolvedName = resolveEmittedToolName(name, knownNames);
|
|
1989
|
+
if (resolvedName === name) return toolCall;
|
|
1990
|
+
changed = true;
|
|
1991
|
+
return { ...toolCall, function: { ...toolCall.function, name: resolvedName } };
|
|
1992
|
+
});
|
|
1993
|
+
if (!changed) return completion;
|
|
1994
|
+
return {
|
|
1995
|
+
...completion,
|
|
1996
|
+
choices: completion.choices.map((entry, index) => (index === 0
|
|
1997
|
+
? { ...entry, message: { ...entry.message, tool_calls: resolved } }
|
|
1998
|
+
: entry)),
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
function parallelToolUsesFromArguments(argumentsText) {
|
|
2003
|
+
const parsed = safeJsonParse(String(argumentsText || ''));
|
|
2004
|
+
if (!parsed.ok || !isObject(parsed.value)) return null;
|
|
2005
|
+
const uses = Array.isArray(parsed.value.tool_uses) ? parsed.value.tool_uses : null;
|
|
2006
|
+
if (!uses || !uses.length) return null;
|
|
2007
|
+
const calls = [];
|
|
2008
|
+
for (const use of uses) {
|
|
2009
|
+
if (!isObject(use)) return null;
|
|
2010
|
+
const name = String(use.recipient_name || use.name || '').replace(/^functions\./, '');
|
|
2011
|
+
if (!name) return null;
|
|
2012
|
+
const parameters = use.parameters ?? use.arguments ?? {};
|
|
2013
|
+
calls.push({
|
|
2014
|
+
name,
|
|
2015
|
+
arguments: typeof parameters === 'string' ? parameters : JSON.stringify(parameters),
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
return calls;
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
function expandParallelToolCalls(toolCalls) {
|
|
2022
|
+
if (!Array.isArray(toolCalls)) return toolCalls;
|
|
2023
|
+
if (!toolCalls.some((toolCall) => isParallelToolWrapperName(toolCall?.function?.name))) return toolCalls;
|
|
2024
|
+
const expanded = [];
|
|
2025
|
+
for (const toolCall of toolCalls) {
|
|
2026
|
+
const uses = isParallelToolWrapperName(toolCall?.function?.name)
|
|
2027
|
+
? parallelToolUsesFromArguments(toolCall.function?.arguments)
|
|
2028
|
+
: null;
|
|
2029
|
+
if (!uses) {
|
|
2030
|
+
expanded.push(toolCall);
|
|
2031
|
+
continue;
|
|
2032
|
+
}
|
|
2033
|
+
for (const use of uses) {
|
|
2034
|
+
expanded.push({
|
|
2035
|
+
id: generateId('call'),
|
|
2036
|
+
type: 'function',
|
|
2037
|
+
function: { name: use.name, arguments: use.arguments },
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
return expanded;
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
export function expandParallelToolCallsInCompletion(completion) {
|
|
2045
|
+
const choice = Array.isArray(completion?.choices) ? completion.choices[0] : null;
|
|
2046
|
+
const toolCalls = choice?.message?.tool_calls;
|
|
2047
|
+
if (!Array.isArray(toolCalls)) return completion;
|
|
2048
|
+
const expanded = expandParallelToolCalls(toolCalls);
|
|
2049
|
+
if (expanded === toolCalls) return completion;
|
|
2050
|
+
return {
|
|
2051
|
+
...completion,
|
|
2052
|
+
choices: completion.choices.map((entry, index) => (index === 0
|
|
2053
|
+
? { ...entry, message: { ...entry.message, tool_calls: expanded } }
|
|
2054
|
+
: entry)),
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
export function convertChatCompletionToResponses({ completion: rawCompletion, model, previousResponseId, normalized, responseId = generateId('resp'), config = {} }) {
|
|
2059
|
+
const toolSchemas = buildToolSchemas(normalized?.tools);
|
|
2060
|
+
const knownToolNames = new Set([...toolSchemas.keys(), INTERNAL_COMMENTARY_TOOL]);
|
|
2061
|
+
const completion = resolveEmittedToolCallNamesInCompletion(
|
|
2062
|
+
expandParallelToolCallsInCompletion(rawCompletion),
|
|
2063
|
+
knownToolNames,
|
|
2064
|
+
);
|
|
1417
2065
|
const createdAt = completion.created || Date.now() / 1000;
|
|
1418
2066
|
const choice = Array.isArray(completion.choices) ? completion.choices[0] : null;
|
|
2067
|
+
const outcome = deepSeekFinishReasonOutcome(choice?.finish_reason);
|
|
1419
2068
|
const message = choice?.message || {};
|
|
1420
2069
|
const content = chatContentToResponseOutputParts(message.content);
|
|
1421
|
-
const
|
|
2070
|
+
const messageHasToolCalls = hasChatToolCalls(message);
|
|
1422
2071
|
const toolNames = buildToolNames(normalized?.tools);
|
|
2072
|
+
const customToolNames = buildCustomToolNames(normalized?.tools);
|
|
1423
2073
|
const output = [];
|
|
1424
2074
|
|
|
1425
|
-
if (content.length
|
|
2075
|
+
if (content.length) {
|
|
1426
2076
|
output.push({
|
|
1427
2077
|
type: 'message',
|
|
1428
2078
|
id: generateId('msg'),
|
|
1429
2079
|
role: 'assistant',
|
|
1430
2080
|
content,
|
|
1431
|
-
status:
|
|
1432
|
-
phase: message.phase || 'final_answer',
|
|
2081
|
+
status: outcome.status,
|
|
2082
|
+
phase: message.phase || (messageHasToolCalls ? 'commentary' : 'final_answer'),
|
|
1433
2083
|
});
|
|
1434
2084
|
}
|
|
1435
2085
|
|
|
1436
|
-
if (
|
|
2086
|
+
if (messageHasToolCalls) {
|
|
2087
|
+
const bridgedCommentary = !requestToolsIncludeCommentary(normalized?.tools);
|
|
1437
2088
|
for (const toolCall of message.tool_calls) {
|
|
1438
|
-
|
|
2089
|
+
if (bridgedCommentary && toolCall?.type === 'function' && String(toolCall?.function?.name || '') === INTERNAL_COMMENTARY_TOOL) {
|
|
2090
|
+
const text = commentaryTextFromArguments(toolCall.function?.arguments);
|
|
2091
|
+
if (text) {
|
|
2092
|
+
output.push({
|
|
2093
|
+
type: 'message',
|
|
2094
|
+
id: generateId('msg'),
|
|
2095
|
+
role: 'assistant',
|
|
2096
|
+
content: [normalizeOutputTextPart(text)],
|
|
2097
|
+
status: 'completed',
|
|
2098
|
+
phase: 'commentary',
|
|
2099
|
+
});
|
|
2100
|
+
}
|
|
2101
|
+
continue;
|
|
2102
|
+
}
|
|
2103
|
+
const item = responseItemFromChatToolCall(toolCall, toolNames, customToolNames);
|
|
2104
|
+
item.status = outcome.status;
|
|
1439
2105
|
normalizeFunctionCallItemArguments(item, toolSchemas, config);
|
|
1440
2106
|
output.push(item);
|
|
1441
2107
|
}
|
|
@@ -1449,8 +2115,8 @@ export function convertChatCompletionToResponses({ completion, model, previousRe
|
|
|
1449
2115
|
summary: [normalizeSummaryTextPart(reasoningSummaryText(reasoningText))],
|
|
1450
2116
|
content: [],
|
|
1451
2117
|
reasoning_content: reasoningText,
|
|
1452
|
-
encrypted_content:
|
|
1453
|
-
status:
|
|
2118
|
+
encrypted_content: encodeGatewayReasoning(reasoningText),
|
|
2119
|
+
status: outcome.status,
|
|
1454
2120
|
});
|
|
1455
2121
|
}
|
|
1456
2122
|
|
|
@@ -1458,13 +2124,14 @@ export function convertChatCompletionToResponses({ completion, model, previousRe
|
|
|
1458
2124
|
id: responseId,
|
|
1459
2125
|
model,
|
|
1460
2126
|
createdAt,
|
|
1461
|
-
status:
|
|
2127
|
+
status: outcome.status,
|
|
1462
2128
|
output,
|
|
1463
2129
|
previousResponseId: previousResponseId ?? null,
|
|
1464
2130
|
usage: normalizeResponsesUsage(completion.usage),
|
|
1465
2131
|
normalized,
|
|
1466
2132
|
completedAt: Date.now() / 1000,
|
|
1467
|
-
incompleteReason:
|
|
2133
|
+
incompleteReason: outcome.incompleteReason,
|
|
2134
|
+
error: outcome.error,
|
|
1468
2135
|
});
|
|
1469
2136
|
}
|
|
1470
2137
|
|
|
@@ -1475,9 +2142,10 @@ export class ResponsesStreamMapper {
|
|
|
1475
2142
|
createdAt = Date.now() / 1000,
|
|
1476
2143
|
previousResponseId = null,
|
|
1477
2144
|
normalized,
|
|
1478
|
-
bufferOutputUntilDone = false,
|
|
1479
2145
|
emitReasoningSummary = true,
|
|
1480
2146
|
emitReasoningText = false,
|
|
2147
|
+
holdToolItemEvents = false,
|
|
2148
|
+
knownToolNames,
|
|
1481
2149
|
config = {},
|
|
1482
2150
|
} = {}) {
|
|
1483
2151
|
this.responseId = responseId;
|
|
@@ -1485,9 +2153,10 @@ export class ResponsesStreamMapper {
|
|
|
1485
2153
|
this.createdAt = createdAt;
|
|
1486
2154
|
this.previousResponseId = previousResponseId;
|
|
1487
2155
|
this.normalized = normalized;
|
|
1488
|
-
this.bufferOutputUntilDone = Boolean(bufferOutputUntilDone);
|
|
1489
2156
|
this.emitReasoningSummary = Boolean(emitReasoningSummary);
|
|
1490
2157
|
this.emitReasoningText = Boolean(emitReasoningText);
|
|
2158
|
+
this.holdToolItemEvents = Boolean(holdToolItemEvents);
|
|
2159
|
+
this.roundStartIndex = 0;
|
|
1491
2160
|
this.sequenceNumber = 0;
|
|
1492
2161
|
this.output = [];
|
|
1493
2162
|
this.messageItem = null;
|
|
@@ -1495,19 +2164,34 @@ export class ResponsesStreamMapper {
|
|
|
1495
2164
|
this.toolItems = new Map();
|
|
1496
2165
|
this.text = '';
|
|
1497
2166
|
this.reasoningText = '';
|
|
1498
|
-
this.streamReasoningLive = true;
|
|
1499
2167
|
this.reasoningItemDone = false;
|
|
1500
2168
|
this.finishReason = null;
|
|
1501
2169
|
this.usage = null;
|
|
2170
|
+
this.pendingFinishReason = null;
|
|
2171
|
+
this.pendingUsage = null;
|
|
1502
2172
|
this.completedAt = null;
|
|
1503
2173
|
this.finalized = false;
|
|
2174
|
+
this.terminalStatus = null;
|
|
2175
|
+
this.messageItemAdded = false;
|
|
1504
2176
|
this.messageContentAdded = false;
|
|
2177
|
+
this.messageItemClosed = false;
|
|
2178
|
+
this.holdVisibleText = false;
|
|
1505
2179
|
this.reasoningContentAdded = false;
|
|
1506
2180
|
this.reasoningSummaryAdded = false;
|
|
1507
2181
|
this.reasoningItemAdded = false;
|
|
1508
2182
|
this.streamedReasoningSummaryText = '';
|
|
1509
2183
|
this.toolSchemas = buildToolSchemas(normalized?.tools);
|
|
2184
|
+
this.knownToolNames = new Set([
|
|
2185
|
+
...(Array.isArray(knownToolNames) || knownToolNames instanceof Set ? knownToolNames : []),
|
|
2186
|
+
...this.toolSchemas.keys(),
|
|
2187
|
+
INTERNAL_COMMENTARY_TOOL,
|
|
2188
|
+
]);
|
|
1510
2189
|
this.toolNames = buildToolNames(normalized?.tools);
|
|
2190
|
+
this.customToolNames = buildCustomToolNames(normalized?.tools);
|
|
2191
|
+
this.bridgedCommentaryTool = !requestToolsIncludeCommentary(normalized?.tools);
|
|
2192
|
+
this.toolItemsAdded = new Set();
|
|
2193
|
+
this.toolItemsBufferedArguments = new Set();
|
|
2194
|
+
this.toolItemsStreamedArgumentLengths = new Map();
|
|
1511
2195
|
this.config = config;
|
|
1512
2196
|
}
|
|
1513
2197
|
|
|
@@ -1517,6 +2201,7 @@ export class ResponsesStreamMapper {
|
|
|
1517
2201
|
}
|
|
1518
2202
|
|
|
1519
2203
|
response(status = 'in_progress') {
|
|
2204
|
+
const outcome = deepSeekFinishReasonOutcome(this.finishReason);
|
|
1520
2205
|
return createBaseResponse({
|
|
1521
2206
|
id: this.responseId,
|
|
1522
2207
|
model: this.model,
|
|
@@ -1527,7 +2212,8 @@ export class ResponsesStreamMapper {
|
|
|
1527
2212
|
usage: this.usage,
|
|
1528
2213
|
normalized: this.normalized,
|
|
1529
2214
|
completedAt: this.completedAt,
|
|
1530
|
-
incompleteReason:
|
|
2215
|
+
incompleteReason: status === 'in_progress' ? null : outcome.incompleteReason,
|
|
2216
|
+
error: status === 'failed' ? outcome.error : null,
|
|
1531
2217
|
});
|
|
1532
2218
|
}
|
|
1533
2219
|
|
|
@@ -1548,27 +2234,92 @@ export class ResponsesStreamMapper {
|
|
|
1548
2234
|
}
|
|
1549
2235
|
|
|
1550
2236
|
ensureMessageItem() {
|
|
2237
|
+
this.ensureMessageItemState();
|
|
2238
|
+
if (!this.messageItemAdded) {
|
|
2239
|
+
this.messageItemAdded = true;
|
|
2240
|
+
return {
|
|
2241
|
+
type: 'response.output_item.added',
|
|
2242
|
+
sequence_number: this.nextSequence(),
|
|
2243
|
+
output_index: this.output.length - 1,
|
|
2244
|
+
item: snapshotResponseItem(this.messageItem),
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
return null;
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
ensureMessageItemState() {
|
|
1551
2251
|
if (!this.messageItem) {
|
|
1552
2252
|
this.messageItem = {
|
|
1553
2253
|
id: generateId('msg'),
|
|
1554
2254
|
type: 'message',
|
|
1555
2255
|
status: 'in_progress',
|
|
1556
2256
|
role: 'assistant',
|
|
1557
|
-
phase: 'final_answer',
|
|
2257
|
+
phase: this.messageStartsAsCommentary() ? 'commentary' : 'final_answer',
|
|
1558
2258
|
content: [],
|
|
1559
2259
|
};
|
|
1560
2260
|
this.output.push(this.messageItem);
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
messageStartsAsCommentary() {
|
|
2265
|
+
return this.toolItems.size > 0 || (this.config.upstreamProvider === 'deepseek' && hasRunnableChatTools(this.normalized));
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
finalizeMessagePhase() {
|
|
2269
|
+
if (!this.messageItem || this.toolItems.size > 0) return;
|
|
2270
|
+
this.messageItem.phase = 'final_answer';
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
closeMessageItemAsCommentary(status = 'completed') {
|
|
2274
|
+
if (!this.messageItem || this.messageItemClosed) return [];
|
|
2275
|
+
this.messageItem.phase = 'commentary';
|
|
2276
|
+
if (!this.messageItemAdded) return [];
|
|
2277
|
+
this.messageItemClosed = true;
|
|
2278
|
+
this.messageItem.status = status;
|
|
2279
|
+
this.messageItem.content[0].text = this.text;
|
|
2280
|
+
const outputIndex = this.output.indexOf(this.messageItem);
|
|
2281
|
+
return [
|
|
2282
|
+
{
|
|
2283
|
+
type: 'response.output_text.done',
|
|
2284
|
+
sequence_number: this.nextSequence(),
|
|
2285
|
+
output_index: outputIndex,
|
|
2286
|
+
content_index: 0,
|
|
2287
|
+
item_id: this.messageItem.id,
|
|
2288
|
+
text: this.text,
|
|
2289
|
+
logprobs: [],
|
|
2290
|
+
},
|
|
2291
|
+
{
|
|
2292
|
+
type: 'response.content_part.done',
|
|
2293
|
+
sequence_number: this.nextSequence(),
|
|
2294
|
+
output_index: outputIndex,
|
|
2295
|
+
content_index: 0,
|
|
2296
|
+
item_id: this.messageItem.id,
|
|
2297
|
+
part: snapshotResponsePart(this.messageItem.content[0]),
|
|
2298
|
+
},
|
|
2299
|
+
{
|
|
2300
|
+
type: 'response.output_item.done',
|
|
2301
|
+
sequence_number: this.nextSequence(),
|
|
2302
|
+
output_index: outputIndex,
|
|
2303
|
+
item: snapshotResponseItem(this.messageItem),
|
|
2304
|
+
},
|
|
2305
|
+
];
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
ensureReasoningItem() {
|
|
2309
|
+
this.ensureReasoningItemState();
|
|
2310
|
+
if (!this.reasoningItemAdded) {
|
|
2311
|
+
this.reasoningItemAdded = true;
|
|
1561
2312
|
return {
|
|
1562
2313
|
type: 'response.output_item.added',
|
|
1563
2314
|
sequence_number: this.nextSequence(),
|
|
1564
|
-
output_index: this.output.
|
|
1565
|
-
item: snapshotResponseItem(this.
|
|
2315
|
+
output_index: this.output.indexOf(this.reasoningItem),
|
|
2316
|
+
item: snapshotResponseItem(this.reasoningItem),
|
|
1566
2317
|
};
|
|
1567
2318
|
}
|
|
1568
2319
|
return null;
|
|
1569
2320
|
}
|
|
1570
2321
|
|
|
1571
|
-
|
|
2322
|
+
ensureReasoningItemState() {
|
|
1572
2323
|
if (!this.reasoningItem) {
|
|
1573
2324
|
this.reasoningItem = {
|
|
1574
2325
|
id: generateId('rs'),
|
|
@@ -1580,16 +2331,6 @@ export class ResponsesStreamMapper {
|
|
|
1580
2331
|
};
|
|
1581
2332
|
this.output.push(this.reasoningItem);
|
|
1582
2333
|
}
|
|
1583
|
-
if (!this.reasoningItemAdded) {
|
|
1584
|
-
this.reasoningItemAdded = true;
|
|
1585
|
-
return {
|
|
1586
|
-
type: 'response.output_item.added',
|
|
1587
|
-
sequence_number: this.nextSequence(),
|
|
1588
|
-
output_index: this.output.indexOf(this.reasoningItem),
|
|
1589
|
-
item: snapshotResponseItem(this.reasoningItem),
|
|
1590
|
-
};
|
|
1591
|
-
}
|
|
1592
|
-
return null;
|
|
1593
2334
|
}
|
|
1594
2335
|
|
|
1595
2336
|
ensureReasoningContentPart(events) {
|
|
@@ -1619,7 +2360,13 @@ export class ResponsesStreamMapper {
|
|
|
1619
2360
|
else delete this.reasoningItem.reasoning_content;
|
|
1620
2361
|
}
|
|
1621
2362
|
|
|
1622
|
-
|
|
2363
|
+
reasoningSummarySourceText({ final = false } = {}) {
|
|
2364
|
+
if (final) return this.reasoningText;
|
|
2365
|
+
const boundary = this.reasoningText.lastIndexOf('\n');
|
|
2366
|
+
return boundary < 0 ? '' : this.reasoningText.slice(0, boundary + 1);
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
appendReasoningSummaryDelta(events, { final = false } = {}) {
|
|
1623
2370
|
if (!this.emitReasoningSummary || !this.reasoningItem) return;
|
|
1624
2371
|
if (!this.reasoningSummaryAdded) {
|
|
1625
2372
|
this.reasoningSummaryAdded = true;
|
|
@@ -1633,14 +2380,17 @@ export class ResponsesStreamMapper {
|
|
|
1633
2380
|
part: snapshotResponsePart(this.reasoningItem.summary[0]),
|
|
1634
2381
|
});
|
|
1635
2382
|
}
|
|
1636
|
-
const nextSummaryText = reasoningSummaryText(this.
|
|
1637
|
-
this.reasoningItem.summary[0].text = nextSummaryText;
|
|
2383
|
+
const nextSummaryText = reasoningSummaryText(this.reasoningSummarySourceText({ final }));
|
|
2384
|
+
if (final) this.reasoningItem.summary[0].text = nextSummaryText;
|
|
2385
|
+
if (nextSummaryText === this.streamedReasoningSummaryText) return;
|
|
1638
2386
|
if (!nextSummaryText.startsWith(this.streamedReasoningSummaryText)) {
|
|
2387
|
+
if (!final) return;
|
|
1639
2388
|
this.streamedReasoningSummaryText = nextSummaryText;
|
|
1640
2389
|
return;
|
|
1641
2390
|
}
|
|
1642
2391
|
const deltaText = nextSummaryText.slice(this.streamedReasoningSummaryText.length);
|
|
1643
2392
|
this.streamedReasoningSummaryText = nextSummaryText;
|
|
2393
|
+
this.reasoningItem.summary[0].text = nextSummaryText;
|
|
1644
2394
|
for (const delta of splitBufferedReasoningDeltas(deltaText)) {
|
|
1645
2395
|
events.push({
|
|
1646
2396
|
type: 'response.reasoning_summary_text.delta',
|
|
@@ -1653,28 +2403,20 @@ export class ResponsesStreamMapper {
|
|
|
1653
2403
|
}
|
|
1654
2404
|
}
|
|
1655
2405
|
|
|
2406
|
+
holdVisibleTextUntilDone() {
|
|
2407
|
+
this.holdVisibleText = true;
|
|
2408
|
+
}
|
|
2409
|
+
|
|
1656
2410
|
textDelta(delta) {
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
status: 'in_progress',
|
|
1663
|
-
role: 'assistant',
|
|
1664
|
-
phase: 'final_answer',
|
|
1665
|
-
content: [normalizeOutputTextPart('')],
|
|
1666
|
-
};
|
|
1667
|
-
this.output.push(this.messageItem);
|
|
1668
|
-
}
|
|
2411
|
+
const events = [...this.closeReasoningItem('completed')];
|
|
2412
|
+
if (this.messageItemClosed) return events;
|
|
2413
|
+
if (!this.messageItemAdded && (this.holdVisibleText || this.toolItems.size > 0)) {
|
|
2414
|
+
this.ensureMessageItemState();
|
|
2415
|
+
if (!this.messageItem.content.length) this.messageItem.content.push(normalizeOutputTextPart(''));
|
|
1669
2416
|
this.text += delta;
|
|
1670
2417
|
this.messageItem.content[0].text = this.text;
|
|
1671
|
-
return
|
|
1672
|
-
}
|
|
1673
|
-
const events = [];
|
|
1674
|
-
if (this.streamReasoningLive) {
|
|
1675
|
-
events.push(...this.closeReasoningItem('completed'));
|
|
2418
|
+
return events;
|
|
1676
2419
|
}
|
|
1677
|
-
this.streamReasoningLive = false;
|
|
1678
2420
|
const added = this.ensureMessageItem();
|
|
1679
2421
|
if (added) events.push(added);
|
|
1680
2422
|
if (!this.messageContentAdded) {
|
|
@@ -1705,15 +2447,11 @@ export class ResponsesStreamMapper {
|
|
|
1705
2447
|
|
|
1706
2448
|
reasoningDelta(delta) {
|
|
1707
2449
|
this.reasoningText += delta;
|
|
1708
|
-
if (this.
|
|
1709
|
-
const events = [];
|
|
1710
|
-
const added = this.ensureReasoningItem();
|
|
1711
|
-
if (added) events.push(added);
|
|
2450
|
+
if (this.reasoningItemDone) {
|
|
1712
2451
|
this.syncReasoningItemContent();
|
|
1713
|
-
|
|
1714
|
-
return events;
|
|
2452
|
+
return [];
|
|
1715
2453
|
}
|
|
1716
|
-
if (
|
|
2454
|
+
if (this.messageItem || this.toolItems.size > 0) {
|
|
1717
2455
|
if (!this.reasoningItem) {
|
|
1718
2456
|
this.reasoningItem = {
|
|
1719
2457
|
id: generateId('rs'),
|
|
@@ -1748,124 +2486,250 @@ export class ResponsesStreamMapper {
|
|
|
1748
2486
|
return events;
|
|
1749
2487
|
}
|
|
1750
2488
|
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
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
|
-
};
|
|
1774
|
-
this.toolItems.set(index, item);
|
|
1775
|
-
this.output.push(item);
|
|
1776
|
-
}
|
|
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
|
-
}
|
|
1782
|
-
item.arguments += toolCall.function?.arguments || '';
|
|
1783
|
-
return [];
|
|
2489
|
+
createToolItem(toolCall) {
|
|
2490
|
+
const callId = toolCall.id || generateId('call');
|
|
2491
|
+
const name = toolCall.function?.name;
|
|
2492
|
+
if (isCodexToolSearchTool(name)) {
|
|
2493
|
+
return {
|
|
2494
|
+
id: generateId('tsc'),
|
|
2495
|
+
type: 'tool_search_call',
|
|
2496
|
+
status: 'in_progress',
|
|
2497
|
+
call_id: callId,
|
|
2498
|
+
execution: 'client',
|
|
2499
|
+
arguments: '',
|
|
2500
|
+
};
|
|
1784
2501
|
}
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
2502
|
+
if (name && this.customToolNames.has(name)) {
|
|
2503
|
+
return {
|
|
2504
|
+
id: callId,
|
|
2505
|
+
type: 'custom_tool_call',
|
|
2506
|
+
status: 'in_progress',
|
|
2507
|
+
call_id: callId,
|
|
2508
|
+
...decodedToolName(name, this.toolNames),
|
|
2509
|
+
input: '',
|
|
2510
|
+
arguments: '',
|
|
2511
|
+
};
|
|
2512
|
+
}
|
|
2513
|
+
return {
|
|
2514
|
+
id: callId,
|
|
2515
|
+
type: 'function_call',
|
|
2516
|
+
status: 'in_progress',
|
|
2517
|
+
call_id: callId,
|
|
2518
|
+
...decodedToolName(name, this.toolNames),
|
|
2519
|
+
arguments: '',
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
applyToolCallName(item, name) {
|
|
2524
|
+
if (!name || item.type === 'tool_search_call') return;
|
|
2525
|
+
Object.assign(item, decodedToolName(name, this.toolNames));
|
|
2526
|
+
if (item.type === 'function_call' && this.customToolNames.has(name)) {
|
|
2527
|
+
item.type = 'custom_tool_call';
|
|
2528
|
+
if (typeof item.input !== 'string') item.input = '';
|
|
1788
2529
|
}
|
|
1789
|
-
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
functionDelta(rawToolCall) {
|
|
2533
|
+
let toolCall = rawToolCall;
|
|
2534
|
+
const emittedName = toolCall.function?.name;
|
|
2535
|
+
if (emittedName) {
|
|
2536
|
+
const resolvedName = resolveEmittedToolName(emittedName, this.knownToolNames);
|
|
2537
|
+
if (resolvedName !== emittedName) {
|
|
2538
|
+
toolCall = { ...toolCall, function: { ...toolCall.function, name: resolvedName } };
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
const events = [...this.closeReasoningItem('completed')];
|
|
2542
|
+
events.push(...this.closeMessageItemAsCommentary('completed'));
|
|
1790
2543
|
const index = toolCall.index ?? 0;
|
|
1791
2544
|
let item = this.toolItems.get(index);
|
|
1792
2545
|
if (!item) {
|
|
1793
|
-
|
|
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
|
-
};
|
|
2546
|
+
item = this.createToolItem(toolCall);
|
|
1811
2547
|
this.toolItems.set(index, item);
|
|
1812
2548
|
this.output.push(item);
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
}
|
|
2549
|
+
if (this.holdToolItemEvents || isParallelToolWrapperName(toolCall.function?.name)) {
|
|
2550
|
+
this.toolItemsBufferedArguments.add(index);
|
|
2551
|
+
}
|
|
2552
|
+
if (!this.holdToolItemEvents && hasToolCallFunctionName(toolCall) && !isParallelToolWrapperName(toolCall.function?.name) && !this.isBridgedCommentaryToolItem(item)) {
|
|
2553
|
+
events.push(...this.addToolItemEvents(index));
|
|
2554
|
+
}
|
|
1819
2555
|
}
|
|
1820
2556
|
if (isCodexToolSearchTool(toolCall.function?.name)) {
|
|
2557
|
+
const wasAdded = this.toolItemsAdded.has(index);
|
|
1821
2558
|
item = convertItemToToolSearchCall(item);
|
|
1822
|
-
|
|
1823
|
-
|
|
2559
|
+
this.toolItems.set(index, item);
|
|
2560
|
+
if (!wasAdded && !this.holdToolItemEvents && hasToolCallFunctionName(toolCall)) {
|
|
2561
|
+
events.push(...this.addToolItemEvents(index));
|
|
2562
|
+
}
|
|
2563
|
+
} else if (toolCall.function?.name) {
|
|
2564
|
+
this.applyToolCallName(item, toolCall.function.name);
|
|
2565
|
+
if (!this.holdToolItemEvents && !this.toolItemsAdded.has(index) && !isParallelToolWrapperName(item.name) && !this.isBridgedCommentaryToolItem(item)) {
|
|
2566
|
+
events.push(...this.addToolItemEvents(index));
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
item.arguments += toolCall.function?.arguments || '';
|
|
2570
|
+
if (functionCallItemNeedsArgumentNormalization(item, this.toolSchemas, this.config)) {
|
|
2571
|
+
this.toolItemsBufferedArguments.add(index);
|
|
1824
2572
|
}
|
|
1825
|
-
const
|
|
1826
|
-
item.arguments
|
|
1827
|
-
if (
|
|
2573
|
+
const streamedLength = this.toolItemsStreamedArgumentLengths.get(index) || 0;
|
|
2574
|
+
const argumentDelta = item.arguments.slice(streamedLength);
|
|
2575
|
+
if (
|
|
2576
|
+
argumentDelta &&
|
|
2577
|
+
item.type === 'function_call' &&
|
|
2578
|
+
this.toolItemsAdded.has(index) &&
|
|
2579
|
+
!this.toolItemsBufferedArguments.has(index)
|
|
2580
|
+
) {
|
|
2581
|
+
this.toolItemsStreamedArgumentLengths.set(index, item.arguments.length);
|
|
1828
2582
|
events.push({
|
|
1829
2583
|
type: 'response.function_call_arguments.delta',
|
|
1830
2584
|
sequence_number: this.nextSequence(),
|
|
1831
2585
|
output_index: this.output.indexOf(item),
|
|
1832
2586
|
item_id: item.id,
|
|
1833
|
-
delta,
|
|
2587
|
+
delta: argumentDelta,
|
|
1834
2588
|
});
|
|
1835
2589
|
}
|
|
1836
2590
|
return events;
|
|
1837
2591
|
}
|
|
1838
2592
|
|
|
1839
|
-
|
|
1840
|
-
if (this.
|
|
1841
|
-
|
|
1842
|
-
|
|
2593
|
+
addToolItemEvents(index) {
|
|
2594
|
+
if (this.toolItemsAdded.has(index)) return [];
|
|
2595
|
+
const item = this.toolItems.get(index);
|
|
2596
|
+
if (!item) return [];
|
|
2597
|
+
this.toolItemsAdded.add(index);
|
|
2598
|
+
return [{
|
|
2599
|
+
type: 'response.output_item.added',
|
|
2600
|
+
sequence_number: this.nextSequence(),
|
|
2601
|
+
output_index: this.output.indexOf(item),
|
|
2602
|
+
item: snapshotResponseItem(item),
|
|
2603
|
+
}];
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2606
|
+
expandParallelToolItems() {
|
|
2607
|
+
for (const [index, item] of [...this.toolItems.entries()]) {
|
|
2608
|
+
if (item.type !== 'function_call' || !isParallelToolWrapperName(item.name)) continue;
|
|
2609
|
+
if (this.toolItemsAdded.has(index)) continue;
|
|
2610
|
+
const uses = parallelToolUsesFromArguments(item.arguments);
|
|
2611
|
+
if (!uses) continue;
|
|
2612
|
+
const at = this.output.indexOf(item);
|
|
2613
|
+
this.toolItems.delete(index);
|
|
2614
|
+
this.toolItemsBufferedArguments.delete(index);
|
|
2615
|
+
this.toolItemsStreamedArgumentLengths.delete(index);
|
|
2616
|
+
const expandedItems = uses.map((use, position) => {
|
|
2617
|
+
const key = `${index}:${position}`;
|
|
2618
|
+
const expanded = this.createToolItem({
|
|
2619
|
+
id: generateId('call'),
|
|
2620
|
+
function: { name: resolveEmittedToolName(use.name, this.knownToolNames) },
|
|
2621
|
+
});
|
|
2622
|
+
expanded.arguments = use.arguments;
|
|
2623
|
+
this.toolItems.set(key, expanded);
|
|
2624
|
+
this.toolItemsBufferedArguments.add(key);
|
|
2625
|
+
return expanded;
|
|
2626
|
+
});
|
|
2627
|
+
if (at >= 0) this.output.splice(at, 1, ...expandedItems);
|
|
2628
|
+
else this.output.push(...expandedItems);
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
1843
2631
|
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
this.completedAt = Date.now() / 1000;
|
|
2632
|
+
isBridgedCommentaryToolItem(item) {
|
|
2633
|
+
return Boolean(this.bridgedCommentaryTool) && item?.type === 'function_call' && item?.name === INTERNAL_COMMENTARY_TOOL;
|
|
2634
|
+
}
|
|
1848
2635
|
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
2636
|
+
convertBridgedCommentaryItems() {
|
|
2637
|
+
const events = [];
|
|
2638
|
+
for (const [index, item] of [...this.toolItems.entries()]) {
|
|
2639
|
+
if (!this.isBridgedCommentaryToolItem(item) || this.toolItemsAdded.has(index)) continue;
|
|
2640
|
+
this.toolItems.delete(index);
|
|
2641
|
+
this.toolItemsBufferedArguments.delete(index);
|
|
2642
|
+
this.toolItemsStreamedArgumentLengths.delete(index);
|
|
2643
|
+
const at = this.output.indexOf(item);
|
|
2644
|
+
const text = commentaryTextFromArguments(item.arguments);
|
|
2645
|
+
if (!text) {
|
|
2646
|
+
if (at >= 0) this.output.splice(at, 1);
|
|
2647
|
+
continue;
|
|
1859
2648
|
}
|
|
2649
|
+
const messageItem = {
|
|
2650
|
+
type: 'message',
|
|
2651
|
+
id: generateId('msg'),
|
|
2652
|
+
role: 'assistant',
|
|
2653
|
+
status: 'completed',
|
|
2654
|
+
phase: 'commentary',
|
|
2655
|
+
content: [normalizeOutputTextPart(text)],
|
|
2656
|
+
};
|
|
2657
|
+
if (at >= 0) this.output.splice(at, 1, messageItem);
|
|
2658
|
+
else this.output.push(messageItem);
|
|
2659
|
+
const outputIndex = this.output.indexOf(messageItem);
|
|
2660
|
+
events.push({
|
|
2661
|
+
type: 'response.output_item.added',
|
|
2662
|
+
sequence_number: this.nextSequence(),
|
|
2663
|
+
output_index: outputIndex,
|
|
2664
|
+
item: snapshotResponseItem({ ...messageItem, status: 'in_progress', content: [] }),
|
|
2665
|
+
});
|
|
2666
|
+
events.push({
|
|
2667
|
+
type: 'response.content_part.added',
|
|
2668
|
+
sequence_number: this.nextSequence(),
|
|
2669
|
+
output_index: outputIndex,
|
|
2670
|
+
content_index: 0,
|
|
2671
|
+
item_id: messageItem.id,
|
|
2672
|
+
part: snapshotResponsePart(normalizeOutputTextPart('')),
|
|
2673
|
+
});
|
|
2674
|
+
events.push({
|
|
2675
|
+
type: 'response.output_text.delta',
|
|
2676
|
+
sequence_number: this.nextSequence(),
|
|
2677
|
+
output_index: outputIndex,
|
|
2678
|
+
content_index: 0,
|
|
2679
|
+
item_id: messageItem.id,
|
|
2680
|
+
delta: text,
|
|
2681
|
+
logprobs: [],
|
|
2682
|
+
});
|
|
2683
|
+
events.push({
|
|
2684
|
+
type: 'response.output_text.done',
|
|
2685
|
+
sequence_number: this.nextSequence(),
|
|
2686
|
+
output_index: outputIndex,
|
|
2687
|
+
content_index: 0,
|
|
2688
|
+
item_id: messageItem.id,
|
|
2689
|
+
text,
|
|
2690
|
+
logprobs: [],
|
|
2691
|
+
});
|
|
2692
|
+
events.push({
|
|
2693
|
+
type: 'response.content_part.done',
|
|
2694
|
+
sequence_number: this.nextSequence(),
|
|
2695
|
+
output_index: outputIndex,
|
|
2696
|
+
content_index: 0,
|
|
2697
|
+
item_id: messageItem.id,
|
|
2698
|
+
part: snapshotResponsePart(messageItem.content[0]),
|
|
2699
|
+
});
|
|
2700
|
+
events.push({
|
|
2701
|
+
type: 'response.output_item.done',
|
|
2702
|
+
sequence_number: this.nextSequence(),
|
|
2703
|
+
output_index: outputIndex,
|
|
2704
|
+
item: snapshotResponseItem(messageItem),
|
|
2705
|
+
});
|
|
2706
|
+
}
|
|
2707
|
+
return events;
|
|
2708
|
+
}
|
|
1860
2709
|
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
2710
|
+
finalize(finishReason = 'stop', usage = null) {
|
|
2711
|
+
if (this.finalized) return [];
|
|
2712
|
+
this.finalized = true;
|
|
2713
|
+
this.expandParallelToolItems();
|
|
2714
|
+
const outcome = deepSeekFinishReasonOutcome(finishReason);
|
|
2715
|
+
const status = outcome.status;
|
|
2716
|
+
this.terminalStatus = status;
|
|
2717
|
+
this.finishReason = finishReason;
|
|
2718
|
+
this.usage = normalizeResponsesUsage(usage);
|
|
2719
|
+
this.completedAt = Date.now() / 1000;
|
|
2720
|
+
const events = [];
|
|
2721
|
+
if (this.messageItem) {
|
|
2722
|
+
this.messageItem.status = status;
|
|
2723
|
+
const outputIndex = this.output.indexOf(this.messageItem);
|
|
2724
|
+
if (!this.messageItemClosed) {
|
|
2725
|
+
this.finalizeMessagePhase();
|
|
2726
|
+
if (!this.messageItem.content.length) this.messageItem.content.push(normalizeOutputTextPart(''));
|
|
2727
|
+
this.messageItem.content[0].text = this.text;
|
|
1864
2728
|
}
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
2729
|
+
if (!this.messageItemAdded) {
|
|
2730
|
+
this.messageItemAdded = true;
|
|
2731
|
+
this.messageContentAdded = true;
|
|
2732
|
+
this.messageItemClosed = true;
|
|
1869
2733
|
events.push({
|
|
1870
2734
|
type: 'response.output_item.added',
|
|
1871
2735
|
sequence_number: this.nextSequence(),
|
|
@@ -1895,36 +2759,22 @@ export class ResponsesStreamMapper {
|
|
|
1895
2759
|
logprobs: [],
|
|
1896
2760
|
});
|
|
1897
2761
|
}
|
|
1898
|
-
events.push(
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
content_index: 0,
|
|
1903
|
-
item_id: this.messageItem.id,
|
|
1904
|
-
text: this.text,
|
|
1905
|
-
logprobs: [],
|
|
1906
|
-
});
|
|
1907
|
-
events.push({
|
|
1908
|
-
type: 'response.content_part.done',
|
|
1909
|
-
sequence_number: this.nextSequence(),
|
|
1910
|
-
output_index: outputIndex,
|
|
1911
|
-
content_index: 0,
|
|
1912
|
-
item_id: this.messageItem.id,
|
|
1913
|
-
part: snapshotResponsePart(this.messageItem.content[0]),
|
|
1914
|
-
});
|
|
1915
|
-
events.push({
|
|
1916
|
-
type: 'response.output_item.done',
|
|
1917
|
-
sequence_number: this.nextSequence(),
|
|
1918
|
-
output_index: outputIndex,
|
|
1919
|
-
item: snapshotResponseItem(this.messageItem),
|
|
1920
|
-
});
|
|
2762
|
+
events.push(...this.messageDoneEvents(outputIndex));
|
|
2763
|
+
} else if (!this.messageItemClosed) {
|
|
2764
|
+
this.messageItemClosed = true;
|
|
2765
|
+
events.push(...this.messageDoneEvents(outputIndex));
|
|
1921
2766
|
}
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
2767
|
+
}
|
|
2768
|
+
events.push(...this.closeReasoningItem(status));
|
|
2769
|
+
events.push(...this.convertBridgedCommentaryItems());
|
|
2770
|
+
for (const [index, item] of this.toolItems.entries()) {
|
|
2771
|
+
item.status = status;
|
|
2772
|
+
if (item.type === 'tool_search_call') finalizeToolSearchCallItemArguments(item);
|
|
2773
|
+
else if (item.type === 'custom_tool_call') item.input = customToolInputFromArguments(item.arguments);
|
|
2774
|
+
else normalizeFunctionCallItemArguments(item, this.toolSchemas, this.config);
|
|
2775
|
+
const outputIndex = this.output.indexOf(item);
|
|
2776
|
+
if (!this.toolItemsAdded.has(index)) {
|
|
2777
|
+
this.toolItemsAdded.add(index);
|
|
1928
2778
|
events.push({
|
|
1929
2779
|
type: 'response.output_item.added',
|
|
1930
2780
|
sequence_number: this.nextSequence(),
|
|
@@ -1932,121 +2782,31 @@ export class ResponsesStreamMapper {
|
|
|
1932
2782
|
item: snapshotResponseItem(item.type === 'tool_search_call' ? {
|
|
1933
2783
|
...item,
|
|
1934
2784
|
status: 'in_progress',
|
|
2785
|
+
} : item.type === 'custom_tool_call' ? {
|
|
2786
|
+
...item,
|
|
2787
|
+
status: 'in_progress',
|
|
2788
|
+
input: '',
|
|
1935
2789
|
} : {
|
|
1936
2790
|
...item,
|
|
1937
2791
|
status: 'in_progress',
|
|
1938
2792
|
arguments: '',
|
|
1939
2793
|
}),
|
|
1940
2794
|
});
|
|
1941
|
-
if (item.type === 'function_call' && item.arguments) {
|
|
1942
|
-
events.push({
|
|
1943
|
-
type: 'response.function_call_arguments.delta',
|
|
1944
|
-
sequence_number: this.nextSequence(),
|
|
1945
|
-
output_index: outputIndex,
|
|
1946
|
-
item_id: item.id,
|
|
1947
|
-
delta: item.arguments,
|
|
1948
|
-
});
|
|
1949
|
-
}
|
|
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
|
-
}
|
|
1959
|
-
events.push({
|
|
1960
|
-
type: 'response.output_item.done',
|
|
1961
|
-
sequence_number: this.nextSequence(),
|
|
1962
|
-
output_index: outputIndex,
|
|
1963
|
-
item: snapshotResponseItem(item),
|
|
1964
|
-
});
|
|
1965
2795
|
}
|
|
1966
|
-
|
|
1967
|
-
const response = createBaseResponse({
|
|
1968
|
-
id: this.responseId,
|
|
1969
|
-
model: this.model,
|
|
1970
|
-
createdAt: this.createdAt,
|
|
1971
|
-
status,
|
|
1972
|
-
output: this.output,
|
|
1973
|
-
previousResponseId: this.previousResponseId,
|
|
1974
|
-
usage: normalizeResponsesUsage(usage),
|
|
1975
|
-
normalized: this.normalized,
|
|
1976
|
-
completedAt: Date.now() / 1000,
|
|
1977
|
-
incompleteReason: finishReason === 'length' ? 'max_output_tokens' : null,
|
|
1978
|
-
});
|
|
1979
|
-
events.push({
|
|
1980
|
-
type: status === 'incomplete' ? 'response.incomplete' : 'response.completed',
|
|
1981
|
-
sequence_number: this.nextSequence(),
|
|
1982
|
-
response,
|
|
1983
|
-
});
|
|
1984
|
-
return events;
|
|
1985
|
-
}
|
|
1986
|
-
|
|
1987
|
-
const events = [];
|
|
1988
|
-
this.finishReason = finishReason;
|
|
1989
|
-
this.usage = normalizeResponsesUsage(usage);
|
|
1990
|
-
this.completedAt = Date.now() / 1000;
|
|
1991
|
-
if (this.reasoningItem) {
|
|
1992
|
-
this.reasoningItem.status = finishReason === 'length' ? 'incomplete' : 'completed';
|
|
1993
|
-
if (this.emitReasoningSummary && this.reasoningText && !this.reasoningSummaryAdded) {
|
|
1994
|
-
this.reasoningSummaryAdded = true;
|
|
1995
|
-
this.reasoningItem.summary.push(normalizeSummaryTextPart(reasoningSummaryText(this.reasoningText)));
|
|
1996
|
-
}
|
|
1997
|
-
this.syncReasoningItemContent();
|
|
1998
|
-
if (this.reasoningItem.summary[0]) {
|
|
1999
|
-
this.reasoningItem.summary[0].text = reasoningSummaryText(this.reasoningText);
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
if (this.messageItem) {
|
|
2003
|
-
this.messageItem.status = finishReason === 'length' ? 'incomplete' : 'completed';
|
|
2004
|
-
this.messageItem.content[0].text = this.text;
|
|
2005
|
-
if (!this.messageContentAdded) {
|
|
2006
|
-
this.messageContentAdded = true;
|
|
2796
|
+
if (item.type === 'function_call' && this.toolItemsBufferedArguments.has(index) && item.arguments) {
|
|
2007
2797
|
events.push({
|
|
2008
|
-
type: 'response.
|
|
2798
|
+
type: 'response.function_call_arguments.delta',
|
|
2009
2799
|
sequence_number: this.nextSequence(),
|
|
2010
|
-
output_index:
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
part: snapshotResponsePart(this.messageItem.content[0]),
|
|
2800
|
+
output_index: outputIndex,
|
|
2801
|
+
item_id: item.id,
|
|
2802
|
+
delta: item.arguments,
|
|
2014
2803
|
});
|
|
2015
2804
|
}
|
|
2016
|
-
events.push({
|
|
2017
|
-
type: 'response.output_text.done',
|
|
2018
|
-
sequence_number: this.nextSequence(),
|
|
2019
|
-
output_index: this.output.indexOf(this.messageItem),
|
|
2020
|
-
content_index: 0,
|
|
2021
|
-
item_id: this.messageItem.id,
|
|
2022
|
-
text: this.text,
|
|
2023
|
-
logprobs: [],
|
|
2024
|
-
});
|
|
2025
|
-
events.push({
|
|
2026
|
-
type: 'response.content_part.done',
|
|
2027
|
-
sequence_number: this.nextSequence(),
|
|
2028
|
-
output_index: this.output.indexOf(this.messageItem),
|
|
2029
|
-
content_index: 0,
|
|
2030
|
-
item_id: this.messageItem.id,
|
|
2031
|
-
part: snapshotResponsePart(this.messageItem.content[0]),
|
|
2032
|
-
});
|
|
2033
|
-
events.push({
|
|
2034
|
-
type: 'response.output_item.done',
|
|
2035
|
-
sequence_number: this.nextSequence(),
|
|
2036
|
-
output_index: this.output.indexOf(this.messageItem),
|
|
2037
|
-
item: snapshotResponseItem(this.messageItem),
|
|
2038
|
-
});
|
|
2039
|
-
}
|
|
2040
|
-
events.push(...this.closeReasoningItem(finishReason === 'length' ? 'incomplete' : 'completed'));
|
|
2041
|
-
for (const item of this.toolItems.values()) {
|
|
2042
|
-
item.status = finishReason === 'length' ? 'incomplete' : 'completed';
|
|
2043
|
-
if (item.type === 'tool_search_call') finalizeToolSearchCallItemArguments(item);
|
|
2044
|
-
else normalizeFunctionCallItemArguments(item, this.toolSchemas, this.config);
|
|
2045
2805
|
if (item.type === 'function_call') {
|
|
2046
2806
|
events.push({
|
|
2047
2807
|
type: 'response.function_call_arguments.done',
|
|
2048
2808
|
sequence_number: this.nextSequence(),
|
|
2049
|
-
output_index:
|
|
2809
|
+
output_index: outputIndex,
|
|
2050
2810
|
item_id: item.id,
|
|
2051
2811
|
arguments: item.arguments,
|
|
2052
2812
|
});
|
|
@@ -2054,7 +2814,7 @@ export class ResponsesStreamMapper {
|
|
|
2054
2814
|
events.push({
|
|
2055
2815
|
type: 'response.output_item.done',
|
|
2056
2816
|
sequence_number: this.nextSequence(),
|
|
2057
|
-
output_index:
|
|
2817
|
+
output_index: outputIndex,
|
|
2058
2818
|
item: snapshotResponseItem(item),
|
|
2059
2819
|
});
|
|
2060
2820
|
}
|
|
@@ -2068,103 +2828,141 @@ export class ResponsesStreamMapper {
|
|
|
2068
2828
|
usage: normalizeResponsesUsage(usage),
|
|
2069
2829
|
normalized: this.normalized,
|
|
2070
2830
|
completedAt: Date.now() / 1000,
|
|
2071
|
-
incompleteReason:
|
|
2831
|
+
incompleteReason: outcome.incompleteReason,
|
|
2832
|
+
error: outcome.error,
|
|
2072
2833
|
});
|
|
2073
2834
|
events.push({
|
|
2074
|
-
type: status === 'incomplete' ? 'response.incomplete' : 'response.
|
|
2835
|
+
type: status === 'completed' ? 'response.completed' : status === 'incomplete' ? 'response.incomplete' : 'response.failed',
|
|
2075
2836
|
sequence_number: this.nextSequence(),
|
|
2076
2837
|
response,
|
|
2077
2838
|
});
|
|
2078
2839
|
return events;
|
|
2079
2840
|
}
|
|
2080
2841
|
|
|
2081
|
-
|
|
2082
|
-
return
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
flushBufferedReasoning(status = 'completed') {
|
|
2086
|
-
if (!this.reasoningItem || this.reasoningItemDone) return [];
|
|
2087
|
-
const events = [];
|
|
2088
|
-
const outputIndex = this.output.indexOf(this.reasoningItem);
|
|
2089
|
-
const summaryText = reasoningSummaryText(this.reasoningText);
|
|
2090
|
-
if (!this.reasoningItemAdded) {
|
|
2091
|
-
this.reasoningItemAdded = true;
|
|
2092
|
-
events.push({
|
|
2093
|
-
type: 'response.output_item.added',
|
|
2842
|
+
messageDoneEvents(outputIndex) {
|
|
2843
|
+
return [
|
|
2844
|
+
{
|
|
2845
|
+
type: 'response.output_text.done',
|
|
2094
2846
|
sequence_number: this.nextSequence(),
|
|
2095
2847
|
output_index: outputIndex,
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
}
|
|
2104
|
-
if (this.emitReasoningText) {
|
|
2105
|
-
this.reasoningContentAdded = true;
|
|
2106
|
-
events.push({
|
|
2107
|
-
type: 'response.content_part.added',
|
|
2848
|
+
content_index: 0,
|
|
2849
|
+
item_id: this.messageItem.id,
|
|
2850
|
+
text: this.messageItem.content[0].text,
|
|
2851
|
+
logprobs: [],
|
|
2852
|
+
},
|
|
2853
|
+
{
|
|
2854
|
+
type: 'response.content_part.done',
|
|
2108
2855
|
sequence_number: this.nextSequence(),
|
|
2109
2856
|
output_index: outputIndex,
|
|
2110
2857
|
content_index: 0,
|
|
2111
|
-
item_id: this.
|
|
2112
|
-
part: snapshotResponsePart(
|
|
2113
|
-
}
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
events.push({
|
|
2117
|
-
type: 'response.reasoning_text.delta',
|
|
2118
|
-
sequence_number: this.nextSequence(),
|
|
2119
|
-
output_index: outputIndex,
|
|
2120
|
-
item_id: this.reasoningItem.id,
|
|
2121
|
-
content_index: 0,
|
|
2122
|
-
delta,
|
|
2123
|
-
});
|
|
2124
|
-
}
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
if (this.emitReasoningSummary && !this.reasoningSummaryAdded) {
|
|
2128
|
-
this.reasoningSummaryAdded = true;
|
|
2129
|
-
this.reasoningItem.summary = [normalizeSummaryTextPart(summaryText)];
|
|
2130
|
-
events.push({
|
|
2131
|
-
type: 'response.reasoning_summary_part.added',
|
|
2858
|
+
item_id: this.messageItem.id,
|
|
2859
|
+
part: snapshotResponsePart(this.messageItem.content[0]),
|
|
2860
|
+
},
|
|
2861
|
+
{
|
|
2862
|
+
type: 'response.output_item.done',
|
|
2132
2863
|
sequence_number: this.nextSequence(),
|
|
2133
2864
|
output_index: outputIndex,
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2865
|
+
item: snapshotResponseItem(this.messageItem),
|
|
2866
|
+
},
|
|
2867
|
+
];
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
assistantMessage() {
|
|
2871
|
+
return assistantMessageFromResponseOutput(this.output);
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
markRoundStart() {
|
|
2875
|
+
this.roundStartIndex = this.output.length;
|
|
2876
|
+
}
|
|
2877
|
+
|
|
2878
|
+
roundAssistantMessage() {
|
|
2879
|
+
return assistantMessageFromResponseOutput(this.output.slice(this.roundStartIndex));
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
removeToolItems(predicate = () => true) {
|
|
2883
|
+
for (const [index, item] of [...this.toolItems.entries()]) {
|
|
2884
|
+
if (!predicate(item)) continue;
|
|
2885
|
+
if (this.toolItemsAdded.has(index)) continue;
|
|
2886
|
+
this.toolItems.delete(index);
|
|
2887
|
+
this.toolItemsBufferedArguments.delete(index);
|
|
2888
|
+
this.toolItemsStreamedArgumentLengths.delete(index);
|
|
2889
|
+
const at = this.output.indexOf(item);
|
|
2890
|
+
if (at >= 0) this.output.splice(at, 1);
|
|
2151
2891
|
}
|
|
2152
|
-
|
|
2153
|
-
|
|
2892
|
+
}
|
|
2893
|
+
|
|
2894
|
+
beginNextRound() {
|
|
2895
|
+
const events = [...this.closeReasoningItem('completed'), ...this.closeMessageItemAsCommentary('completed')];
|
|
2896
|
+
if (this.reasoningItem && !this.reasoningItemAdded) {
|
|
2897
|
+
const at = this.output.indexOf(this.reasoningItem);
|
|
2898
|
+
if (at >= 0) this.output.splice(at, 1);
|
|
2899
|
+
}
|
|
2900
|
+
if (this.messageItem && !this.messageItemAdded) {
|
|
2901
|
+
const at = this.output.indexOf(this.messageItem);
|
|
2902
|
+
if (at >= 0) this.output.splice(at, 1);
|
|
2903
|
+
}
|
|
2904
|
+
for (const [index, item] of this.toolItems.entries()) {
|
|
2905
|
+
if (this.toolItemsAdded.has(index)) continue;
|
|
2906
|
+
const at = this.output.indexOf(item);
|
|
2907
|
+
if (at >= 0) this.output.splice(at, 1);
|
|
2908
|
+
}
|
|
2909
|
+
this.messageItem = null;
|
|
2910
|
+
this.reasoningItem = null;
|
|
2911
|
+
this.toolItems = new Map();
|
|
2912
|
+
this.toolItemsAdded = new Set();
|
|
2913
|
+
this.toolItemsBufferedArguments = new Set();
|
|
2914
|
+
this.toolItemsStreamedArgumentLengths = new Map();
|
|
2915
|
+
this.text = '';
|
|
2916
|
+
this.reasoningText = '';
|
|
2917
|
+
this.reasoningItemDone = false;
|
|
2918
|
+
this.messageItemAdded = false;
|
|
2919
|
+
this.messageContentAdded = false;
|
|
2920
|
+
this.messageItemClosed = false;
|
|
2921
|
+
this.holdVisibleText = false;
|
|
2922
|
+
this.reasoningContentAdded = false;
|
|
2923
|
+
this.reasoningSummaryAdded = false;
|
|
2924
|
+
this.reasoningItemAdded = false;
|
|
2925
|
+
this.streamedReasoningSummaryText = '';
|
|
2926
|
+
this.pendingFinishReason = null;
|
|
2927
|
+
this.pendingUsage = null;
|
|
2154
2928
|
return events;
|
|
2155
2929
|
}
|
|
2156
2930
|
|
|
2931
|
+
replaceBufferedAssistantText(text) {
|
|
2932
|
+
if (this.messageItemAdded || this.messageContentAdded) return null;
|
|
2933
|
+
if (!this.messageItem) return this.textDelta(String(text ?? ''));
|
|
2934
|
+
this.text = String(text ?? '');
|
|
2935
|
+
if (this.messageItem.content.length) this.messageItem.content[0].text = this.text;
|
|
2936
|
+
return [];
|
|
2937
|
+
}
|
|
2938
|
+
|
|
2157
2939
|
closeReasoningItem(status = 'completed') {
|
|
2158
2940
|
if (!this.reasoningItem || this.reasoningItemDone) return [];
|
|
2159
2941
|
this.reasoningItemDone = true;
|
|
2160
2942
|
this.reasoningItem.status = status;
|
|
2161
2943
|
this.syncReasoningItemContent();
|
|
2162
|
-
|
|
2163
|
-
if (this.emitReasoningSummary && this.reasoningText && !this.reasoningSummaryAdded) {
|
|
2164
|
-
this.reasoningSummaryAdded = true;
|
|
2165
|
-
this.reasoningItem.summary.push(normalizeSummaryTextPart(summaryText));
|
|
2166
|
-
}
|
|
2944
|
+
this.reasoningItem.encrypted_content = encodeGatewayReasoning(normalizeReasoningContent(this.reasoningText));
|
|
2167
2945
|
const events = [];
|
|
2946
|
+
if (!this.reasoningItemAdded) {
|
|
2947
|
+
this.reasoningItemAdded = true;
|
|
2948
|
+
const addedItem = {
|
|
2949
|
+
...this.reasoningItem,
|
|
2950
|
+
status: 'in_progress',
|
|
2951
|
+
summary: [],
|
|
2952
|
+
content: this.emitReasoningText ? [normalizeReasoningTextPart('')] : [],
|
|
2953
|
+
encrypted_content: null,
|
|
2954
|
+
};
|
|
2955
|
+
delete addedItem.reasoning_content;
|
|
2956
|
+
events.push({
|
|
2957
|
+
type: 'response.output_item.added',
|
|
2958
|
+
sequence_number: this.nextSequence(),
|
|
2959
|
+
output_index: this.output.indexOf(this.reasoningItem),
|
|
2960
|
+
item: snapshotResponseItem(addedItem),
|
|
2961
|
+
});
|
|
2962
|
+
}
|
|
2963
|
+
if (this.emitReasoningSummary && this.reasoningText) {
|
|
2964
|
+
this.appendReasoningSummaryDelta(events, { final: true });
|
|
2965
|
+
}
|
|
2168
2966
|
if (this.reasoningSummaryAdded) {
|
|
2169
2967
|
for (const [summaryIndex, part] of this.reasoningItem.summary.entries()) {
|
|
2170
2968
|
events.push({
|
|
@@ -2212,11 +3010,36 @@ export class ResponsesStreamMapper {
|
|
|
2212
3010
|
return events;
|
|
2213
3011
|
}
|
|
2214
3012
|
|
|
3013
|
+
streamFailed(message) {
|
|
3014
|
+
if (this.finalized) return [];
|
|
3015
|
+
this.finalized = true;
|
|
3016
|
+
this.terminalStatus = 'failed';
|
|
3017
|
+
this.completedAt = Date.now() / 1000;
|
|
3018
|
+
const response = this.response('failed');
|
|
3019
|
+
response.error = { code: 'upstream_error', message: String(message || 'upstream stream failed') };
|
|
3020
|
+
return [{
|
|
3021
|
+
type: 'response.failed',
|
|
3022
|
+
sequence_number: this.nextSequence(),
|
|
3023
|
+
response,
|
|
3024
|
+
}];
|
|
3025
|
+
}
|
|
3026
|
+
|
|
2215
3027
|
mapChatEvent(event) {
|
|
2216
3028
|
if (!event) return [];
|
|
2217
|
-
if (event.done)
|
|
2218
|
-
|
|
3029
|
+
if (event.done) {
|
|
3030
|
+
if (this.pendingFinishReason || !event.eof) {
|
|
3031
|
+
return this.finalize(this.pendingFinishReason || 'stop', this.pendingUsage);
|
|
3032
|
+
}
|
|
3033
|
+
return this.streamFailed('upstream stream ended before completion');
|
|
3034
|
+
}
|
|
3035
|
+
let payload = event.data;
|
|
3036
|
+
if (typeof payload === 'string') {
|
|
3037
|
+
const parsed = safeJsonParse(payload);
|
|
3038
|
+
if (!parsed.ok) return [];
|
|
3039
|
+
payload = parsed.value;
|
|
3040
|
+
}
|
|
2219
3041
|
if (!isObject(payload)) return [];
|
|
3042
|
+
if (isObject(payload.usage)) this.pendingUsage = payload.usage;
|
|
2220
3043
|
const choice = Array.isArray(payload.choices) ? payload.choices[0] : null;
|
|
2221
3044
|
if (!choice) return [];
|
|
2222
3045
|
const delta = choice.delta || choice.message || {};
|
|
@@ -2236,7 +3059,7 @@ export class ResponsesStreamMapper {
|
|
|
2236
3059
|
}
|
|
2237
3060
|
}
|
|
2238
3061
|
if (choice.finish_reason) {
|
|
2239
|
-
|
|
3062
|
+
this.pendingFinishReason = choice.finish_reason;
|
|
2240
3063
|
}
|
|
2241
3064
|
return events;
|
|
2242
3065
|
}
|