From d838388443be24af0622130dc9e00a79a7d900d8 Mon Sep 17 00:00:00 2001 From: tombii Date: Mon, 30 Mar 2026 23:33:48 +0200 Subject: [PATCH 1/2] fix(ui): improve cache page header sizing and context - Match CacheStatsCard header size with other card headers (text-sm) - Show request counts in cache hit rate label (116/359) for clarity - Add CacheStatsCard component to cache page for cumulative metrics Co-Authored-By: Claude Sonnet 4.6 --- src/app/(dashboard)/dashboard/cache/page.tsx | 8 +- .../settings/components/CacheStatsCard.tsx | 257 +++++++++--------- 2 files changed, 140 insertions(+), 125 deletions(-) diff --git a/src/app/(dashboard)/dashboard/cache/page.tsx b/src/app/(dashboard)/dashboard/cache/page.tsx index 24064e84..0f514543 100644 --- a/src/app/(dashboard)/dashboard/cache/page.tsx +++ b/src/app/(dashboard)/dashboard/cache/page.tsx @@ -5,6 +5,7 @@ import { Card, Button, EmptyState } from "@/shared/components"; import { useNotificationStore } from "@/store/notificationStore"; import { useTranslations } from "next-intl"; import CacheEntriesTab from "./components/CacheEntriesTab"; +import CacheStatsCard from "../settings/components/CacheStatsCard"; // ─── Types ─────────────────────────────────────────────────────────────────── @@ -371,7 +372,9 @@ export default function CachePage() {
{promptCacheHitRate.toFixed(1)}%
-
{t("cacheHitRate")}
+
+ {t("cacheHitRate")} ({pc.requestsWithCacheControl}/{pc.totalRequests}) +
@@ -432,6 +435,9 @@ export default function CachePage() { )} + {/* Prompt Cache Metrics (cumulative with reset) */} + + {/* Cache Trend (24h) */} {trend.length > 0 && ( diff --git a/src/app/(dashboard)/dashboard/settings/components/CacheStatsCard.tsx b/src/app/(dashboard)/dashboard/settings/components/CacheStatsCard.tsx index d2699076..7876a67f 100644 --- a/src/app/(dashboard)/dashboard/settings/components/CacheStatsCard.tsx +++ b/src/app/(dashboard)/dashboard/settings/components/CacheStatsCard.tsx @@ -63,132 +63,141 @@ export default function CacheStatsCard() { : 0; return ( - -
-

- insights - Prompt Cache Metrics -

- -
- - {metrics ? ( -
- {/* Overview Stats */} -
-
-

Total Requests

-

{metrics.totalRequests}

-
-
-

With Cache Control

-

{metrics.requestsWithCacheControl}

-
+ +
+
+
+ +

Prompt Cache Metrics

- - {/* Token Stats */} -
-
-

Input Tokens

-

- {metrics.totalInputTokens.toLocaleString()} -

-
-
-

Cached Tokens (Read)

-

- {metrics.totalCachedTokens.toLocaleString()} -

-
-
-

Cache Creation (Write)

-

- {metrics.totalCacheCreationTokens.toLocaleString()} -

-
-
- - {/* Cache Ratio */} -
-
-
-

Cache Reuse Ratio

-

Cached tokens / Total input tokens

-
-

{cacheHitRate.toFixed(1)}%

-
- {/* Progress bar */} -
-
-
-
- - {/* Savings */} -
-
-

Tokens Saved

-

- {metrics.tokensSaved.toLocaleString()} -

-
-
-

Est. Cost Saved

-

- ${metrics.estimatedCostSaved.toFixed(4)} -

-
-
- - {/* By Provider */} - {Object.keys(metrics.byProvider).length > 0 && ( -
-

By Provider

-
- {Object.entries(metrics.byProvider).map(([provider, stats]) => { - const providerCacheRate = - stats.inputTokens > 0 ? (stats.cachedTokens / stats.inputTokens) * 100 : 0; - return ( -
-
- {provider} - {stats.requests} reqs -
-
- - In: {stats.inputTokens.toLocaleString()} - - - Cached: {stats.cachedTokens.toLocaleString()} - - - Write: {stats.cacheCreationTokens.toLocaleString()} - - - {providerCacheRate.toFixed(0)}% - -
-
- ); - })} -
-
- )} +
- ) : ( -

Loading cache metrics...

- )} + + {metrics ? ( +
+ {/* Overview Stats */} +
+
+

Total Requests

+

{metrics.totalRequests}

+
+
+

With Cache Control

+

+ {metrics.requestsWithCacheControl} +

+
+
+ + {/* Token Stats */} +
+
+

Input Tokens

+

+ {metrics.totalInputTokens.toLocaleString()} +

+
+
+

Cached Tokens (Read)

+

+ {metrics.totalCachedTokens.toLocaleString()} +

+
+
+

Cache Creation (Write)

+

+ {metrics.totalCacheCreationTokens.toLocaleString()} +

+
+
+ + {/* Cache Ratio */} +
+
+
+

Cache Reuse Ratio

+

Cached tokens / Total input tokens

+
+

{cacheHitRate.toFixed(1)}%

+
+ {/* Progress bar */} +
+
+
+
+ + {/* Savings */} +
+
+

Tokens Saved

+

+ {metrics.tokensSaved.toLocaleString()} +

+
+
+

Est. Cost Saved

+

+ ${metrics.estimatedCostSaved.toFixed(4)} +

+
+
+ + {/* By Provider */} + {Object.keys(metrics.byProvider).length > 0 && ( +
+

By Provider

+
+ {Object.entries(metrics.byProvider).map(([provider, stats]) => { + const providerCacheRate = + stats.inputTokens > 0 ? (stats.cachedTokens / stats.inputTokens) * 100 : 0; + return ( +
+
+ {provider} + {stats.requests} reqs +
+
+ + In: {stats.inputTokens.toLocaleString()} + + + Cached: {stats.cachedTokens.toLocaleString()} + + + Write: {stats.cacheCreationTokens.toLocaleString()} + + + {providerCacheRate.toFixed(0)}% + +
+
+ ); + })} +
+
+ )} +
+ ) : ( +

Loading cache metrics...

+ )} +
); } From 67b9a3bc0eb0e564cb5fa43a751dd2f0ab3d5796 Mon Sep 17 00:00:00 2001 From: tombii Date: Tue, 31 Mar 2026 00:32:37 +0200 Subject: [PATCH 2/2] fix(ui): internationalize CacheStatsCard and add auto-refresh - Add 10s auto-refresh interval matching cache page pattern - Replace all hardcoded English strings with translation keys - Add 13 new i18n keys to cache namespace for metrics display - Reorganize header layout with auto-refresh indicator and reset button Addresses Gemini Code Assist feedback on PR #835: - Fixed internationalization (all text now uses t()) - Maintains distinct metrics (cumulative/resettable vs real-time rolling) Co-Authored-By: Claude Sonnet 4.6 --- .../settings/components/CacheStatsCard.tsx | 76 +++++++++++-------- src/i18n/messages/en.json | 13 ++++ 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/app/(dashboard)/dashboard/settings/components/CacheStatsCard.tsx b/src/app/(dashboard)/dashboard/settings/components/CacheStatsCard.tsx index 7876a67f..4798fbba 100644 --- a/src/app/(dashboard)/dashboard/settings/components/CacheStatsCard.tsx +++ b/src/app/(dashboard)/dashboard/settings/components/CacheStatsCard.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useCallback } from "react"; import { Card } from "@/shared/components"; import { useTranslations } from "next-intl"; @@ -33,19 +33,26 @@ interface CacheMetrics { lastUpdated: string; } +const REFRESH_INTERVAL_MS = 10_000; +const REFRESH_INTERVAL_SECONDS = REFRESH_INTERVAL_MS / 1000; + export default function CacheStatsCard() { const [metrics, setMetrics] = useState(null); const [resetting, setResetting] = useState(false); - const t = useTranslations("settings"); + const t = useTranslations("cache"); - const fetchMetrics = () => { + const fetchMetrics = useCallback(() => { fetch("/api/settings/cache-metrics") .then((r) => r.json()) .then(setMetrics) .catch(() => {}); - }; + }, []); - useEffect(fetchMetrics, []); + useEffect(() => { + void fetchMetrics(); + const id = setInterval(() => void fetchMetrics(), REFRESH_INTERVAL_MS); + return () => clearInterval(id); + }, [fetchMetrics]); const handleReset = async () => { setResetting(true); @@ -73,15 +80,20 @@ export default function CacheStatsCard() { > insights -

Prompt Cache Metrics

+

{t("cacheMetrics")}

+
+
+ + {t("autoRefresh", { seconds: REFRESH_INTERVAL_SECONDS })} + +
-
{metrics ? ( @@ -89,11 +101,11 @@ export default function CacheStatsCard() { {/* Overview Stats */}
-

Total Requests

+

{t("totalRequests")}

{metrics.totalRequests}

-

With Cache Control

+

{t("withCacheControl")}

{metrics.requestsWithCacheControl}

@@ -103,19 +115,19 @@ export default function CacheStatsCard() { {/* Token Stats */}
-

Input Tokens

+

{t("inputTokens")}

{metrics.totalInputTokens.toLocaleString()}

-

Cached Tokens (Read)

+

{t("cachedTokensRead")}

{metrics.totalCachedTokens.toLocaleString()}

-

Cache Creation (Write)

+

{t("cacheCreationWrite")}

{metrics.totalCacheCreationTokens.toLocaleString()}

@@ -126,8 +138,8 @@ export default function CacheStatsCard() {
-

Cache Reuse Ratio

-

Cached tokens / Total input tokens

+

{t("cacheReuseRatio")}

+

{t("cacheReuseRatioDesc")}

{cacheHitRate.toFixed(1)}%

@@ -143,13 +155,13 @@ export default function CacheStatsCard() { {/* Savings */}
-

Tokens Saved

+

{t("tokensSaved")}

{metrics.tokensSaved.toLocaleString()}

-

Est. Cost Saved

+

{t("estCostSaved")}

${metrics.estimatedCostSaved.toFixed(4)}

@@ -159,7 +171,7 @@ export default function CacheStatsCard() { {/* By Provider */} {Object.keys(metrics.byProvider).length > 0 && (
-

By Provider

+

{t("byProvider")}

{Object.entries(metrics.byProvider).map(([provider, stats]) => { const providerCacheRate = @@ -171,17 +183,19 @@ export default function CacheStatsCard() { >
{provider} - {stats.requests} reqs + + {stats.requests} {t("requestsShort")} +
- - In: {stats.inputTokens.toLocaleString()} + + {t("inputShort")}: {stats.inputTokens.toLocaleString()} - - Cached: {stats.cachedTokens.toLocaleString()} + + {t("cachedShort")}: {stats.cachedTokens.toLocaleString()} - - Write: {stats.cacheCreationTokens.toLocaleString()} + + {t("writeShort")}: {stats.cacheCreationTokens.toLocaleString()} {providerCacheRate.toFixed(0)}% @@ -195,7 +209,7 @@ export default function CacheStatsCard() { )}
) : ( -

Loading cache metrics...

+

{t("loading")}

)}
diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 43463d7c..0a1ce25a 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -2937,6 +2937,19 @@ "cacheHitRate": "Cache Hit Rate", "cachedTokens": "Cached Tokens", "cacheCreationTokens": "Cache Creation Tokens", + "cacheMetrics": "Prompt Cache Metrics", + "withCacheControl": "With Cache Control", + "cachedTokensRead": "Cached Tokens (Read)", + "cacheCreationWrite": "Cache Creation (Write)", + "cacheReuseRatio": "Cache Reuse Ratio", + "cacheReuseRatioDesc": "Cached tokens / Total input tokens", + "estCostSaved": "Est. Cost Saved", + "requestsShort": "reqs", + "inputShort": "In", + "cachedShort": "Cached", + "writeShort": "Write", + "resetting": "Resetting...", + "resetMetrics": "Reset Metrics", "byProvider": "Breakdown by Provider", "provider": "Provider", "requests": "Requests",