route-claudecode
Version:
Advanced routing and transformation system for Claude Code outputs to multiple AI providers
192 lines • 7.42 kB
JavaScript
;
/**
* Finish Reason一致性验证器
* 在输出前进行最终的finish reason与工具调用一致性检查和修复
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.FinishReasonConsistencyValidator = void 0;
exports.getConsistencyValidator = getConsistencyValidator;
exports.createConsistencyValidator = createConsistencyValidator;
const logging_1 = require("../../logging");
class FinishReasonConsistencyValidator {
logger;
constructor(port) {
this.logger = (0, logging_1.getLogger)(port);
}
/**
* 🎯 验证并修复stop_reason一致性
* 如果有工具调用,不能是end_turn,必须是tool_use
*/
validateAndFix(response, requestId) {
const startTime = Date.now();
const originalStopReason = response.stop_reason || 'unknown';
const issues = [];
// 1. 统计工具调用
const toolCount = this.countToolUseBlocks(response);
const hasTools = toolCount > 0;
// 2. 检查一致性
let consistent = true;
let fixed = false;
if (hasTools && (response.stop_reason !== 'tool_use')) {
consistent = false;
issues.push(`Found ${toolCount} tool(s) but stop_reason is '${response.stop_reason}', should be 'tool_use'`);
// 修复:设置为tool_use
response.stop_reason = 'tool_use';
fixed = true;
this.logger.warn('🔧 [CONSISTENCY] Fixed inconsistent stop_reason', {
originalStopReason,
fixedStopReason: 'tool_use',
toolCount,
requestId,
duration: `${Date.now() - startTime}ms`
}, requestId, 'consistency-validator');
}
if (!hasTools && (response.stop_reason === 'tool_use')) {
consistent = false;
issues.push(`No tools found but stop_reason is 'tool_use', should be 'end_turn'`);
// 修复:设置为end_turn
response.stop_reason = 'end_turn';
fixed = true;
this.logger.warn('🔧 [CONSISTENCY] Fixed unnecessary tool_use stop_reason', {
originalStopReason,
fixedStopReason: 'end_turn',
toolCount: 0,
requestId,
duration: `${Date.now() - startTime}ms`
}, requestId, 'consistency-validator');
}
// 3. 记录验证结果
const result = {
consistent: consistent && !fixed,
fixed,
originalStopReason,
finalStopReason: response.stop_reason || 'unknown',
toolCount,
issues
};
if (consistent && !fixed) {
this.logger.debug('✅ [CONSISTENCY] Stop reason already consistent', {
stopReason: response.stop_reason,
toolCount,
requestId,
duration: `${Date.now() - startTime}ms`
}, requestId, 'consistency-validator');
}
return { response, result };
}
/**
* 统计tool_use块的数量
*/
countToolUseBlocks(response) {
if (!response.content || !Array.isArray(response.content)) {
this.logger.debug('🔍 [TOOL-COUNT] No content array found', {
hasContent: !!response.content,
isArray: Array.isArray(response.content)
});
return 0;
}
const toolBlocks = response.content.filter(block => block.type === 'tool_use');
const textBlocks = response.content.filter(block => block.type === 'text');
this.logger.debug('🔍 [TOOL-COUNT] Content analysis', {
totalBlocks: response.content.length,
toolBlocks: toolBlocks.length,
textBlocks: textBlocks.length,
blockTypes: response.content.map(b => b.type),
textContent: textBlocks.map(b => b.text?.substring(0, 50) + '...'),
toolNames: toolBlocks.map(b => b.name)
});
// 检查文本块中是否包含未转换的工具调用
const textWithToolCalls = textBlocks.filter(block => block.text && block.text.includes('Tool call:'));
if (textWithToolCalls.length > 0) {
this.logger.warn('🚨 [TOOL-COUNT] Found unconverted tool calls in text blocks', {
unconvertedCount: textWithToolCalls.length,
samples: textWithToolCalls.map(b => b.text?.substring(0, 100) + '...')
});
}
return toolBlocks.length;
}
/**
* 获取工具调用详情
*/
getToolDetails(response) {
if (!response.content || !Array.isArray(response.content)) {
return [];
}
return response.content
.filter(block => block.type === 'tool_use')
.map(block => ({
id: block.id || 'unknown',
name: block.name || 'unknown',
hasInput: block.input !== undefined
}));
}
/**
* 批量验证多个响应
*/
validateBatch(responses, requestId) {
const results = responses.map((response, index) => this.validateAndFix(response, `${requestId}-batch-${index}`));
const summary = {
total: responses.length,
consistent: results.filter(r => r.result.consistent).length,
fixed: results.filter(r => r.result.fixed).length,
totalTools: results.reduce((sum, r) => sum + r.result.toolCount, 0),
totalIssues: results.reduce((sum, r) => sum + r.result.issues.length, 0)
};
this.logger.info('📊 [CONSISTENCY] Batch validation completed', {
...summary,
requestId
}, requestId, 'consistency-validator');
return {
responses: results.map(r => r.response),
summary
};
}
/**
* 检查是否需要验证
*/
shouldValidate(response) {
// 只验证有content且有stop_reason的响应
return !!(response &&
response.content &&
Array.isArray(response.content) &&
response.stop_reason);
}
/**
* 生成验证报告
*/
generateValidationReport(result) {
const { consistent, fixed, originalStopReason, finalStopReason, toolCount, issues } = result;
if (consistent && !fixed) {
return `✅ Consistent: ${finalStopReason} with ${toolCount} tools`;
}
if (fixed) {
return `🔧 Fixed: ${originalStopReason} → ${finalStopReason} (${toolCount} tools)`;
}
return `❌ Issues: ${issues.join('; ')}`;
}
}
exports.FinishReasonConsistencyValidator = FinishReasonConsistencyValidator;
/**
* 全局一致性验证器实例
*/
const validatorInstances = new Map();
/**
* 获取或创建一致性验证器实例
*/
function getConsistencyValidator(port) {
const key = port || 'default';
if (!validatorInstances.has(key)) {
validatorInstances.set(key, new FinishReasonConsistencyValidator(port));
}
return validatorInstances.get(key);
}
/**
* 创建新的一致性验证器实例
*/
function createConsistencyValidator(port) {
const key = port || 'default';
const instance = new FinishReasonConsistencyValidator(port);
validatorInstances.set(key, instance);
return instance;
}
//# sourceMappingURL=consistency-validator.js.map