route-claudecode
Version:
Advanced routing and transformation system for Claude Code outputs to multiple AI providers
289 lines • 11.8 kB
JavaScript
"use strict";
/**
* Max Tokens中间件 - 集成到请求处理流程
* Project Owner: Jason Zhang
*
* 职责:
* 1. 预处理检查:发送前验证token使用
* 2. 错误拦截:捕获max_tokens错误并智能处理
* 3. 用户反馈:提供清晰的处理状态和选择
* 4. 安全控制:防止无限重试和上下文丢失
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MaxTokensMiddleware = void 0;
const logger_1 = require("./logger");
const safe_max_tokens_handler_1 = require("./safe-max-tokens-handler");
class MaxTokensMiddleware {
safeHandler;
options;
processingRequests = new Set();
constructor(options = {}) {
this.options = {
enablePreflightCheck: true,
enableAutoRetry: true,
maxRetryAttempts: 1,
userInteractionMode: 'auto', // 默认自动处理
tokenSafetyMargin: 500,
...options
};
this.safeHandler = new safe_max_tokens_handler_1.SafeMaxTokensHandler({
enablePreemptiveCheck: this.options.enablePreflightCheck,
maxRetryAttempts: this.options.maxRetryAttempts,
tokenSafetyMargin: this.options.tokenSafetyMargin,
enableProgressiveReduction: true
});
logger_1.logger.info('Max Tokens Middleware initialized', {
preflightCheck: this.options.enablePreflightCheck,
autoRetry: this.options.enableAutoRetry,
userInteractionMode: this.options.userInteractionMode
});
}
/**
* 预处理请求 - 在发送到Provider前检查
*/
async preprocessRequest(request, maxTokens, requestId) {
if (this.processingRequests.has(requestId)) {
logger_1.logger.warn('🔄 [MIDDLEWARE] Request already being processed', { requestId });
return {
action: 'error',
error: new Error('Request is already being processed to prevent infinite loops')
};
}
this.processingRequests.add(requestId);
try {
const handlingResult = await this.safeHandler.preflightCheck(request, maxTokens, requestId);
return this.processHandlingResult(handlingResult, request, requestId, 'preflight');
}
catch (error) {
logger_1.logger.error('🚨 [MIDDLEWARE] Preflight check failed', error, requestId);
return {
action: 'error',
error: error
};
}
finally {
this.processingRequests.delete(requestId);
}
}
/**
* 后处理响应 - 处理max_tokens错误
*/
async postprocessResponse(originalRequest, response, error, requestId) {
// 只处理max_tokens相关错误
if (!this.isMaxTokensError(response, error)) {
return { action: 'proceed' };
}
if (!this.options.enableAutoRetry) {
logger_1.logger.info('🚨 [MIDDLEWARE] Max tokens error detected, auto-retry disabled', { requestId });
return {
action: 'error',
error: this.createUserFriendlyError(response, error, originalRequest),
metadata: {
tokensEstimated: this.estimateTokens(originalRequest),
truncationApplied: false,
retryAttempt: 0
}
};
}
if (this.processingRequests.has(requestId)) {
logger_1.logger.warn('🔄 [MIDDLEWARE] Max tokens retry already in progress', { requestId });
return {
action: 'error',
error: new Error('Max tokens retry already in progress to prevent infinite loops')
};
}
this.processingRequests.add(requestId);
try {
const handlingResult = await this.safeHandler.handleMaxTokensError(originalRequest, response || error, requestId);
return this.processHandlingResult(handlingResult, originalRequest, requestId, 'postprocess');
}
catch (handlingError) {
logger_1.logger.error('🚨 [MIDDLEWARE] Max tokens handling failed', handlingError, requestId);
return {
action: 'error',
error: handlingError
};
}
finally {
this.processingRequests.delete(requestId);
}
}
/**
* 处理SafeHandler的结果
*/
processHandlingResult(handlingResult, originalRequest, requestId, stage) {
switch (handlingResult.action) {
case 'proceed':
if (handlingResult.warning) {
logger_1.logger.info(`⚠️ [MIDDLEWARE-${stage.toUpperCase()}] Warning issued`, {
type: handlingResult.warning.type,
message: handlingResult.warning.message,
requestId
});
}
return {
action: 'proceed',
request: originalRequest,
metadata: {
tokensEstimated: handlingResult.warning?.estimatedTokens || this.estimateTokens(originalRequest),
truncationApplied: false,
retryAttempt: 0
}
};
case 'truncate':
if (handlingResult.truncatedRequest) {
logger_1.logger.info(`🔧 [MIDDLEWARE-${stage.toUpperCase()}] Request truncated`, {
originalTokens: handlingResult.warning?.truncationDetails?.originalTokens,
reducedTokens: handlingResult.warning?.estimatedTokens,
requestId
});
return {
action: 'modified',
request: handlingResult.truncatedRequest,
metadata: {
tokensEstimated: handlingResult.warning?.truncationDetails?.originalTokens || 0,
tokensReduced: handlingResult.warning?.estimatedTokens || 0,
truncationApplied: true,
retryAttempt: stage === 'postprocess' ? 1 : 0
}
};
}
break;
case 'user_choice':
if (this.options.userInteractionMode === 'auto') {
// 自动模式:选择最安全的选项(简化)
logger_1.logger.info(`🤖 [MIDDLEWARE-${stage.toUpperCase()}] Auto-choosing safest option`, { requestId });
// 这里可以调用简化处理逻辑
return {
action: 'error',
error: this.createUserFriendlyError(null, null, originalRequest, handlingResult.warning?.message)
};
}
else if (this.options.userInteractionMode === 'prompt') {
return {
action: 'user_required',
userPrompt: {
message: handlingResult.warning?.message || 'Request exceeds token limit. How would you like to proceed?',
options: [
'Auto-truncate (may lose context)',
'Use simplified prompt only',
'Cancel request'
],
defaultAction: 'Auto-truncate (may lose context)'
},
metadata: {
tokensEstimated: handlingResult.warning?.estimatedTokens || 0,
truncationApplied: false,
retryAttempt: 0
}
};
}
break;
case 'abort':
logger_1.logger.warn(`🛑 [MIDDLEWARE-${stage.toUpperCase()}] Processing aborted`, {
reason: handlingResult.warning?.message,
requestId
});
return {
action: 'error',
error: this.createUserFriendlyError(null, null, originalRequest, handlingResult.warning?.message)
};
}
return {
action: 'error',
error: new Error('Unknown handling result action')
};
}
/**
* 检查是否是max_tokens错误
*/
isMaxTokensError(response, error) {
// 检查响应中的finish_reason
if (response?.choices?.[0]?.finish_reason) {
const finishReason = response.choices[0].finish_reason.toLowerCase();
if (finishReason === 'length' || finishReason === 'max_tokens') {
return true;
}
}
// 检查Anthropic格式
if (response?.stop_reason) {
const stopReason = response.stop_reason.toLowerCase();
if (stopReason === 'max_tokens') {
return true;
}
}
// 检查错误信息
if (error) {
const errorMessage = error.message?.toLowerCase() || '';
if (errorMessage.includes('max_tokens') ||
errorMessage.includes('token limit') ||
errorMessage.includes('context length')) {
return true;
}
// 检查错误码
if (error.code === 'MAX_TOKENS_EXCEEDED' ||
error.status === 400 && errorMessage.includes('tokens')) {
return true;
}
}
return false;
}
/**
* 创建用户友好的错误
*/
createUserFriendlyError(response, error, originalRequest, customMessage) {
const estimatedTokens = this.estimateTokens(originalRequest);
const message = customMessage ||
`Request exceeded token limit (~${estimatedTokens} tokens). ` +
`To resolve this: 1) Reduce input length, 2) Use fewer examples, or 3) Simplify your request. ` +
`Consider breaking complex tasks into smaller parts.`;
const enhancedError = new Error(message);
enhancedError.code = 'MAX_TOKENS_EXCEEDED';
enhancedError.status = 400;
enhancedError.details = {
estimatedTokens,
suggestion: 'Break your request into smaller parts or reduce the input length',
autoTruncationAvailable: true
};
return enhancedError;
}
/**
* 估算token数量
*/
estimateTokens(request) {
let totalChars = 0;
if (request.messages) {
request.messages.forEach(msg => {
if (typeof msg.content === 'string') {
totalChars += msg.content.length;
}
});
}
if (request.metadata?.system) {
totalChars += JSON.stringify(request.metadata.system).length;
}
if (request.metadata?.tools) {
totalChars += JSON.stringify(request.metadata.tools).length;
}
return Math.ceil(totalChars / 4);
}
/**
* 清理处理状态
*/
cleanup() {
this.processingRequests.clear();
logger_1.logger.info('Max Tokens Middleware cleaned up');
}
/**
* 获取统计信息
*/
getStats() {
return {
activeRequests: this.processingRequests.size,
safeHandlerStats: this.safeHandler.getStats()
};
}
}
exports.MaxTokensMiddleware = MaxTokensMiddleware;
exports.default = MaxTokensMiddleware;
//# sourceMappingURL=max-tokens-middleware.js.map