feat(dashboard): add time-frame filtering and used-models-only pricing
Some checks failed
CI / Check (push) Has been cancelled
CI / Clippy (push) Has been cancelled
CI / Formatting (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Release Build (push) Has been cancelled

- All usage endpoints now accept ?period=today|24h|7d|30d|all|custom
  with optional &from=ISO&to=ISO for custom ranges
- Time-series chart adapts granularity: hourly for today/24h, daily for
  7d/30d/all
- Analytics and Costs pages have period selector buttons with custom
  date-range picker
- Pricing table on Costs page now only shows models that have actually
  been used (GET /models?used_only=true)
- Cache-bust version bumped to v=6
This commit is contained in:
2026-03-02 15:29:23 -05:00
parent 54c45cbfca
commit 5bf41be343
7 changed files with 498 additions and 144 deletions

View File

@@ -33,6 +33,8 @@ pub(super) struct ModelListParams {
pub reasoning: Option<bool>,
/// Only models that have pricing data.
pub has_cost: Option<bool>,
/// Only models that have been used in requests.
pub used_only: Option<bool>,
/// Sort field (name, id, provider, context_limit, input_cost, output_cost).
pub sort_by: Option<ModelSortBy>,
/// Sort direction (asc, desc).
@@ -46,6 +48,22 @@ pub(super) async fn handle_get_models(
let registry = &state.app_state.model_registry;
let pool = &state.app_state.db_pool;
// If used_only, fetch the set of models that appear in llm_requests
let used_models: Option<std::collections::HashSet<String>> =
if params.used_only.unwrap_or(false) {
match sqlx::query_scalar::<_, String>(
"SELECT DISTINCT model FROM llm_requests",
)
.fetch_all(pool)
.await
{
Ok(models) => Some(models.into_iter().collect()),
Err(_) => Some(std::collections::HashSet::new()),
}
} else {
None
};
// Build filter from query params
let filter = ModelFilter {
provider: params.provider,
@@ -79,6 +97,14 @@ pub(super) async fn handle_get_models(
for entry in &entries {
let m_key = entry.model_key;
// Skip models not in the used set (when used_only is active)
if let Some(ref used) = used_models {
if !used.contains(m_key) {
continue;
}
}
let m_meta = entry.metadata;
let mut enabled = true;