feat: 添加对 ephemeral 5m 和 1h 令牌的支持,优化费用计算逻辑

This commit is contained in:
sczheng189
2026-02-23 21:20:18 +08:00
parent 5376428dd9
commit bfae62bfeb
7 changed files with 331 additions and 18 deletions

View File

@@ -1286,6 +1286,8 @@ class RedisClient {
outputTokens = 0,
cacheCreateTokens = 0,
cacheReadTokens = 0,
ephemeral5mTokens = 0,
ephemeral1hTokens = 0,
model = 'unknown',
isLongContextRequest = false
) {
@@ -1317,6 +1319,8 @@ class RedisClient {
const finalOutputTokens = outputTokens || 0
const finalCacheCreateTokens = cacheCreateTokens || 0
const finalCacheReadTokens = cacheReadTokens || 0
const finalEphemeral5mTokens = ephemeral5mTokens || 0
const finalEphemeral1hTokens = ephemeral1hTokens || 0
const actualTotalTokens =
finalInputTokens + finalOutputTokens + finalCacheCreateTokens + finalCacheReadTokens
const coreTokens = finalInputTokens + finalOutputTokens
@@ -1329,6 +1333,8 @@ class RedisClient {
this.client.hincrby(accountKey, 'totalOutputTokens', finalOutputTokens),
this.client.hincrby(accountKey, 'totalCacheCreateTokens', finalCacheCreateTokens),
this.client.hincrby(accountKey, 'totalCacheReadTokens', finalCacheReadTokens),
this.client.hincrby(accountKey, 'totalEphemeral5mTokens', finalEphemeral5mTokens),
this.client.hincrby(accountKey, 'totalEphemeral1hTokens', finalEphemeral1hTokens),
this.client.hincrby(accountKey, 'totalAllTokens', actualTotalTokens),
this.client.hincrby(accountKey, 'totalRequests', 1),
@@ -1338,6 +1344,8 @@ class RedisClient {
this.client.hincrby(accountDaily, 'outputTokens', finalOutputTokens),
this.client.hincrby(accountDaily, 'cacheCreateTokens', finalCacheCreateTokens),
this.client.hincrby(accountDaily, 'cacheReadTokens', finalCacheReadTokens),
this.client.hincrby(accountDaily, 'ephemeral5mTokens', finalEphemeral5mTokens),
this.client.hincrby(accountDaily, 'ephemeral1hTokens', finalEphemeral1hTokens),
this.client.hincrby(accountDaily, 'allTokens', actualTotalTokens),
this.client.hincrby(accountDaily, 'requests', 1),
@@ -1347,6 +1355,8 @@ class RedisClient {
this.client.hincrby(accountMonthly, 'outputTokens', finalOutputTokens),
this.client.hincrby(accountMonthly, 'cacheCreateTokens', finalCacheCreateTokens),
this.client.hincrby(accountMonthly, 'cacheReadTokens', finalCacheReadTokens),
this.client.hincrby(accountMonthly, 'ephemeral5mTokens', finalEphemeral5mTokens),
this.client.hincrby(accountMonthly, 'ephemeral1hTokens', finalEphemeral1hTokens),
this.client.hincrby(accountMonthly, 'allTokens', actualTotalTokens),
this.client.hincrby(accountMonthly, 'requests', 1),
@@ -1356,6 +1366,8 @@ class RedisClient {
this.client.hincrby(accountHourly, 'outputTokens', finalOutputTokens),
this.client.hincrby(accountHourly, 'cacheCreateTokens', finalCacheCreateTokens),
this.client.hincrby(accountHourly, 'cacheReadTokens', finalCacheReadTokens),
this.client.hincrby(accountHourly, 'ephemeral5mTokens', finalEphemeral5mTokens),
this.client.hincrby(accountHourly, 'ephemeral1hTokens', finalEphemeral1hTokens),
this.client.hincrby(accountHourly, 'allTokens', actualTotalTokens),
this.client.hincrby(accountHourly, 'requests', 1),
@@ -1376,6 +1388,16 @@ class RedisClient {
`model:${normalizedModel}:cacheReadTokens`,
finalCacheReadTokens
),
this.client.hincrby(
accountHourly,
`model:${normalizedModel}:ephemeral5mTokens`,
finalEphemeral5mTokens
),
this.client.hincrby(
accountHourly,
`model:${normalizedModel}:ephemeral1hTokens`,
finalEphemeral1hTokens
),
this.client.hincrby(accountHourly, `model:${normalizedModel}:allTokens`, actualTotalTokens),
this.client.hincrby(accountHourly, `model:${normalizedModel}:requests`, 1),
@@ -1384,6 +1406,8 @@ class RedisClient {
this.client.hincrby(accountModelDaily, 'outputTokens', finalOutputTokens),
this.client.hincrby(accountModelDaily, 'cacheCreateTokens', finalCacheCreateTokens),
this.client.hincrby(accountModelDaily, 'cacheReadTokens', finalCacheReadTokens),
this.client.hincrby(accountModelDaily, 'ephemeral5mTokens', finalEphemeral5mTokens),
this.client.hincrby(accountModelDaily, 'ephemeral1hTokens', finalEphemeral1hTokens),
this.client.hincrby(accountModelDaily, 'allTokens', actualTotalTokens),
this.client.hincrby(accountModelDaily, 'requests', 1),
@@ -1392,6 +1416,8 @@ class RedisClient {
this.client.hincrby(accountModelMonthly, 'outputTokens', finalOutputTokens),
this.client.hincrby(accountModelMonthly, 'cacheCreateTokens', finalCacheCreateTokens),
this.client.hincrby(accountModelMonthly, 'cacheReadTokens', finalCacheReadTokens),
this.client.hincrby(accountModelMonthly, 'ephemeral5mTokens', finalEphemeral5mTokens),
this.client.hincrby(accountModelMonthly, 'ephemeral1hTokens', finalEphemeral1hTokens),
this.client.hincrby(accountModelMonthly, 'allTokens', actualTotalTokens),
this.client.hincrby(accountModelMonthly, 'requests', 1),
@@ -1400,6 +1426,8 @@ class RedisClient {
this.client.hincrby(accountModelHourly, 'outputTokens', finalOutputTokens),
this.client.hincrby(accountModelHourly, 'cacheCreateTokens', finalCacheCreateTokens),
this.client.hincrby(accountModelHourly, 'cacheReadTokens', finalCacheReadTokens),
this.client.hincrby(accountModelHourly, 'ephemeral5mTokens', finalEphemeral5mTokens),
this.client.hincrby(accountModelHourly, 'ephemeral1hTokens', finalEphemeral1hTokens),
this.client.hincrby(accountModelHourly, 'allTokens', actualTotalTokens),
this.client.hincrby(accountModelHourly, 'requests', 1),
@@ -1867,6 +1895,16 @@ class RedisClient {
cache_read_input_tokens: parseInt(modelUsage.cacheReadTokens || 0)
}
// 添加 cache_creation 子对象以支持精确 ephemeral 定价
const eph5m = parseInt(modelUsage.ephemeral5mTokens) || 0
const eph1h = parseInt(modelUsage.ephemeral1hTokens) || 0
if (eph5m > 0 || eph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: eph5m,
ephemeral_1h_input_tokens: eph1h
}
}
const costResult = CostCalculator.calculateCost(usage, model)
totalCost += costResult.costs.total
@@ -1955,6 +1993,16 @@ class RedisClient {
cache_read_input_tokens: parseInt(modelUsage.cacheReadTokens || 0)
}
// 添加 cache_creation 子对象以支持精确 ephemeral 定价
const eph5m = parseInt(modelUsage.ephemeral5mTokens) || 0
const eph1h = parseInt(modelUsage.ephemeral1hTokens) || 0
if (eph5m > 0 || eph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: eph5m,
ephemeral_1h_input_tokens: eph1h
}
}
const costResult = CostCalculator.calculateCost(usage, model)
costMap.set(accountId, costMap.get(accountId) + costResult.costs.total)
}
@@ -1996,6 +2044,17 @@ class RedisClient {
cache_creation_input_tokens: parseInt(modelUsage.cacheCreateTokens || 0),
cache_read_input_tokens: parseInt(modelUsage.cacheReadTokens || 0)
}
// 添加 cache_creation 子对象以支持精确 ephemeral 定价
const eph5m = parseInt(modelUsage.ephemeral5mTokens) || 0
const eph1h = parseInt(modelUsage.ephemeral1hTokens) || 0
if (eph5m > 0 || eph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: eph5m,
ephemeral_1h_input_tokens: eph1h
}
}
const costResult = CostCalculator.calculateCost(usage, model)
totalCost += costResult.costs.total
}
@@ -3646,6 +3705,8 @@ class RedisClient {
outputTokens: 0,
cacheCreateTokens: 0,
cacheReadTokens: 0,
ephemeral5mTokens: 0,
ephemeral1hTokens: 0,
allTokens: 0,
requests: 0
}
@@ -3659,6 +3720,10 @@ class RedisClient {
modelUsage[modelName].cacheCreateTokens += parseInt(value || 0)
} else if (metric === 'cacheReadTokens') {
modelUsage[modelName].cacheReadTokens += parseInt(value || 0)
} else if (metric === 'ephemeral5mTokens') {
modelUsage[modelName].ephemeral5mTokens += parseInt(value || 0)
} else if (metric === 'ephemeral1hTokens') {
modelUsage[modelName].ephemeral1hTokens += parseInt(value || 0)
} else if (metric === 'allTokens') {
modelUsage[modelName].allTokens += parseInt(value || 0)
} else if (metric === 'requests') {

View File

@@ -417,6 +417,14 @@ router.get('/claude-accounts', authenticateAdmin, async (req, res) => {
cache_read_input_tokens: usage.cacheReadTokens
}
// 添加 cache_creation 子对象以支持精确 ephemeral 定价
if (usage.ephemeral5mTokens > 0 || usage.ephemeral1hTokens > 0) {
usageData.cache_creation = {
ephemeral_5m_input_tokens: usage.ephemeral5mTokens,
ephemeral_1h_input_tokens: usage.ephemeral1hTokens
}
}
logger.debug(`💰 Calculating cost for model ${modelName}:`, JSON.stringify(usageData))
const costResult = CostCalculator.calculateCost(usageData, modelName)
logger.debug(`💰 Cost result for ${modelName}: total=${costResult.costs.total}`)

View File

@@ -362,6 +362,16 @@ router.get('/accounts/:accountId/usage-history', authenticateAdmin, async (req,
cache_read_input_tokens: parseInt(modelData.cacheReadTokens) || 0
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
const eph5m = parseInt(modelData.ephemeral5mTokens) || 0
const eph1h = parseInt(modelData.ephemeral1hTokens) || 0
if (eph5m > 0 || eph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: eph5m,
ephemeral_1h_input_tokens: eph1h
}
}
const costResult = CostCalculator.calculateCost(usage, modelName)
summedCost += costResult.costs.total
}
@@ -403,6 +413,15 @@ router.get('/accounts/:accountId/usage-history', authenticateAdmin, async (req,
cache_creation_input_tokens: cacheCreateTokens,
cache_read_input_tokens: cacheReadTokens
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
const fbEph5m = parseInt(dailyData?.ephemeral5mTokens) || 0
const fbEph1h = parseInt(dailyData?.ephemeral1hTokens) || 0
if (fbEph5m > 0 || fbEph1h > 0) {
fallbackUsage.cache_creation = {
ephemeral_5m_input_tokens: fbEph5m,
ephemeral_1h_input_tokens: fbEph1h
}
}
const fallbackResult = CostCalculator.calculateCost(fallbackUsage, fallbackModel)
cost = fallbackResult.costs.total
}
@@ -653,12 +672,23 @@ router.get('/usage-trend', authenticateAdmin, async (req, res) => {
cache_creation_input_tokens: modelCacheCreateTokens,
cache_read_input_tokens: modelCacheReadTokens
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
const mEph5m = parseInt(data.ephemeral5mTokens) || 0
const mEph1h = parseInt(data.ephemeral1hTokens) || 0
if (mEph5m > 0 || mEph1h > 0) {
modelUsage.cache_creation = {
ephemeral_5m_input_tokens: mEph5m,
ephemeral_1h_input_tokens: mEph1h
}
}
const modelCostResult = CostCalculator.calculateCost(modelUsage, model)
hourCost += modelCostResult.costs.total
}
// 如果没有模型级别的数据尝试API Key级别的数据
if (modelKeys.length === 0) {
let hourEph5m = 0
let hourEph1h = 0
for (const key of usageKeys) {
const data = usageDataMap.get(key)
if (data) {
@@ -667,6 +697,8 @@ router.get('/usage-trend', authenticateAdmin, async (req, res) => {
hourRequests += parseInt(data.requests) || 0
hourCacheCreateTokens += parseInt(data.cacheCreateTokens) || 0
hourCacheReadTokens += parseInt(data.cacheReadTokens) || 0
hourEph5m += parseInt(data.ephemeral5mTokens) || 0
hourEph1h += parseInt(data.ephemeral1hTokens) || 0
}
}
@@ -676,6 +708,13 @@ router.get('/usage-trend', authenticateAdmin, async (req, res) => {
cache_creation_input_tokens: hourCacheCreateTokens,
cache_read_input_tokens: hourCacheReadTokens
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
if (hourEph5m > 0 || hourEph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: hourEph5m,
ephemeral_1h_input_tokens: hourEph1h
}
}
const costResult = CostCalculator.calculateCost(usage, 'unknown')
hourCost = costResult.costs.total
}
@@ -817,6 +856,8 @@ router.get('/usage-trend', authenticateAdmin, async (req, res) => {
// 如果没有模型级别的数据,回退到原始方法
if (modelKeys.length === 0 && usageKeys.length > 0) {
let dayEph5m = 0
let dayEph1h = 0
for (const key of usageKeys) {
const data = usageDataMap.get(key)
if (data) {
@@ -825,6 +866,8 @@ router.get('/usage-trend', authenticateAdmin, async (req, res) => {
dayRequests += parseInt(data.requests) || 0
dayCacheCreateTokens += parseInt(data.cacheCreateTokens) || 0
dayCacheReadTokens += parseInt(data.cacheReadTokens) || 0
dayEph5m += parseInt(data.ephemeral5mTokens) || 0
dayEph1h += parseInt(data.ephemeral1hTokens) || 0
}
}
@@ -834,6 +877,13 @@ router.get('/usage-trend', authenticateAdmin, async (req, res) => {
cache_creation_input_tokens: dayCacheCreateTokens,
cache_read_input_tokens: dayCacheReadTokens
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
if (dayEph5m > 0 || dayEph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: dayEph5m,
ephemeral_1h_input_tokens: dayEph1h
}
}
const costResult = CostCalculator.calculateCost(usage, 'unknown')
dayCost = costResult.costs.total
}
@@ -1097,6 +1147,16 @@ router.get('/api-keys/:keyId/model-stats', authenticateAdmin, async (req, res) =
cache_read_input_tokens: usageData.cacheReadTokens || 0
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
const histEph5m = usageData.ephemeral5mTokens || 0
const histEph1h = usageData.ephemeral1hTokens || 0
if (histEph5m > 0 || histEph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: histEph5m,
ephemeral_1h_input_tokens: histEph1h
}
}
// 对于汇总数据,使用默认模型计算费用
const costData = CostCalculator.calculateCost(usage, 'claude-3-5-sonnet-20241022')
@@ -1472,6 +1532,15 @@ router.get('/account-usage-trend', authenticateAdmin, async (req, res) => {
cache_creation_input_tokens: cacheCreateTokens,
cache_read_input_tokens: cacheReadTokens
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
const fbEph5m = parseInt(data.ephemeral5mTokens) || 0
const fbEph1h = parseInt(data.ephemeral1hTokens) || 0
if (fbEph5m > 0 || fbEph1h > 0) {
fallbackUsage.cache_creation = {
ephemeral_5m_input_tokens: fbEph5m,
ephemeral_1h_input_tokens: fbEph1h
}
}
const fallbackResult = CostCalculator.calculateCost(fallbackUsage, fallbackModel)
cost = fallbackResult.costs.total
}
@@ -1640,6 +1709,15 @@ router.get('/account-usage-trend', authenticateAdmin, async (req, res) => {
cache_creation_input_tokens: cacheCreateTokens,
cache_read_input_tokens: cacheReadTokens
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
const fbEph5m = parseInt(data.ephemeral5mTokens) || 0
const fbEph1h = parseInt(data.ephemeral1hTokens) || 0
if (fbEph5m > 0 || fbEph1h > 0) {
fallbackUsage.cache_creation = {
ephemeral_5m_input_tokens: fbEph5m,
ephemeral_1h_input_tokens: fbEph1h
}
}
const fallbackResult = CostCalculator.calculateCost(fallbackUsage, fallbackModel)
cost = fallbackResult.costs.total
}
@@ -1834,7 +1912,9 @@ router.get('/api-keys-usage-trend', authenticateAdmin, async (req, res) => {
inputTokens,
outputTokens,
cacheCreateTokens,
cacheReadTokens
cacheReadTokens,
ephemeral5mTokens: parseInt(data.ephemeral5mTokens) || 0,
ephemeral1hTokens: parseInt(data.ephemeral1hTokens) || 0
})
}
@@ -1860,6 +1940,16 @@ router.get('/api-keys-usage-trend', authenticateAdmin, async (req, res) => {
cache_read_input_tokens: parseInt(modelData.cacheReadTokens) || 0
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
const eph5m = parseInt(modelData.ephemeral5mTokens) || 0
const eph1h = parseInt(modelData.ephemeral1hTokens) || 0
if (eph5m > 0 || eph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: eph5m,
ephemeral_1h_input_tokens: eph1h
}
}
const costResult = CostCalculator.calculateCost(usage, model)
const currentCost = apiKeyCostMap.get(apiKeyId) || 0
apiKeyCostMap.set(apiKeyId, currentCost + costResult.costs.total)
@@ -1878,6 +1968,12 @@ router.get('/api-keys-usage-trend', authenticateAdmin, async (req, res) => {
cache_creation_input_tokens: data.cacheCreateTokens,
cache_read_input_tokens: data.cacheReadTokens
}
if (data.ephemeral5mTokens > 0 || data.ephemeral1hTokens > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: data.ephemeral5mTokens,
ephemeral_1h_input_tokens: data.ephemeral1hTokens
}
}
const fallbackResult = CostCalculator.calculateCost(usage, 'claude-3-5-sonnet-20241022')
cost = fallbackResult.costs.total
formattedCost = fallbackResult.formatted.total
@@ -1994,7 +2090,9 @@ router.get('/api-keys-usage-trend', authenticateAdmin, async (req, res) => {
inputTokens,
outputTokens,
cacheCreateTokens,
cacheReadTokens
cacheReadTokens,
ephemeral5mTokens: parseInt(data.ephemeral5mTokens) || 0,
ephemeral1hTokens: parseInt(data.ephemeral1hTokens) || 0
})
}
@@ -2020,6 +2118,16 @@ router.get('/api-keys-usage-trend', authenticateAdmin, async (req, res) => {
cache_read_input_tokens: parseInt(modelData.cacheReadTokens) || 0
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
const eph5m = parseInt(modelData.ephemeral5mTokens) || 0
const eph1h = parseInt(modelData.ephemeral1hTokens) || 0
if (eph5m > 0 || eph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: eph5m,
ephemeral_1h_input_tokens: eph1h
}
}
const costResult = CostCalculator.calculateCost(usage, model)
const currentCost = apiKeyCostMap.get(apiKeyId) || 0
apiKeyCostMap.set(apiKeyId, currentCost + costResult.costs.total)
@@ -2038,6 +2146,12 @@ router.get('/api-keys-usage-trend', authenticateAdmin, async (req, res) => {
cache_creation_input_tokens: data.cacheCreateTokens,
cache_read_input_tokens: data.cacheReadTokens
}
if (data.ephemeral5mTokens > 0 || data.ephemeral1hTokens > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: data.ephemeral5mTokens,
ephemeral_1h_input_tokens: data.ephemeral1hTokens
}
}
const fallbackResult = CostCalculator.calculateCost(usage, 'claude-3-5-sonnet-20241022')
cost = fallbackResult.costs.total
formattedCost = fallbackResult.formatted.total
@@ -2189,7 +2303,9 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
inputTokens: 0,
outputTokens: 0,
cacheCreateTokens: 0,
cacheReadTokens: 0
cacheReadTokens: 0,
ephemeral5mTokens: 0,
ephemeral1hTokens: 0
})
}
@@ -2198,6 +2314,8 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
modelUsage.outputTokens += parseInt(data.outputTokens) || 0
modelUsage.cacheCreateTokens += parseInt(data.cacheCreateTokens) || 0
modelUsage.cacheReadTokens += parseInt(data.cacheReadTokens) || 0
modelUsage.ephemeral5mTokens += parseInt(data.ephemeral5mTokens) || 0
modelUsage.ephemeral1hTokens += parseInt(data.ephemeral1hTokens) || 0
}
// 计算7天统计的费用
@@ -2211,6 +2329,14 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
cache_read_input_tokens: usage.cacheReadTokens
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
if (usage.ephemeral5mTokens > 0 || usage.ephemeral1hTokens > 0) {
usageData.cache_creation = {
ephemeral_5m_input_tokens: usage.ephemeral5mTokens,
ephemeral_1h_input_tokens: usage.ephemeral1hTokens
}
}
const costResult = CostCalculator.calculateCost(usageData, model)
totalCosts.inputCost += costResult.costs.input
totalCosts.outputCost += costResult.costs.output
@@ -2290,7 +2416,9 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
inputTokens: 0,
outputTokens: 0,
cacheCreateTokens: 0,
cacheReadTokens: 0
cacheReadTokens: 0,
ephemeral5mTokens: 0,
ephemeral1hTokens: 0
})
}
@@ -2299,6 +2427,8 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
modelUsage.outputTokens += parseInt(data.outputTokens) || 0
modelUsage.cacheCreateTokens += parseInt(data.cacheCreateTokens) || 0
modelUsage.cacheReadTokens += parseInt(data.cacheReadTokens) || 0
modelUsage.ephemeral5mTokens += parseInt(data.ephemeral5mTokens) || 0
modelUsage.ephemeral1hTokens += parseInt(data.ephemeral1hTokens) || 0
}
// 使用模型级别的数据计算费用
@@ -2312,6 +2442,14 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
cache_read_input_tokens: usage.cacheReadTokens
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
if (usage.ephemeral5mTokens > 0 || usage.ephemeral1hTokens > 0) {
usageData.cache_creation = {
ephemeral_5m_input_tokens: usage.ephemeral5mTokens,
ephemeral_1h_input_tokens: usage.ephemeral1hTokens
}
}
const costResult = CostCalculator.calculateCost(usageData, model)
totalCosts.inputCost += costResult.costs.input
totalCosts.outputCost += costResult.costs.output
@@ -2352,6 +2490,16 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
cache_read_input_tokens: apiKey.usage.total.cacheReadTokens || 0
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
const totalEph5m = apiKey.usage.total.ephemeral5mTokens || 0
const totalEph1h = apiKey.usage.total.ephemeral1hTokens || 0
if (totalEph5m > 0 || totalEph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: totalEph5m,
ephemeral_1h_input_tokens: totalEph1h
}
}
// 使用加权平均价格计算(基于当前活跃模型的价格分布)
const costResult = CostCalculator.calculateCost(usage, 'claude-3-5-haiku-20241022')
totalCosts.inputCost += costResult.costs.input
@@ -2424,6 +2572,16 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
cache_read_input_tokens: parseInt(data.cacheReadTokens) || 0
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
const eph5m = parseInt(data.ephemeral5mTokens) || 0
const eph1h = parseInt(data.ephemeral1hTokens) || 0
if (eph5m > 0 || eph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: eph5m,
ephemeral_1h_input_tokens: eph1h
}
}
const costResult = CostCalculator.calculateCost(usage, model)
// 累加总费用
@@ -2564,13 +2722,27 @@ router.get('/api-keys/:keyId/usage-records', authenticateAdmin, async (req, res)
return null
}
const toUsageObject = (record) => ({
input_tokens: record.inputTokens || 0,
output_tokens: record.outputTokens || 0,
cache_creation_input_tokens: record.cacheCreateTokens || 0,
cache_read_input_tokens: record.cacheReadTokens || 0,
cache_creation: record.cacheCreation || record.cache_creation || null
})
const toUsageObject = (record) => {
const usage = {
input_tokens: record.inputTokens || 0,
output_tokens: record.outputTokens || 0,
cache_creation_input_tokens: record.cacheCreateTokens || 0,
cache_read_input_tokens: record.cacheReadTokens || 0,
cache_creation: record.cacheCreation || record.cache_creation || null
}
// 如果没有 cache_creation 但有独立存储的 ephemeral 字段,构建子对象
if (!usage.cache_creation) {
const eph5m = parseInt(record.ephemeral5mTokens) || 0
const eph1h = parseInt(record.ephemeral1hTokens) || 0
if (eph5m > 0 || eph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: eph5m,
ephemeral_1h_input_tokens: eph1h
}
}
}
return usage
}
const withinRange = (record) => {
if (!record.timestamp) {
@@ -2863,13 +3035,27 @@ router.get('/accounts/:accountId/usage-records', authenticateAdmin, async (req,
keysToUse = [{ id: apiKeyId }]
}
const toUsageObject = (record) => ({
input_tokens: record.inputTokens || 0,
output_tokens: record.outputTokens || 0,
cache_creation_input_tokens: record.cacheCreateTokens || 0,
cache_read_input_tokens: record.cacheReadTokens || 0,
cache_creation: record.cacheCreation || record.cache_creation || null
})
const toUsageObject = (record) => {
const usage = {
input_tokens: record.inputTokens || 0,
output_tokens: record.outputTokens || 0,
cache_creation_input_tokens: record.cacheCreateTokens || 0,
cache_read_input_tokens: record.cacheReadTokens || 0,
cache_creation: record.cacheCreation || record.cache_creation || null
}
// 如果没有 cache_creation 但有独立存储的 ephemeral 字段,构建子对象
if (!usage.cache_creation) {
const eph5m = parseInt(record.ephemeral5mTokens) || 0
const eph1h = parseInt(record.ephemeral1hTokens) || 0
if (eph5m > 0 || eph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: eph5m,
ephemeral_1h_input_tokens: eph1h
}
}
}
return usage
}
const withinRange = (record) => {
if (!record.timestamp) {

View File

@@ -317,6 +317,14 @@ router.post('/api/user-stats', async (req, res) => {
cache_read_input_tokens: usage.cacheReadTokens || 0
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
if (usage.ephemeral5mTokens > 0 || usage.ephemeral1hTokens > 0) {
costUsage.cache_creation = {
ephemeral_5m_input_tokens: usage.ephemeral5mTokens,
ephemeral_1h_input_tokens: usage.ephemeral1hTokens
}
}
const costResult = CostCalculator.calculateCost(costUsage, 'claude-3-5-sonnet-20241022')
totalCost = costResult.costs.total
}
@@ -335,6 +343,14 @@ router.post('/api/user-stats', async (req, res) => {
cache_read_input_tokens: usage.cacheReadTokens || 0
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
if (usage.ephemeral5mTokens > 0 || usage.ephemeral1hTokens > 0) {
costUsage.cache_creation = {
ephemeral_5m_input_tokens: usage.ephemeral5mTokens,
ephemeral_1h_input_tokens: usage.ephemeral1hTokens
}
}
const costResult = CostCalculator.calculateCost(costUsage, 'claude-3-5-sonnet-20241022')
totalCost = costResult.costs.total
formattedCost = costResult.formatted.total
@@ -804,6 +820,8 @@ router.post('/api/batch-model-stats', async (req, res) => {
outputTokens: 0,
cacheCreateTokens: 0,
cacheReadTokens: 0,
ephemeral5mTokens: 0,
ephemeral1hTokens: 0,
allTokens: 0,
realCostMicro: 0,
ratedCostMicro: 0,
@@ -817,6 +835,8 @@ router.post('/api/batch-model-stats', async (req, res) => {
modelUsage.outputTokens += parseInt(data.outputTokens) || 0
modelUsage.cacheCreateTokens += parseInt(data.cacheCreateTokens) || 0
modelUsage.cacheReadTokens += parseInt(data.cacheReadTokens) || 0
modelUsage.ephemeral5mTokens += parseInt(data.ephemeral5mTokens) || 0
modelUsage.ephemeral1hTokens += parseInt(data.ephemeral1hTokens) || 0
modelUsage.allTokens += parseInt(data.allTokens) || 0
modelUsage.realCostMicro += parseInt(data.realCostMicro) || 0
modelUsage.ratedCostMicro += parseInt(data.ratedCostMicro) || 0
@@ -839,6 +859,14 @@ router.post('/api/batch-model-stats', async (req, res) => {
cache_read_input_tokens: usage.cacheReadTokens
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
if (usage.ephemeral5mTokens > 0 || usage.ephemeral1hTokens > 0) {
usageData.cache_creation = {
ephemeral_5m_input_tokens: usage.ephemeral5mTokens,
ephemeral_1h_input_tokens: usage.ephemeral1hTokens
}
}
// 优先使用存储的费用,否则回退到重新计算
const { hasStoredCost } = usage
const costData = CostCalculator.calculateCost(usageData, model)
@@ -1368,6 +1396,8 @@ router.post('/api/user-model-stats', async (req, res) => {
const model = match[1]
if (data && Object.keys(data).length > 0) {
const ephemeral5m = parseInt(data.ephemeral5mTokens) || 0
const ephemeral1h = parseInt(data.ephemeral1hTokens) || 0
const usage = {
input_tokens: parseInt(data.inputTokens) || 0,
output_tokens: parseInt(data.outputTokens) || 0,
@@ -1375,6 +1405,14 @@ router.post('/api/user-model-stats', async (req, res) => {
cache_read_input_tokens: parseInt(data.cacheReadTokens) || 0
}
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
if (ephemeral5m > 0 || ephemeral1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: ephemeral5m,
ephemeral_1h_input_tokens: ephemeral1h
}
}
// 优先使用存储的费用,否则回退到重新计算
// 检查字段是否存在(而非 > 0以支持真正的零成本场景
const realCostMicro = parseInt(data.realCostMicro) || 0

View File

@@ -1599,6 +1599,8 @@ class ApiKeyService {
outputTokens,
cacheCreateTokens,
cacheReadTokens,
0, // ephemeral5mTokens - recordUsage 不含详细缓存数据
0, // ephemeral1hTokens - recordUsage 不含详细缓存数据
model,
isLongContextRequest
)
@@ -1834,6 +1836,8 @@ class ApiKeyService {
outputTokens,
cacheCreateTokens,
cacheReadTokens,
ephemeral5mTokens,
ephemeral1hTokens,
model,
costInfo.isLongContextRequest || false
)

View File

@@ -201,6 +201,16 @@ class CostInitService {
parseInt(data.totalCacheReadTokens) || parseInt(data.cacheReadTokens) || 0
}
// 添加 cache_creation 子对象以支持精确 ephemeral 定价
const eph5m = parseInt(data.ephemeral5mTokens) || 0
const eph1h = parseInt(data.ephemeral1hTokens) || 0
if (eph5m > 0 || eph1h > 0) {
usage.cache_creation = {
ephemeral_5m_input_tokens: eph5m,
ephemeral_1h_input_tokens: eph1h
}
}
const costResult = CostCalculator.calculateCost(usage, model)
const cost = costResult.costs.total

View File

@@ -1275,6 +1275,8 @@ class DroidRelayService {
usageObject.output_tokens || 0,
usageObject.cache_creation_input_tokens || 0,
usageObject.cache_read_input_tokens || 0,
0, // ephemeral5mTokens - Droid 不含详细缓存数据
0, // ephemeral1hTokens - Droid 不含详细缓存数据
model,
false
)