fix: openai tool use convert
This commit is contained in:
parent
ea86844be2
commit
d1d7ead694
1 changed files with 108 additions and 46 deletions
|
|
@ -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,12 +296,25 @@ export class OpenAIConverter extends BaseConverter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (openaiRequest.tools?.length) {
|
if (openaiRequest.tools?.length) {
|
||||||
claudeRequest.tools = openaiRequest.tools.map(t => ({
|
claudeRequest.tools = openaiRequest.tools
|
||||||
name: t.function.name,
|
.filter(t => t && ((t.function && t.function.name) || t.name))
|
||||||
description: t.function.description || '',
|
.map(t => {
|
||||||
input_schema: t.function.parameters || { type: 'object', properties: {} }
|
if (t.function) {
|
||||||
}));
|
return {
|
||||||
claudeRequest.tool_choice = this.buildClaudeToolChoice(openaiRequest.tool_choice);
|
name: t.function.name,
|
||||||
|
description: t.function.description || '',
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional passthrough: request-side "thinking" controls for Claude/Kiro.
|
// Optional passthrough: request-side "thinking" controls for Claude/Kiro.
|
||||||
|
|
@ -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,41 +913,35 @@ export class OpenAIConverter extends BaseConverter {
|
||||||
functionCallIds.push(fid);
|
functionCallIds.push(fid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 添加 model 消息
|
// 添加 model 消息
|
||||||
if (node.parts.length > 0) {
|
if (node.parts.length > 0) {
|
||||||
processedMessages.push(node);
|
processedMessages.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加对应的 functionResponse(作为 user 消息)
|
// 添加对应的 functionResponse(作为 user 消息)
|
||||||
if (functionCallIds.length > 0) {
|
if (functionCallIds.length > 0) {
|
||||||
const toolNode = { role: 'user', parts: [] };
|
const toolNode = { role: 'user', parts: [] };
|
||||||
for (const fid of functionCallIds) {
|
for (const fid of functionCallIds) {
|
||||||
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);
|
|
||||||
}
|
|
||||||
toolNode.parts.push({
|
|
||||||
functionResponse: {
|
|
||||||
name: name,
|
|
||||||
response: {
|
|
||||||
result: resp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
toolNode.parts.push({
|
||||||
if (toolNode.parts.length > 0) {
|
functionResponse: {
|
||||||
processedMessages.push(toolNode);
|
name: name,
|
||||||
|
response: {
|
||||||
|
result: resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
if (toolNode.parts.length > 0) {
|
||||||
// 没有 tool_calls,直接添加
|
processedMessages.push(toolNode);
|
||||||
if (node.parts.length > 0) {
|
|
||||||
processedMessages.push(node);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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 工具
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue