UNPKG

route-claudecode

Version:

Advanced routing and transformation system for Claude Code outputs to multiple AI providers

289 lines 11.8 kB
"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