mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-25 13:45:03 +08:00
feat(usage): filter-driven Hero with cache-normalized totals
- Normalize OpenAI/Gemini input_tokens semantics in SQL via the new fresh_input_sql helper (cache_read subtracted at query time, no data migration). Recovers correct cache hit rates for Codex/Gemini. - Add get_usage_summary_by_app endpoint for per-app split (single UNION ALL + GROUP BY, avoids N+1). - Replace UsageSummaryCards + AppBreakdownRail with a single filter-driven UsageHero card; clicking a filter button now truly changes the displayed numbers and the title accent color. - Tighten KNOWN_APP_TYPES to the 3 app_types whose token data is reliably collected (claude/codex/gemini); hide claude-desktop, hermes, opencode, openclaw filter buttons and i18n keys. - Flag cache_creation as N/A for OpenAI-style protocols (Codex, Gemini); show a "partial" tooltip when the All view mixes both protocol families.
This commit is contained in:
+65
-1
@@ -71,6 +71,15 @@ export interface UsageSummary {
|
||||
totalCacheCreationTokens: number;
|
||||
totalCacheReadTokens: number;
|
||||
successRate: number;
|
||||
/** input + output + cache_creation + cache_read, all cache-normalized */
|
||||
realTotalTokens: number;
|
||||
/** cache_read / (input + cache_creation + cache_read), range 0–1 */
|
||||
cacheHitRate: number;
|
||||
}
|
||||
|
||||
export interface UsageSummaryByApp {
|
||||
appType: string;
|
||||
summary: UsageSummary;
|
||||
}
|
||||
|
||||
export interface DailyStats {
|
||||
@@ -129,7 +138,62 @@ export interface UsageRangeSelection {
|
||||
customEndDate?: number;
|
||||
}
|
||||
|
||||
export type AppTypeFilter = "all" | "claude" | "codex" | "gemini";
|
||||
/**
|
||||
* App types whose token usage is reliably collected by the proxy.
|
||||
*
|
||||
* The proxy has handlers for `claude-desktop` too, but in practice those
|
||||
* requests overwhelmingly fail (500/503) and contribute near-zero tokens, so
|
||||
* it is hidden from the Dashboard. `opencode` / `openclaw` / `hermes` have
|
||||
* no proxy handler at all — they appear only as managed apps elsewhere.
|
||||
*/
|
||||
export type AppType = "claude" | "codex" | "gemini";
|
||||
|
||||
export type AppTypeFilter = "all" | AppType;
|
||||
|
||||
export const KNOWN_APP_TYPES: ReadonlyArray<AppType> = [
|
||||
"claude",
|
||||
"codex",
|
||||
"gemini",
|
||||
];
|
||||
|
||||
/**
|
||||
* App types whose proxy uses an OpenAI-style protocol. Two consequences:
|
||||
*
|
||||
* 1. `inputTokens` already includes the cached portion (must subtract
|
||||
* `cacheReadTokens` to get fresh-input semantics — see
|
||||
* [getFreshInputTokens]).
|
||||
* 2. The protocol does not report cache _creation_ separately, only cache
|
||||
* _reads_. So `cacheCreationTokens` is always 0 for these app types and
|
||||
* the UI should label it as N/A rather than 0.
|
||||
*
|
||||
* Mirror of the Rust `CACHE_INCLUSIVE_APP_TYPES` whitelist.
|
||||
*/
|
||||
export const CACHE_INCLUSIVE_APP_TYPES: ReadonlySet<string> = new Set([
|
||||
"codex",
|
||||
"gemini",
|
||||
]);
|
||||
|
||||
/** Subset of request-log fields needed to derive cache-normalized input. */
|
||||
export interface CacheNormalizableLog {
|
||||
appType: string;
|
||||
inputTokens: number;
|
||||
cacheReadTokens: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a single request log, return the input token count with cache reads
|
||||
* removed. Anthropic-style providers already report `inputTokens` without
|
||||
* cache, so they pass through unchanged.
|
||||
*/
|
||||
export function getFreshInputTokens(log: CacheNormalizableLog): number {
|
||||
if (
|
||||
CACHE_INCLUSIVE_APP_TYPES.has(log.appType) &&
|
||||
log.inputTokens >= log.cacheReadTokens
|
||||
) {
|
||||
return log.inputTokens - log.cacheReadTokens;
|
||||
}
|
||||
return log.inputTokens;
|
||||
}
|
||||
|
||||
export interface StatsFilters {
|
||||
timeRange: UsageRangePreset;
|
||||
|
||||
Reference in New Issue
Block a user