2026-04-29 16:26:24 +08:00
|
|
|
import { fetchUsageStats, type UsageStatsResponse } from '@/api/hermes/sessions'
|
2026-04-14 14:47:18 +08:00
|
|
|
import { defineStore } from 'pinia'
|
|
|
|
|
import { computed, ref } from 'vue'
|
|
|
|
|
|
|
|
|
|
interface DailyUsage {
|
|
|
|
|
date: string
|
2026-05-02 10:36:33 +10:00
|
|
|
input_tokens: number
|
|
|
|
|
output_tokens: number
|
|
|
|
|
cache_read_tokens: number
|
|
|
|
|
cache_write_tokens: number
|
2026-04-14 14:47:18 +08:00
|
|
|
sessions: number
|
2026-05-02 10:36:33 +10:00
|
|
|
errors: number
|
2026-04-14 14:47:18 +08:00
|
|
|
cost: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface ModelUsage {
|
|
|
|
|
model: string
|
|
|
|
|
inputTokens: number
|
|
|
|
|
outputTokens: number
|
|
|
|
|
cacheTokens: number
|
|
|
|
|
totalTokens: number
|
|
|
|
|
sessions: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const useUsageStore = defineStore('usage', () => {
|
2026-04-29 16:26:24 +08:00
|
|
|
const stats = ref<UsageStatsResponse | null>(null)
|
2026-04-14 14:47:18 +08:00
|
|
|
const isLoading = ref(false)
|
|
|
|
|
|
2026-04-30 13:46:31 +02:00
|
|
|
async function loadSessions(days = 30) {
|
2026-04-14 14:47:18 +08:00
|
|
|
isLoading.value = true
|
|
|
|
|
try {
|
2026-04-30 13:46:31 +02:00
|
|
|
stats.value = await fetchUsageStats(days)
|
2026-04-14 14:47:18 +08:00
|
|
|
} catch (err) {
|
2026-04-29 16:26:24 +08:00
|
|
|
console.error('Failed to load usage stats:', err)
|
2026-04-14 14:47:18 +08:00
|
|
|
} finally {
|
|
|
|
|
isLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-29 16:26:24 +08:00
|
|
|
const hasData = computed(() => !!stats.value && stats.value.total_sessions > 0)
|
2026-04-14 14:47:18 +08:00
|
|
|
|
2026-04-29 16:26:24 +08:00
|
|
|
const totalInputTokens = computed(() => stats.value?.total_input_tokens ?? 0)
|
|
|
|
|
const totalOutputTokens = computed(() => stats.value?.total_output_tokens ?? 0)
|
2026-04-14 14:47:18 +08:00
|
|
|
const totalTokens = computed(() => totalInputTokens.value + totalOutputTokens.value)
|
2026-04-29 16:26:24 +08:00
|
|
|
const totalSessions = computed(() => stats.value?.total_sessions ?? 0)
|
2026-04-14 14:47:18 +08:00
|
|
|
|
2026-04-29 16:26:24 +08:00
|
|
|
const totalCacheTokens = computed(() => stats.value?.total_cache_read_tokens ?? 0)
|
2026-04-14 14:47:18 +08:00
|
|
|
|
|
|
|
|
const cacheHitRate = computed(() => {
|
2026-04-29 16:26:24 +08:00
|
|
|
const total = totalInputTokens.value + totalCacheTokens.value
|
2026-04-14 14:47:18 +08:00
|
|
|
if (total === 0) return null
|
|
|
|
|
return ((totalCacheTokens.value / total) * 100)
|
|
|
|
|
})
|
|
|
|
|
|
2026-04-29 16:26:24 +08:00
|
|
|
const estimatedCost = computed(() => stats.value?.total_cost ?? 0)
|
2026-04-14 14:47:18 +08:00
|
|
|
|
|
|
|
|
const modelUsage = computed<ModelUsage[]>(() => {
|
2026-04-29 16:26:24 +08:00
|
|
|
if (!stats.value) return []
|
|
|
|
|
return stats.value.model_usage.map(m => ({
|
2026-04-30 13:46:31 +02:00
|
|
|
model: m.model || 'unknown',
|
2026-04-29 16:26:24 +08:00
|
|
|
inputTokens: m.input_tokens,
|
|
|
|
|
outputTokens: m.output_tokens,
|
|
|
|
|
cacheTokens: m.cache_read_tokens,
|
|
|
|
|
totalTokens: m.input_tokens + m.output_tokens,
|
|
|
|
|
sessions: m.sessions,
|
|
|
|
|
})).sort((a, b) => b.totalTokens - a.totalTokens)
|
2026-04-14 14:47:18 +08:00
|
|
|
})
|
|
|
|
|
|
2026-04-29 16:26:24 +08:00
|
|
|
const dailyUsage = computed<DailyUsage[]>(() => stats.value?.daily_usage ?? [])
|
2026-04-14 14:47:18 +08:00
|
|
|
|
|
|
|
|
const avgSessionsPerDay = computed(() => {
|
2026-04-29 16:26:24 +08:00
|
|
|
if (!stats.value || stats.value.daily_usage.length === 0) return 0
|
|
|
|
|
const daysWithActivity = stats.value.daily_usage.filter(d => d.sessions > 0).length
|
|
|
|
|
const days = Math.max(1, daysWithActivity)
|
2026-04-14 14:47:18 +08:00
|
|
|
return totalSessions.value / days
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return {
|
2026-04-29 16:26:24 +08:00
|
|
|
stats,
|
2026-04-14 14:47:18 +08:00
|
|
|
isLoading,
|
2026-04-29 16:26:24 +08:00
|
|
|
hasData,
|
2026-04-14 14:47:18 +08:00
|
|
|
loadSessions,
|
|
|
|
|
totalInputTokens,
|
|
|
|
|
totalOutputTokens,
|
|
|
|
|
totalTokens,
|
|
|
|
|
totalSessions,
|
|
|
|
|
totalCacheTokens,
|
|
|
|
|
cacheHitRate,
|
|
|
|
|
estimatedCost,
|
|
|
|
|
modelUsage,
|
|
|
|
|
dailyUsage,
|
|
|
|
|
avgSessionsPerDay,
|
|
|
|
|
}
|
|
|
|
|
})
|