diff --git a/src/services/apiKeyService.js b/src/services/apiKeyService.js index 9d0a94dd..5d63e72f 100644 --- a/src/services/apiKeyService.js +++ b/src/services/apiKeyService.js @@ -947,9 +947,46 @@ class ApiKeyService { await pricingService.initialize() } costInfo = pricingService.calculateCost(usageObject, model) + + // 验证计算结果 + if (!costInfo || typeof costInfo.totalCost !== 'number') { + logger.error(`❌ Invalid cost calculation result for model ${model}:`, costInfo) + // 使用 CostCalculator 作为后备 + const CostCalculator = require('../utils/costCalculator') + const fallbackCost = CostCalculator.calculateCost(usageObject, model) + if (fallbackCost && fallbackCost.costs && fallbackCost.costs.total > 0) { + logger.warn( + `⚠️ Using fallback cost calculation for ${model}: $${fallbackCost.costs.total}` + ) + costInfo = { + totalCost: fallbackCost.costs.total, + ephemeral5mCost: 0, + ephemeral1hCost: 0 + } + } else { + costInfo = { totalCost: 0, ephemeral5mCost: 0, ephemeral1hCost: 0 } + } + } } catch (pricingError) { - logger.error('❌ Failed to calculate cost:', pricingError) - // 继续执行,不要因为费用计算失败而跳过统计记录 + logger.error(`❌ Failed to calculate cost for model ${model}:`, pricingError) + logger.error(` Usage object:`, JSON.stringify(usageObject)) + // 使用 CostCalculator 作为后备 + try { + const CostCalculator = require('../utils/costCalculator') + const fallbackCost = CostCalculator.calculateCost(usageObject, model) + if (fallbackCost && fallbackCost.costs && fallbackCost.costs.total > 0) { + logger.warn( + `⚠️ Using fallback cost calculation for ${model}: $${fallbackCost.costs.total}` + ) + costInfo = { + totalCost: fallbackCost.costs.total, + ephemeral5mCost: 0, + ephemeral1hCost: 0 + } + } + } catch (fallbackError) { + logger.error(`❌ Fallback cost calculation also failed:`, fallbackError) + } } // 提取详细的缓存创建数据 @@ -994,7 +1031,15 @@ class ApiKeyService { ) } } else { - logger.debug(`💰 No cost recorded for ${keyId} - zero cost for model: ${model}`) + // 如果有 token 使用但费用为 0,记录警告 + if (totalTokens > 0) { + logger.warn( + `⚠️ No cost recorded for ${keyId} - zero cost for model: ${model} (tokens: ${totalTokens})` + ) + logger.warn(` This may indicate a pricing issue or model not found in pricing data`) + } else { + logger.debug(`💰 No cost recorded for ${keyId} - zero tokens for model: ${model}`) + } } // 获取API Key数据以确定关联的账户 diff --git a/src/utils/costCalculator.js b/src/utils/costCalculator.js index a8ccec50..e2e0cc91 100644 --- a/src/utils/costCalculator.js +++ b/src/utils/costCalculator.js @@ -15,6 +15,12 @@ const MODEL_PRICING = { cacheWrite: 3.75, cacheRead: 0.3 }, + 'claude-sonnet-4-5-20250929': { + input: 3.0, + output: 15.0, + cacheWrite: 3.75, + cacheRead: 0.3 + }, // Claude 3.5 Haiku 'claude-3-5-haiku-20241022': { diff --git a/web/admin-spa/src/stores/apistats.js b/web/admin-spa/src/stores/apistats.js index cf5828c0..2d1054ed 100644 --- a/web/admin-spa/src/stores/apistats.js +++ b/web/admin-spa/src/stores/apistats.js @@ -92,11 +92,19 @@ export const useApiStatsStore = defineStore('apistats', () => { return queryBatchStats() } - if (!apiKey.value.trim()) { + const trimmedKey = apiKey.value.trim() + + if (!trimmedKey) { error.value = '请输入 API Key' return } + // 验证 API Key 格式:长度应在 10-512 之间 + if (trimmedKey.length < 10 || trimmedKey.length > 512) { + error.value = 'API Key 格式无效:长度应在 10-512 个字符之间' + return + } + loading.value = true error.value = '' statsData.value = null @@ -105,7 +113,7 @@ export const useApiStatsStore = defineStore('apistats', () => { try { // 获取 API Key ID - const idResult = await apiStatsClient.getKeyId(apiKey.value) + const idResult = await apiStatsClient.getKeyId(trimmedKey) if (idResult.success) { apiId.value = idResult.data.id @@ -431,7 +439,7 @@ export const useApiStatsStore = defineStore('apistats', () => { const keys = apiKey.value .split(/[,\n]+/) .map((key) => key.trim()) - .filter((key) => key.length > 0) + .filter((key) => key.length >= 10 && key.length <= 512) // 验证 API Key 格式 // 去重并限制最多30个 const uniqueKeys = [...new Set(keys)]