mirror of
https://github.com/EKKOLearnAI/hermes-web-ui.git
synced 2026-06-02 09:20:16 +00:00
feat(usage): filter usage stats by active profile
Usage stats now automatically filter by the current active profile. Changes: - getLocalUsageStats() accepts optional profile parameter - Add WHERE profile = ? clause to all SQL queries when profile is provided - usageStats controller uses getActiveProfileName() to get current profile - Local session_usage data is now filtered by current profile - Hermes state.db sessions remain unfiltered (no profile field) This allows users to see usage stats specific to their current profile, making multi-profile usage tracking more useful. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -289,8 +289,11 @@ export async function contextLength(ctx: any) {
|
||||
}
|
||||
|
||||
export async function usageStats(ctx: any) {
|
||||
// 1. Local session_usage (web UI chat runs)
|
||||
const local = getLocalUsageStats()
|
||||
// Get current active profile
|
||||
const currentProfile = getActiveProfileName()
|
||||
|
||||
// 1. Local session_usage (web UI chat runs) - filtered by current profile
|
||||
const local = getLocalUsageStats(currentProfile)
|
||||
|
||||
// 2. Hermes state.db sessions (exclude api_server source)
|
||||
let hermesSessions: Array<{
|
||||
@@ -307,6 +310,9 @@ export async function usageStats(ctx: any) {
|
||||
|
||||
try {
|
||||
const allSessions = await listSessionSummaries(undefined, 100000)
|
||||
// Only include sessions from current profile
|
||||
// Note: Hermes sessions don't have profile field, so we include all
|
||||
// This could be improved in the future by filtering by some criteria
|
||||
hermesSessions = allSessions.filter(s => s.source !== 'api_server')
|
||||
} catch (err) {
|
||||
logger.warn(err, 'usageStats: failed to load Hermes sessions')
|
||||
|
||||
@@ -199,7 +199,7 @@ export interface LocalUsageStats {
|
||||
by_day: UsageStatsDailyRow[]
|
||||
}
|
||||
|
||||
export function getLocalUsageStats(): LocalUsageStats {
|
||||
export function getLocalUsageStats(profile?: string): LocalUsageStats {
|
||||
const empty: LocalUsageStats = {
|
||||
input_tokens: 0, output_tokens: 0, cache_read_tokens: 0,
|
||||
cache_write_tokens: 0, reasoning_tokens: 0, sessions: 0,
|
||||
@@ -208,6 +208,7 @@ export function getLocalUsageStats(): LocalUsageStats {
|
||||
if (!isSqliteAvailable()) return empty
|
||||
|
||||
const db = getDb()!
|
||||
const profileFilter = profile ? `WHERE profile = ?` : ''
|
||||
|
||||
const totals = db.prepare(`
|
||||
SELECT COALESCE(SUM(input_tokens),0) as input_tokens,
|
||||
@@ -217,7 +218,8 @@ export function getLocalUsageStats(): LocalUsageStats {
|
||||
COALESCE(SUM(reasoning_tokens),0) as reasoning_tokens,
|
||||
COUNT(DISTINCT session_id) as sessions
|
||||
FROM ${TABLE}
|
||||
`).get() as any
|
||||
${profileFilter}
|
||||
`).get(...(profile ? [profile] : [])) as any
|
||||
|
||||
const byModel = db.prepare(`
|
||||
SELECT model,
|
||||
@@ -228,21 +230,30 @@ export function getLocalUsageStats(): LocalUsageStats {
|
||||
SUM(reasoning_tokens) as reasoning_tokens,
|
||||
COUNT(DISTINCT session_id) as sessions
|
||||
FROM ${TABLE}
|
||||
${profileFilter}
|
||||
GROUP BY model
|
||||
ORDER BY sessions DESC
|
||||
`).all() as unknown as UsageStatsModelRow[]
|
||||
`).all(...(profile ? [profile] : [])) as unknown as UsageStatsModelRow[]
|
||||
|
||||
const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1000
|
||||
const byDay = db.prepare(`
|
||||
SELECT DATE(created_at / 1000, 'unixepoch') as date,
|
||||
const byDayStmt = profile
|
||||
? `SELECT DATE(created_at / 1000, 'unixepoch') as date,
|
||||
SUM(input_tokens + output_tokens) as tokens,
|
||||
SUM(cache_read_tokens) as cache,
|
||||
COUNT(DISTINCT session_id) as sessions
|
||||
FROM ${TABLE}
|
||||
WHERE created_at > ?
|
||||
GROUP BY date
|
||||
ORDER BY date
|
||||
`).all(thirtyDaysAgo) as Array<{ date: string; tokens: number; cache: number; sessions: number }>
|
||||
FROM ${TABLE}
|
||||
WHERE profile = ? AND created_at > ?
|
||||
GROUP BY date
|
||||
ORDER BY date`
|
||||
: `SELECT DATE(created_at / 1000, 'unixepoch') as date,
|
||||
SUM(input_tokens + output_tokens) as tokens,
|
||||
SUM(cache_read_tokens) as cache,
|
||||
COUNT(DISTINCT session_id) as sessions
|
||||
FROM ${TABLE}
|
||||
WHERE created_at > ?
|
||||
GROUP BY date
|
||||
ORDER BY date`
|
||||
const byDay = db.prepare(byDayStmt).all(...(profile ? [profile, thirtyDaysAgo] : [thirtyDaysAgo])) as Array<{ date: string; tokens: number; cache: number; sessions: number }>
|
||||
|
||||
return {
|
||||
input_tokens: totals.input_tokens,
|
||||
|
||||
Reference in New Issue
Block a user