Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions docs/pro.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,8 @@ showing exactly what each rewriter changed, so the full before/after is
always recoverable.

**Aggregate** — Prometheus counters on `/metrics`, all prefixed
`gomodel_pro_compression_`, and the dashboard **Usage** page shows "Tokens
Saved" and "Rewrite Saved" (estimated money) next to Estimated Cost.
`gomodel_pro_compression_`, and the dashboard **Usage** page shows a "Pro
Saved" card next to Estimated Cost: removed prompt tokens in Tokens mode,
the money they would have cost in Costs mode, each followed by the share of
the period it saved — Costs mode shows the value alone when the models in
play have no pricing configured.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions internal/admin/dashboard/static/dist/index.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 37 additions & 12 deletions web/dashboard/src/pages/usage/UsageStatCards.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@
import { formatCost, formatNumber } from "$lib/utils/format.js";
import { usagePage } from "./usage.svelte.js";
import {
rewriteCostSaved,
rewriteSavedTitle,
proSavedPercentText,
proSavedTitle,
proSavedValueText,
rewriteSavingsVisible,
rewriteTokensSaved,
usagePageCostTitle,
usagePageRequestsTitle,
usagePageTotalRequests,
} from "./usage-helpers.js";
import CacheOverviewCards from "./CacheOverviewCards.svelte";

const savingsVisible = $derived(rewriteSavingsVisible(usagePage.usageSummary));
// One savings number, in whatever unit the page's Tokens/Costs toggle is on.
const savedValue = $derived(
proSavedValueText(usagePage.usageSummary, usagePage.usageMode),
);
const savedPercent = $derived(
proSavedPercentText(usagePage.usageSummary, usagePage.usageMode),
);
</script>

