route-claudecode
Version:
Advanced routing and transformation system for Claude Code outputs to multiple AI providers
336 lines • 13.7 kB
JavaScript
"use strict";
/**
* Max Tokens错误处理模块 - 集中处理token限制问题
* Project Owner: Jason Zhang
*
* 当收到max_tokens/length stop_reason时的智能处理方案
* Option 1: 滚动截断处理机制 (当前实现)
* Option 2: LongContext模型压缩 (预留接口)
*/
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.MaxTokensErrorHandlingModule = void 0;
const logger_1 = require("./logger");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
class MaxTokensErrorHandlingModule {
config;
configPath;
constructor(configPath) {
this.configPath = configPath || path.join(process.cwd(), 'config', 'max-tokens-handling.json');
this.loadConfiguration();
}
/**
* 加载配置文件
*/
loadConfiguration() {
try {
if (fs.existsSync(this.configPath)) {
const configData = fs.readFileSync(this.configPath, 'utf8');
this.config = JSON.parse(configData);
logger_1.logger.debug('Max tokens handling configuration loaded', {
configPath: this.configPath,
strategy: this.config.strategy
});
}
else {
// 创建默认配置
this.config = this.getDefaultConfig();
this.saveConfiguration();
logger_1.logger.info('Created default max tokens handling configuration', {
configPath: this.configPath
});
}
}
catch (error) {
logger_1.logger.error('Failed to load max tokens handling configuration', error);
this.config = this.getDefaultConfig();
}
}
/**
* 获取默认配置
*/
getDefaultConfig() {
return {
strategy: 'rolling_truncation',
rollingTruncation: {
historyRetentionPercent: 80,
useSimplifiedPrompt: true,
simplifiedPromptPath: path.join(process.cwd(), 'config', 'simplified-system-prompt.json')
},
longcontextCompression: {
enabled: false,
recentHistoryPercent: 20,
compressionModel: 'claude-3-haiku', // 用于压缩的高效模型
compressionEndpoint: '/v1/chat/completions'
}
};
}
/**
* 保存配置文件
*/
saveConfiguration() {
try {
// 确保配置目录存在
const configDir = path.dirname(this.configPath);
if (!fs.existsSync(configDir)) {
fs.mkdirSync(configDir, { recursive: true });
}
fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2), 'utf8');
logger_1.logger.debug('Max tokens handling configuration saved', {
configPath: this.configPath
});
}
catch (error) {
logger_1.logger.error('Failed to save max tokens handling configuration', error);
}
}
/**
* 处理max tokens错误 - 主入口
*/
async handleMaxTokensError(originalRequest, errorDetails, requestId) {
logger_1.logger.info('🔧 [MAX-TOKENS] Starting max tokens error handling', {
strategy: this.config.strategy,
originalModel: originalRequest.model,
requestId
});
try {
switch (this.config.strategy) {
case 'rolling_truncation':
return await this.handleRollingTruncation(originalRequest, requestId);
case 'longcontext_compression':
if (this.config.longcontextCompression?.enabled) {
return await this.handleLongcontextCompression(originalRequest, requestId);
}
else {
// 降级到滚动截断
logger_1.logger.warn('LongContext compression disabled, falling back to rolling truncation');
return await this.handleRollingTruncation(originalRequest, requestId);
}
default:
throw new Error(`Unknown max tokens handling strategy: ${this.config.strategy}`);
}
}
catch (error) {
logger_1.logger.error('Max tokens error handling failed', error, requestId, 'max-tokens-handler');
throw error;
}
}
/**
* Option 1: 滚动截断处理
*/
async handleRollingTruncation(originalRequest, requestId) {
const config = this.config.rollingTruncation;
let truncatedRequest = JSON.parse(JSON.stringify(originalRequest)); // 深拷贝
const originalTokens = this.estimateTokens(originalRequest);
let systemPromptReduced = false;
let historyMessagesRemoved = 0;
logger_1.logger.debug('🔧 [ROLLING-TRUNCATION] Starting truncation process', {
originalTokens,
historyRetentionPercent: config.historyRetentionPercent,
useSimplifiedPrompt: config.useSimplifiedPrompt,
requestId
});
// 1. 简化系统提示词 (如果启用)
if (config.useSimplifiedPrompt) {
const simplifiedSystemPrompt = await this.getSimplifiedSystemPrompt();
if (simplifiedSystemPrompt && truncatedRequest.metadata?.system) {
const originalSystemLength = JSON.stringify(truncatedRequest.metadata.system).length;
truncatedRequest.metadata.system = simplifiedSystemPrompt;
const newSystemLength = JSON.stringify(simplifiedSystemPrompt).length;
systemPromptReduced = newSystemLength < originalSystemLength;
logger_1.logger.info('🔧 [ROLLING-TRUNCATION] System prompt simplified', {
originalLength: originalSystemLength,
newLength: newSystemLength,
reductionPercent: Math.round((1 - newSystemLength / originalSystemLength) * 100),
requestId
});
}
}
// 2. 历史记录截断
if (truncatedRequest.messages && truncatedRequest.messages.length > 1) {
const totalMessages = truncatedRequest.messages.length;
const messagesToKeep = Math.max(1, Math.ceil(totalMessages * (config.historyRetentionPercent / 100)));
// 保留最新的消息(通常最后一个是用户的当前请求)
const messagesToRemove = totalMessages - messagesToKeep;
if (messagesToRemove > 0) {
// 从最旧的消息开始移除,但保留第一条(通常是系统消息或重要上下文)
const startIndex = Math.max(1, Math.min(messagesToRemove, totalMessages - messagesToKeep));
truncatedRequest.messages = [
...truncatedRequest.messages.slice(0, 1), // 保留第一条消息
...truncatedRequest.messages.slice(startIndex + messagesToRemove) // 保留最新的消息
];
historyMessagesRemoved = messagesToRemove;
logger_1.logger.info('🔧 [ROLLING-TRUNCATION] History messages truncated', {
originalMessages: totalMessages,
messagesRemoved: historyMessagesRemoved,
remainingMessages: truncatedRequest.messages.length,
requestId
});
}
}
const reducedTokens = this.estimateTokens(truncatedRequest);
const reductionPercent = Math.round((1 - reducedTokens / originalTokens) * 100);
logger_1.logger.info('🔧 [ROLLING-TRUNCATION] Truncation completed', {
originalTokens,
reducedTokens,
reductionPercent,
systemPromptReduced,
historyMessagesRemoved,
requestId
});
return {
success: true,
originalTokens,
reducedTokens,
truncatedRequest,
strategy: 'rolling_truncation',
details: {
systemPromptReduced,
historyMessagesRemoved,
totalMessagesRemaining: truncatedRequest.messages?.length || 0
}
};
}
/**
* Option 2: LongContext模型压缩 (预留实现)
*/
async handleLongcontextCompression(originalRequest, requestId) {
// TODO: 实现LongContext模型压缩逻辑
logger_1.logger.warn('🔧 [LONGCONTEXT-COMPRESSION] Not implemented yet, falling back to rolling truncation');
return await this.handleRollingTruncation(originalRequest, requestId);
}
/**
* 获取简化版系统提示词
*/
async getSimplifiedSystemPrompt() {
const config = this.config.rollingTruncation;
try {
if (fs.existsSync(config.simplifiedPromptPath)) {
const promptData = fs.readFileSync(config.simplifiedPromptPath, 'utf8');
return JSON.parse(promptData);
}
else {
// 创建默认简化提示词文件
const defaultPrompt = this.createDefaultSimplifiedPrompt();
await this.saveSimplifiedPrompt(defaultPrompt);
return defaultPrompt;
}
}
catch (error) {
logger_1.logger.error('Failed to load simplified system prompt', error);
return null;
}
}
/**
* 创建默认简化系统提示词
*/
createDefaultSimplifiedPrompt() {
return {
type: "text",
text: "You are Claude, an AI assistant. Be helpful, harmless, and honest. Use available tools when needed."
};
}
/**
* 保存简化版系统提示词
*/
async saveSimplifiedPrompt(prompt) {
const config = this.config.rollingTruncation;
try {
const promptDir = path.dirname(config.simplifiedPromptPath);
if (!fs.existsSync(promptDir)) {
fs.mkdirSync(promptDir, { recursive: true });
}
fs.writeFileSync(config.simplifiedPromptPath, JSON.stringify(prompt, null, 2), 'utf8');
logger_1.logger.info('Default simplified system prompt created', {
path: config.simplifiedPromptPath
});
}
catch (error) {
logger_1.logger.error('Failed to save simplified system prompt', error);
}
}
/**
* 估算请求的token数量
*/
estimateTokens(request) {
let totalChars = 0;
// 计算消息内容
if (request.messages) {
request.messages.forEach(msg => {
if (typeof msg.content === 'string') {
totalChars += msg.content.length;
}
else if (Array.isArray(msg.content)) {
msg.content.forEach((block) => {
if (block.text)
totalChars += block.text.length;
if (block.content)
totalChars += JSON.stringify(block.content).length;
});
}
});
}
// 计算系统提示词
if (request.metadata?.system) {
totalChars += JSON.stringify(request.metadata.system).length;
}
// 计算工具定义
if (request.metadata?.tools) {
totalChars += JSON.stringify(request.metadata.tools).length;
}
// 估算:~4个字符等于1个token
return Math.ceil(totalChars / 4);
}
/**
* 获取当前配置
*/
getConfig() {
return { ...this.config };
}
/**
* 更新配置
*/
updateConfig(updates) {
this.config = { ...this.config, ...updates };
this.saveConfiguration();
logger_1.logger.info('Max tokens handling configuration updated', {
strategy: this.config.strategy
});
}
}
exports.MaxTokensErrorHandlingModule = MaxTokensErrorHandlingModule;
exports.default = MaxTokensErrorHandlingModule;
//# sourceMappingURL=max-tokens-error-handling-module.js.map