/** * dashboard-client/src/components/ProviderCacheCard.tsx — Provider prompt cache stats card. * * Displays lifetime provider prompt cache hit-rate aggregates + estimated * dollar savings from the /api/provider-cache endpoint. * Reuses the `ov-stat-row` / `StatRow` pattern from CacheHitsCard.tsx. * * B.2 spec: Cache Hit Rate with color classes, humanized token counts, * priced $ rows with green/red net, model name, locale date + relative time. */ import type React from "react"; import { fmtTokens, fmtDollars, fmtDate, fmtRelativeTime, } from "../utils/format"; import { lookupModelInputRate } from "@pricing"; import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card"; // --------------------------------------------------------------------------- // Per-model breakdown type (mirrors the API contract). // --------------------------------------------------------------------------- export interface ByModelEntry { model: string; hitPct: number; totalCacheRead: number; totalCacheWrite: number; sampleCount: number; } // --------------------------------------------------------------------------- // Flattened props — destructured from ProviderCacheResponse in CacheTab. // --------------------------------------------------------------------------- export interface ProviderCacheCardProps { /** Cache hit rate percentage (0–100). */ hitPct: number; /** Number of turns with cache_hit_pct samples. */ turnCount: number; /** Total cache-read tokens across all sampled turns. */ totalCacheRead: number; /** Total cache-write tokens across all sampled turns. */ totalCacheWrite: number; /** Total input tokens across all sampled turns. */ totalInput: number; /** $ saved from cache reads (null when unpriced). */ cacheReadSaved: number | null; /** $ spent on cache writes (null when unpriced). */ cacheWriteCost: number | null; /** netSaved = cacheReadSaved - cacheWriteCost (null when unpriced). */ netSaved: number | null; /** Model display name (modelName || modelId, or null). */ modelLabel: string | null; /** ISO timestamp of first recorded turn. */ firstTurnAt: string | null; /** ISO timestamp of latest recorded turn. */ latestTurnAt: string | null; /** Per-model breakdown (F4). */ byModel: ByModelEntry[]; } // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- /** Hit-rate color: green ≥80, yellow ≥50, red <50. */ function hitPctClass(pct: number): string { if (pct >= 80) return "ov-stat-value-green"; if (pct >= 50) return "ov-stat-value-yellow"; return "ov-stat-value-red"; } /** Net-saved color: green positive, red negative. */ function netClass(v: number | null): string { if (v == null) return ""; return v >= 0 ? "ov-stat-value-green" : "ov-stat-value-red"; } function StatRow({ label, value, className, title, }: { label: string; value: string; className?: string; title?: string; }): React.ReactElement { return (