fix: openai tool use convert

This commit is contained in:
Simon Shi 2026-03-07 18:28:06 +08:00
parent ea86844be2
commit d1d7ead694

View file

@ -161,7 +161,7 @@ export class OpenAIConverter extends BaseConverter {
} }
content.push({ content.push({
type: 'tool_result', type: 'tool_result',
tool_use_id: message.tool_call_id, tool_use_id: message.tool_call_id || message.tool_use_id,
content: toolContent content: toolContent
}); });
claudeMessages.push({ role: 'user', content: content }); claudeMessages.push({ role: 'user', content: content });
@ -219,6 +219,26 @@ export class OpenAIConverter extends BaseConverter {
content.push({ type: 'text', text: `[Audio: ${audioUrl}]` }); content.push({ type: 'text', text: `[Audio: ${audioUrl}]` });
} }
break; break;
case 'tool_use':
content.push({
type: 'tool_use',
id: item.id,
name: item.name,
input: typeof item.input === 'string' ? safeParseJSON(item.input) : (item.input || {})
});
break;
case 'tool_result': {
let resultContent = item.content;
if (typeof resultContent === 'object' && resultContent !== null) {
resultContent = JSON.stringify(resultContent);
}
content.push({
type: 'tool_result',
tool_use_id: item.tool_use_id || item.id,
content: resultContent
});
break;
}
} }
}); });
} }
@ -276,13 +296,26 @@ export class OpenAIConverter extends BaseConverter {
} }
if (openaiRequest.tools?.length) { if (openaiRequest.tools?.length) {
claudeRequest.tools = openaiRequest.tools.map(t => ({ claudeRequest.tools = openaiRequest.tools
.filter(t => t && ((t.function && t.function.name) || t.name))
.map(t => {
if (t.function) {
return {
name: t.function.name, name: t.function.name,
description: t.function.description || '', description: t.function.description || '',
input_schema: t.function.parameters || { type: 'object', properties: {} } input_schema: t.function.parameters || { type: 'object', properties: {} }
})); };
}
return {
name: t.name,
description: t.description || '',
input_schema: t.input_schema || { type: 'object', properties: {} }
};
});
if (claudeRequest.tools.length > 0) {
claudeRequest.tool_choice = this.buildClaudeToolChoice(openaiRequest.tool_choice); claudeRequest.tool_choice = this.buildClaudeToolChoice(openaiRequest.tool_choice);
} }
}
// Optional passthrough: request-side "thinking" controls for Claude/Kiro. // Optional passthrough: request-side "thinking" controls for Claude/Kiro.
// OpenAI-compatible clients can provide these via `extra_body.anthropic.thinking`. // OpenAI-compatible clients can provide these via `extra_body.anthropic.thinking`.
@ -625,6 +658,14 @@ export class OpenAIConverter extends BaseConverter {
} }
} }
} }
// Claude 格式content 数组中的 tool_use
if (message.role === 'assistant' && Array.isArray(message.content)) {
for (const item of message.content) {
if (item && item.type === 'tool_use' && item.id && item.name) {
tcID2Name[item.id] = item.name;
}
}
}
} }
// 构建 tool_call_id -> response 映射 // 构建 tool_call_id -> response 映射
@ -633,6 +674,14 @@ export class OpenAIConverter extends BaseConverter {
if (message.role === 'tool' && message.tool_call_id) { if (message.role === 'tool' && message.tool_call_id) {
toolResponses[message.tool_call_id] = message.content; toolResponses[message.tool_call_id] = message.content;
} }
// Claude 格式user content 数组中的 tool_result
if (message.role === 'user' && Array.isArray(message.content)) {
for (const item of message.content) {
if (item && item.type === 'tool_result' && item.tool_use_id) {
toolResponses[item.tool_use_id] = item.content;
}
}
}
} }
const processedMessages = []; const processedMessages = [];
@ -790,6 +839,7 @@ export class OpenAIConverter extends BaseConverter {
const node = { role: 'model', parts: [] }; const node = { role: 'model', parts: [] };
// 处理文本内容 // 处理文本内容
const functionCallIds = [];
if (typeof content === 'string' && content) { if (typeof content === 'string' && content) {
node.parts.push({ text: content }); node.parts.push({ text: content });
} else if (Array.isArray(content)) { } else if (Array.isArray(content)) {
@ -797,6 +847,19 @@ export class OpenAIConverter extends BaseConverter {
if (!item) continue; if (!item) continue;
if (item.type === 'text' && item.text) { if (item.type === 'text' && item.text) {
node.parts.push({ text: item.text }); node.parts.push({ text: item.text });
} else if (item.type === 'tool_use') {
// Claude 格式 tool_use -> Gemini functionCall
const fid = item.id || '';
const fname = item.name || '';
const argsObj = typeof item.input === 'string' ? (() => { try { return JSON.parse(item.input); } catch(e) { return {}; } })() : (item.input || {});
node.parts.push({
functionCall: {
name: fname,
args: argsObj
},
thoughtSignature: OpenAIConverter.GEMINI_OPENAI_THOUGHT_SIGNATURE
});
if (fid) functionCallIds.push(fid);
} else if (item.type === 'image_url' && item.image_url) { } else if (item.type === 'image_url' && item.image_url) {
const imageUrl = typeof item.image_url === 'string' const imageUrl = typeof item.image_url === 'string'
? item.image_url ? item.image_url
@ -823,9 +886,8 @@ export class OpenAIConverter extends BaseConverter {
} }
} }
// 处理 tool_calls -> functionCall // 处理 OpenAI 格式 tool_calls -> functionCall
if (message.tool_calls && Array.isArray(message.tool_calls)) { if (message.tool_calls && Array.isArray(message.tool_calls)) {
const functionCallIds = [];
for (const tc of message.tool_calls) { for (const tc of message.tool_calls) {
if (tc.type !== 'function') continue; if (tc.type !== 'function') continue;
const fid = tc.id || ''; const fid = tc.id || '';
@ -851,6 +913,7 @@ export class OpenAIConverter extends BaseConverter {
functionCallIds.push(fid); functionCallIds.push(fid);
} }
} }
}
// 添加 model 消息 // 添加 model 消息
if (node.parts.length > 0) { if (node.parts.length > 0) {
@ -864,7 +927,6 @@ export class OpenAIConverter extends BaseConverter {
const name = tcID2Name[fid]; const name = tcID2Name[fid];
if (name) { if (name) {
let resp = toolResponses[fid] || '{}'; let resp = toolResponses[fid] || '{}';
// 确保 resp 是字符串
if (typeof resp !== 'string') { if (typeof resp !== 'string') {
resp = JSON.stringify(resp); resp = JSON.stringify(resp);
} }
@ -882,12 +944,6 @@ export class OpenAIConverter extends BaseConverter {
processedMessages.push(toolNode); processedMessages.push(toolNode);
} }
} }
} else {
// 没有 tool_calls直接添加
if (node.parts.length > 0) {
processedMessages.push(node);
}
}
} }
// tool 消息已经在 assistant 的 tool_calls 处理中合并了,这里跳过 // tool 消息已经在 assistant 的 tool_calls 处理中合并了,这里跳过
} }
@ -1025,6 +1081,12 @@ export class OpenAIConverter extends BaseConverter {
} }
functionDeclarations.push(fnDecl); functionDeclarations.push(fnDecl);
} else if (t.name) {
functionDeclarations.push({
name: String(t.name),
description: String(t.description || ''),
parametersJsonSchema: cleanJsonSchema(t.input_schema || { type: 'object', properties: {} })
});
} }
// 处理 google_search 工具 // 处理 google_search 工具