mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-03-29 23:14:57 +00:00
fix: 添加对 ephemeral 5m 和 1h 令牌的支持,优化费用计算逻辑
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -247,3 +247,5 @@ web/apiStats/
|
||||
|
||||
# Admin SPA build files
|
||||
web/admin-spa/dist/
|
||||
|
||||
.serena/
|
||||
|
||||
@@ -1084,6 +1084,9 @@ class RedisClient {
|
||||
pipeline.hincrby(modelDaily, 'cacheReadTokens', finalCacheReadTokens)
|
||||
pipeline.hincrby(modelDaily, 'allTokens', totalTokens)
|
||||
pipeline.hincrby(modelDaily, 'requests', 1)
|
||||
// 详细缓存类型统计
|
||||
pipeline.hincrby(modelDaily, 'ephemeral5mTokens', ephemeral5mTokens)
|
||||
pipeline.hincrby(modelDaily, 'ephemeral1hTokens', ephemeral1hTokens)
|
||||
|
||||
// 按模型统计 - 每月
|
||||
pipeline.hincrby(modelMonthly, 'inputTokens', finalInputTokens)
|
||||
@@ -1092,6 +1095,9 @@ class RedisClient {
|
||||
pipeline.hincrby(modelMonthly, 'cacheReadTokens', finalCacheReadTokens)
|
||||
pipeline.hincrby(modelMonthly, 'allTokens', totalTokens)
|
||||
pipeline.hincrby(modelMonthly, 'requests', 1)
|
||||
// 详细缓存类型统计
|
||||
pipeline.hincrby(modelMonthly, 'ephemeral5mTokens', ephemeral5mTokens)
|
||||
pipeline.hincrby(modelMonthly, 'ephemeral1hTokens', ephemeral1hTokens)
|
||||
|
||||
// API Key级别的模型统计 - 每日
|
||||
pipeline.hincrby(keyModelDaily, 'inputTokens', finalInputTokens)
|
||||
@@ -1136,6 +1142,9 @@ class RedisClient {
|
||||
pipeline.hincrby(keyModelAlltime, 'cacheCreateTokens', finalCacheCreateTokens)
|
||||
pipeline.hincrby(keyModelAlltime, 'cacheReadTokens', finalCacheReadTokens)
|
||||
pipeline.hincrby(keyModelAlltime, 'requests', 1)
|
||||
// 详细缓存类型统计
|
||||
pipeline.hincrby(keyModelAlltime, 'ephemeral5mTokens', ephemeral5mTokens)
|
||||
pipeline.hincrby(keyModelAlltime, 'ephemeral1hTokens', ephemeral1hTokens)
|
||||
// 费用统计
|
||||
if (realCost > 0) {
|
||||
pipeline.hincrby(keyModelAlltime, 'realCostMicro', Math.round(realCost * 1000000))
|
||||
@@ -1152,6 +1161,9 @@ class RedisClient {
|
||||
pipeline.hincrby(hourly, 'cacheReadTokens', finalCacheReadTokens)
|
||||
pipeline.hincrby(hourly, 'allTokens', totalTokens)
|
||||
pipeline.hincrby(hourly, 'requests', 1)
|
||||
// 详细缓存类型统计
|
||||
pipeline.hincrby(hourly, 'ephemeral5mTokens', ephemeral5mTokens)
|
||||
pipeline.hincrby(hourly, 'ephemeral1hTokens', ephemeral1hTokens)
|
||||
|
||||
// 按模型统计 - 每小时
|
||||
pipeline.hincrby(modelHourly, 'inputTokens', finalInputTokens)
|
||||
@@ -1160,6 +1172,9 @@ class RedisClient {
|
||||
pipeline.hincrby(modelHourly, 'cacheReadTokens', finalCacheReadTokens)
|
||||
pipeline.hincrby(modelHourly, 'allTokens', totalTokens)
|
||||
pipeline.hincrby(modelHourly, 'requests', 1)
|
||||
// 详细缓存类型统计
|
||||
pipeline.hincrby(modelHourly, 'ephemeral5mTokens', ephemeral5mTokens)
|
||||
pipeline.hincrby(modelHourly, 'ephemeral1hTokens', ephemeral1hTokens)
|
||||
|
||||
// API Key级别的模型统计 - 每小时
|
||||
pipeline.hincrby(keyModelHourly, 'inputTokens', finalInputTokens)
|
||||
@@ -1168,6 +1183,9 @@ class RedisClient {
|
||||
pipeline.hincrby(keyModelHourly, 'cacheReadTokens', finalCacheReadTokens)
|
||||
pipeline.hincrby(keyModelHourly, 'allTokens', totalTokens)
|
||||
pipeline.hincrby(keyModelHourly, 'requests', 1)
|
||||
// 详细缓存类型统计
|
||||
pipeline.hincrby(keyModelHourly, 'ephemeral5mTokens', ephemeral5mTokens)
|
||||
pipeline.hincrby(keyModelHourly, 'ephemeral1hTokens', ephemeral1hTokens)
|
||||
// 费用统计
|
||||
if (realCost > 0) {
|
||||
pipeline.hincrby(keyModelHourly, 'realCostMicro', Math.round(realCost * 1000000))
|
||||
@@ -1235,18 +1253,24 @@ class RedisClient {
|
||||
pipeline.hincrby('usage:global:total', 'cacheCreateTokens', finalCacheCreateTokens)
|
||||
pipeline.hincrby('usage:global:total', 'cacheReadTokens', finalCacheReadTokens)
|
||||
pipeline.hincrby('usage:global:total', 'allTokens', totalTokens)
|
||||
pipeline.hincrby('usage:global:total', 'ephemeral5mTokens', ephemeral5mTokens)
|
||||
pipeline.hincrby('usage:global:total', 'ephemeral1hTokens', ephemeral1hTokens)
|
||||
pipeline.hincrby(globalDaily, 'requests', 1)
|
||||
pipeline.hincrby(globalDaily, 'inputTokens', finalInputTokens)
|
||||
pipeline.hincrby(globalDaily, 'outputTokens', finalOutputTokens)
|
||||
pipeline.hincrby(globalDaily, 'cacheCreateTokens', finalCacheCreateTokens)
|
||||
pipeline.hincrby(globalDaily, 'cacheReadTokens', finalCacheReadTokens)
|
||||
pipeline.hincrby(globalDaily, 'allTokens', totalTokens)
|
||||
pipeline.hincrby(globalDaily, 'ephemeral5mTokens', ephemeral5mTokens)
|
||||
pipeline.hincrby(globalDaily, 'ephemeral1hTokens', ephemeral1hTokens)
|
||||
pipeline.hincrby(globalMonthly, 'requests', 1)
|
||||
pipeline.hincrby(globalMonthly, 'inputTokens', finalInputTokens)
|
||||
pipeline.hincrby(globalMonthly, 'outputTokens', finalOutputTokens)
|
||||
pipeline.hincrby(globalMonthly, 'cacheCreateTokens', finalCacheCreateTokens)
|
||||
pipeline.hincrby(globalMonthly, 'cacheReadTokens', finalCacheReadTokens)
|
||||
pipeline.hincrby(globalMonthly, 'allTokens', totalTokens)
|
||||
pipeline.hincrby(globalMonthly, 'ephemeral5mTokens', ephemeral5mTokens)
|
||||
pipeline.hincrby(globalMonthly, 'ephemeral1hTokens', ephemeral1hTokens)
|
||||
pipeline.expire(globalDaily, 86400 * 32)
|
||||
pipeline.expire(globalMonthly, 86400 * 365)
|
||||
|
||||
|
||||
@@ -1289,6 +1289,8 @@ async function calculateKeyStats(keyId, timeRange, startDate, endDate) {
|
||||
outputTokens: 0,
|
||||
cacheCreateTokens: 0,
|
||||
cacheReadTokens: 0,
|
||||
ephemeral5mTokens: 0,
|
||||
ephemeral1hTokens: 0,
|
||||
requests: 0
|
||||
})
|
||||
}
|
||||
@@ -1300,6 +1302,10 @@ async function calculateKeyStats(keyId, timeRange, startDate, endDate) {
|
||||
parseInt(data.totalCacheCreateTokens) || parseInt(data.cacheCreateTokens) || 0
|
||||
stats.cacheReadTokens +=
|
||||
parseInt(data.totalCacheReadTokens) || parseInt(data.cacheReadTokens) || 0
|
||||
stats.ephemeral5mTokens +=
|
||||
parseInt(data.totalEphemeral5mTokens) || parseInt(data.ephemeral5mTokens) || 0
|
||||
stats.ephemeral1hTokens +=
|
||||
parseInt(data.totalEphemeral1hTokens) || parseInt(data.ephemeral1hTokens) || 0
|
||||
stats.requests += parseInt(data.totalRequests) || parseInt(data.requests) || 0
|
||||
|
||||
totalRequests += parseInt(data.totalRequests) || parseInt(data.requests) || 0
|
||||
@@ -1318,15 +1324,22 @@ async function calculateKeyStats(keyId, timeRange, startDate, endDate) {
|
||||
cacheCreateTokens += stats.cacheCreateTokens
|
||||
cacheReadTokens += stats.cacheReadTokens
|
||||
|
||||
const costResult = CostCalculator.calculateCost(
|
||||
{
|
||||
const costUsage = {
|
||||
input_tokens: stats.inputTokens,
|
||||
output_tokens: stats.outputTokens,
|
||||
cache_creation_input_tokens: stats.cacheCreateTokens,
|
||||
cache_read_input_tokens: stats.cacheReadTokens
|
||||
},
|
||||
model
|
||||
)
|
||||
}
|
||||
|
||||
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
|
||||
if (stats.ephemeral5mTokens > 0 || stats.ephemeral1hTokens > 0) {
|
||||
costUsage.cache_creation = {
|
||||
ephemeral_5m_input_tokens: stats.ephemeral5mTokens,
|
||||
ephemeral_1h_input_tokens: stats.ephemeral1hTokens
|
||||
}
|
||||
}
|
||||
|
||||
const costResult = CostCalculator.calculateCost(costUsage, model)
|
||||
totalCost += costResult.costs.total
|
||||
}
|
||||
|
||||
|
||||
@@ -472,7 +472,9 @@ router.get('/model-stats', authenticateAdmin, async (req, res) => {
|
||||
outputTokens: 0,
|
||||
cacheCreateTokens: 0,
|
||||
cacheReadTokens: 0,
|
||||
allTokens: 0
|
||||
allTokens: 0,
|
||||
ephemeral5mTokens: 0,
|
||||
ephemeral1hTokens: 0
|
||||
}
|
||||
|
||||
stats.requests += parseInt(data.requests) || 0
|
||||
@@ -481,6 +483,8 @@ router.get('/model-stats', authenticateAdmin, async (req, res) => {
|
||||
stats.cacheCreateTokens += parseInt(data.cacheCreateTokens) || 0
|
||||
stats.cacheReadTokens += parseInt(data.cacheReadTokens) || 0
|
||||
stats.allTokens += parseInt(data.allTokens) || 0
|
||||
stats.ephemeral5mTokens += parseInt(data.ephemeral5mTokens) || 0
|
||||
stats.ephemeral1hTokens += parseInt(data.ephemeral1hTokens) || 0
|
||||
|
||||
modelStatsMap.set(normalizedModel, stats)
|
||||
}
|
||||
@@ -497,6 +501,14 @@ router.get('/model-stats', authenticateAdmin, async (req, res) => {
|
||||
cache_read_input_tokens: stats.cacheReadTokens
|
||||
}
|
||||
|
||||
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
|
||||
if (stats.ephemeral5mTokens > 0 || stats.ephemeral1hTokens > 0) {
|
||||
usage.cache_creation = {
|
||||
ephemeral_5m_input_tokens: stats.ephemeral5mTokens,
|
||||
ephemeral_1h_input_tokens: stats.ephemeral1hTokens
|
||||
}
|
||||
}
|
||||
|
||||
// 计算费用
|
||||
const costData = CostCalculator.calculateCost(usage, model)
|
||||
|
||||
|
||||
@@ -786,6 +786,8 @@ router.get('/usage-trend', authenticateAdmin, async (req, res) => {
|
||||
const modelOutputTokens = parseInt(data.outputTokens) || 0
|
||||
const modelCacheCreateTokens = parseInt(data.cacheCreateTokens) || 0
|
||||
const modelCacheReadTokens = parseInt(data.cacheReadTokens) || 0
|
||||
const modelEphemeral5mTokens = parseInt(data.ephemeral5mTokens) || 0
|
||||
const modelEphemeral1hTokens = parseInt(data.ephemeral1hTokens) || 0
|
||||
const modelRequests = parseInt(data.requests) || 0
|
||||
|
||||
dayInputTokens += modelInputTokens
|
||||
@@ -800,6 +802,15 @@ router.get('/usage-trend', authenticateAdmin, async (req, res) => {
|
||||
cache_creation_input_tokens: modelCacheCreateTokens,
|
||||
cache_read_input_tokens: modelCacheReadTokens
|
||||
}
|
||||
|
||||
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
|
||||
if (modelEphemeral5mTokens > 0 || modelEphemeral1hTokens > 0) {
|
||||
modelUsage.cache_creation = {
|
||||
ephemeral_5m_input_tokens: modelEphemeral5mTokens,
|
||||
ephemeral_1h_input_tokens: modelEphemeral1hTokens
|
||||
}
|
||||
}
|
||||
|
||||
const modelCostResult = CostCalculator.calculateCost(modelUsage, model)
|
||||
dayCost += modelCostResult.costs.total
|
||||
}
|
||||
@@ -948,6 +959,8 @@ router.get('/api-keys/:keyId/model-stats', authenticateAdmin, async (req, res) =
|
||||
outputTokens: 0,
|
||||
cacheCreateTokens: 0,
|
||||
cacheReadTokens: 0,
|
||||
ephemeral5mTokens: 0,
|
||||
ephemeral1hTokens: 0,
|
||||
allTokens: 0
|
||||
})
|
||||
}
|
||||
@@ -957,6 +970,8 @@ router.get('/api-keys/:keyId/model-stats', authenticateAdmin, async (req, res) =
|
||||
stats.outputTokens += parseInt(data.outputTokens) || 0
|
||||
stats.cacheCreateTokens += parseInt(data.cacheCreateTokens) || 0
|
||||
stats.cacheReadTokens += parseInt(data.cacheReadTokens) || 0
|
||||
stats.ephemeral5mTokens += parseInt(data.ephemeral5mTokens) || 0
|
||||
stats.ephemeral1hTokens += parseInt(data.ephemeral1hTokens) || 0
|
||||
stats.allTokens += parseInt(data.allTokens) || 0
|
||||
}
|
||||
}
|
||||
@@ -992,6 +1007,8 @@ router.get('/api-keys/:keyId/model-stats', authenticateAdmin, async (req, res) =
|
||||
outputTokens: 0,
|
||||
cacheCreateTokens: 0,
|
||||
cacheReadTokens: 0,
|
||||
ephemeral5mTokens: 0,
|
||||
ephemeral1hTokens: 0,
|
||||
allTokens: 0
|
||||
})
|
||||
}
|
||||
@@ -1001,6 +1018,8 @@ router.get('/api-keys/:keyId/model-stats', authenticateAdmin, async (req, res) =
|
||||
stats.outputTokens += parseInt(data.outputTokens) || 0
|
||||
stats.cacheCreateTokens += parseInt(data.cacheCreateTokens) || 0
|
||||
stats.cacheReadTokens += parseInt(data.cacheReadTokens) || 0
|
||||
stats.ephemeral5mTokens += parseInt(data.ephemeral5mTokens) || 0
|
||||
stats.ephemeral1hTokens += parseInt(data.ephemeral1hTokens) || 0
|
||||
stats.allTokens += parseInt(data.allTokens) || 0
|
||||
}
|
||||
}
|
||||
@@ -1016,6 +1035,14 @@ router.get('/api-keys/:keyId/model-stats', authenticateAdmin, async (req, res) =
|
||||
cache_read_input_tokens: stats.cacheReadTokens
|
||||
}
|
||||
|
||||
// 如果有 ephemeral 5m/1h 拆分数据,添加 cache_creation 子对象以实现精确计费
|
||||
if (stats.ephemeral5mTokens > 0 || stats.ephemeral1hTokens > 0) {
|
||||
usage.cache_creation = {
|
||||
ephemeral_5m_input_tokens: stats.ephemeral5mTokens,
|
||||
ephemeral_1h_input_tokens: stats.ephemeral1hTokens
|
||||
}
|
||||
}
|
||||
|
||||
// 使用CostCalculator计算费用
|
||||
const costData = CostCalculator.calculateCost(usage, model)
|
||||
|
||||
@@ -1424,6 +1451,16 @@ router.get('/account-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, modelName)
|
||||
cost += costResult.costs.total
|
||||
}
|
||||
@@ -1582,6 +1619,16 @@ router.get('/account-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, modelName)
|
||||
cost += costResult.costs.total
|
||||
}
|
||||
|
||||
@@ -270,7 +270,9 @@ router.post('/api/user-stats', async (req, res) => {
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cacheCreateTokens: 0,
|
||||
cacheReadTokens: 0
|
||||
cacheReadTokens: 0,
|
||||
ephemeral5mTokens: 0,
|
||||
ephemeral1hTokens: 0
|
||||
})
|
||||
}
|
||||
|
||||
@@ -279,6 +281,8 @@ router.post('/api/user-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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,6 +295,14 @@ router.post('/api/user-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 costResult = CostCalculator.calculateCost(usageData, model)
|
||||
totalCost += costResult.costs.total
|
||||
}
|
||||
|
||||
@@ -607,6 +607,16 @@ class AccountBalanceService {
|
||||
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)
|
||||
totalCost += costResult.costs.total || 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user