mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-03-30 00:33:35 +00:00
feat: 添加对 ephemeral 5m 和 1h 令牌的支持,优化费用计算逻辑
This commit is contained in:
@@ -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') {
|
||||
|
||||
@@ -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}`)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user