Skip to content
Open
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,8 @@ default) is the most pessimistic reading.

### What the number does not include

**Prompt caching.** The money figures price every re-send of the context at full input rate. In Claude Code the conversation prefix is served from the prompt cache — first send a cache write at 1.25x input, re-sends cache reads at 0.1x — so a fast conversation pays far less to hold the same context. In the worked example above, `held 10 turns` at full price is $1.000, but with the cache holding it is about $0.215, which turns the 93% saving into roughly 78%. The flat figure is the honest one when turns are more than a cache TTL apart (5m by default) and the prefix is re-bought each turn; `gain -v` and `gain --json` print the cache-aware figure beside it.

**Follow-up verification.** After a worker answers, the model often runs a `grep` and a
narrow re-read to check it. Those tokens do enter context and are not in the ledger, so
real savings are somewhat lower than reported.
Expand Down
12 changes: 12 additions & 0 deletions scripts/offload-gain
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,14 @@ def main():
still_pay = tout * main_in * a.amp
usd = would_have - worker_cost - still_pay
usd_amp = usd
# Prompt-cache-aware companion: Claude Code keeps the prompt cached, so the
# first send is a cache WRITE (1.25x input) and every re-send a READ (0.1x).
# The flat figure above prices every re-send at full input — right when the
# cache expires between turns (5m TTL), an overstatement in a fast one.
hold_mult = 1.25 + 0.10 * (a.amp - 1) if a.amp >= 1 else a.amp
would_have_cached = (tin + b_tok_undelegated) * main_in * hold_mult
still_pay_cached = tout * main_in * hold_mult
usd_cached = would_have_cached - worker_cost - still_pay_cached
by = collections.defaultdict(lambda: {"calls": 0, "in": 0, "out": 0, "secs": 0})
for r in rows:
k = f'{r.get("provider","?")}/{r.get("model","?")}@{r.get("effort","?")}'
Expand All @@ -180,6 +188,8 @@ def main():
"blocked_tokens_not_delegated": b_tok_undelegated, "worker_secs": secs_total,
"usd_would_have_paid": round(would_have, 4), "usd_worker_cost": round(worker_cost, 4),
"usd_still_paid": round(still_pay, 4), "usd_net_saved": round(usd, 4),
"usd_would_have_paid_cached": round(would_have_cached, 4),
"usd_saved_cached": round(usd_cached, 4),
"blocks_dropped": dropped, "amp": a.amp, "usd_saved_amplified": round(usd_amp, 2), "by_worker": by, "ledger": LEDGER}, indent=2))
return

Expand Down Expand Up @@ -292,6 +302,8 @@ def main():
print(f" What the cheap worker charged instead -${worker_cost:>9,.4f}")
print(f" What you still pay for its answers -${still_pay:>9,.4f}")
print(f" You saved ${usd:>9,.4f}")
print(f" With prompt caching (1.25x write, 0.1x re-reads)")
print(f" would have paid / saved ${would_have_cached:>9,.4f} / ${usd_cached:,.4f}")
print(f" (your model at ${a.rate:g}/1M input, file held for {a.amp:g} turn{'s' if a.amp != 1 else ''})")

if a.history:
Expand Down