codexmate 0.0.37 → 0.0.39
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/cli/analytics-export-args.js +68 -0
- package/cli/builtin-proxy.js +626 -207
- package/cli/openai-bridge.js +541 -210
- package/cli/session-usage.js +187 -1
- package/cli.js +84 -2
- package/package.json +1 -1
- package/web-ui/app.js +12 -3
- package/web-ui/modules/app.computed.main-tabs.mjs +37 -30
- package/web-ui/modules/app.methods.claude-config.mjs +111 -9
- package/web-ui/modules/app.methods.openclaw-editing.mjs +48 -0
- package/web-ui/modules/app.methods.openclaw-persist.mjs +13 -7
- package/web-ui/modules/app.methods.providers.mjs +36 -10
- package/web-ui/modules/app.methods.runtime.mjs +76 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +1 -0
- package/web-ui/modules/config-mode.computed.mjs +3 -3
- package/web-ui/modules/i18n.dict.mjs +13 -0
- package/web-ui/modules/i18n.mjs +65 -16
- package/web-ui/modules/skills.methods.mjs +1 -1
- package/web-ui/partials/index/layout-header.html +16 -46
- package/web-ui/partials/index/modal-openclaw-config.html +135 -71
- package/web-ui/partials/index/modal-webhook.html +8 -8
- package/web-ui/partials/index/modals-basic.html +56 -16
- package/web-ui/partials/index/panel-config-claude.html +20 -20
- package/web-ui/partials/index/panel-config-codex.html +5 -5
- package/web-ui/partials/index/panel-config-openclaw.html +70 -64
- package/web-ui/partials/index/panel-dashboard.html +62 -77
- package/web-ui/partials/index/panel-settings.html +28 -7
- package/web-ui/partials/index/panel-trash.html +14 -14
- package/web-ui/res/web-ui-render.precompiled.js +846 -539
- package/web-ui/styles/controls-forms.css +6 -0
- package/web-ui/styles/dashboard.css +46 -14
- package/web-ui/styles/layout-shell.css +45 -0
- package/web-ui/styles/navigation-panels.css +3 -3
- package/web-ui/styles/openclaw-structured.css +383 -33
- package/web-ui/styles/responsive.css +68 -0
- package/web-ui/styles/sessions-usage.css +105 -9
- package/web-ui/styles/settings-panel.css +4 -0
- package/web-ui/partials/index/panel-config-codex.html.bak +0 -337
package/cli/openai-bridge.js
CHANGED
|
@@ -222,226 +222,530 @@ function extractChatCompletionResult(payload) {
|
|
|
222
222
|
return { text, toolCalls };
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
function
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
225
|
+
function stringifyJsonValue(value, fallback = '') {
|
|
226
|
+
if (typeof value === 'string') return value;
|
|
227
|
+
if (value == null) return fallback;
|
|
228
|
+
try {
|
|
229
|
+
return JSON.stringify(value);
|
|
230
|
+
} catch (_) {
|
|
231
|
+
return fallback;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function parseJsonValueOrNull(value) {
|
|
236
|
+
if (typeof value !== 'string') return null;
|
|
237
|
+
const text = value.trim();
|
|
238
|
+
if (!text) return null;
|
|
239
|
+
try {
|
|
240
|
+
return JSON.parse(text);
|
|
241
|
+
} catch (_) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function isRecord(value) {
|
|
247
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function asTrimmedString(value) {
|
|
251
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function cloneJsonValue(value) {
|
|
255
|
+
if (Array.isArray(value)) return value.map((item) => cloneJsonValue(item));
|
|
256
|
+
if (isRecord(value)) {
|
|
257
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, cloneJsonValue(item)]));
|
|
258
|
+
}
|
|
259
|
+
return value;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function normalizeResponsesToolOutput(value) {
|
|
263
|
+
if (typeof value === 'string') return value;
|
|
264
|
+
if (value == null) return '';
|
|
265
|
+
return stringifyJsonValue(value, '');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function normalizeOpenAiToolArguments(value) {
|
|
269
|
+
if (typeof value === 'string') return value;
|
|
270
|
+
if (value == null) return '{}';
|
|
271
|
+
return stringifyJsonValue(value, '{}');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function normalizeInputFileBlock(item) {
|
|
275
|
+
if (!isRecord(item)) return null;
|
|
276
|
+
const file = isRecord(item.file) ? item.file : item;
|
|
277
|
+
const out = {};
|
|
278
|
+
const fileId = asTrimmedString(file.file_id || file.id);
|
|
279
|
+
const filename = asTrimmedString(file.filename || file.name);
|
|
280
|
+
const fileData = asTrimmedString(file.file_data || file.data);
|
|
281
|
+
const mimeType = asTrimmedString(file.mime_type || file.media_type);
|
|
282
|
+
if (fileId) out.file_id = fileId;
|
|
283
|
+
if (filename) out.filename = filename;
|
|
284
|
+
if (fileData) out.file_data = fileData;
|
|
285
|
+
if (mimeType) out.mime_type = mimeType;
|
|
286
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function normalizeResponsesContentBlockForChat(item) {
|
|
290
|
+
if (typeof item === 'string') return item.trim() ? item : null;
|
|
291
|
+
if (!isRecord(item)) return null;
|
|
292
|
+
|
|
293
|
+
const type = asTrimmedString(item.type).toLowerCase();
|
|
294
|
+
if (!type) {
|
|
295
|
+
const text = asTrimmedString(item.text || item.content || item.output_text);
|
|
296
|
+
return text ? { type: 'text', text } : null;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (type === 'input_text' || type === 'output_text' || type === 'text' || type === 'summary_text' || type === 'reasoning_text') {
|
|
300
|
+
const text = typeof item.text === 'string' ? item.text : asTrimmedString(item.content || item.output_text);
|
|
301
|
+
return text ? { type: 'text', text } : null;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (type === 'refusal' && typeof item.refusal === 'string') {
|
|
305
|
+
return item.refusal ? { type: 'text', text: item.refusal } : null;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (type === 'input_image') {
|
|
309
|
+
const raw = item.image_url != null ? item.image_url : (item.url != null ? item.url : item.imageUrl);
|
|
310
|
+
if (raw === undefined) return null;
|
|
311
|
+
return {
|
|
312
|
+
type: 'image_url',
|
|
313
|
+
image_url: typeof raw === 'string' ? { url: raw } : cloneJsonValue(raw)
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (type === 'image_url' && item.image_url !== undefined) {
|
|
318
|
+
return { type: 'image_url', image_url: item.image_url };
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (type === 'input_audio') {
|
|
322
|
+
if (item.input_audio !== undefined) return { type: 'input_audio', input_audio: item.input_audio };
|
|
323
|
+
if (item.data !== undefined || item.format !== undefined) {
|
|
324
|
+
return { type: 'input_audio', input_audio: { data: item.data, format: item.format } };
|
|
282
325
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
};
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
286
328
|
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
329
|
+
if (type === 'input_file' || type === 'file') {
|
|
330
|
+
const file = normalizeInputFileBlock(item);
|
|
331
|
+
return file ? { type: 'file', file } : null;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (type === 'reasoning' || type === 'thinking' || type === 'redacted_reasoning') {
|
|
335
|
+
const text = asTrimmedString(item.text || item.content);
|
|
336
|
+
return text ? { type: 'text', text } : null;
|
|
337
|
+
}
|
|
294
338
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
339
|
+
const text = asTrimmedString(item.text || item.content);
|
|
340
|
+
return text ? { type: 'text', text } : cloneJsonValue(item);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function toOpenAiMessageContent(content) {
|
|
344
|
+
if (typeof content === 'string') return content;
|
|
345
|
+
if (!Array.isArray(content)) {
|
|
346
|
+
if (isRecord(content)) {
|
|
347
|
+
const single = normalizeResponsesContentBlockForChat(content);
|
|
348
|
+
if (!single) return '';
|
|
349
|
+
return typeof single === 'string' ? single : [single];
|
|
302
350
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
351
|
+
return '';
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const blocks = content
|
|
355
|
+
.map((item) => normalizeResponsesContentBlockForChat(item))
|
|
356
|
+
.filter((item) => !!item);
|
|
357
|
+
|
|
358
|
+
if (blocks.length === 0) return '';
|
|
359
|
+
if (blocks.length === 1 && typeof blocks[0] === 'string') return blocks[0];
|
|
360
|
+
return blocks;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const RESPONSES_TOOL_CALL_INPUT_TYPES = new Set(['function_call', 'custom_tool_call', 'mcp_tool_call', 'local_shell_call']);
|
|
364
|
+
const RESPONSES_TOOL_CALL_OUTPUT_TYPES = new Set(['function_call_output', 'custom_tool_call_output', 'mcp_tool_call_output', 'tool_search_output', 'local_shell_call_output']);
|
|
365
|
+
|
|
366
|
+
function stripOrphanedResponsesToolOutputs(input) {
|
|
367
|
+
if (!Array.isArray(input)) return input;
|
|
368
|
+
const seenToolCallIds = new Set();
|
|
369
|
+
const sanitized = [];
|
|
370
|
+
for (const item of input) {
|
|
371
|
+
if (!isRecord(item)) {
|
|
372
|
+
sanitized.push(item);
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
const type = asTrimmedString(item.type).toLowerCase();
|
|
376
|
+
if (RESPONSES_TOOL_CALL_INPUT_TYPES.has(type)) {
|
|
377
|
+
const callId = asTrimmedString(item.call_id || item.id);
|
|
378
|
+
if (callId) seenToolCallIds.add(callId);
|
|
379
|
+
sanitized.push(item);
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (RESPONSES_TOOL_CALL_OUTPUT_TYPES.has(type)) {
|
|
383
|
+
const callId = asTrimmedString(item.call_id || item.id);
|
|
384
|
+
if (!callId || !seenToolCallIds.has(callId)) continue;
|
|
385
|
+
sanitized.push(item);
|
|
386
|
+
continue;
|
|
306
387
|
}
|
|
307
|
-
|
|
388
|
+
sanitized.push(item);
|
|
308
389
|
}
|
|
390
|
+
return sanitized;
|
|
391
|
+
}
|
|
309
392
|
|
|
310
|
-
|
|
311
|
-
|
|
393
|
+
function normalizeFreeformToolArguments(value) {
|
|
394
|
+
if (typeof value === 'string') return stringifyJsonValue({ input: value }, '{"input":""}');
|
|
395
|
+
if (value == null) return '{"input":""}';
|
|
396
|
+
if (isRecord(value) && Object.prototype.hasOwnProperty.call(value, 'input')) {
|
|
397
|
+
return stringifyJsonValue(value, '{"input":""}');
|
|
312
398
|
}
|
|
399
|
+
return stringifyJsonValue({ input: normalizeResponsesToolOutput(value) }, '{"input":""}');
|
|
400
|
+
}
|
|
313
401
|
|
|
402
|
+
function toOpenAiToolCall(item, fallbackIndex) {
|
|
403
|
+
if (!isRecord(item)) return null;
|
|
404
|
+
const callId = asTrimmedString(item.call_id || item.id) || `call_${crypto.randomBytes(8).toString('hex')}_${fallbackIndex}`;
|
|
405
|
+
const type = asTrimmedString(item.type).toLowerCase();
|
|
406
|
+
const name = asTrimmedString(item.name)
|
|
407
|
+
|| asTrimmedString(item.server_label)
|
|
408
|
+
|| (type === 'local_shell_call' ? 'local_shell' : '');
|
|
409
|
+
if (!name) return null;
|
|
410
|
+
const rawArguments = item.arguments != null
|
|
411
|
+
? item.arguments
|
|
412
|
+
: (item.input != null ? item.input : (item.action != null ? item.action : item.command));
|
|
413
|
+
const args = (type === 'custom_tool_call' && item.arguments == null)
|
|
414
|
+
? normalizeFreeformToolArguments(rawArguments)
|
|
415
|
+
: normalizeOpenAiToolArguments(rawArguments);
|
|
416
|
+
return {
|
|
417
|
+
id: callId,
|
|
418
|
+
type: 'function',
|
|
419
|
+
function: {
|
|
420
|
+
name,
|
|
421
|
+
arguments: args
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function hasOpenAiMessageContent(content) {
|
|
427
|
+
return typeof content === 'string'
|
|
428
|
+
? content.trim().length > 0
|
|
429
|
+
: Array.isArray(content) && content.length > 0;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function normalizeResponsesInputToChatMessages(input) {
|
|
433
|
+
// Keep the OpenAI bridge in lockstep with the builtin proxy's Responses → Chat shim.
|
|
434
|
+
// Codex long-running tasks append richer Responses history (custom/local_shell/MCP calls)
|
|
435
|
+
// back into `input`; dropping those items makes the next model turn lose tool state and stop early.
|
|
314
436
|
const messages = [];
|
|
315
|
-
|
|
316
|
-
|
|
437
|
+
const normalizedInput = stripOrphanedResponsesToolOutputs(input);
|
|
438
|
+
let functionCallIndex = 0;
|
|
439
|
+
let pendingToolCalls = [];
|
|
440
|
+
const emittedToolCallIds = new Set();
|
|
441
|
+
|
|
442
|
+
const flushPendingToolCalls = () => {
|
|
443
|
+
if (pendingToolCalls.length <= 0) return;
|
|
444
|
+
for (const toolCall of pendingToolCalls) {
|
|
445
|
+
const callId = asTrimmedString(toolCall.id);
|
|
446
|
+
if (callId) emittedToolCallIds.add(callId);
|
|
447
|
+
}
|
|
448
|
+
messages.push({
|
|
449
|
+
role: 'assistant',
|
|
450
|
+
content: null,
|
|
451
|
+
tool_calls: pendingToolCalls
|
|
452
|
+
});
|
|
453
|
+
pendingToolCalls = [];
|
|
454
|
+
};
|
|
317
455
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
if (
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
456
|
+
const pushToolOutputMessage = (callIdRaw, outputRaw) => {
|
|
457
|
+
const toolCallId = asTrimmedString(callIdRaw);
|
|
458
|
+
if (!toolCallId) return;
|
|
459
|
+
messages.push({
|
|
460
|
+
role: 'tool',
|
|
461
|
+
tool_call_id: toolCallId,
|
|
462
|
+
content: normalizeResponsesToolOutput(outputRaw)
|
|
463
|
+
});
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
const processInputItem = (item) => {
|
|
467
|
+
if (typeof item === 'string') {
|
|
468
|
+
flushPendingToolCalls();
|
|
469
|
+
const text = item.trim();
|
|
470
|
+
if (text) messages.push({ role: 'user', content: text });
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
if (!isRecord(item)) return;
|
|
474
|
+
|
|
475
|
+
const itemType = asTrimmedString(item.type).toLowerCase();
|
|
476
|
+
if (RESPONSES_TOOL_CALL_INPUT_TYPES.has(itemType)) {
|
|
477
|
+
const toolCall = toOpenAiToolCall(item, functionCallIndex);
|
|
478
|
+
functionCallIndex += 1;
|
|
479
|
+
if (toolCall) pendingToolCalls.push(toolCall);
|
|
480
|
+
return;
|
|
335
481
|
}
|
|
336
482
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if (
|
|
350
|
-
|
|
483
|
+
if (RESPONSES_TOOL_CALL_OUTPUT_TYPES.has(itemType)) {
|
|
484
|
+
flushPendingToolCalls();
|
|
485
|
+
const toolCallId = asTrimmedString(item.call_id || item.id);
|
|
486
|
+
if (!toolCallId || !emittedToolCallIds.has(toolCallId)) return;
|
|
487
|
+
pushToolOutputMessage(toolCallId, item.output != null ? item.output : item.content);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (itemType === 'reasoning') {
|
|
492
|
+
flushPendingToolCalls();
|
|
493
|
+
const reasoningContent = toOpenAiMessageContent(item.summary != null ? item.summary : (item.content != null ? item.content : item));
|
|
494
|
+
const reasoningSignature = asTrimmedString(item.encrypted_content || item.reasoning_signature);
|
|
495
|
+
if (!hasOpenAiMessageContent(reasoningContent) && !reasoningSignature) return;
|
|
496
|
+
const message = { role: 'assistant', content: reasoningContent };
|
|
497
|
+
if (reasoningSignature) message.reasoning_signature = reasoningSignature;
|
|
498
|
+
messages.push(message);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
flushPendingToolCalls();
|
|
503
|
+
const role = asTrimmedString(item.role).toLowerCase() || 'user';
|
|
504
|
+
const normalizedRole = role === 'developer' ? 'system' : role;
|
|
505
|
+
const content = toOpenAiMessageContent(item.content != null ? item.content : (item.input != null ? item.input : item));
|
|
506
|
+
|
|
507
|
+
if (normalizedRole === 'tool') {
|
|
508
|
+
const toolCallId = asTrimmedString(item.tool_call_id || item.call_id || item.id);
|
|
509
|
+
if (!toolCallId || !emittedToolCallIds.has(toolCallId)) return;
|
|
510
|
+
pushToolOutputMessage(toolCallId, item.content);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (!hasOpenAiMessageContent(content)) return;
|
|
515
|
+
const message = { role: normalizedRole, content };
|
|
516
|
+
const phase = asTrimmedString(item.phase);
|
|
517
|
+
if (phase) message.phase = phase;
|
|
518
|
+
messages.push(message);
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
if (typeof normalizedInput === 'string') {
|
|
522
|
+
const text = normalizedInput.trim();
|
|
523
|
+
if (text) messages.push({ role: 'user', content: text });
|
|
524
|
+
} else if (Array.isArray(normalizedInput)) {
|
|
525
|
+
for (const item of normalizedInput) processInputItem(item);
|
|
526
|
+
} else if (isRecord(normalizedInput)) {
|
|
527
|
+
processInputItem(normalizedInput);
|
|
528
|
+
}
|
|
529
|
+
flushPendingToolCalls();
|
|
530
|
+
return messages;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function normalizeFunctionToolForChat(tool) {
|
|
534
|
+
if (!isRecord(tool)) return null;
|
|
535
|
+
const sourceFn = isRecord(tool.function) ? tool.function : tool;
|
|
536
|
+
const name = asTrimmedString(sourceFn.name) || asTrimmedString(tool.name);
|
|
537
|
+
if (!name) return null;
|
|
538
|
+
const fn = { name };
|
|
539
|
+
const description = asTrimmedString(sourceFn.description) || asTrimmedString(tool.description);
|
|
540
|
+
if (description) fn.description = description;
|
|
541
|
+
if (sourceFn.parameters !== undefined) {
|
|
542
|
+
fn.parameters = cloneJsonValue(sourceFn.parameters);
|
|
543
|
+
} else if (tool.parameters !== undefined) {
|
|
544
|
+
fn.parameters = cloneJsonValue(tool.parameters);
|
|
545
|
+
}
|
|
546
|
+
if (typeof sourceFn.strict === 'boolean') {
|
|
547
|
+
fn.strict = sourceFn.strict;
|
|
548
|
+
} else if (typeof tool.strict === 'boolean') {
|
|
549
|
+
fn.strict = tool.strict;
|
|
550
|
+
}
|
|
551
|
+
return { type: 'function', function: fn };
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
function buildLocalShellToolForChat(tool) {
|
|
555
|
+
return {
|
|
556
|
+
type: 'function',
|
|
557
|
+
function: {
|
|
558
|
+
name: asTrimmedString(tool && tool.name) || 'local_shell',
|
|
559
|
+
description: asTrimmedString(tool && tool.description) || 'Run a local shell command and return its output.',
|
|
560
|
+
parameters: {
|
|
561
|
+
type: 'object',
|
|
562
|
+
properties: {
|
|
563
|
+
cmd: { type: 'string', description: 'Shell command to execute.' },
|
|
564
|
+
yield_time_ms: { type: 'number', description: 'Milliseconds to wait before yielding partial output.' },
|
|
565
|
+
max_output_tokens: { type: 'number', description: 'Maximum output tokens to return.' }
|
|
566
|
+
},
|
|
567
|
+
required: ['cmd'],
|
|
568
|
+
additionalProperties: true
|
|
351
569
|
}
|
|
352
|
-
continue;
|
|
353
570
|
}
|
|
571
|
+
};
|
|
572
|
+
}
|
|
354
573
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
574
|
+
function buildFreeformToolForChat(tool, fallbackName = 'custom_tool') {
|
|
575
|
+
return {
|
|
576
|
+
type: 'function',
|
|
577
|
+
function: {
|
|
578
|
+
name: asTrimmedString(tool && tool.name) || fallbackName,
|
|
579
|
+
description: asTrimmedString(tool && tool.description) || 'Pass raw freeform input to the local tool.',
|
|
580
|
+
parameters: {
|
|
581
|
+
type: 'object',
|
|
582
|
+
properties: {
|
|
583
|
+
input: { type: 'string', description: 'Raw tool input.' }
|
|
584
|
+
},
|
|
585
|
+
required: ['input'],
|
|
586
|
+
additionalProperties: false
|
|
363
587
|
}
|
|
364
|
-
continue;
|
|
365
588
|
}
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const MAX_RESPONSES_TOOL_NAMESPACE_DEPTH = 5;
|
|
593
|
+
|
|
594
|
+
function rememberResponsesToolType(tool, target, depth = 0) {
|
|
595
|
+
if (!isRecord(tool) || !target || depth > MAX_RESPONSES_TOOL_NAMESPACE_DEPTH) return;
|
|
596
|
+
const type = asTrimmedString(tool.type).toLowerCase();
|
|
597
|
+
if (type === 'namespace' && Array.isArray(tool.tools)) {
|
|
598
|
+
for (const inner of tool.tools) rememberResponsesToolType(inner, target, depth + 1);
|
|
599
|
+
return;
|
|
366
600
|
}
|
|
601
|
+
const sourceFn = isRecord(tool.function) ? tool.function : tool;
|
|
602
|
+
const name = asTrimmedString(sourceFn.name) || asTrimmedString(tool.name);
|
|
603
|
+
if (!name) return;
|
|
604
|
+
if (type === 'local_shell') {
|
|
605
|
+
target[name] = 'local_shell_call';
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
if (type === 'custom' || type === 'custom_tool' || (!type && name === 'apply_patch')) {
|
|
609
|
+
target[name] = 'custom_tool_call';
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
if (type === 'function') {
|
|
613
|
+
target[name] = 'function_call';
|
|
614
|
+
}
|
|
615
|
+
}
|
|
367
616
|
|
|
368
|
-
|
|
369
|
-
|
|
617
|
+
function collectResponsesToolTypesByName(tools) {
|
|
618
|
+
const result = {};
|
|
619
|
+
if (!Array.isArray(tools)) return result;
|
|
620
|
+
for (const tool of tools) rememberResponsesToolType(tool, result);
|
|
621
|
+
return result;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
function extractFreeformInputFromChatArguments(argumentsText) {
|
|
625
|
+
if (typeof argumentsText !== 'string') return '';
|
|
626
|
+
const parsed = parseJsonValueOrNull(argumentsText);
|
|
627
|
+
if (isRecord(parsed) && Object.prototype.hasOwnProperty.call(parsed, 'input')) {
|
|
628
|
+
return typeof parsed.input === 'string' ? parsed.input : normalizeResponsesToolOutput(parsed.input);
|
|
370
629
|
}
|
|
630
|
+
return argumentsText;
|
|
631
|
+
}
|
|
371
632
|
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
if (
|
|
375
|
-
|
|
633
|
+
function extractLocalShellActionFromChatArguments(argumentsText) {
|
|
634
|
+
const parsed = parseJsonValueOrNull(argumentsText);
|
|
635
|
+
if (isRecord(parsed)) return cloneJsonValue(parsed);
|
|
636
|
+
return { cmd: typeof argumentsText === 'string' ? argumentsText : '' };
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function buildResponsesToolCallItemFromChatToolCall(toolCall, toolTypesByName = {}) {
|
|
640
|
+
if (!isRecord(toolCall)) return null;
|
|
641
|
+
const fn = isRecord(toolCall.function) ? toolCall.function : {};
|
|
642
|
+
const name = asTrimmedString(fn.name);
|
|
643
|
+
if (!name) return null;
|
|
644
|
+
const callId = asTrimmedString(toolCall.id) || `call_${crypto.randomBytes(8).toString('hex')}`;
|
|
645
|
+
const argumentsText = typeof fn.arguments === 'string' ? fn.arguments : '';
|
|
646
|
+
const responseType = toolTypesByName && toolTypesByName[name] ? toolTypesByName[name] : 'function_call';
|
|
647
|
+
|
|
648
|
+
if (responseType === 'custom_tool_call') {
|
|
649
|
+
return {
|
|
650
|
+
type: 'custom_tool_call',
|
|
651
|
+
call_id: callId,
|
|
652
|
+
name,
|
|
653
|
+
input: extractFreeformInputFromChatArguments(argumentsText)
|
|
654
|
+
};
|
|
376
655
|
}
|
|
377
|
-
|
|
656
|
+
if (responseType === 'local_shell_call') {
|
|
657
|
+
return {
|
|
658
|
+
type: 'local_shell_call',
|
|
659
|
+
call_id: callId,
|
|
660
|
+
name,
|
|
661
|
+
action: extractLocalShellActionFromChatArguments(argumentsText)
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
return {
|
|
665
|
+
type: 'function_call',
|
|
666
|
+
call_id: callId,
|
|
667
|
+
name,
|
|
668
|
+
arguments: argumentsText
|
|
669
|
+
};
|
|
378
670
|
}
|
|
379
671
|
|
|
672
|
+
function normalizeSingleResponsesToolToChatTools(tool, depth = 0) {
|
|
673
|
+
if (!isRecord(tool) || depth > MAX_RESPONSES_TOOL_NAMESPACE_DEPTH) return [];
|
|
674
|
+
const type = asTrimmedString(tool.type).toLowerCase();
|
|
675
|
+
if (type === 'namespace' && Array.isArray(tool.tools)) {
|
|
676
|
+
return tool.tools.flatMap((inner) => normalizeSingleResponsesToolToChatTools(inner, depth + 1));
|
|
677
|
+
}
|
|
678
|
+
if (type === 'function') {
|
|
679
|
+
const converted = normalizeFunctionToolForChat(tool);
|
|
680
|
+
return converted ? [converted] : [];
|
|
681
|
+
}
|
|
682
|
+
if (type === 'local_shell') {
|
|
683
|
+
return [buildLocalShellToolForChat(tool)];
|
|
684
|
+
}
|
|
685
|
+
const name = asTrimmedString(tool.name);
|
|
686
|
+
if (type === 'custom' || type === 'custom_tool' || (!type && name === 'apply_patch')) {
|
|
687
|
+
return [buildFreeformToolForChat(tool, name || 'custom_tool')];
|
|
688
|
+
}
|
|
689
|
+
return [];
|
|
690
|
+
}
|
|
380
691
|
|
|
381
692
|
function normalizeResponsesToolsToChatTools(tools) {
|
|
382
693
|
if (!Array.isArray(tools)) return tools;
|
|
383
|
-
return tools
|
|
384
|
-
.map((tool) => {
|
|
385
|
-
if (!tool || typeof tool !== 'object') return null;
|
|
386
|
-
if (tool.type !== 'function') return null;
|
|
387
|
-
const sourceFn = tool.function && typeof tool.function === 'object' && !Array.isArray(tool.function)
|
|
388
|
-
? tool.function
|
|
389
|
-
: {};
|
|
390
|
-
const name = typeof sourceFn.name === 'string' && sourceFn.name.trim()
|
|
391
|
-
? sourceFn.name.trim()
|
|
392
|
-
: (typeof tool.name === 'string' ? tool.name.trim() : '');
|
|
393
|
-
if (!name) return null;
|
|
394
|
-
const parameters = sourceFn.parameters && typeof sourceFn.parameters === 'object' && !Array.isArray(sourceFn.parameters)
|
|
395
|
-
? sourceFn.parameters
|
|
396
|
-
: (tool.parameters && typeof tool.parameters === 'object' && !Array.isArray(tool.parameters) ? tool.parameters : {});
|
|
397
|
-
const fn = { name, parameters };
|
|
398
|
-
const description = typeof sourceFn.description === 'string'
|
|
399
|
-
? sourceFn.description
|
|
400
|
-
: (typeof tool.description === 'string' ? tool.description : undefined);
|
|
401
|
-
const strict = typeof sourceFn.strict === 'boolean'
|
|
402
|
-
? sourceFn.strict
|
|
403
|
-
: (typeof tool.strict === 'boolean' ? tool.strict : undefined);
|
|
404
|
-
if (description !== undefined) fn.description = description;
|
|
405
|
-
if (strict !== undefined) fn.strict = strict;
|
|
406
|
-
return { type: 'function', function: fn };
|
|
407
|
-
})
|
|
408
|
-
.filter(Boolean);
|
|
694
|
+
return tools.flatMap((tool) => normalizeSingleResponsesToolToChatTools(tool));
|
|
409
695
|
}
|
|
410
696
|
|
|
411
697
|
function normalizeResponsesToolChoiceToChatToolChoice(toolChoice) {
|
|
412
|
-
if (
|
|
413
|
-
if (
|
|
414
|
-
|
|
698
|
+
if (toolChoice === undefined) return undefined;
|
|
699
|
+
if (typeof toolChoice === 'string') return toolChoice;
|
|
700
|
+
if (!isRecord(toolChoice)) return toolChoice;
|
|
701
|
+
|
|
702
|
+
const type = asTrimmedString(toolChoice.type).toLowerCase();
|
|
703
|
+
if (type === 'tool' || type === 'function' || type === 'custom' || type === 'custom_tool' || type === 'local_shell') {
|
|
704
|
+
if (isRecord(toolChoice.function) && asTrimmedString(toolChoice.function.name)) return cloneJsonValue(toolChoice);
|
|
705
|
+
const name = asTrimmedString(toolChoice.name) || asTrimmedString(toolChoice.server_label);
|
|
706
|
+
if (!name) return 'required';
|
|
707
|
+
return { type: 'function', function: { name } };
|
|
708
|
+
}
|
|
709
|
+
if (type === 'auto' || type === 'none' || type === 'required') return type;
|
|
710
|
+
return 'auto';
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
function getChatToolChoiceName(toolChoice) {
|
|
714
|
+
if (!isRecord(toolChoice)) return '';
|
|
715
|
+
if (isRecord(toolChoice.function)) return asTrimmedString(toolChoice.function.name);
|
|
716
|
+
return '';
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function pruneInvalidChatToolChoice(chatBody) {
|
|
720
|
+
if (!isRecord(chatBody) || !Array.isArray(chatBody.tools)) return;
|
|
721
|
+
if (chatBody.tools.length === 0) {
|
|
722
|
+
delete chatBody.tools;
|
|
723
|
+
delete chatBody.tool_choice;
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
const chosenName = getChatToolChoiceName(chatBody.tool_choice);
|
|
727
|
+
if (!chosenName) return;
|
|
728
|
+
const toolNames = new Set(chatBody.tools
|
|
729
|
+
.map((tool) => isRecord(tool) && isRecord(tool.function) ? asTrimmedString(tool.function.name) : '')
|
|
730
|
+
.filter(Boolean));
|
|
731
|
+
if (!toolNames.has(chosenName)) {
|
|
732
|
+
delete chatBody.tool_choice;
|
|
415
733
|
}
|
|
416
|
-
return toolChoice;
|
|
417
734
|
}
|
|
418
735
|
|
|
419
736
|
function normalizeResponsesToolsForResponsesApi(tools) {
|
|
420
737
|
if (!Array.isArray(tools)) return tools;
|
|
421
738
|
return tools
|
|
422
739
|
.map((tool) => {
|
|
423
|
-
|
|
424
|
-
if (
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
:
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
if (
|
|
432
|
-
const out = { type: 'function', name };
|
|
433
|
-
const description = typeof sourceFn.description === 'string'
|
|
434
|
-
? sourceFn.description
|
|
435
|
-
: (typeof tool.description === 'string' ? tool.description : undefined);
|
|
436
|
-
const parameters = sourceFn.parameters && typeof sourceFn.parameters === 'object' && !Array.isArray(sourceFn.parameters)
|
|
437
|
-
? sourceFn.parameters
|
|
438
|
-
: (tool.parameters && typeof tool.parameters === 'object' && !Array.isArray(tool.parameters) ? tool.parameters : undefined);
|
|
439
|
-
const strict = typeof sourceFn.strict === 'boolean'
|
|
440
|
-
? sourceFn.strict
|
|
441
|
-
: (typeof tool.strict === 'boolean' ? tool.strict : undefined);
|
|
442
|
-
if (description !== undefined) out.description = description;
|
|
443
|
-
if (parameters !== undefined) out.parameters = parameters;
|
|
444
|
-
if (strict !== undefined) out.strict = strict;
|
|
740
|
+
const converted = normalizeFunctionToolForChat(tool);
|
|
741
|
+
if (!converted || !converted.function) return null;
|
|
742
|
+
const out = {
|
|
743
|
+
type: 'function',
|
|
744
|
+
name: converted.function.name
|
|
745
|
+
};
|
|
746
|
+
if (converted.function.description !== undefined) out.description = converted.function.description;
|
|
747
|
+
if (converted.function.parameters !== undefined) out.parameters = converted.function.parameters;
|
|
748
|
+
if (converted.function.strict !== undefined) out.strict = converted.function.strict;
|
|
445
749
|
return out;
|
|
446
750
|
})
|
|
447
751
|
.filter(Boolean);
|
|
@@ -484,6 +788,39 @@ function mergeLeadingSystemMessages(messages, leadingInstructions) {
|
|
|
484
788
|
return out;
|
|
485
789
|
}
|
|
486
790
|
|
|
791
|
+
function messageContentAsText(content) {
|
|
792
|
+
if (typeof content === 'string') return content;
|
|
793
|
+
if (!Array.isArray(content)) return '';
|
|
794
|
+
return content
|
|
795
|
+
.map((item) => {
|
|
796
|
+
if (typeof item === 'string') return item;
|
|
797
|
+
if (!isRecord(item)) return '';
|
|
798
|
+
if (typeof item.text === 'string') return item.text;
|
|
799
|
+
if (typeof item.content === 'string') return item.content;
|
|
800
|
+
return '';
|
|
801
|
+
})
|
|
802
|
+
.filter(Boolean)
|
|
803
|
+
.join('\n');
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
function hasRunningCodexExecSession(messages) {
|
|
807
|
+
if (!Array.isArray(messages)) return false;
|
|
808
|
+
return messages.some((message) => {
|
|
809
|
+
if (!isRecord(message) || message.role !== 'tool') return false;
|
|
810
|
+
return /Process running with session ID\s+\d+/i.test(messageContentAsText(message.content));
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
function appendChatFallbackRuntimeInstructions(baseInstructions, rawMessages) {
|
|
815
|
+
const segments = [];
|
|
816
|
+
const base = typeof baseInstructions === 'string' ? baseInstructions.trim() : '';
|
|
817
|
+
if (base) segments.push(base);
|
|
818
|
+
if (hasRunningCodexExecSession(rawMessages)) {
|
|
819
|
+
segments.push('Codex tool output indicates a command is still running ("Process running with session ID ..."). You must call write_stdin with that numeric session_id and empty chars to poll/wait for completion before giving a final answer. Do not merely say that you are waiting.');
|
|
820
|
+
}
|
|
821
|
+
return segments.join('\n\n');
|
|
822
|
+
}
|
|
823
|
+
|
|
487
824
|
function convertResponsesRequestToChatCompletions(payload) {
|
|
488
825
|
const body = payload && typeof payload === 'object' ? payload : {};
|
|
489
826
|
const model = typeof body.model === 'string' ? body.model.trim() : '';
|
|
@@ -492,9 +829,10 @@ function convertResponsesRequestToChatCompletions(payload) {
|
|
|
492
829
|
}
|
|
493
830
|
|
|
494
831
|
const rawMessages = normalizeResponsesInputToChatMessages(body.input);
|
|
832
|
+
const leadingInstructions = appendChatFallbackRuntimeInstructions(body.instructions, rawMessages);
|
|
495
833
|
// codex 同时下发 body.instructions(内置 prompt)与 input 内 developer/system 消息(AGENTS.md)。
|
|
496
834
|
// 合流为一条领头 system,避免某些上游"只认第一条 system"导致 AGENTS.md 失效。
|
|
497
|
-
const messages = mergeLeadingSystemMessages(rawMessages,
|
|
835
|
+
const messages = mergeLeadingSystemMessages(rawMessages, leadingInstructions);
|
|
498
836
|
if (!messages.length) {
|
|
499
837
|
// codex sometimes sends empty input for probes; tolerate.
|
|
500
838
|
messages.push({ role: 'user', content: '' });
|
|
@@ -527,13 +865,15 @@ function convertResponsesRequestToChatCompletions(payload) {
|
|
|
527
865
|
chat.metadata = body.metadata;
|
|
528
866
|
}
|
|
529
867
|
|
|
868
|
+
pruneInvalidChatToolChoice(chat);
|
|
869
|
+
|
|
530
870
|
// Remove undefined keys
|
|
531
871
|
Object.keys(chat).forEach((key) => chat[key] === undefined && delete chat[key]);
|
|
532
872
|
|
|
533
|
-
return { chat, streamRequested: stream };
|
|
873
|
+
return { chat, streamRequested: stream, toolTypesByName: collectResponsesToolTypesByName(body.tools) };
|
|
534
874
|
}
|
|
535
875
|
|
|
536
|
-
function buildResponsesPayloadFromChatResult(model, text, toolCalls, upstreamPayload) {
|
|
876
|
+
function buildResponsesPayloadFromChatResult(model, text, toolCalls, upstreamPayload, options = {}) {
|
|
537
877
|
const responseId = `resp_${crypto.randomBytes(10).toString('hex')}`;
|
|
538
878
|
const usage = upstreamPayload && upstreamPayload.usage && typeof upstreamPayload.usage === 'object'
|
|
539
879
|
? upstreamPayload.usage
|
|
@@ -550,22 +890,13 @@ function buildResponsesPayloadFromChatResult(model, text, toolCalls, upstreamPay
|
|
|
550
890
|
});
|
|
551
891
|
}
|
|
552
892
|
|
|
553
|
-
// Convert chat.completions tool_calls into Responses
|
|
554
|
-
//
|
|
893
|
+
// Convert chat.completions tool_calls back into the original Responses item type.
|
|
894
|
+
// Treating every call as `function_call` makes Codex built-ins (custom/local_shell)
|
|
895
|
+
// degrade into ordinary chat text instead of executable agent steps.
|
|
555
896
|
if (Array.isArray(toolCalls)) {
|
|
556
897
|
for (const call of toolCalls) {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
const fn = call.function && typeof call.function === 'object' ? call.function : {};
|
|
560
|
-
const name = typeof fn.name === 'string' ? fn.name : '';
|
|
561
|
-
const args = typeof fn.arguments === 'string' ? fn.arguments : '';
|
|
562
|
-
if (!name) continue;
|
|
563
|
-
output.push({
|
|
564
|
-
type: 'function_call',
|
|
565
|
-
call_id: callId,
|
|
566
|
-
name,
|
|
567
|
-
arguments: args
|
|
568
|
-
});
|
|
898
|
+
const item = buildResponsesToolCallItemFromChatToolCall(call, options.toolTypesByName || {});
|
|
899
|
+
if (item) output.push(item);
|
|
569
900
|
}
|
|
570
901
|
}
|
|
571
902
|
|
|
@@ -932,14 +1263,8 @@ function finishChatStreamResponsesSse(state) {
|
|
|
932
1263
|
|
|
933
1264
|
for (const toolCall of state.toolCalls) {
|
|
934
1265
|
if (!toolCall) continue;
|
|
935
|
-
const
|
|
936
|
-
if (!
|
|
937
|
-
const item = {
|
|
938
|
-
type: 'function_call',
|
|
939
|
-
call_id: toolCall.id || `call_${crypto.randomBytes(8).toString('hex')}`,
|
|
940
|
-
name,
|
|
941
|
-
arguments: toolCall.function && typeof toolCall.function.arguments === 'string' ? toolCall.function.arguments : ''
|
|
942
|
-
};
|
|
1266
|
+
const item = buildResponsesToolCallItemFromChatToolCall(toolCall, state.toolTypesByName || {});
|
|
1267
|
+
if (!item) continue;
|
|
943
1268
|
const outputIndex = state.output.length;
|
|
944
1269
|
state.output.push(item);
|
|
945
1270
|
writeSse(state.res, 'response.output_item.added', {
|
|
@@ -1102,7 +1427,9 @@ function streamChatCompletionsAsResponsesSse(targetUrl, options = {}) {
|
|
|
1102
1427
|
return;
|
|
1103
1428
|
}
|
|
1104
1429
|
const extracted = extractChatCompletionResult(parsedJson.value);
|
|
1105
|
-
sendResponsesSse(res, buildResponsesPayloadFromChatResult(fallbackModel, extracted.text, extracted.toolCalls, parsedJson.value
|
|
1430
|
+
sendResponsesSse(res, buildResponsesPayloadFromChatResult(fallbackModel, extracted.text, extracted.toolCalls, parsedJson.value, {
|
|
1431
|
+
toolTypesByName: options.toolTypesByName || {}
|
|
1432
|
+
}));
|
|
1106
1433
|
if (!res.writableEnded && !res.destroyed) res.end();
|
|
1107
1434
|
finish({ ok: true });
|
|
1108
1435
|
});
|
|
@@ -1119,6 +1446,7 @@ function streamChatCompletionsAsResponsesSse(targetUrl, options = {}) {
|
|
|
1119
1446
|
messageItem: null,
|
|
1120
1447
|
messageText: '',
|
|
1121
1448
|
toolCalls: [],
|
|
1449
|
+
toolTypesByName: options.toolTypesByName || {},
|
|
1122
1450
|
finished: false,
|
|
1123
1451
|
sawDone: false,
|
|
1124
1452
|
sawFinishReason: false,
|
|
@@ -1475,7 +1803,8 @@ function createOpenaiBridgeHttpHandler(options = {}) {
|
|
|
1475
1803
|
httpAgent,
|
|
1476
1804
|
httpsAgent,
|
|
1477
1805
|
res,
|
|
1478
|
-
model: typeof chatBody.model === 'string' ? chatBody.model : ''
|
|
1806
|
+
model: typeof chatBody.model === 'string' ? chatBody.model : '',
|
|
1807
|
+
toolTypesByName: converted.toolTypesByName || {}
|
|
1479
1808
|
}));
|
|
1480
1809
|
if (!streamed.ok) {
|
|
1481
1810
|
if (res.writableEnded || res.destroyed) {
|
|
@@ -1598,7 +1927,9 @@ function createOpenaiBridgeHttpHandler(options = {}) {
|
|
|
1598
1927
|
const extracted = extractChatCompletionResult(upstreamJson.value);
|
|
1599
1928
|
const text = extracted && typeof extracted.text === 'string' ? extracted.text : '';
|
|
1600
1929
|
const toolCalls = extracted && Array.isArray(extracted.toolCalls) ? extracted.toolCalls : [];
|
|
1601
|
-
const responsesPayload = buildResponsesPayloadFromChatResult(model, text, toolCalls, upstreamJson.value
|
|
1930
|
+
const responsesPayload = buildResponsesPayloadFromChatResult(model, text, toolCalls, upstreamJson.value, {
|
|
1931
|
+
toolTypesByName: converted.toolTypesByName || {}
|
|
1932
|
+
});
|
|
1602
1933
|
|
|
1603
1934
|
if (converted.streamRequested && wantsSse) {
|
|
1604
1935
|
res.writeHead(200, {
|