diff --git a/src/converters/strategies/ClaudeConverter.js b/src/converters/strategies/ClaudeConverter.js index fd4e8f2..35c49a8 100644 --- a/src/converters/strategies/ClaudeConverter.js +++ b/src/converters/strategies/ClaudeConverter.js @@ -315,6 +315,10 @@ export class ClaudeConverter extends BaseConverter { prompt_tokens: claudeResponse.usage?.input_tokens || 0, completion_tokens: claudeResponse.usage?.output_tokens || 0, total_tokens: (claudeResponse.usage?.input_tokens || 0) + (claudeResponse.usage?.output_tokens || 0), + cached_tokens: claudeResponse.usage?.cache_read_input_tokens || 0, + prompt_tokens_details: { + cached_tokens: claudeResponse.usage?.cache_read_input_tokens || 0 + } }, }; } diff --git a/src/plugins/api-potluck/api-routes.js b/src/plugins/api-potluck/api-routes.js index 2c8d9dd..9253e67 100644 --- a/src/plugins/api-potluck/api-routes.js +++ b/src/plugins/api-potluck/api-routes.js @@ -423,13 +423,15 @@ export async function handlePotluckUserApiRoutes(method, path, req, res) { resetDate: keyData.lastResetDate, promptTokens: keyData.todayPromptTokens || 0, completionTokens: keyData.todayCompletionTokens || 0, - totalTokens: keyData.todayTotalTokens || 0 + totalTokens: keyData.todayTotalTokens || 0, + cachedTokens: keyData.todayCachedTokens || 0 }, total: keyData.totalUsage, tokens: { prompt: keyData.totalPromptTokens || 0, completion: keyData.totalCompletionTokens || 0, - total: keyData.totalTokens || 0 + total: keyData.totalTokens || 0, + cached: keyData.totalCachedTokens || 0 }, lastUsedAt: keyData.lastUsedAt, createdAt: keyData.createdAt, diff --git a/src/plugins/api-potluck/index.js b/src/plugins/api-potluck/index.js index e56dd58..fec5bbe 100644 --- a/src/plugins/api-potluck/index.js +++ b/src/plugins/api-potluck/index.js @@ -65,12 +65,20 @@ function normalizeUsageCandidate(candidate) { candidate.total_tokens ?? usage?.total_tokens ?? usage?.totalTokenCount - ) || promptTokens + completionTokens; + ); + + const cachedTokens = toNumber( + candidate.cached_tokens ?? + usage?.cached_tokens ?? + usage?.cache_read_input_tokens ?? + usage?.cachedContentTokenCount + ); return { promptTokens, completionTokens, - totalTokens + totalTokens: totalTokens || (promptTokens + completionTokens), + cachedTokens }; } @@ -79,7 +87,8 @@ function mergeUsage(baseUsage, nextUsage) { return { promptTokens: Math.max(baseUsage.promptTokens, nextUsage.promptTokens), completionTokens: Math.max(baseUsage.completionTokens, nextUsage.completionTokens), - totalTokens: Math.max(baseUsage.totalTokens, nextUsage.totalTokens) + totalTokens: Math.max(baseUsage.totalTokens, nextUsage.totalTokens), + cachedTokens: Math.max(baseUsage.cachedTokens || 0, nextUsage.cachedTokens || 0) }; } @@ -87,7 +96,8 @@ function extractUsage(...candidates) { return candidates.reduce((usage, candidate) => mergeUsage(usage, normalizeUsageCandidate(candidate)), { promptTokens: 0, completionTokens: 0, - totalTokens: 0 + totalTokens: 0, + cachedTokens: 0 }); } diff --git a/src/plugins/api-potluck/key-manager.js b/src/plugins/api-potluck/key-manager.js index e5df5a6..397d6a8 100644 --- a/src/plugins/api-potluck/key-manager.js +++ b/src/plugins/api-potluck/key-manager.js @@ -52,7 +52,8 @@ function createUsageBucket() { requestCount: 0, promptTokens: 0, completionTokens: 0, - totalTokens: 0 + totalTokens: 0, + cachedTokens: 0 }; } @@ -75,7 +76,8 @@ function normalizeUsageBucket(bucket) { requestCount: toNumber(bucket?.requestCount), promptTokens: toNumber(bucket?.promptTokens), completionTokens: toNumber(bucket?.completionTokens), - totalTokens: toNumber(bucket?.totalTokens) + totalTokens: toNumber(bucket?.totalTokens), + cachedTokens: toNumber(bucket?.cachedTokens) }; } @@ -105,9 +107,11 @@ function normalizeKeyData(keyData = {}) { todayPromptTokens: toNumber(keyData.todayPromptTokens), todayCompletionTokens: toNumber(keyData.todayCompletionTokens), todayTotalTokens: toNumber(keyData.todayTotalTokens), + todayCachedTokens: toNumber(keyData.todayCachedTokens), totalPromptTokens: toNumber(keyData.totalPromptTokens), totalCompletionTokens: toNumber(keyData.totalCompletionTokens), totalTokens: toNumber(keyData.totalTokens), + totalCachedTokens: toNumber(keyData.totalCachedTokens), usageHistory: {} }; @@ -131,6 +135,7 @@ function addUsage(target, usage = {}) { target.promptTokens += toNumber(usage.promptTokens); target.completionTokens += toNumber(usage.completionTokens); target.totalTokens += toNumber(usage.totalTokens); + target.cachedTokens += toNumber(usage.cachedTokens); } /** @@ -172,7 +177,7 @@ function syncWriteToFile() { try { const dir = path.dirname(KEYS_STORE_FILE); if (!existsSync(dir)) { - require('fs').mkdirSync(dir, { recursive: true }); + mkdirSync(dir, { recursive: true }); } writeFileSync(KEYS_STORE_FILE, JSON.stringify(keyStore, null, 2), 'utf8'); } catch (error) { @@ -248,6 +253,7 @@ function checkAndResetDailyCount(keyData) { keyData.todayPromptTokens = 0; keyData.todayCompletionTokens = 0; keyData.todayTotalTokens = 0; + keyData.todayCachedTokens = 0; keyData.lastResetDate = today; } return keyData; @@ -255,7 +261,6 @@ function checkAndResetDailyCount(keyData) { /** * 创建新的 API Key - * @param {string} name - Key 名称 * @param {number} [dailyLimit] - 每日限额,不传则使用配置的默认值 */ @@ -278,9 +283,11 @@ export async function createKey(name = '', dailyLimit = null) { todayPromptTokens: 0, todayCompletionTokens: 0, todayTotalTokens: 0, + todayCachedTokens: 0, totalPromptTokens: 0, totalCompletionTokens: 0, totalTokens: 0, + totalCachedTokens: 0, lastResetDate: today, lastUsedAt: null, enabled: true @@ -354,6 +361,7 @@ export async function resetKeyUsage(keyId) { keyStore.keys[keyId].todayPromptTokens = 0; keyStore.keys[keyId].todayCompletionTokens = 0; keyStore.keys[keyId].todayTotalTokens = 0; + keyStore.keys[keyId].todayCachedTokens = 0; keyStore.keys[keyId].lastResetDate = getTodayDateString(); if (!keyStore.keys[keyId].usageHistory) keyStore.keys[keyId].usageHistory = {}; keyStore.keys[keyId].usageHistory[getTodayDateString()] = normalizeUsageHistoryDay(); @@ -448,7 +456,7 @@ export async function validateKey(apiKey) { * @param {string} apiKey - API Key * @param {string} provider - 使用的提供商 * @param {string} model - 使用的模型 - * @param {{promptTokens?: number, completionTokens?: number, totalTokens?: number}} usage - token 用量 + * @param {{promptTokens?: number, completionTokens?: number, totalTokens?: number, cachedTokens?: number}} usage - token 用量 */ export async function incrementUsage(apiKey, provider = 'unknown', model = 'unknown', usage = {}) { ensureLoaded(); @@ -469,9 +477,11 @@ export async function incrementUsage(apiKey, provider = 'unknown', model = 'unkn keyData.todayPromptTokens += toNumber(usage.promptTokens); keyData.todayCompletionTokens += toNumber(usage.completionTokens); keyData.todayTotalTokens += toNumber(usage.totalTokens); + keyData.todayCachedTokens += toNumber(usage.cachedTokens); keyData.totalPromptTokens += toNumber(usage.promptTokens); keyData.totalCompletionTokens += toNumber(usage.completionTokens); keyData.totalTokens += toNumber(usage.totalTokens); + keyData.totalCachedTokens += toNumber(usage.cachedTokens); keyData.lastUsedAt = new Date().toISOString(); // 记录个人按天统计 (每个 Key 独立) @@ -514,8 +524,8 @@ export async function getStats() { ensureLoaded(); const keys = Object.values(keyStore.keys); let enabledKeys = 0, todayTotalUsage = 0, totalUsage = 0; - let todayPromptTokens = 0, todayCompletionTokens = 0, todayTotalTokens = 0; - let totalPromptTokens = 0, totalCompletionTokens = 0, totalTokens = 0; + let todayPromptTokens = 0, todayCompletionTokens = 0, todayTotalTokens = 0, todayCachedTokens = 0; + let totalPromptTokens = 0, totalCompletionTokens = 0, totalTokens = 0, totalCachedTokens = 0; const aggregatedHistory = {}; for (const key of keys) { @@ -526,9 +536,11 @@ export async function getStats() { todayPromptTokens += key.todayPromptTokens || 0; todayCompletionTokens += key.todayCompletionTokens || 0; todayTotalTokens += key.todayTotalTokens || 0; + todayCachedTokens += key.todayCachedTokens || 0; totalPromptTokens += key.totalPromptTokens || 0; totalCompletionTokens += key.totalCompletionTokens || 0; totalTokens += key.totalTokens || 0; + totalCachedTokens += key.totalCachedTokens || 0; // 汇总每个 Key 的历史数据 if (key.usageHistory) { @@ -566,9 +578,11 @@ export async function getStats() { todayPromptTokens, todayCompletionTokens, todayTotalTokens, + todayCachedTokens, totalPromptTokens, totalCompletionTokens, totalTokens, + totalCachedTokens, usageHistory: aggregatedHistory }; } @@ -576,7 +590,7 @@ export async function getStats() { /** * 批量更新所有 Key 的每日限额 - * @param {number} newLimit - 新的每日限额 + * @param {number} newLimit - 新s的每日限额 * @returns {Promise<{total: number, updated: number}>} */ export async function applyDailyLimitToAllKeys(newLimit) { diff --git a/src/plugins/model-usage-stats/stats-manager.js b/src/plugins/model-usage-stats/stats-manager.js index 2d50fb0..d128a8f 100644 --- a/src/plugins/model-usage-stats/stats-manager.js +++ b/src/plugins/model-usage-stats/stats-manager.js @@ -254,7 +254,7 @@ function normalizeUsageCandidate(candidate) { return { promptTokens, completionTokens, - totalTokens: totalTokens || promptTokens + completionTokens, + totalTokens: totalTokens || (promptTokens + completionTokens), cachedTokens }; } diff --git a/static/model-usage-stats.html b/static/model-usage-stats.html index 6a9b33e..3a72c70 100644 --- a/static/model-usage-stats.html +++ b/static/model-usage-stats.html @@ -8,7 +8,7 @@
@@ -57,6 +57,7 @@| Provider | Model | 请求数 | Prompt | Completion | Total | 最近使用 | |
|---|---|---|---|---|---|---|---|
| Provider | Model | 请求数 | Prompt | Cached | Completion | Total | 最近使用 |