1
0
forked from boos/BoosAPI

Fix orphaned responses tool outputs in messages fallback (#342)

This commit is contained in:
rcocco
2026-03-31 14:56:54 +08:00
committed by GitHub
parent 6e04db61dd
commit e8921e63ce
2 changed files with 56 additions and 2 deletions
@@ -863,6 +863,51 @@ describe('convertOpenAiBodyToResponsesBody', () => {
]); ]);
}); });
it('drops tool outputs whose prior tool call cannot be converted into an OpenAI-compatible tool call', () => {
const result = convertResponsesBodyToOpenAiBody(
{
model: 'gpt-5',
input: [
{
type: 'function_call',
call_id: 'call_invalid',
name: ' ',
arguments: '{"plan":true}',
},
{
type: 'function_call_output',
call_id: 'call_invalid',
output: 'unsupported call',
},
{
type: 'message',
role: 'assistant',
content: [
{
type: 'output_text',
text: 'continue normally',
},
],
},
],
},
'gpt-5',
false,
);
expect(result.messages).toEqual([
{
role: 'assistant',
content: [
{
type: 'text',
text: 'continue normally',
},
],
},
]);
});
it('shortens long MCP tool names consistently across tools, tool_choice and assistant tool calls', () => { it('shortens long MCP tool names consistently across tools, tool_choice and assistant tool calls', () => {
const sharedSuffix = 'server__execute_super_long_nested_tool_name_that_needs_shortening'; const sharedSuffix = 'server__execute_super_long_nested_tool_name_that_needs_shortening';
const firstName = `mcp__alpha_workspace__${sharedSuffix}`; const firstName = `mcp__alpha_workspace__${sharedSuffix}`;
@@ -844,9 +844,14 @@ export function convertResponsesBodyToOpenAiBody(
const input = stripOrphanedResponsesToolOutputs(normalizedBody.input); const input = stripOrphanedResponsesToolOutputs(normalizedBody.input);
let functionCallIndex = 0; let functionCallIndex = 0;
let pendingToolCalls: OpenAiToolCall[] = []; let pendingToolCalls: OpenAiToolCall[] = [];
const emittedToolCallIds = new Set<string>();
const flushPendingToolCalls = () => { const flushPendingToolCalls = () => {
if (pendingToolCalls.length <= 0) return; if (pendingToolCalls.length <= 0) return;
for (const toolCall of pendingToolCalls) {
const callId = asTrimmedString(toolCall.id);
if (callId) emittedToolCallIds.add(callId);
}
messages.push({ messages.push({
role: 'assistant', role: 'assistant',
content: '', content: '',
@@ -894,7 +899,9 @@ export function convertResponsesBodyToOpenAiBody(
if (itemType === 'function_call_output' || itemType === 'custom_tool_call_output') { if (itemType === 'function_call_output' || itemType === 'custom_tool_call_output') {
flushPendingToolCalls(); flushPendingToolCalls();
pushToolOutputMessage(item.call_id ?? item.id, item.output ?? item.content); const toolCallId = asTrimmedString(item.call_id ?? item.id);
if (!toolCallId || !emittedToolCallIds.has(toolCallId)) return;
pushToolOutputMessage(toolCallId, item.output ?? item.content);
return; return;
} }
@@ -924,7 +931,9 @@ export function convertResponsesBodyToOpenAiBody(
const content = toOpenAiMessageContent(item.content ?? item.input ?? item); const content = toOpenAiMessageContent(item.content ?? item.input ?? item);
if (normalizedRole === 'tool') { if (normalizedRole === 'tool') {
pushToolOutputMessage(item.tool_call_id ?? item.call_id ?? item.id, item.content); const toolCallId = asTrimmedString(item.tool_call_id ?? item.call_id ?? item.id);
if (!toolCallId || !emittedToolCallIds.has(toolCallId)) return;
pushToolOutputMessage(toolCallId, item.content);
return; return;
} }