fix: /v1/responses endpoint drops tool calls (function_call) in non-streaming mode
Also fixes streaming tool call event type and response status. Non-streaming: - ClaudeConverter.toOpenAIResponsesResponse now handles tool_use content blocks, emitting function_call output items - OpenAIConverter.toOpenAIResponsesResponse now handles message.tool_calls, emitting function_call output items - Both set status to 'requires_action' when tool calls are present Streaming: - OpenAIConverter uses response.function_call_arguments.delta instead of response.custom_tool_call_input.delta - generateResponseCompleted sets status to 'requires_action' when tool calls are tracked in stream state Fixes #471
This commit is contained in:
parent
82a6ec2f43
commit
e443d9d891
3 changed files with 52 additions and 14 deletions
|
|
@ -1648,22 +1648,42 @@ export class ClaudeConverter extends BaseConverter {
|
||||||
* Claude响应 -> OpenAI Responses响应
|
* Claude响应 -> OpenAI Responses响应
|
||||||
*/
|
*/
|
||||||
toOpenAIResponsesResponse(claudeResponse, model) {
|
toOpenAIResponsesResponse(claudeResponse, model) {
|
||||||
const content = this.processClaudeResponseContent(claudeResponse.content);
|
const output = [];
|
||||||
const textContent = typeof content === 'string' ? content : JSON.stringify(content);
|
const messageContent = [];
|
||||||
|
let hasToolUse = false;
|
||||||
|
|
||||||
let output = [];
|
// Process Claude content blocks, handling both text and tool_use
|
||||||
output.push({
|
if (Array.isArray(claudeResponse.content)) {
|
||||||
|
for (const block of claudeResponse.content) {
|
||||||
|
if (block.type === 'text' && block.text) {
|
||||||
|
messageContent.push({
|
||||||
|
annotations: [],
|
||||||
|
logprobs: [],
|
||||||
|
text: block.text,
|
||||||
|
type: "output_text"
|
||||||
|
});
|
||||||
|
} else if (block.type === 'tool_use') {
|
||||||
|
hasToolUse = true;
|
||||||
|
output.push({
|
||||||
|
type: "function_call",
|
||||||
|
id: block.id || `fc_${uuidv4().replace(/-/g, '')}`,
|
||||||
|
call_id: block.id || `call_${uuidv4().replace(/-/g, '')}`,
|
||||||
|
name: block.name,
|
||||||
|
arguments: typeof block.input === 'string' ? block.input : JSON.stringify(block.input || {}),
|
||||||
|
status: "completed"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always include the message output item (even if content is empty)
|
||||||
|
output.unshift({
|
||||||
type: "message",
|
type: "message",
|
||||||
id: `msg_${uuidv4().replace(/-/g, '')}`,
|
id: `msg_${uuidv4().replace(/-/g, '')}`,
|
||||||
summary: [],
|
summary: [],
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
status: "completed",
|
status: "completed",
|
||||||
content: [{
|
content: messageContent
|
||||||
annotations: [],
|
|
||||||
logprobs: [],
|
|
||||||
text: textContent,
|
|
||||||
type: "output_text"
|
|
||||||
}]
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -1684,7 +1704,7 @@ export class ClaudeConverter extends BaseConverter {
|
||||||
reasoning: {},
|
reasoning: {},
|
||||||
safety_identifier: "user-" + uuidv4().replace(/-/g, ''),
|
safety_identifier: "user-" + uuidv4().replace(/-/g, ''),
|
||||||
service_tier: "default",
|
service_tier: "default",
|
||||||
status: "completed",
|
status: hasToolUse ? "requires_action" : "completed",
|
||||||
store: false,
|
store: false,
|
||||||
temperature: 1,
|
temperature: 1,
|
||||||
text: {
|
text: {
|
||||||
|
|
|
||||||
|
|
@ -1642,11 +1642,29 @@ export class OpenAIConverter extends BaseConverter {
|
||||||
content: messageContent
|
content: messageContent
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle tool calls (function_call output items)
|
||||||
|
if (message.tool_calls && message.tool_calls.length > 0) {
|
||||||
|
for (const tc of message.tool_calls) {
|
||||||
|
if (tc.type === 'function' && tc.function) {
|
||||||
|
output.push({
|
||||||
|
type: 'function_call',
|
||||||
|
id: tc.id || `fc_${Date.now()}`,
|
||||||
|
call_id: tc.id || `call_${Date.now()}`,
|
||||||
|
name: tc.function.name,
|
||||||
|
arguments: tc.function.arguments || '{}',
|
||||||
|
status: 'completed'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasToolCalls = message.tool_calls && message.tool_calls.length > 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: openaiResponse.id || `resp_${Date.now()}`,
|
id: openaiResponse.id || `resp_${Date.now()}`,
|
||||||
object: 'response',
|
object: 'response',
|
||||||
created_at: openaiResponse.created || Math.floor(Date.now() / 1000),
|
created_at: openaiResponse.created || Math.floor(Date.now() / 1000),
|
||||||
status: choice.finish_reason === 'stop' ? 'completed' : 'in_progress',
|
status: hasToolCalls ? 'requires_action' : (choice.finish_reason === 'stop' ? 'completed' : 'in_progress'),
|
||||||
model: model || openaiResponse.model || 'unknown',
|
model: model || openaiResponse.model || 'unknown',
|
||||||
output: output,
|
output: output,
|
||||||
usage: openaiResponse.usage ? {
|
usage: openaiResponse.usage ? {
|
||||||
|
|
@ -1736,7 +1754,7 @@ export class OpenAIConverter extends BaseConverter {
|
||||||
item_id: toolCall.id || `call_${uuidv4().replace(/-/g, '')}`,
|
item_id: toolCall.id || `call_${uuidv4().replace(/-/g, '')}`,
|
||||||
output_index: outputIndex,
|
output_index: outputIndex,
|
||||||
sequence_number: 3,
|
sequence_number: 3,
|
||||||
type: "response.custom_tool_call_input.delta"
|
type: "response.function_call_arguments.delta"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -304,7 +304,7 @@ function generateResponseCompleted(requestId, usage) {
|
||||||
},
|
},
|
||||||
safety_identifier: `user-${uuidv4().replace(/-/g, '')}`, // 随机值
|
safety_identifier: `user-${uuidv4().replace(/-/g, '')}`, // 随机值
|
||||||
service_tier: "default",
|
service_tier: "default",
|
||||||
status: "completed",
|
status: (state.toolCalls && state.toolCalls.length > 0) ? "requires_action" : "completed",
|
||||||
store: false,
|
store: false,
|
||||||
temperature: 1,
|
temperature: 1,
|
||||||
text: {
|
text: {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue