nexus_core/app/usage.rs
1//! The `/usage` popup's domain half: aggregate token/cache/cost analytics
2//! drawn from the per-request `usage_log`. Content-free — only
3//! backend/model/tokens — so it works even for sessions long compacted
4//! away. The popup's cursor/range flow lives in the view layer.
5
6use super::App;
7
8use crate::db::{UsageByBackend, UsageByModel, UsageRow, UsageTotals};
9
10/// Snapshot of the aggregates the popup renders, loaded on open (and on
11/// Ctrl+R refresh).
12pub struct UsageData {
13 pub totals: UsageTotals,
14 pub by_backend: Vec<UsageByBackend>,
15 pub by_model: Vec<UsageByModel>,
16 pub recent: Vec<UsageRow>,
17}
18
19impl App {
20 /// Load the aggregates for the currently selected range (the view owns
21 /// the cursor; the range is a persisted core preference).
22 pub fn load_usage(&self) -> UsageData {
23 let since = self.usage_range.since().map(|t| t.to_rfc3339());
24 UsageData {
25 totals: self.db.usage_totals(since.as_deref()).unwrap_or_default(),
26 by_backend: self
27 .db
28 .usage_by_backend(since.as_deref())
29 .unwrap_or_default(),
30 by_model: self
31 .db
32 .usage_by_model(10, since.as_deref())
33 .unwrap_or_default(),
34 recent: self
35 .db
36 .usage_recent(200, since.as_deref())
37 .unwrap_or_default(),
38 }
39 }
40
41 /// Domain half of the range cycle: persist the choice (the view switches
42 /// `usage_range` and reloads).
43 pub fn persist_usage_range(&mut self) {
44 let _ = self.db.set_setting("usage_range", self.usage_range.key());
45 }
46
47 /// Recompute historical costs from the current catalog before rendering —
48 /// rows logged before pricing existed stay accurate. Called on popup open
49 /// and refresh.
50 pub fn backfill_usage_costs(&mut self) {
51 let _ = self.db.backfill_usage_costs();
52 }
53}