Skip to content

Commit f223733

Browse files
authored
fix(responses): align chat fallback and bump version to 0.0.54 (#202)
* fix(responses): align chat fallback edge cases * chore(release): bump version to 0.0.54
1 parent c9fb5be commit f223733

6 files changed

Lines changed: 751 additions & 30 deletions

File tree

cli/builtin-proxy.js

Lines changed: 222 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,6 @@ function createBuiltinProxyRuntimeController(deps = {}) {
940940
'n',
941941
'modalities',
942942
'audio',
943-
'reasoning',
944943
'reasoning_effort',
945944
'service_tier'
946945
];
@@ -964,10 +963,15 @@ function createBuiltinProxyRuntimeController(deps = {}) {
964963
if (isRecord(source.text) && asTrimmedString(source.text.verbosity)) {
965964
chatBody.verbosity = asTrimmedString(source.text.verbosity);
966965
}
966+
if (isRecord(source.reasoning) && asTrimmedString(source.reasoning.effort)) {
967+
chatBody.reasoning_effort = asTrimmedString(source.reasoning.effort);
968+
}
967969

968970
pruneInvalidChatToolChoice(chatBody);
969971

970-
if (Object.prototype.hasOwnProperty.call(source, 'max_tokens')) {
972+
if (Object.prototype.hasOwnProperty.call(source, 'max_completion_tokens')) {
973+
chatBody.max_tokens = Math.max(128, Number(source.max_completion_tokens) || 0);
974+
} else if (Object.prototype.hasOwnProperty.call(source, 'max_tokens')) {
971975
chatBody.max_tokens = source.max_tokens;
972976
} else if (source.max_output_tokens != null) {
973977
chatBody.max_tokens = source.max_output_tokens;
@@ -976,6 +980,18 @@ function createBuiltinProxyRuntimeController(deps = {}) {
976980
return chatBody;
977981
}
978982

983+
function normalizeResponsesPayloadForUpstream(payload, stream) {
984+
const source = isRecord(payload) ? payload : {};
985+
const normalized = { ...source, stream };
986+
if (isRecord(source.reasoning)) {
987+
const include = Array.isArray(source.include) ? source.include.filter((item) => typeof item === 'string') : [];
988+
if (!include.includes('reasoning.encrypted_content')) {
989+
normalized.include = [...include, 'reasoning.encrypted_content'];
990+
}
991+
}
992+
return normalized;
993+
}
994+
979995
function ensureResponseMetadata(payload) {
980996
const base = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload : {};
981997
const id = typeof base.id === 'string' && base.id.trim()
@@ -1001,6 +1017,112 @@ function createBuiltinProxyRuntimeController(deps = {}) {
10011017
res.write(`data: ${JSON.stringify(dataObj)}\n\n`);
10021018
}
10031019

1020+
function buildResponsesSseItem(item, fallbackId) {
1021+
const source = isRecord(item) ? item : {};
1022+
const itemId = asTrimmedString(source.id || source.call_id) || fallbackId || `item_${crypto.randomBytes(8).toString('hex')}`;
1023+
if (source.type === 'message') {
1024+
return {
1025+
...source,
1026+
id: itemId,
1027+
role: source.role || 'assistant',
1028+
content: Array.isArray(source.content)
1029+
? source.content.map((part) => {
1030+
if (!isRecord(part)) return part;
1031+
if (part.type !== 'output_text') return cloneJsonValue(part);
1032+
return {
1033+
...part,
1034+
text: typeof part.text === 'string' ? part.text : '',
1035+
annotations: Array.isArray(part.annotations) ? part.annotations : [],
1036+
logprobs: Array.isArray(part.logprobs) ? part.logprobs : []
1037+
};
1038+
})
1039+
: []
1040+
};
1041+
}
1042+
if (source.type === 'reasoning') {
1043+
return {
1044+
...source,
1045+
id: itemId,
1046+
summary: Array.isArray(source.summary) ? source.summary : []
1047+
};
1048+
}
1049+
if (source.type === 'function_call') {
1050+
return {
1051+
...source,
1052+
id: itemId,
1053+
call_id: asTrimmedString(source.call_id) || itemId,
1054+
name: asTrimmedString(source.name),
1055+
arguments: typeof source.arguments === 'string' ? source.arguments : ''
1056+
};
1057+
}
1058+
if (source.type === 'custom_tool_call') {
1059+
return {
1060+
...source,
1061+
id: itemId,
1062+
call_id: asTrimmedString(source.call_id) || itemId,
1063+
name: asTrimmedString(source.name),
1064+
input: typeof source.input === 'string' ? source.input : ''
1065+
};
1066+
}
1067+
return { ...source, id: itemId };
1068+
}
1069+
1070+
function emitResponsesTextPartAdded(res, itemId, outputIndex, contentIndex, nextSeq) {
1071+
writeSse(res, 'response.content_part.added', {
1072+
type: 'response.content_part.added',
1073+
item_id: itemId,
1074+
output_index: outputIndex,
1075+
content_index: contentIndex,
1076+
part: { type: 'output_text', text: '', annotations: [], logprobs: [] },
1077+
sequence_number: nextSeq()
1078+
});
1079+
}
1080+
1081+
function emitResponsesTextPartDone(res, itemId, outputIndex, contentIndex, text, nextSeq) {
1082+
writeSse(res, 'response.content_part.done', {
1083+
type: 'response.content_part.done',
1084+
item_id: itemId,
1085+
output_index: outputIndex,
1086+
content_index: contentIndex,
1087+
part: { type: 'output_text', text: typeof text === 'string' ? text : '', annotations: [], logprobs: [] },
1088+
sequence_number: nextSeq()
1089+
});
1090+
}
1091+
1092+
function emitResponsesToolArgumentEvents(res, item, outputIndex, nextSeq) {
1093+
const eventType = item.type === 'custom_tool_call'
1094+
? 'response.custom_tool_call_input.delta'
1095+
: 'response.function_call_arguments.delta';
1096+
const doneType = item.type === 'custom_tool_call'
1097+
? ''
1098+
: 'response.function_call_arguments.done';
1099+
const value = item.type === 'custom_tool_call'
1100+
? (typeof item.input === 'string' ? item.input : '')
1101+
: (typeof item.arguments === 'string' ? item.arguments : '');
1102+
if (value) {
1103+
writeSse(res, eventType, {
1104+
type: eventType,
1105+
item_id: item.id,
1106+
output_index: outputIndex,
1107+
call_id: item.call_id,
1108+
name: item.name,
1109+
delta: value,
1110+
sequence_number: nextSeq()
1111+
});
1112+
}
1113+
if (doneType) {
1114+
writeSse(res, doneType, {
1115+
type: doneType,
1116+
item_id: item.id,
1117+
output_index: outputIndex,
1118+
call_id: item.call_id,
1119+
name: item.name,
1120+
arguments: value,
1121+
sequence_number: nextSeq()
1122+
});
1123+
}
1124+
}
1125+
10041126
function sendResponsesSse(res, responsePayload) {
10051127
const response = ensureResponseMetadata(responsePayload);
10061128
const responseId = response.id;
@@ -1027,12 +1149,14 @@ function createBuiltinProxyRuntimeController(deps = {}) {
10271149
const itemType = typeof item.type === 'string' ? item.type : '';
10281150
const itemId = typeof item.id === 'string' && item.id.trim()
10291151
? item.id.trim()
1030-
: `item_${crypto.randomBytes(8).toString('hex')}`;
1152+
: (typeof item.call_id === 'string' && item.call_id.trim() ? item.call_id.trim() : `item_${crypto.randomBytes(8).toString('hex')}`);
1153+
const wireItem = buildResponsesSseItem(item, itemId);
10311154

10321155
writeSse(res, 'response.output_item.added', {
10331156
type: 'response.output_item.added',
10341157
output_index: outputIndex,
1035-
item: { ...item, id: itemId }
1158+
item: wireItem,
1159+
sequence_number: nextSeq()
10361160
});
10371161

10381162
if (itemType === 'message') {
@@ -1042,6 +1166,7 @@ function createBuiltinProxyRuntimeController(deps = {}) {
10421166
if (!block || typeof block !== 'object') continue;
10431167
if (block.type !== 'output_text') continue;
10441168
const text = typeof block.text === 'string' ? block.text : '';
1169+
emitResponsesTextPartAdded(res, itemId, outputIndex, contentIndex, nextSeq);
10451170
if (text) {
10461171
writeSse(res, 'response.output_text.delta', {
10471172
type: 'response.output_text.delta',
@@ -1060,13 +1185,40 @@ function createBuiltinProxyRuntimeController(deps = {}) {
10601185
text,
10611186
sequence_number: nextSeq()
10621187
});
1188+
emitResponsesTextPartDone(res, itemId, outputIndex, contentIndex, text, nextSeq);
1189+
}
1190+
} else if (itemType === 'function_call' || itemType === 'custom_tool_call') {
1191+
emitResponsesToolArgumentEvents(res, wireItem, outputIndex, nextSeq);
1192+
} else if (itemType === 'reasoning') {
1193+
const summary = Array.isArray(item.summary) ? item.summary : [];
1194+
for (let summaryIndex = 0; summaryIndex < summary.length; summaryIndex += 1) {
1195+
const block = summary[summaryIndex];
1196+
const text = block && typeof block.text === 'string' ? block.text : '';
1197+
if (text) {
1198+
writeSse(res, 'response.reasoning_summary_text.delta', {
1199+
type: 'response.reasoning_summary_text.delta',
1200+
item_id: itemId,
1201+
output_index: outputIndex,
1202+
summary_index: summaryIndex,
1203+
delta: text,
1204+
sequence_number: nextSeq()
1205+
});
1206+
}
1207+
writeSse(res, 'response.reasoning_summary_text.done', {
1208+
type: 'response.reasoning_summary_text.done',
1209+
item_id: itemId,
1210+
output_index: outputIndex,
1211+
summary_index: summaryIndex,
1212+
text,
1213+
sequence_number: nextSeq()
1214+
});
10631215
}
10641216
}
10651217

10661218
writeSse(res, 'response.output_item.done', {
10671219
type: 'response.output_item.done',
10681220
output_index: outputIndex,
1069-
item: { ...item, id: itemId },
1221+
item: wireItem,
10701222
sequence_number: nextSeq()
10711223
});
10721224
}
@@ -1105,6 +1257,40 @@ function createBuiltinProxyRuntimeController(deps = {}) {
11051257
const delta = choice && choice.delta && typeof choice.delta === 'object' ? choice.delta : null;
11061258
if (!delta) continue;
11071259

1260+
const reasoningSegment = typeof delta.reasoning_content === 'string'
1261+
? delta.reasoning_content
1262+
: (typeof delta.reasoning === 'string'
1263+
? delta.reasoning
1264+
: (typeof delta.reasoning_text === 'string' ? delta.reasoning_text : ''));
1265+
if (reasoningSegment) {
1266+
if (!state.reasoningItem) {
1267+
state.reasoningItem = {
1268+
id: `rs_${crypto.randomBytes(8).toString('hex')}`,
1269+
type: 'reasoning',
1270+
summary: [{ type: 'summary_text', text: '' }]
1271+
};
1272+
state.output.push(state.reasoningItem);
1273+
state.outputStarted = true;
1274+
beginChatStreamResponsesSse(state);
1275+
writeSse(state.res, 'response.output_item.added', {
1276+
type: 'response.output_item.added',
1277+
output_index: state.output.length - 1,
1278+
item: buildResponsesSseItem(state.reasoningItem, state.reasoningItem.id),
1279+
sequence_number: state.nextSeq()
1280+
});
1281+
}
1282+
state.reasoningText += reasoningSegment;
1283+
state.reasoningItem.summary[0].text = state.reasoningText;
1284+
writeSse(state.res, 'response.reasoning_summary_text.delta', {
1285+
type: 'response.reasoning_summary_text.delta',
1286+
item_id: state.reasoningItem.id,
1287+
output_index: state.output.indexOf(state.reasoningItem),
1288+
summary_index: 0,
1289+
delta: reasoningSegment,
1290+
sequence_number: state.nextSeq()
1291+
});
1292+
}
1293+
11081294
if (typeof delta.content === 'string' && delta.content) {
11091295
if (!state.messageItem) {
11101296
state.messageItem = {
@@ -1119,8 +1305,10 @@ function createBuiltinProxyRuntimeController(deps = {}) {
11191305
writeSse(state.res, 'response.output_item.added', {
11201306
type: 'response.output_item.added',
11211307
output_index: state.output.length - 1,
1122-
item: state.messageItem
1308+
item: buildResponsesSseItem(state.messageItem, state.messageItem.id),
1309+
sequence_number: state.nextSeq()
11231310
});
1311+
emitResponsesTextPartAdded(state.res, state.messageItem.id, state.output.length - 1, 0, state.nextSeq);
11241312
}
11251313
state.messageText += delta.content;
11261314
state.messageItem.content[0].text = state.messageText;
@@ -1172,6 +1360,24 @@ function createBuiltinProxyRuntimeController(deps = {}) {
11721360
state.finished = true;
11731361
stopChatStreamHeartbeat(state);
11741362

1363+
if (state.reasoningItem) {
1364+
const outputIndex = state.output.indexOf(state.reasoningItem);
1365+
writeSse(state.res, 'response.reasoning_summary_text.done', {
1366+
type: 'response.reasoning_summary_text.done',
1367+
item_id: state.reasoningItem.id,
1368+
output_index: outputIndex,
1369+
summary_index: 0,
1370+
text: state.reasoningText,
1371+
sequence_number: state.nextSeq()
1372+
});
1373+
writeSse(state.res, 'response.output_item.done', {
1374+
type: 'response.output_item.done',
1375+
output_index: outputIndex,
1376+
item: buildResponsesSseItem(state.reasoningItem, state.reasoningItem.id),
1377+
sequence_number: state.nextSeq()
1378+
});
1379+
}
1380+
11751381
if (state.messageItem) {
11761382
const outputIndex = state.output.indexOf(state.messageItem);
11771383
writeSse(state.res, 'response.output_text.done', {
@@ -1182,10 +1388,11 @@ function createBuiltinProxyRuntimeController(deps = {}) {
11821388
text: state.messageText,
11831389
sequence_number: state.nextSeq()
11841390
});
1391+
emitResponsesTextPartDone(state.res, state.messageItem.id, outputIndex, 0, state.messageText, state.nextSeq);
11851392
writeSse(state.res, 'response.output_item.done', {
11861393
type: 'response.output_item.done',
11871394
output_index: outputIndex,
1188-
item: state.messageItem,
1395+
item: buildResponsesSseItem(state.messageItem, state.messageItem.id),
11891396
sequence_number: state.nextSeq()
11901397
});
11911398
}
@@ -1200,12 +1407,14 @@ function createBuiltinProxyRuntimeController(deps = {}) {
12001407
writeSse(state.res, 'response.output_item.added', {
12011408
type: 'response.output_item.added',
12021409
output_index: outputIndex,
1203-
item
1410+
item: buildResponsesSseItem(item, item.call_id),
1411+
sequence_number: state.nextSeq()
12041412
});
1413+
emitResponsesToolArgumentEvents(state.res, buildResponsesSseItem(item, item.call_id), outputIndex, state.nextSeq);
12051414
writeSse(state.res, 'response.output_item.done', {
12061415
type: 'response.output_item.done',
12071416
output_index: outputIndex,
1208-
item,
1417+
item: buildResponsesSseItem(item, item.call_id),
12091418
sequence_number: state.nextSeq()
12101419
});
12111420
}
@@ -1282,6 +1491,8 @@ function createBuiltinProxyRuntimeController(deps = {}) {
12821491
output: [],
12831492
messageItem: null,
12841493
messageText: '',
1494+
reasoningItem: null,
1495+
reasoningText: '',
12851496
toolCalls: [],
12861497
toolTypesByName: options.toolTypesByName || {},
12871498
finished: false,
@@ -1983,7 +2194,7 @@ function createBuiltinProxyRuntimeController(deps = {}) {
19832194
method: 'POST',
19842195
headers: commonHeaders,
19852196
timeoutMs,
1986-
body: { ...payload, stream: true },
2197+
body: normalizeResponsesPayloadForUpstream(payload, true),
19872198
res,
19882199
model
19892200
});
@@ -2031,7 +2242,7 @@ function createBuiltinProxyRuntimeController(deps = {}) {
20312242
method: 'POST',
20322243
headers: commonHeaders,
20332244
timeoutMs,
2034-
body: { ...payload, stream: false }
2245+
body: normalizeResponsesPayloadForUpstream(payload, false)
20352246
});
20362247

20372248
// 优先走上游 /responses(如果支持)。若上游报错且不是“端点不支持”,则直接透传错误。

0 commit comments

Comments
 (0)