mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 00:53:33 +00:00
feat: add model name normalization for statistics aggregation in Redis and admin routes
This commit is contained in:
@@ -2095,6 +2095,24 @@ router.get('/model-stats', authenticateAdmin, async (req, res) => {
|
||||
|
||||
logger.info(`📊 Found ${allKeys.length} matching keys in total`);
|
||||
|
||||
// 模型名标准化函数(与redis.js保持一致)
|
||||
const normalizeModelName = (model) => {
|
||||
if (!model || model === 'unknown') return model;
|
||||
|
||||
// 对于Bedrock模型,去掉区域前缀进行统一
|
||||
if (model.includes('.anthropic.') || model.includes('.claude')) {
|
||||
// 匹配所有AWS区域格式:region.anthropic.model-name-v1:0 -> claude-model-name
|
||||
// 支持所有AWS区域格式,如:us-east-1, eu-west-1, ap-southeast-1, ca-central-1等
|
||||
let normalized = model.replace(/^[a-z0-9-]+\./, ''); // 去掉任何区域前缀(更通用)
|
||||
normalized = normalized.replace('anthropic.', ''); // 去掉anthropic前缀
|
||||
normalized = normalized.replace(/-v\d+:\d+$/, ''); // 去掉版本后缀(如-v1:0, -v2:1等)
|
||||
return normalized;
|
||||
}
|
||||
|
||||
// 对于其他模型,去掉常见的版本后缀
|
||||
return model.replace(/-v\d+:\d+$|:latest$/, '');
|
||||
};
|
||||
|
||||
// 聚合相同模型的数据
|
||||
const modelStatsMap = new Map();
|
||||
|
||||
@@ -2106,11 +2124,12 @@ router.get('/model-stats', authenticateAdmin, async (req, res) => {
|
||||
continue;
|
||||
}
|
||||
|
||||
const model = match[1];
|
||||
const rawModel = match[1];
|
||||
const normalizedModel = normalizeModelName(rawModel);
|
||||
const data = await client.hgetall(key);
|
||||
|
||||
if (data && Object.keys(data).length > 0) {
|
||||
const stats = modelStatsMap.get(model) || {
|
||||
const stats = modelStatsMap.get(normalizedModel) || {
|
||||
requests: 0,
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
@@ -2126,7 +2145,7 @@ router.get('/model-stats', authenticateAdmin, async (req, res) => {
|
||||
stats.cacheReadTokens += parseInt(data.cacheReadTokens) || 0;
|
||||
stats.allTokens += parseInt(data.allTokens) || 0;
|
||||
|
||||
modelStatsMap.set(model, stats);
|
||||
modelStatsMap.set(normalizedModel, stats);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2950,6 +2969,24 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
|
||||
|
||||
logger.info(`💰 Calculating usage costs for period: ${period}`);
|
||||
|
||||
// 模型名标准化函数(与redis.js保持一致)
|
||||
const normalizeModelName = (model) => {
|
||||
if (!model || model === 'unknown') return model;
|
||||
|
||||
// 对于Bedrock模型,去掉区域前缀进行统一
|
||||
if (model.includes('.anthropic.') || model.includes('.claude')) {
|
||||
// 匹配所有AWS区域格式:region.anthropic.model-name-v1:0 -> claude-model-name
|
||||
// 支持所有AWS区域格式,如:us-east-1, eu-west-1, ap-southeast-1, ca-central-1等
|
||||
let normalized = model.replace(/^[a-z0-9-]+\./, ''); // 去掉任何区域前缀(更通用)
|
||||
normalized = normalized.replace('anthropic.', ''); // 去掉anthropic前缀
|
||||
normalized = normalized.replace(/-v\d+:\d+$/, ''); // 去掉版本后缀(如-v1:0, -v2:1等)
|
||||
return normalized;
|
||||
}
|
||||
|
||||
// 对于其他模型,去掉常见的版本后缀
|
||||
return model.replace(/-v\d+:\d+$|:latest$/, '');
|
||||
};
|
||||
|
||||
// 获取所有API Keys的使用统计
|
||||
const apiKeys = await apiKeyService.getAllApiKeys();
|
||||
|
||||
@@ -2992,12 +3029,13 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
|
||||
const modelMatch = key.match(/usage:model:daily:(.+):\d{4}-\d{2}-\d{2}$/);
|
||||
if (!modelMatch) continue;
|
||||
|
||||
const model = modelMatch[1];
|
||||
const rawModel = modelMatch[1];
|
||||
const normalizedModel = normalizeModelName(rawModel);
|
||||
const data = await client.hgetall(key);
|
||||
|
||||
if (data && Object.keys(data).length > 0) {
|
||||
if (!modelUsageMap.has(model)) {
|
||||
modelUsageMap.set(model, {
|
||||
if (!modelUsageMap.has(normalizedModel)) {
|
||||
modelUsageMap.set(normalizedModel, {
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cacheCreateTokens: 0,
|
||||
@@ -3005,7 +3043,7 @@ router.get('/usage-costs', authenticateAdmin, async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
const modelUsage = modelUsageMap.get(model);
|
||||
const modelUsage = modelUsageMap.get(normalizedModel);
|
||||
modelUsage.inputTokens += parseInt(data.inputTokens) || 0;
|
||||
modelUsage.outputTokens += parseInt(data.outputTokens) || 0;
|
||||
modelUsage.cacheCreateTokens += parseInt(data.cacheCreateTokens) || 0;
|
||||
|
||||
Reference in New Issue
Block a user