route-claudecode
Version:
Advanced routing and transformation system for Claude Code outputs to multiple AI providers
357 lines • 13.8 kB
JavaScript
;
/**
* 工具调用解析失败日志记录器
* 将工具调用解析失败的详细信息记录到数据库
* Project Owner: Jason Zhang
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.toolParsingFailureLogger = exports.ToolParsingFailureLogger = void 0;
exports.getToolParsingFailureLogger = getToolParsingFailureLogger;
const logger_1 = require("./logger");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
class ToolParsingFailureLogger {
dbPath;
failures = [];
maxRecords = 1000; // 最多保存1000条记录
constructor(dbPath) {
this.dbPath = dbPath || path.join(process.cwd(), 'database', 'tool-parsing-failures.json');
this.loadExistingFailures();
}
/**
* 记录工具调用解析失败
*/
async logFailure(requestId, provider, model, endpoint, originalResponse, errorDetails) {
const failure = {
timestamp: new Date().toISOString(),
requestId,
provider,
model,
endpoint,
failureType: errorDetails.failureType,
originalResponse: this.sanitizeResponse(originalResponse),
expectedFormat: errorDetails.expectedFormat,
errorMessage: errorDetails.errorMessage,
responseAnalysis: this.analyzeResponse(originalResponse),
debugInfo: {
requestTools: errorDetails.requestTools || [],
responseStructure: this.analyzeStructure(originalResponse),
parsingAttempts: errorDetails.parsingAttempts || []
}
};
// 添加到内存中的失败记录
this.failures.unshift(failure); // 最新的在前面
// 保持记录数量限制
if (this.failures.length > this.maxRecords) {
this.failures = this.failures.slice(0, this.maxRecords);
}
// 持久化到文件
await this.saveFailures();
// 记录到系统日志
logger_1.logger.error('🚨 [TOOL-PARSING] Tool call parsing failed', {
requestId,
provider,
model,
failureType: errorDetails.failureType,
errorMessage: errorDetails.errorMessage,
responseAnalysis: failure.responseAnalysis,
dbPath: this.dbPath
}, requestId, 'tool-parsing-failure');
// 如果是特定类型的失败,发出警告
if (errorDetails.failureType === 'text_instead_of_tools') {
logger_1.logger.warn('🔧 [TOOL-PARSING] Provider returned tool calls as text - preprocessor needed', {
provider,
model,
contentPreview: failure.responseAnalysis.contentPreview,
requestId
});
}
}
/**
* 分析响应内容
*/
analyzeResponse(response) {
const analysis = {
hasToolCalls: false,
hasContent: false,
contentLength: 0,
contentPreview: '',
finishReason: 'unknown'
};
// 检查OpenAI格式
if (response?.choices?.[0]) {
const choice = response.choices[0];
analysis.finishReason = choice.finish_reason || 'unknown';
if (choice.message) {
analysis.hasToolCalls = !!(choice.message.tool_calls && choice.message.tool_calls.length > 0);
analysis.hasContent = !!choice.message.content;
analysis.contentLength = choice.message.content?.length || 0;
analysis.contentPreview = choice.message.content?.substring(0, 200) || '';
// 检查文本内容中是否包含工具调用模式
if (analysis.hasContent && !analysis.hasToolCalls) {
analysis.toolCallPatterns = this.detectToolCallPatterns(choice.message.content);
}
}
}
// 检查Anthropic格式
else if (response?.content) {
analysis.finishReason = response.stop_reason || 'unknown';
analysis.hasContent = Array.isArray(response.content) && response.content.length > 0;
if (analysis.hasContent) {
const textContent = response.content
.filter((block) => block.type === 'text')
.map((block) => block.text)
.join(' ');
analysis.contentLength = textContent.length;
analysis.contentPreview = textContent.substring(0, 200);
analysis.hasToolCalls = response.content.some((block) => block.type === 'tool_use');
if (analysis.hasContent && !analysis.hasToolCalls) {
analysis.toolCallPatterns = this.detectToolCallPatterns(textContent);
}
}
}
return analysis;
}
/**
* 检测文本内容中的工具调用模式
*/
detectToolCallPatterns(content) {
const patterns = [
{ name: 'function_call', regex: /function_call\s*\(/gi },
{ name: 'tool_call', regex: /tool_call\s*\(/gi },
{ name: 'json_function', regex: /\{\s*"name"\s*:\s*"[\w_]+"/gi },
{ name: 'search_call', regex: /search\s*\(/gi },
{ name: 'generate_call', regex: /generate\s*\(/gi },
{ name: 'action_format', regex: /Action:\s*[\w_]+/gi },
{ name: 'tool_use_block', regex: /<tool_use>/gi }
];
const foundPatterns = [];
patterns.forEach(pattern => {
if (pattern.regex.test(content)) {
foundPatterns.push(pattern.name);
}
});
return foundPatterns;
}
/**
* 分析响应结构
*/
analyzeStructure(response) {
if (!response || typeof response !== 'object') {
return { type: typeof response, structure: 'primitive' };
}
const structure = {
type: 'object',
keys: Object.keys(response),
hasChoices: !!response.choices,
hasContent: !!response.content,
hasUsage: !!response.usage
};
if (response.choices && Array.isArray(response.choices)) {
structure.choicesCount = response.choices.length;
if (response.choices[0]) {
structure.firstChoice = {
hasMessage: !!response.choices[0].message,
hasToolCalls: !!(response.choices[0].message?.tool_calls?.length),
finishReason: response.choices[0].finish_reason
};
}
}
return structure;
}
/**
* 清理响应内容(移除敏感信息)
*/
sanitizeResponse(response) {
if (!response)
return response;
// 创建深拷贝
const sanitized = JSON.parse(JSON.stringify(response));
// 限制内容长度以节省存储空间
if (sanitized?.choices?.[0]?.message?.content) {
const content = sanitized.choices[0].message.content;
if (content.length > 1000) {
sanitized.choices[0].message.content = content.substring(0, 1000) + '... [truncated]';
}
}
return sanitized;
}
/**
* 加载现有的失败记录
*/
loadExistingFailures() {
try {
if (fs.existsSync(this.dbPath)) {
const data = fs.readFileSync(this.dbPath, 'utf8');
this.failures = JSON.parse(data);
// 🔧 安全调用logger:如果logger未初始化则使用console
this.safeLog('debug', 'Loaded existing tool parsing failure records', {
recordCount: this.failures.length,
dbPath: this.dbPath
});
}
}
catch (error) {
// 🔧 安全调用logger:如果logger未初始化则使用console
this.safeLog('error', 'Failed to load tool parsing failure records', error);
this.failures = [];
}
}
/**
* 安全的日志记录 - 如果logger未初始化则降级使用console
*/
safeLog(level, message, data) {
try {
if (level === 'debug') {
logger_1.logger.debug(message, data);
}
else {
logger_1.logger.error(message, data);
}
}
catch {
// Logger未初始化,降级使用console
if (level === 'debug') {
console.log(`[DEBUG] ${message}`, data ? JSON.stringify(data, null, 2) : '');
}
else {
console.error(`[ERROR] ${message}`, data);
}
}
}
/**
* 保存失败记录到文件
*/
async saveFailures() {
try {
// 确保目录存在
const dbDir = path.dirname(this.dbPath);
if (!fs.existsSync(dbDir)) {
fs.mkdirSync(dbDir, { recursive: true });
}
// 保存到文件
fs.writeFileSync(this.dbPath, JSON.stringify(this.failures, null, 2), 'utf8');
// 🔧 安全调用logger
this.safeLog('debug', 'Saved tool parsing failure records', {
recordCount: this.failures.length,
dbPath: this.dbPath
});
}
catch (error) {
// 🔧 安全调用logger
this.safeLog('error', 'Failed to save tool parsing failure records', error);
}
}
/**
* 获取失败统计信息
*/
getStats() {
const now = new Date();
const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
const recentFailures = this.failures.filter(f => new Date(f.timestamp) > oneDayAgo);
const failuresByType = {};
const failuresByProvider = {};
const modelCounts = {};
this.failures.forEach(failure => {
failuresByType[failure.failureType] = (failuresByType[failure.failureType] || 0) + 1;
failuresByProvider[failure.provider] = (failuresByProvider[failure.provider] || 0) + 1;
modelCounts[failure.model] = (modelCounts[failure.model] || 0) + 1;
});
const topFailedModels = Object.entries(modelCounts)
.map(([model, count]) => ({ model, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 10);
return {
totalFailures: this.failures.length,
recentFailures: recentFailures.length,
failuresByType,
failuresByProvider,
topFailedModels
};
}
/**
* 获取特定provider的失败记录
*/
getFailuresForProvider(provider, limit = 50) {
return this.failures
.filter(f => f.provider === provider)
.slice(0, limit);
}
/**
* 获取最近的失败记录
*/
getRecentFailures(hours = 24, limit = 100) {
const cutoff = new Date(Date.now() - hours * 60 * 60 * 1000);
return this.failures
.filter(f => new Date(f.timestamp) > cutoff)
.slice(0, limit);
}
/**
* 清理旧记录
*/
cleanup(olderThanDays = 30) {
const cutoff = new Date(Date.now() - olderThanDays * 24 * 60 * 60 * 1000);
const originalCount = this.failures.length;
this.failures = this.failures.filter(f => new Date(f.timestamp) > cutoff);
if (this.failures.length !== originalCount) {
this.saveFailures();
logger_1.logger.info('Cleaned up old tool parsing failure records', {
removed: originalCount - this.failures.length,
remaining: this.failures.length
});
}
}
}
exports.ToolParsingFailureLogger = ToolParsingFailureLogger;
// 🔧 修复过早初始化:延迟初始化全局单例
let _globalInstance = null;
/**
* 获取全局单例实例 - 延迟初始化避免过早调用logger
*/
function getToolParsingFailureLogger() {
if (!_globalInstance) {
_globalInstance = new ToolParsingFailureLogger();
}
return _globalInstance;
}
// 兼容旧的导出方式
exports.toolParsingFailureLogger = {
get instance() {
return getToolParsingFailureLogger();
}
};
exports.default = ToolParsingFailureLogger;
//# sourceMappingURL=tool-parsing-failure-logger.js.map