<div class="cards">
Expand Down Expand Up @@ -56,17 +63,35 @@
</div>
{#if savingsVisible}
<div class="card">
<div class="card-label">Rewrite Saved</div>
<div class="card-value" title={rewriteSavedTitle(usagePage.usageSummary)}>
{formatCost(rewriteCostSaved(usagePage.usageSummary))}
</div>
</div>
<div class="card">
<div class="card-label">Tokens Saved</div>
<div class="card-value" title={rewriteSavedTitle(usagePage.usageSummary)}>
{formatNumber(rewriteTokensSaved(usagePage.usageSummary))}
<div class="card-label">Pro Saved</div>
<div
class="card-value pro-saved-value"
title={proSavedTitle(usagePage.usageSummary, usagePage.usageMode)}
>
<span>{savedValue}</span>
{#if savedPercent}
<span class="pro-saved-delta">{savedPercent}</span>
{/if}
</div>
</div>
{/if}
<CacheOverviewCards />
</div>

<style>
/* The savings share trails the value on the same line, sized down so the
card still reads as one number. */
.pro-saved-value {
align-items: baseline;
display: flex;
flex-wrap: wrap;
gap: 8px;
}

.pro-saved-delta {
color: var(--success);
font-size: 13px;
font-weight: 600;
letter-spacing: 0;
}
</style>
64 changes: 55 additions & 9 deletions web/dashboard/src/pages/usage/usage-helpers.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Pure logic for the Usage page.
// Kept free of Svelte/DOM dependencies so node:test can exercise it.

import { formatCost, formatNumber } from "../../lib/utils/format.js";
import { formatCost, formatNumber, formatTokensShort } from "../../lib/utils/format.js";

// emptyUsagePageSummary: the filtered usage-page summaries carry the cache
// split and rewrite-savings fields on top of the shared totals.
Expand Down Expand Up @@ -100,9 +100,12 @@ export function usagePageCostTitle(summary) {
return formatCost(s.total_input_cost) + " input + " + formatCost(s.total_output_cost) + " output";
}

// --- Rewrite savings (request rewriters, e.g. token compression) ---
// Savings ride on provider usage rows, so the uncached summary holds the full
// totals. Cards only appear once a rewriter reported savings.
// --- Rewrite savings ("Pro Saved" card) ---
// Request rewriters (e.g. GoModel Pro token compression) strip prompt tokens
// before the request leaves the gateway. Savings ride on provider usage rows,
// so the uncached summary holds the full totals, and the card only appears
// once a rewriter reported savings. A single card follows the page's
// Tokens/Costs mode instead of showing the same saving twice.
export function rewriteTokensSaved(summary) {
const saved = Number((summary && summary.rewrite_tokens_saved) || 0);
return Number.isFinite(saved) && saved > 0 ? saved : 0;
Expand All @@ -117,13 +120,56 @@ export function rewriteCostSaved(summary) {
return s.rewrite_cost_saved === undefined ? null : s.rewrite_cost_saved;
}

export function rewriteSavedTitle(summary) {
// Card value: priced savings in costs mode, removed prompt tokens otherwise.
// Tokens use the short form the overview cards and chart axes use — the exact
// count stays in the tooltip.
export function proSavedValueText(summary, mode) {
if (mode === "costs") return formatCost(rewriteCostSaved(summary));
return formatTokensShort(rewriteTokensSaved(summary));
}

// Share of the pre-rewrite total that the rewriters removed:
// saved / (recorded + saved) — what the period would have cost or weighed
// without them. Null when the baseline is unknown (costs mode with no
// pricing), so the card shows the value alone instead of a bogus 0%.
export function proSavedPercent(summary, mode) {
const costs = mode === "costs";
const saved = costs ? Number(rewriteCostSaved(summary)) : rewriteTokensSaved(summary);
if (!Number.isFinite(saved) || saved <= 0) return null;
// Read the baseline before coercing: a null total_cost means "not priced",
// and coercing it to 0 would put the whole baseline in the savings and
// claim "100% less" for a period whose spend is simply unknown.
const raw = summary && (costs ? summary.total_cost : summary.total_tokens);
if (raw === null || raw === undefined) return null;
const recorded = Number(raw);
if (!Number.isFinite(recorded) || recorded < 0) return null;
// saved > 0 and recorded >= 0, so the baseline is positive by construction.
return (saved / (recorded + saved)) * 100;
}

export function proSavedPercentText(summary, mode) {
const pct = proSavedPercent(summary, mode);
if (pct === null) return "";
return (pct < 0.1 ? "<0.1" : pct.toFixed(1)) + "% less";
}

export function proSavedTitle(summary, mode) {
const tokens = rewriteTokensSaved(summary);
if (tokens <= 0) return "";
return (
formatNumber(tokens) +
" prompt tokens removed by request rewriters before reaching providers"
);
const lines = [
formatNumber(tokens) + " prompt tokens removed by request rewriters before reaching providers",
];
const cost = rewriteCostSaved(summary);
if (cost !== null && cost !== undefined) {
lines.push(formatCost(cost) + " saved at the requests' input pricing");
}
const pct = proSavedPercentText(summary, mode);
if (pct) {
lines.push(
pct + " than the same traffic without rewriting (" + (mode === "costs" ? "cost" : "tokens") + ")",
);
}
return lines.join("\n");
}

// --- Request-log entry helpers ---
Expand Down
84 changes: 75 additions & 9 deletions web/dashboard/tests/usage.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ import {
labelColor,
providerCacheLabel,
providerCacheTitle,
proSavedPercent,
proSavedPercentText,
proSavedTitle,
proSavedValueText,
rewriteCostSaved,
rewriteSavedTitle,
rewriteSavingsVisible,
rewriteTokensSaved,
usageEntryCached,
Expand Down Expand Up @@ -194,24 +197,24 @@ test("usage page stat cards follow the log cache scope and derive hits from the
assert.equal(usagePageCostTitle({}), "");
});

test("usage page rewrite savings cards show only when rewriters saved tokens", () => {
// No savings reported: cards hidden, zero-safe values.
test("usage page Pro Saved card shows only when rewriters saved tokens", () => {
// No savings reported: card hidden, zero-safe values.
assert.equal(rewriteSavingsVisible({ total_requests: 10 }), false);
assert.equal(rewriteTokensSaved({ total_requests: 10 }), 0);
assert.equal(rewriteCostSaved({ total_requests: 10 }), null);
assert.equal(rewriteSavedTitle({ total_requests: 10 }), "");
assert.equal(proSavedTitle({ total_requests: 10 }, "tokens"), "");

// Savings with pricing: both cards visible with tokens and cost.
// Savings with pricing: card visible, tokens and cost both available.
const withSavings = { rewrite_tokens_saved: 4200, rewrite_cost_saved: 0.0125 };
assert.equal(rewriteSavingsVisible(withSavings), true);
assert.equal(rewriteTokensSaved(withSavings), 4200);
assert.equal(rewriteCostSaved(withSavings), 0.0125);
assert.equal(
rewriteSavedTitle(withSavings),
"4,200 prompt tokens removed by request rewriters before reaching providers",
assert.match(
proSavedTitle(withSavings, "tokens"),
/^4,200 prompt tokens removed by request rewriters before reaching providers\n\$0\.0125 saved/,
);

// Savings without pricing: tokens card carries the value, cost stays null.
// Savings without pricing: tokens still surface, cost stays null.
assert.equal(rewriteSavingsVisible({ rewrite_tokens_saved: 100, rewrite_cost_saved: null }), true);
assert.equal(rewriteCostSaved({ rewrite_tokens_saved: 100, rewrite_cost_saved: null }), null);

Expand All @@ -220,6 +223,69 @@ test("usage page rewrite savings cards show only when rewriters saved tokens", (
assert.equal(rewriteSavingsVisible({ rewrite_tokens_saved: "NaN?" }), false);
});

test("Pro Saved value and share follow the Tokens/Costs mode", () => {
const summary = {
total_tokens: 8000,
total_cost: 0.08,
rewrite_tokens_saved: 2000,
rewrite_cost_saved: 0.02,
};

// Tokens mode: removed prompt tokens in the overview's short form,
// 2000 / (8000 + 2000) = 20%.
assert.equal(proSavedValueText(summary, "tokens"), "2K");
assert.equal(proSavedPercentText(summary, "tokens"), "20.0% less");

// Short form scales like the overview cards; exact counts live in the title.
assert.equal(proSavedValueText({ rewrite_tokens_saved: 2186 }, "tokens"), "2.2K");
assert.equal(proSavedValueText({ rewrite_tokens_saved: 3_400_000 }, "tokens"), "3.4M");
assert.equal(proSavedValueText({ rewrite_tokens_saved: 840 }, "tokens"), "840");
assert.match(proSavedTitle({ rewrite_tokens_saved: 2186 }, "tokens"), /^2,186 prompt tokens/);

// Costs mode: priced savings against the recorded spend, same 20%.
assert.equal(proSavedValueText(summary, "costs"), "$0.02");
assert.equal(proSavedPercentText(summary, "costs"), "20.0% less");

// Tiny but non-zero shares stay visible rather than rounding to 0.0%.
assert.equal(
proSavedPercentText({ total_tokens: 10_000_000, rewrite_tokens_saved: 1 }, "tokens"),
"<0.1% less",
);

// No pricing in costs mode: the value renders, the share is suppressed.
const unpriced = { total_tokens: 900, total_cost: null, rewrite_tokens_saved: 100 };
assert.equal(proSavedValueText(unpriced, "costs"), "---");
assert.equal(proSavedPercentText(unpriced, "costs"), "");
assert.equal(proSavedPercent(unpriced, "costs"), null);
assert.equal(proSavedPercentText(unpriced, "tokens"), "10.0% less");

// Priced savings against an unpriced period: the baseline is unknown, not
// zero, so the value still renders but the share must stay suppressed
// rather than claim the period was all savings.
const savedButUnpriced = {
total_tokens: 900,
total_cost: null,
rewrite_tokens_saved: 100,
rewrite_cost_saved: 0.02,
};
assert.equal(proSavedValueText(savedButUnpriced, "costs"), "$0.02");
assert.equal(proSavedPercent(savedButUnpriced, "costs"), null);
assert.equal(proSavedPercentText(savedButUnpriced, "costs"), "");

// Same for a missing token baseline.
assert.equal(proSavedPercentText({ rewrite_tokens_saved: 100 }, "tokens"), "");

// A genuinely zero baseline is knowable, so the share still renders.
assert.equal(
proSavedPercentText({ total_tokens: 0, rewrite_tokens_saved: 100 }, "tokens"),
"100.0% less",
);

// No savings at all: nothing to show in either mode.
assert.equal(proSavedPercentText({ total_tokens: 500 }, "tokens"), "");
assert.equal(proSavedPercentText({}, "costs"), "");
});

test("emptyUsagePageSummary carries the cache split and rewrite fields", () => {
const summary = emptyUsagePageSummary();
assert.equal(summary.total_requests, 0);
Expand Down