fix: 添加 OpenAI 兼容路由对 Claude Console 账户的支持

This commit is contained in:
QTom
2026-01-09 14:31:06 +08:00
parent 114e9facee
commit 4723328be4

View File

@@ -8,6 +8,7 @@ const router = express.Router()
const logger = require('../utils/logger') const logger = require('../utils/logger')
const { authenticateApiKey } = require('../middleware/auth') const { authenticateApiKey } = require('../middleware/auth')
const claudeRelayService = require('../services/claudeRelayService') const claudeRelayService = require('../services/claudeRelayService')
const claudeConsoleRelayService = require('../services/claudeConsoleRelayService')
const openaiToClaude = require('../services/openaiToClaude') const openaiToClaude = require('../services/openaiToClaude')
const apiKeyService = require('../services/apiKeyService') const apiKeyService = require('../services/apiKeyService')
const unifiedClaudeScheduler = require('../services/unifiedClaudeScheduler') const unifiedClaudeScheduler = require('../services/unifiedClaudeScheduler')
@@ -235,7 +236,7 @@ async function handleChatCompletion(req, res, apiKeyData) {
} }
throw error throw error
} }
const { accountId } = accountSelection const { accountId, accountType } = accountSelection
// 获取该账号存储的 Claude Code headers // 获取该账号存储的 Claude Code headers
const claudeCodeHeaders = await claudeCodeHeadersService.getAccountHeaders(accountId) 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( // 创建 usage 回调函数
claudeRequest, const usageCallback = (usage) => {
apiKeyData, // 记录使用统计
res, if (usage && usage.input_tokens !== undefined && usage.output_tokens !== undefined) {
claudeCodeHeaders, const model = usage.model || claudeRequest.model
(usage) => { const cacheCreateTokens =
// 记录使用统计 (usage.cache_creation && typeof usage.cache_creation === 'object'
if (usage && usage.input_tokens !== undefined && usage.output_tokens !== undefined) { ? (usage.cache_creation.ephemeral_5m_input_tokens || 0) +
const model = usage.model || claudeRequest.model (usage.cache_creation.ephemeral_1h_input_tokens || 0)
const cacheCreateTokens = : usage.cache_creation_input_tokens || 0) || 0
(usage.cache_creation && typeof usage.cache_creation === 'object' const cacheReadTokens = usage.cache_read_input_tokens || 0
? (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 方法来支持详细的缓存数据 // 使用新的 recordUsageWithDetails 方法来支持详细的缓存数据
apiKeyService apiKeyService
.recordUsageWithDetails( .recordUsageWithDetails(
apiKeyData.id, apiKeyData.id,
usage, // 直接传递整个 usage 对象,包含可能的 cache_creation 详细数据 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
},
model, model,
'openai-claude-stream' accountId,
accountType
) )
} .catch((error) => {
}, logger.error('❌ Failed to record usage:', error)
// 流转换器 })
(() => {
// 为每个请求创建独立的会话ID queueRateLimitUpdate(
const sessionId = `chatcmpl-${Math.random().toString(36).substring(2, 15)}${Math.random().toString(36).substring(2, 15)}` req.rateLimitInfo,
return (chunk) => openaiToClaude.convertStreamChunk(chunk, req.body.model, sessionId) {
})(), inputTokens: usage.input_tokens || 0,
{ outputTokens: usage.output_tokens || 0,
betaHeader: cacheCreateTokens,
'oauth-2025-04-20,claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14' 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 { } else {
// 非流式请求 // 非流式请求
logger.info(`📄 Processing OpenAI non-stream request for model: ${req.body.model}`) 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( let claudeResponse
claudeRequest, if (accountType === 'claude-console') {
apiKeyData, // Claude Console 账户使用 Console 转发服务
req, claudeResponse = await claudeConsoleRelayService.relayRequest(
res, claudeRequest,
claudeCodeHeaders, apiKeyData,
{ betaHeader: 'oauth-2025-04-20' } req,
) res,
claudeCodeHeaders,
accountId
)
} else {
// Claude Official 账户使用标准转发服务
claudeResponse = await claudeRelayService.relayRequest(
claudeRequest,
apiKeyData,
req,
res,
claudeCodeHeaders,
{ betaHeader: 'oauth-2025-04-20' }
)
}
// 解析 Claude 响应 // 解析 Claude 响应
let claudeData let claudeData
@@ -376,7 +410,8 @@ async function handleChatCompletion(req, res, apiKeyData) {
apiKeyData.id, apiKeyData.id,
usage, // 直接传递整个 usage 对象,包含可能的 cache_creation 详细数据 usage, // 直接传递整个 usage 对象,包含可能的 cache_creation 详细数据
claudeRequest.model, claudeRequest.model,
accountId accountId,
accountType
) )
.catch((error) => { .catch((error) => {
logger.error('❌ Failed to record usage:', error) logger.error('❌ Failed to record usage:', error)
@@ -391,7 +426,7 @@ async function handleChatCompletion(req, res, apiKeyData) {
cacheReadTokens cacheReadTokens
}, },
claudeRequest.model, claudeRequest.model,
'openai-claude-non-stream' `openai-${accountType}-non-stream`
) )
} }