diff --git a/changelog.d/features/dashboard-cache-percentage.md b/changelog.d/features/dashboard-cache-percentage.md new file mode 100644 index 0000000000..784cc34f8e --- /dev/null +++ b/changelog.d/features/dashboard-cache-percentage.md @@ -0,0 +1 @@ +- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#PR_NUMBER](https://github.com/diegosouzapw/OmniRoute/pull/PR_NUMBER)) diff --git a/src/shared/components/RequestLoggerV2.tsx b/src/shared/components/RequestLoggerV2.tsx index 699bf1f36d..f9cd90a63b 100644 --- a/src/shared/components/RequestLoggerV2.tsx +++ b/src/shared/components/RequestLoggerV2.tsx @@ -25,6 +25,7 @@ import { maskAccount, stableAccountSuffix, formatApiKeyLabel, + formatCachePercentage, } from "@/shared/utils/formatting"; import { getProviderDisplayLabel } from "@/shared/utils/providerDisplayLabel"; import useEmailPrivacyStore from "@/store/emailPrivacyStore"; @@ -1572,7 +1573,8 @@ const RequestLoggerV2 = forwardRef - {log.tokens.cacheRead.toLocaleString()} + {log.tokens.cacheRead.toLocaleString()} ( + {formatCachePercentage(log.tokens.in, log.tokens.cacheRead)}%) )} diff --git a/src/shared/utils/formatting.ts b/src/shared/utils/formatting.ts index bb6091c4f2..7213ff99b8 100644 --- a/src/shared/utils/formatting.ts +++ b/src/shared/utils/formatting.ts @@ -216,3 +216,13 @@ export function formatErrorForDisplay(err: unknown): string | null { return String(err); } } + +/** Percentage of `tokensIn` served from cache, clamped to [0, 100]. */ +export function formatCachePercentage( + tokensIn: number | null | undefined, + cacheRead: number | null | undefined +): number { + if (!tokensIn || tokensIn <= 0) return 0; + if (!cacheRead || cacheRead <= 0) return 0; + return Math.min(100, Math.round((cacheRead / tokensIn) * 100)); +} diff --git a/tests/unit/request-logger-cache-percentage.test.ts b/tests/unit/request-logger-cache-percentage.test.ts new file mode 100644 index 0000000000..61467db1a0 --- /dev/null +++ b/tests/unit/request-logger-cache-percentage.test.ts @@ -0,0 +1,33 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { formatCachePercentage } from "../../src/shared/utils/formatting"; + +test("formatCachePercentage", async (t) => { + await t.test("returns 0 when tokensIn is 0, null, or undefined", () => { + assert.strictEqual(formatCachePercentage(0, 100), 0); + assert.strictEqual(formatCachePercentage(null, 100), 0); + assert.strictEqual(formatCachePercentage(undefined, 100), 0); + }); + + await t.test("returns 0 when cacheRead is 0, null, or undefined", () => { + assert.strictEqual(formatCachePercentage(100, 0), 0); + assert.strictEqual(formatCachePercentage(100, null), 0); + assert.strictEqual(formatCachePercentage(100, undefined), 0); + }); + + await t.test("returns 0 for negative values", () => { + assert.strictEqual(formatCachePercentage(-100, 50), 0); + assert.strictEqual(formatCachePercentage(100, -50), 0); + }); + + await t.test("calculates standard percentages correctly", () => { + assert.strictEqual(formatCachePercentage(100, 50), 50); + assert.strictEqual(formatCachePercentage(1000, 250), 25); + assert.strictEqual(formatCachePercentage(3, 1), 33); + }); + + await t.test("clamps percentage at 100 when cacheRead > tokensIn", () => { + assert.strictEqual(formatCachePercentage(100, 150), 100); + assert.strictEqual(formatCachePercentage(50, 1000), 100); + }); +});