mirror of
https://github.com/teamchong/pxpipe.git
synced 2026-07-22 02:02:51 +02:00
feat(dashboard): direct compressed-vs-passthrough cost split
Add per-path actual-billed cost counters to the dashboard alongside the existing counterfactual baseline. The compressed-vs-passthrough split sums what each code path actually cost on real traffic, partitioned by `info.compressed`, with no probe gating or counterfactuals. Surfaces sample counts in the UI so operators can judge sufficiency rather than having the dashboard auto-claim significance. Headline math also switched to share-of-all-paid-spend (counterfactual denominator bounded at 100%) so the slice number never lies when pixelpipe didn't run on every request. Regenerated dashboard-bundle.
This commit is contained in:
+36
-35
File diff suppressed because one or more lines are too long
@@ -185,6 +185,22 @@ interface Totals {
|
||||
/** Count of requests that contributed to allActualInputWeighted (had a
|
||||
* usage block). Lets the UI annotate "N of M paid requests". */
|
||||
allUsageRequests: number;
|
||||
/** Direct compressed-vs-passthrough actual-cost split. No counterfactuals,
|
||||
* no probe gating — just sum what each path actually billed.
|
||||
*
|
||||
* These accumulate over `haveUsage` rows only (same gate as the all-rows
|
||||
* counters above) and partition that set by `info.compressed`. The point
|
||||
* is to answer "did the compressed path actually cost less per request
|
||||
* than the passthrough path, on real traffic" without inventing a
|
||||
* counterfactual. Selection bias is real (the gate decides which path
|
||||
* each turn lands on), so the UI surfaces sample counts so the operator
|
||||
* can judge sufficiency — the dashboard does not auto-claim significance. */
|
||||
compressedPaidRequests: number;
|
||||
compressedActualInputWeighted: number;
|
||||
compressedOutputWeighted: number;
|
||||
passthroughPaidRequests: number;
|
||||
passthroughActualInputWeighted: number;
|
||||
passthroughOutputWeighted: number;
|
||||
/** Sum of ground-truth output character counts from the SSE/JSON scanner
|
||||
* (see `OutputMeasurement` in proxy.ts). These three accumulators are
|
||||
* independent of Anthropic's `usage.output_tokens` — they let the operator
|
||||
@@ -256,6 +272,12 @@ export class DashboardState {
|
||||
allActualInputWeighted: 0,
|
||||
allOutputWeighted: 0,
|
||||
allUsageRequests: 0,
|
||||
compressedPaidRequests: 0,
|
||||
compressedActualInputWeighted: 0,
|
||||
compressedOutputWeighted: 0,
|
||||
passthroughPaidRequests: 0,
|
||||
passthroughActualInputWeighted: 0,
|
||||
passthroughOutputWeighted: 0,
|
||||
textCharsMeasured: 0,
|
||||
thinkingCharsMeasured: 0,
|
||||
toolUseCharsMeasured: 0,
|
||||
@@ -421,6 +443,21 @@ export class DashboardState {
|
||||
this.totals.allActualInputWeighted += actualInputEff;
|
||||
this.totals.allOutputWeighted += outputEquiv;
|
||||
this.totals.allUsageRequests += 1;
|
||||
// Direct observed compressed-vs-passthrough split. No counterfactual,
|
||||
// no probe gating — just partition the paid-rows set by which path
|
||||
// actually ran this turn. Headline answers "is the compressed path
|
||||
// cheaper per request on real traffic". Selection bias (the gate
|
||||
// routes each turn) is real; sample counts go to the UI so the
|
||||
// operator can judge sufficiency.
|
||||
if (compressed) {
|
||||
this.totals.compressedPaidRequests += 1;
|
||||
this.totals.compressedActualInputWeighted += actualInputEff;
|
||||
this.totals.compressedOutputWeighted += outputEquiv;
|
||||
} else {
|
||||
this.totals.passthroughPaidRequests += 1;
|
||||
this.totals.passthroughActualInputWeighted += actualInputEff;
|
||||
this.totals.passthroughOutputWeighted += outputEquiv;
|
||||
}
|
||||
}
|
||||
|
||||
// Measurement totals are independent of usage/baseline gating — they
|
||||
@@ -568,6 +605,42 @@ export class DashboardState {
|
||||
const allCounterfactualBill = allBaselineEquiv + allOutput;
|
||||
const pctAllSpend =
|
||||
allCounterfactualBill > 0 ? (saved / allCounterfactualBill) * 100 : 0;
|
||||
|
||||
// Direct observed split — actual $ per request, partitioned by which
|
||||
// path ran. Token-equivalent (input × 1.0 + cache_create × 1.25 +
|
||||
// cache_read × 0.10 + output × 5) → $ at the assumed Opus 4.7 input
|
||||
// rate. Same $/Mtok rate is applied to both buckets, so the bias from
|
||||
// the rate assumption cancels in the delta. Selection bias from the
|
||||
// gate is NOT cancelled — the operator interprets that via the
|
||||
// sample-count caveat below.
|
||||
const compressedTokenEquiv =
|
||||
this.totals.compressedActualInputWeighted +
|
||||
this.totals.compressedOutputWeighted;
|
||||
const passthroughTokenEquiv =
|
||||
this.totals.passthroughActualInputWeighted +
|
||||
this.totals.passthroughOutputWeighted;
|
||||
const compressedActualUsd =
|
||||
(compressedTokenEquiv * ASSUMED_INPUT_USD_PER_MTOK) / 1e6;
|
||||
const passthroughActualUsd =
|
||||
(passthroughTokenEquiv * ASSUMED_INPUT_USD_PER_MTOK) / 1e6;
|
||||
const compressedAvgUsd =
|
||||
this.totals.compressedPaidRequests > 0
|
||||
? compressedActualUsd / this.totals.compressedPaidRequests
|
||||
: 0;
|
||||
const passthroughAvgUsd =
|
||||
this.totals.passthroughPaidRequests > 0
|
||||
? passthroughActualUsd / this.totals.passthroughPaidRequests
|
||||
: 0;
|
||||
// Sufficient-sample threshold is a soft heuristic. 20 paid requests per
|
||||
// bucket is enough to see a real effect on Opus 4.7 traffic (a single
|
||||
// big cold-miss can dominate at lower n). Below this, the UI shows the
|
||||
// bucket numbers but hides the delta and surfaces "small sample".
|
||||
const SUFFICIENT = 20;
|
||||
const splitSufficient =
|
||||
this.totals.compressedPaidRequests >= SUFFICIENT &&
|
||||
this.totals.passthroughPaidRequests >= SUFFICIENT;
|
||||
const splitDeltaUsd = compressedAvgUsd - passthroughAvgUsd;
|
||||
|
||||
const uptimeSec = Date.now() / 1000 - this.totals.startedAt;
|
||||
const payload = {
|
||||
requests: this.totals.requests,
|
||||
@@ -590,6 +663,19 @@ export class DashboardState {
|
||||
all_actual_input_weighted: Math.round(allActual),
|
||||
all_output_weighted: Math.round(allOutput),
|
||||
all_usage_requests: this.totals.allUsageRequests,
|
||||
// Direct observed split — replaces "share of spend saved" as the
|
||||
// headline. Total actual $ and average $/req per path, plus a delta
|
||||
// gated on `split_sufficient_sample`. No counterfactual: each
|
||||
// bucket is what each path actually billed.
|
||||
compressed_paid_requests: this.totals.compressedPaidRequests,
|
||||
passthrough_paid_requests: this.totals.passthroughPaidRequests,
|
||||
compressed_actual_usd: round4(compressedActualUsd),
|
||||
passthrough_actual_usd: round4(passthroughActualUsd),
|
||||
compressed_avg_usd_per_request: round4(compressedAvgUsd),
|
||||
passthrough_avg_usd_per_request: round4(passthroughAvgUsd),
|
||||
compressed_minus_passthrough_avg_usd: round4(splitDeltaUsd),
|
||||
split_sufficient_sample: splitSufficient,
|
||||
split_min_sample_per_bucket: SUFFICIENT,
|
||||
saved_usd: round4((saved * ASSUMED_INPUT_USD_PER_MTOK) / 1e6),
|
||||
output_weighted: Math.round(output),
|
||||
baseline_token_equivalent: Math.round(baselineTotal),
|
||||
|
||||
@@ -51,17 +51,18 @@
|
||||
`<span class="src">source: ${escapeHtml(pa.source || 'docs.anthropic.com pricing')}</span>`
|
||||
: '';
|
||||
|
||||
// Diagnostic only — no longer the headline. See `split` math below for
|
||||
// the headline.
|
||||
$: pctMath = s && pa
|
||||
? `<div><span class="k">formula:</span> <span class="v">` +
|
||||
` share_of_spend = saved / (all_baseline_equivalent + all_output × ` +
|
||||
(pa.output_multiplier ?? 5) +
|
||||
`)</span></div>` +
|
||||
`<div><span class="k">why this denominator:</span> <span class="v">` +
|
||||
`the question is "did pixelpipe move my real bill", not "did pixelpipe help on the slice it ran on". ` +
|
||||
`Denominator = what the user WOULD have paid with no pixelpipe — measured rows use their cache-aware ` +
|
||||
`baseline, unmeasured/passthrough rows fall back to actual (pixelpipe didn't run there, so counterfactual = actual). ` +
|
||||
`Dividing by counterfactual bill instead of actual bill keeps the ratio bounded at 100%; saved/actual ` +
|
||||
`is unbounded because pixelpipe shrinks the denominator.</span></div>` +
|
||||
`<div><span class="k">why this is diagnostic, not the headline:</span> <span class="v">` +
|
||||
`this is a counterfactual ("what the user WOULD have paid"). It depends on the count_tokens probe, ` +
|
||||
`the cache-aware baseline split, and an Opus 4.7 input-rate assumption. Useful as a sanity check, ` +
|
||||
`but the operator's real question is "did the compressed path cost less per request than the ` +
|
||||
`passthrough path on real traffic" — that's the headline split above, no counterfactuals.</span></div>` +
|
||||
`<div style="height:6px"></div>` +
|
||||
row('saved', s.saved_input_tokens, '(measured-rows numerator; cache-aware)') +
|
||||
row(
|
||||
@@ -92,6 +93,43 @@
|
||||
`<span class="src">measured numerator, all-rows counterfactual denominator - bounded at 100%</span>`
|
||||
: '';
|
||||
|
||||
// Headline "compressed vs passthrough" math. Direct observed split — no
|
||||
// counterfactuals, no probe gating. Each bucket is the sum of actual
|
||||
// billed token-equivalents (input + cc×1.25 + cr×0.10 + out×5) for that
|
||||
// path, converted to $ at the assumed input rate.
|
||||
$: splitMath = s && pa
|
||||
? `<div><span class="k">formula:</span> <span class="v">` +
|
||||
`bucket_$ = (Σ actual_input + Σ output × ` + (pa.output_multiplier ?? 5) +
|
||||
`) × $` + (pa.input_per_mtok ?? 5) + `/Mtok</span></div>` +
|
||||
`<div><span class="k">why:</span> <span class="v">` +
|
||||
`partition the paid-rows set by which path actually ran this turn ` +
|
||||
`(\`info.compressed = true\` for slab/history compression; false for ` +
|
||||
`passthrough or bypassed). Same $/Mtok rate on both sides so the ` +
|
||||
`rate-assumption bias cancels in the delta. Selection bias (the ` +
|
||||
`gate routes each turn) does NOT cancel — interpret with sample ` +
|
||||
`counts.</span></div>` +
|
||||
`<div style="height:6px"></div>` +
|
||||
row(
|
||||
'compressed (n=' + s.compressed_paid_requests + ')',
|
||||
'$' + (s.compressed_actual_usd || 0).toFixed(4),
|
||||
'total · avg $' + (s.compressed_avg_usd_per_request || 0).toFixed(4) + '/req',
|
||||
) +
|
||||
row(
|
||||
'passthrough (n=' + s.passthrough_paid_requests + ')',
|
||||
'$' + (s.passthrough_actual_usd || 0).toFixed(4),
|
||||
'total · avg $' + (s.passthrough_avg_usd_per_request || 0).toFixed(4) + '/req',
|
||||
) +
|
||||
row(
|
||||
'compressed − passthrough',
|
||||
'$' + (s.compressed_minus_passthrough_avg_usd || 0).toFixed(4) + '/req',
|
||||
s.split_sufficient_sample
|
||||
? '(both buckets ≥ ' + s.split_min_sample_per_bucket + ' — delta is meaningful)'
|
||||
: '(small sample: need ≥ ' + s.split_min_sample_per_bucket +
|
||||
' per bucket; treat delta as noisy)',
|
||||
) +
|
||||
`<span class="src">no counterfactual, no probe gate — pure observed $/req on each path</span>`
|
||||
: '';
|
||||
|
||||
$: tokeqMath = s && pa
|
||||
? `<div><span class="k">formula:</span> <span class="v">` +
|
||||
`token_equivalent = input + output × ` +
|
||||
@@ -179,15 +217,37 @@
|
||||
{/if}
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="label">share of spend saved</div>
|
||||
<div class="value" class:pos={(s?.saved_pct_of_all_spend ?? 0) >= 0} class:neg={(s?.saved_pct_of_all_spend ?? 0) < 0}>{(s?.saved_pct_of_all_spend ?? 0).toFixed(1)}%</div>
|
||||
<div class="label">compressed $/req</div>
|
||||
<div class="value">$ {(s?.compressed_avg_usd_per_request ?? 0).toFixed(4)}</div>
|
||||
<div class="small">
|
||||
÷ all paid requests ({numFmt(s?.all_usage_requests)} req · compressed + passthrough + probe-failed) · negative = pixelpipe added cost
|
||||
n={numFmt(s?.compressed_paid_requests)} · total $ {(s?.compressed_actual_usd ?? 0).toFixed(4)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="label">passthrough $/req</div>
|
||||
<div class="value">$ {(s?.passthrough_avg_usd_per_request ?? 0).toFixed(4)}</div>
|
||||
<div class="small">
|
||||
n={numFmt(s?.passthrough_paid_requests)} · total $ {(s?.passthrough_actual_usd ?? 0).toFixed(4)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="label">compressed − passthrough $/req</div>
|
||||
{#if s?.split_sufficient_sample}
|
||||
<div class="value" class:pos={(s?.compressed_minus_passthrough_avg_usd ?? 0) <= 0} class:neg={(s?.compressed_minus_passthrough_avg_usd ?? 0) > 0}>
|
||||
{((s?.compressed_minus_passthrough_avg_usd ?? 0) >= 0 ? '+$ ' : '-$ ')}{Math.abs(s?.compressed_minus_passthrough_avg_usd ?? 0).toFixed(4)}
|
||||
</div>
|
||||
<div class="small">negative = compressed path cheaper · both buckets ≥ {s?.split_min_sample_per_bucket}</div>
|
||||
{:else}
|
||||
<div class="value small-sample">small sample</div>
|
||||
<div class="small">
|
||||
need ≥ {s?.split_min_sample_per_bucket} requests per bucket · have
|
||||
{numFmt(s?.compressed_paid_requests)} / {numFmt(s?.passthrough_paid_requests)}
|
||||
</div>
|
||||
{/if}
|
||||
{#if s && pa}
|
||||
<details class="math">
|
||||
<summary>show calculation</summary>
|
||||
<div class="formula">{@html pctMath}</div>
|
||||
<div class="formula">{@html splitMath}</div>
|
||||
</details>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -204,14 +264,32 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if s && pa}
|
||||
<details class="diagnostic">
|
||||
<summary>diagnostic: counterfactual "share of spend saved"</summary>
|
||||
<div class="diagnostic-body">
|
||||
<div class="diag-headline">
|
||||
share of spend saved (counterfactual):
|
||||
<span class:pos={(s?.saved_pct_of_all_spend ?? 0) >= 0} class:neg={(s?.saved_pct_of_all_spend ?? 0) < 0}>
|
||||
{(s?.saved_pct_of_all_spend ?? 0).toFixed(1)}%
|
||||
</span>
|
||||
<span class="small">
|
||||
({numFmt(s?.all_usage_requests)} paid req · compressed + passthrough + probe-failed)
|
||||
</span>
|
||||
</div>
|
||||
<div class="formula">{@html pctMath}</div>
|
||||
</div>
|
||||
</details>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: 14px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
@media (max-width: 1200px) {
|
||||
@media (max-width: 1400px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
@@ -246,6 +324,52 @@
|
||||
.value.neg {
|
||||
color: #f85149;
|
||||
}
|
||||
.value.small-sample {
|
||||
color: #8b949e;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.diagnostic {
|
||||
margin: 0 0 22px;
|
||||
font-size: 12px;
|
||||
color: #8b949e;
|
||||
}
|
||||
.diagnostic :global(summary) {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
color: #58a6ff;
|
||||
padding: 6px 0;
|
||||
}
|
||||
.diagnostic :global(summary::-webkit-details-marker) {
|
||||
display: none;
|
||||
}
|
||||
.diagnostic :global(summary::before) {
|
||||
content: '▸ ';
|
||||
color: #6e7681;
|
||||
font-size: 9px;
|
||||
}
|
||||
.diagnostic :global([open] summary::before) {
|
||||
content: '▾ ';
|
||||
}
|
||||
.diagnostic-body {
|
||||
background: #161b22;
|
||||
border: 1px solid #30363d;
|
||||
border-radius: 6px;
|
||||
padding: 12px 14px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.diag-headline {
|
||||
color: #c9d1d9;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.diag-headline .pos {
|
||||
color: #3fb950;
|
||||
font-weight: 600;
|
||||
}
|
||||
.diag-headline .neg {
|
||||
color: #f85149;
|
||||
font-weight: 600;
|
||||
}
|
||||
.small {
|
||||
font-size: 11px;
|
||||
color: #6e7681;
|
||||
|
||||
@@ -25,6 +25,20 @@ export interface StatsPayload {
|
||||
all_actual_input_weighted: number;
|
||||
all_output_weighted: number;
|
||||
all_usage_requests: number;
|
||||
/** Direct observed compressed-vs-passthrough split. Headline answers
|
||||
* "is the compressed path cheaper per request on real traffic" without
|
||||
* inventing a counterfactual. `split_sufficient_sample` gates the
|
||||
* per-request delta on a minimum count per bucket (UI hides the delta
|
||||
* number below the threshold and shows a "small sample" caveat). */
|
||||
compressed_paid_requests: number;
|
||||
passthrough_paid_requests: number;
|
||||
compressed_actual_usd: number;
|
||||
passthrough_actual_usd: number;
|
||||
compressed_avg_usd_per_request: number;
|
||||
passthrough_avg_usd_per_request: number;
|
||||
compressed_minus_passthrough_avg_usd: number;
|
||||
split_sufficient_sample: boolean;
|
||||
split_min_sample_per_bucket: number;
|
||||
saved_usd: number;
|
||||
output_weighted: number;
|
||||
baseline_token_equivalent: number;
|
||||
|
||||
Reference in New Issue
Block a user