route-claudecode
Version:
Advanced routing and transformation system for Claude Code outputs to multiple AI providers
141 lines • 5.03 kB
JavaScript
;
/**
* Tool Call Finish Reason Fixer
* 统一修复所有工具调用相关的finish reason问题
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.hasToolCalls = hasToolCalls;
exports.hasToolCallInText = hasToolCallInText;
exports.fixToolCallFinishReason = fixToolCallFinishReason;
exports.countToolCalls = countToolCalls;
exports.generateToolCallDebugInfo = generateToolCallDebugInfo;
/**
* 检查响应是否包含工具调用
*/
function hasToolCalls(data) {
// 检查Anthropic格式的tool_use
if (data?.content && Array.isArray(data.content)) {
const hasToolUse = data.content.some((block) => block.type === 'tool_use');
if (hasToolUse)
return true;
}
// 检查OpenAI格式的tool_calls
if (data?.choices && Array.isArray(data.choices)) {
const hasOpenAIToolCalls = data.choices.some((choice) => choice.message?.tool_calls && choice.message.tool_calls.length > 0);
if (hasOpenAIToolCalls)
return true;
}
// 检查直接的tool_calls字段
if (data?.tool_calls && Array.isArray(data.tool_calls) && data.tool_calls.length > 0) {
return true;
}
return false;
}
/**
* 检查文本内容是否包含工具调用模式
*/
function hasToolCallInText(data) {
if (!data?.content && !data?.choices)
return false;
// 检查Anthropic格式的文本内容
if (data.content && Array.isArray(data.content)) {
const hasToolInText = data.content.some((block) => {
if (block.type !== 'text' || !block.text)
return false;
// 检查各种工具调用模式
const patterns = [
/Tool\s+call:\s*\w+\s*\(/i, // GLM-4.5格式: Tool call: FunctionName(
/\{\s*"type"\s*:\s*"tool_use"\s*,/i, // Anthropic JSON格式
/\{\s*"id"\s*:\s*"toolu_[^"]+"\s*,/i, // Anthropic ID格式
/"name"\s*:\s*"[^"]+"\s*,\s*"input"\s*:\s*\{/i, // Anthropic工具格式
/\w+\s*\(\s*\{\s*"[^"]+"\s*:\s*"[^"]*"/i // 直接调用格式
];
return patterns.some(pattern => pattern.test(block.text));
});
if (hasToolInText)
return true;
}
// 检查OpenAI格式的文本内容
if (data.choices && Array.isArray(data.choices)) {
const hasToolInChoices = data.choices.some((choice) => {
const content = choice.message?.content;
if (typeof content !== 'string')
return false;
// 使用相同的模式检查
const patterns = [
/Tool\s+call:\s*\w+\s*\(/i,
/"function"\s*:\s*\{\s*"name"/i, // OpenAI函数调用
/"tool_calls"\s*:\s*\[/i // OpenAI工具调用数组
];
return patterns.some(pattern => pattern.test(content));
});
if (hasToolInChoices)
return true;
}
return false;
}
/**
* 修复finish reason为正确的工具调用状态
* @param data 响应数据
* @param targetFormat 目标格式 ('anthropic' | 'openai')
* @returns 更新后的数据
*/
function fixToolCallFinishReason(data, targetFormat = 'anthropic') {
if (!data)
return data;
const result = { ...data };
const hasTools = hasToolCalls(result) || hasToolCallInText(result);
if (!hasTools) {
return result;
}
// 根据目标格式设置正确的finish reason
if (targetFormat === 'anthropic') {
result.stop_reason = 'tool_use';
}
else {
// OpenAI格式处理
if (result.choices && Array.isArray(result.choices)) {
result.choices = result.choices.map((choice) => ({
...choice,
finish_reason: 'tool_calls'
}));
}
if (result.finish_reason !== undefined) {
result.finish_reason = 'tool_calls';
}
}
return result;
}
/**
* 获取工具调用数量
*/
function countToolCalls(data) {
let count = 0;
// 计算Anthropic格式的tool_use
if (data?.content && Array.isArray(data.content)) {
count += data.content.filter((block) => block.type === 'tool_use').length;
}
// 计算OpenAI格式的tool_calls
if (data?.choices && Array.isArray(data.choices)) {
data.choices.forEach((choice) => {
if (choice.message?.tool_calls) {
count += choice.message.tool_calls.length;
}
});
}
// 计算直接的tool_calls
if (data?.tool_calls && Array.isArray(data.tool_calls)) {
count += data.tool_calls.length;
}
return count;
}
/**
* 生成调试信息
*/
function generateToolCallDebugInfo(data) {
const toolCount = countToolCalls(data);
const hasDirectTools = hasToolCalls(data);
const hasTextTools = hasToolCallInText(data);
return `Tools: ${toolCount}, Direct: ${hasDirectTools}, Text: ${hasTextTools}`;
}
//# sourceMappingURL=tool-call-finish-reason-fixer.js.map