From 4723328be4c69bc4e02a6c487945e39cb51e12ab Mon Sep 17 00:00:00 2001 From: QTom Date: Fri, 9 Jan 2026 14:31:06 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0=20OpenAI=20=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E8=B7=AF=E7=94=B1=E5=AF=B9=20Claude=20Console=20?= =?UTF-8?q?=E8=B4=A6=E6=88=B7=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/openaiClaudeRoutes.js | 157 +++++++++++++++++++------------ 1 file changed, 96 insertions(+), 61 deletions(-) diff --git a/src/routes/openaiClaudeRoutes.js b/src/routes/openaiClaudeRoutes.js index 35314e4a..200ef94e 100644 --- a/src/routes/openaiClaudeRoutes.js +++ b/src/routes/openaiClaudeRoutes.js @@ -8,6 +8,7 @@ const router = express.Router() const logger = require('../utils/logger') const { authenticateApiKey } = require('../middleware/auth') const claudeRelayService = require('../services/claudeRelayService') +const claudeConsoleRelayService = require('../services/claudeConsoleRelayService') const openaiToClaude = require('../services/openaiToClaude') const apiKeyService = require('../services/apiKeyService') const unifiedClaudeScheduler = require('../services/unifiedClaudeScheduler') @@ -235,7 +236,7 @@ async function handleChatCompletion(req, res, apiKeyData) { } throw error } - const { accountId } = accountSelection + const { accountId, accountType } = accountSelection // 获取该账号存储的 Claude Code headers const claudeCodeHeaders = await claudeCodeHeadersService.getAccountHeaders(accountId) @@ -265,72 +266,105 @@ async function handleChatCompletion(req, res, apiKeyData) { } }) - // 使用转换后的响应流 (使用 OAuth-only beta header,添加 Claude Code 必需的 headers) - await claudeRelayService.relayStreamRequestWithUsageCapture( - claudeRequest, - apiKeyData, - res, - claudeCodeHeaders, - (usage) => { - // 记录使用统计 - if (usage && usage.input_tokens !== undefined && usage.output_tokens !== undefined) { - const model = usage.model || claudeRequest.model - const cacheCreateTokens = - (usage.cache_creation && typeof usage.cache_creation === 'object' - ? (usage.cache_creation.ephemeral_5m_input_tokens || 0) + - (usage.cache_creation.ephemeral_1h_input_tokens || 0) - : usage.cache_creation_input_tokens || 0) || 0 - const cacheReadTokens = usage.cache_read_input_tokens || 0 + // 使用转换后的响应流 (根据账户类型选择转发服务) + // 创建 usage 回调函数 + const usageCallback = (usage) => { + // 记录使用统计 + if (usage && usage.input_tokens !== undefined && usage.output_tokens !== undefined) { + const model = usage.model || claudeRequest.model + const cacheCreateTokens = + (usage.cache_creation && typeof usage.cache_creation === 'object' + ? (usage.cache_creation.ephemeral_5m_input_tokens || 0) + + (usage.cache_creation.ephemeral_1h_input_tokens || 0) + : usage.cache_creation_input_tokens || 0) || 0 + const cacheReadTokens = usage.cache_read_input_tokens || 0 - // 使用新的 recordUsageWithDetails 方法来支持详细的缓存数据 - apiKeyService - .recordUsageWithDetails( - apiKeyData.id, - usage, // 直接传递整个 usage 对象,包含可能的 cache_creation 详细数据 - model, - accountId - ) - .catch((error) => { - logger.error('❌ Failed to record usage:', error) - }) - - queueRateLimitUpdate( - req.rateLimitInfo, - { - inputTokens: usage.input_tokens || 0, - outputTokens: usage.output_tokens || 0, - cacheCreateTokens, - cacheReadTokens - }, + // 使用新的 recordUsageWithDetails 方法来支持详细的缓存数据 + apiKeyService + .recordUsageWithDetails( + apiKeyData.id, + usage, // 直接传递整个 usage 对象,包含可能的 cache_creation 详细数据 model, - 'openai-claude-stream' + accountId, + accountType ) - } - }, - // 流转换器 - (() => { - // 为每个请求创建独立的会话ID - const sessionId = `chatcmpl-${Math.random().toString(36).substring(2, 15)}${Math.random().toString(36).substring(2, 15)}` - return (chunk) => openaiToClaude.convertStreamChunk(chunk, req.body.model, sessionId) - })(), - { - betaHeader: - 'oauth-2025-04-20,claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14' + .catch((error) => { + logger.error('❌ Failed to record usage:', error) + }) + + queueRateLimitUpdate( + req.rateLimitInfo, + { + inputTokens: usage.input_tokens || 0, + outputTokens: usage.output_tokens || 0, + cacheCreateTokens, + cacheReadTokens + }, + model, + `openai-${accountType}-stream` + ) } - ) + } + + // 创建流转换器 + const sessionId = `chatcmpl-${Math.random().toString(36).substring(2, 15)}${Math.random().toString(36).substring(2, 15)}` + const streamTransformer = (chunk) => + openaiToClaude.convertStreamChunk(chunk, req.body.model, sessionId) + + // 根据账户类型选择转发服务 + if (accountType === 'claude-console') { + // Claude Console 账户使用 Console 转发服务 + await claudeConsoleRelayService.relayStreamRequestWithUsageCapture( + claudeRequest, + apiKeyData, + res, + claudeCodeHeaders, + usageCallback, + accountId, + streamTransformer + ) + } else { + // Claude Official 账户使用标准转发服务 + await claudeRelayService.relayStreamRequestWithUsageCapture( + claudeRequest, + apiKeyData, + res, + claudeCodeHeaders, + usageCallback, + streamTransformer, + { + betaHeader: + 'oauth-2025-04-20,claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14' + } + ) + } } else { // 非流式请求 logger.info(`📄 Processing OpenAI non-stream request for model: ${req.body.model}`) - // 发送请求到 Claude (使用 OAuth-only beta header,添加 Claude Code 必需的 headers) - const claudeResponse = await claudeRelayService.relayRequest( - claudeRequest, - apiKeyData, - req, - res, - claudeCodeHeaders, - { betaHeader: 'oauth-2025-04-20' } - ) + // 根据账户类型选择转发服务 + let claudeResponse + if (accountType === 'claude-console') { + // Claude Console 账户使用 Console 转发服务 + claudeResponse = await claudeConsoleRelayService.relayRequest( + claudeRequest, + apiKeyData, + req, + res, + claudeCodeHeaders, + accountId + ) + } else { + // Claude Official 账户使用标准转发服务 + claudeResponse = await claudeRelayService.relayRequest( + claudeRequest, + apiKeyData, + req, + res, + claudeCodeHeaders, + { betaHeader: 'oauth-2025-04-20' } + ) + } // 解析 Claude 响应 let claudeData @@ -376,7 +410,8 @@ async function handleChatCompletion(req, res, apiKeyData) { apiKeyData.id, usage, // 直接传递整个 usage 对象,包含可能的 cache_creation 详细数据 claudeRequest.model, - accountId + accountId, + accountType ) .catch((error) => { logger.error('❌ Failed to record usage:', error) @@ -391,7 +426,7 @@ async function handleChatCompletion(req, res, apiKeyData) { cacheReadTokens }, claudeRequest.model, - 'openai-claude-non-stream' + `openai-${accountType}-non-stream` ) }