From 11eb20a2d1f38a7fc5fd73caed1dd25cef8fdb98 Mon Sep 17 00:00:00 2001 From: Evan Yang Date: Tue, 3 Mar 2026 13:00:19 +0800 Subject: [PATCH] fix(ci): resolve backend type/lint and ui format issues --- routstr/core/usage_analytics_store.py | 16 ++++----- routstr/nostr/analytics.py | 18 +++++++--- ui/components/top-models-usage-chart.tsx | 44 ++++++++++++++---------- 3 files changed, 48 insertions(+), 30 deletions(-) diff --git a/routstr/core/usage_analytics_store.py b/routstr/core/usage_analytics_store.py index c5ce3023..2e05d6ae 100644 --- a/routstr/core/usage_analytics_store.py +++ b/routstr/core/usage_analytics_store.py @@ -673,7 +673,7 @@ class UsageAnalyticsStore: ) if model_updates: - rows = [ + model_rows = [ ( minute_ts, model, @@ -713,11 +713,11 @@ class UsageAnalyticsStore: output_tokens = output_tokens + excluded.output_tokens, total_tokens = total_tokens + excluded.total_tokens """, - rows, + model_rows, ) if model_presence_updates: - rows = [ + presence_rows = [ (minute_ts, model, count) for (minute_ts, model), count in model_presence_updates.items() ] @@ -732,11 +732,11 @@ class UsageAnalyticsStore: ON CONFLICT(minute_ts, model) DO UPDATE SET count = count + excluded.count """, - rows, + presence_rows, ) if error_type_updates: - rows = [ + error_type_rows = [ (minute_ts, error_type, count) for (minute_ts, error_type), count in error_type_updates.items() ] @@ -751,7 +751,7 @@ class UsageAnalyticsStore: ON CONFLICT(minute_ts, error_type) DO UPDATE SET count = count + excluded.count """, - rows, + error_type_rows, ) if error_events: @@ -1215,9 +1215,9 @@ class UsageAnalyticsStore: for row in top_model_rows: bucket_ts = str(row["bucket_ts"]) - bucket = bucket_index.get(bucket_ts) - if bucket is None: + if bucket_ts not in bucket_index: continue + bucket = bucket_index[bucket_ts] model = str(row["model"]) successful = int(row["successful"]) diff --git a/routstr/nostr/analytics.py b/routstr/nostr/analytics.py index 137040cb..1c33f8ff 100644 --- a/routstr/nostr/analytics.py +++ b/routstr/nostr/analytics.py @@ -12,7 +12,7 @@ import json import math import time from datetime import datetime, timezone -from typing import Any +from typing import Any, TypedDict from nostr.event import Event from nostr.key import PrivateKey @@ -46,6 +46,13 @@ WINDOW_DEFINITIONS: tuple[tuple[str, int, int], ...] = ( ) +class PayloadSpec(TypedDict): + period_type: str + period_key: str + d_tag: str + payload: dict[str, Any] + + def _event_to_dict(ev: Event) -> dict[str, Any]: return { "id": ev.id, @@ -197,7 +204,10 @@ def _aggregate_top_model_usage( } for model, values in model_totals.items() ] - model_rows.sort(key=lambda row: row["successful_requests"], reverse=True) + model_rows.sort( + key=lambda row: _to_int(row.get("successful_requests", 0)), + reverse=True, + ) return model_rows, others @@ -600,7 +610,7 @@ async def publish_usage_analytics() -> None: generated_at=now_ts, ) - payload_specs = [ + payload_specs: list[PayloadSpec] = [ { "period_type": "latest", "period_key": "latest", @@ -624,7 +634,7 @@ async def publish_usage_analytics() -> None: to_publish: list[dict[str, Any]] = [] refs: dict[str, dict[str, str]] = {} for spec in payload_specs: - payload = spec["payload"] + payload: dict[str, Any] = spec["payload"] payload_hash = _fingerprint_payload(payload) d_tag = str(spec["d_tag"]) period_type = str(spec["period_type"]) diff --git a/ui/components/top-models-usage-chart.tsx b/ui/components/top-models-usage-chart.tsx index 90b3cf77..f6a64110 100644 --- a/ui/components/top-models-usage-chart.tsx +++ b/ui/components/top-models-usage-chart.tsx @@ -198,7 +198,11 @@ function detectProviderFromModel(model: string): string { if (value.includes('claude')) return 'anthropic'; if (value.includes('gpt') || value.includes('openai')) return 'openai'; if (value.includes('gemini')) return 'google'; - if (value.includes('grok') || value.includes('x-ai') || value.includes('xai')) { + if ( + value.includes('grok') || + value.includes('x-ai') || + value.includes('xai') + ) { return 'x-ai'; } if (value.includes('deepseek')) return 'deepseek'; @@ -206,15 +210,20 @@ function detectProviderFromModel(model: string): string { if (value.includes('kimi') || value.includes('moonshot')) return 'moonshot'; if (value.includes('mistral')) return 'mistral'; if (value.includes('qwen') || value.includes('alibaba')) return 'alibaba'; - if (value.includes('glm') || value.includes('z-ai') || value.includes('z ai')) { + if ( + value.includes('glm') || + value.includes('z-ai') || + value.includes('z ai') + ) { return 'z-ai'; } return 'unknown'; } -function getModelPresentation( - model: string -): { displayName: string; provider: string } { +function getModelPresentation(model: string): { + displayName: string; + provider: string; +} { const trimmed = model.trim(); const slashIndex = trimmed.indexOf('/'); if (slashIndex > 0 && slashIndex < trimmed.length - 1) { @@ -257,10 +266,7 @@ export function TopModelsUsageChart({ [mix.metrics] ); - const chartModels = useMemo( - () => mixTopModels.slice(0, 20), - [mixTopModels] - ); + const chartModels = useMemo(() => mixTopModels.slice(0, 20), [mixTopModels]); const leaderboardModels = useMemo( () => mixTopModels.slice(0, 20), [mixTopModels] @@ -472,9 +478,7 @@ export function TopModelsUsageChart({ const windowSize = Math.floor(mixMetrics.length / 2); const previousMetrics = - windowSize > 0 - ? mixMetrics.slice(-windowSize * 2, -windowSize) - : []; + windowSize > 0 ? mixMetrics.slice(-windowSize * 2, -windowSize) : []; const currentMetrics = windowSize > 0 ? mixMetrics.slice(-windowSize) : mixMetrics; @@ -482,10 +486,10 @@ export function TopModelsUsageChart({ .map((model) => { const readMetric = (metric: (typeof mixMetrics)[number]): number => mode === 'requests' - ? (metric.model_counts ?? {})[model] ?? 0 + ? ((metric.model_counts ?? {})[model] ?? 0) : mode === 'revenue' - ? (metric.model_revenue_msats ?? {})[model] ?? 0 - : (metric.model_tokens ?? {})[model] ?? 0; + ? ((metric.model_revenue_msats ?? {})[model] ?? 0) + : ((metric.model_tokens ?? {})[model] ?? 0); const totalRaw = mixMetrics.reduce( (sum, metric) => sum + readMetric(metric), @@ -557,7 +561,9 @@ export function TopModelsUsageChart({
- Model Usage + + Model Usage +

Stacked requests, revenue, or tokens by model ( {mix.interval_minutes}m buckets). @@ -689,7 +695,9 @@ export function TopModelsUsageChart({ value, } satisfies TooltipRow; }) - .filter((row) => Number.isFinite(row.value) && row.value > 0) + .filter( + (row) => Number.isFinite(row.value) && row.value > 0 + ) .sort((a, b) => b.value - a.value); const total = rows.reduce((sum, row) => sum + row.value, 0); @@ -814,7 +822,7 @@ export function TopModelsUsageChart({ className={cn( 'grid grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center gap-3 rounded-md px-2 py-2 text-xs', rowIsLinked && - 'cursor-pointer transition hover:bg-muted/25', + 'hover:bg-muted/25 cursor-pointer transition', rowIsActive && 'bg-muted/30', rowIsDimmed && 'opacity-45' )}