From 4a163cd96eb36ea269f06dba9e3ff8d58353ca30 Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Thu, 17 Sep 2026 23:06:16 -0700 Subject: [PATCH] Quote the session's own cache discount; examples price deepseek-flash The cache tip used the card-wide minimum (Pro's 30x). V4.1 Flash, now the default, discounts a hit 50x. cacheDiscountOf(models) reads the models that billed. 0.5.2. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 13 +++++++++++++ README.md | 4 ++-- README.zh.md | 2 +- docs/index.html | 15 +++++++++++++++ docs/pricing.json | 2 +- lib/client.js | 17 ++++++++++++++++- lib/core.js | 15 +++++++++++++++ package.json | 2 +- src/ui.js | 2 +- tests/tariff.spec.mjs | 21 ++++++++++++++++++++- 10 files changed, 85 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3596aa5..16a573f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 0.5.2 - 2026-09-18 + +### Changed + +- **The cache tip quotes the session's own model.** "A stable prompt prefix + bills at 1/N of the miss rate" used the smallest discount on the whole + card, which is Pro's 30x. V4.1 Flash, the default model, discounts a hit + 50x, so every Flash session was told the payoff was 40% smaller than it + is. `cacheDiscountOf(models)` takes the smallest discount among the models + that actually billed; the rate-table footnote still quotes the card-wide + floor, which is right there as an "at least". +- README examples price `deepseek-flash`, the model most sessions now run. + ## 0.5.1 - 2026-09-18 ### Fixed diff --git a/README.md b/README.md index f156556..318474f 100644 --- a/README.md +++ b/README.md @@ -314,7 +314,7 @@ In JavaScript, skip the fetch and call the same module directly: import { costOf, tariffAt } from '@dshworks/dsh-meter/core' const tokens = { miss: 188_542, hit: 1_204_880, out: 9_310 } -costOf(tokens, 'deepseek-v4-pro', tariffAt(Date.now()), 'cny') +costOf(tokens, 'deepseek-flash', tariffAt(Date.now()), 'cny') ``` `lib/core.js` imports nothing. Rate card, tariff clock, and cost fold are @@ -333,7 +333,7 @@ running: curl -s https://dsh.works/dsh-meter/pricing.json | jq -r ' ((now + 8*3600) | gmtime) as $b | .timeOfUse.scheduleBeijing[$b[6]][$b[3]] as $t - | "\($t) · v4-pro out $\(.models["deepseek-v4-pro"].rates[$t].usd.out)/1M"' + | "\($t) · flash out $\(.models["deepseek-flash"].rates[$t].usd.out)/1M"' ``` Shift by `8*3600` first, then break down **once**, and take both indices diff --git a/README.zh.md b/README.zh.md index 09f4dac..8f677f3 100644 --- a/README.zh.md +++ b/README.zh.md @@ -175,7 +175,7 @@ JavaScript 里可以跳过这次 fetch,直接调同一个模块: import { costOf, tariffAt } from '@dshworks/dsh-meter/core' const tokens = { miss: 188_542, hit: 1_204_880, out: 9_310 } -costOf(tokens, 'deepseek-v4-pro', tariffAt(Date.now()), 'cny') +costOf(tokens, 'deepseek-flash', tariffAt(Date.now()), 'cny') ``` `lib/core.js` 不依赖任何包。价目表、档位时钟、成本折叠都是纯函数,内部不读时钟,所以历史会话按它当时真实的档位回算。 diff --git a/docs/index.html b/docs/index.html index f878536..efe3d2a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -742,6 +742,21 @@

Saving mode — let the meter talk back

Object.values(byTariff[tariff]).map((rate) => rate.miss / rate.hit)))), ) +/** + * The same ratio for the models one session actually billed, so the cache + * tip speaks about the model in front of the user. On the live card Flash + * discounts a hit 50x and Pro 30x; quoting the card-wide 30 to a Flash + * session understated the payoff by 40%. + * @param {string[]} models - billed model ids. + * @returns {number} the smallest live discount among them, or CACHE_DISCOUNT when none is on the live card. + */ +function cacheDiscountOf(models) { + const ratios = models.filter(model => RATES[model] !== undefined).flatMap(model => + TARIFFS.filter(tariff => tariff !== 'flat' && RATES[model][tariff] !== undefined).flatMap(tariff => + Object.values(RATES[model][tariff]).map(rate => rate.miss / rate.hit))) + return ratios.length === 0 ? CACHE_DISCOUNT : Math.floor(Math.min(...ratios)) +} + /** Currency symbols for the two published tables. */ const CURRENCY_SYMBOL = { usd: '$', cny: '¥' } diff --git a/docs/pricing.json b/docs/pricing.json index 1998261..aa982eb 100644 --- a/docs/pricing.json +++ b/docs/pricing.json @@ -1,6 +1,6 @@ { "schema": "dsh-meter/pricing@2", - "generator": "@dshworks/dsh-meter@0.5.1", + "generator": "@dshworks/dsh-meter@0.5.2", "homepage": "https://dsh.works/dsh-meter/", "source": { "usd": "https://api-docs.deepseek.com/quick_start/pricing", diff --git a/lib/client.js b/lib/client.js index ab517ea..497c5ec 100644 --- a/lib/client.js +++ b/lib/client.js @@ -197,6 +197,21 @@ const CACHE_DISCOUNT = Math.floor( Object.values(byTariff[tariff]).map((rate) => rate.miss / rate.hit)))), ) +/** + * The same ratio for the models one session actually billed, so the cache + * tip speaks about the model in front of the user. On the live card Flash + * discounts a hit 50x and Pro 30x; quoting the card-wide 30 to a Flash + * session understated the payoff by 40%. + * @param {string[]} models - billed model ids. + * @returns {number} the smallest live discount among them, or CACHE_DISCOUNT when none is on the live card. + */ +function cacheDiscountOf(models) { + const ratios = models.filter(model => RATES[model] !== undefined).flatMap(model => + TARIFFS.filter(tariff => tariff !== 'flat' && RATES[model][tariff] !== undefined).flatMap(tariff => + Object.values(RATES[model][tariff]).map(rate => rate.miss / rate.hit))) + return ratios.length === 0 ? CACHE_DISCOUNT : Math.floor(Math.min(...ratios)) +} + /** Currency symbols for the two published tables. */ const CURRENCY_SYMBOL = { usd: '$', cny: '¥' } @@ -1301,7 +1316,7 @@ function MeterCard({ value, balance, currency, now, t, position }) { if (value.tokens.hit > 0) { notes.push(t('meter.cacheSaved', { amount: formatMoney(saved, currency), percent: hitShare })) } else if (promptTokens > 0) { - notes.push(t('meter.cacheCold', { factor: CACHE_DISCOUNT })) + notes.push(t('meter.cacheCold', { factor: cacheDiscountOf(value.models.map(entry => entry.model)) })) } if (clock.tariff === 'flat') { notes.push(t('meter.ifNewRates', { diff --git a/lib/core.js b/lib/core.js index 197658f..d190dd7 100644 --- a/lib/core.js +++ b/lib/core.js @@ -188,6 +188,21 @@ export const CACHE_DISCOUNT = Math.floor( Object.values(byTariff[tariff]).map((rate) => rate.miss / rate.hit)))), ) +/** + * The same ratio for the models one session actually billed, so the cache + * tip speaks about the model in front of the user. On the live card Flash + * discounts a hit 50x and Pro 30x; quoting the card-wide 30 to a Flash + * session understated the payoff by 40%. + * @param {string[]} models - billed model ids. + * @returns {number} the smallest live discount among them, or CACHE_DISCOUNT when none is on the live card. + */ +export function cacheDiscountOf(models) { + const ratios = models.filter(model => RATES[model] !== undefined).flatMap(model => + TARIFFS.filter(tariff => tariff !== 'flat' && RATES[model][tariff] !== undefined).flatMap(tariff => + Object.values(RATES[model][tariff]).map(rate => rate.miss / rate.hit))) + return ratios.length === 0 ? CACHE_DISCOUNT : Math.floor(Math.min(...ratios)) +} + /** Currency symbols for the two published tables. */ export const CURRENCY_SYMBOL = { usd: '$', cny: '¥' } diff --git a/package.json b/package.json index f33c413..3ce9cad 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@dshworks/dsh-meter", "description": "The DeepSeek time-of-use meter for dsh: what this session cost, which tariff is running, when it flips, and the account balance behind it — one line under the composer.", - "version": "0.5.1", + "version": "0.5.2", "license": "MIT", "homepage": "https://dsh.works/dsh-meter/", "publishConfig": { diff --git a/src/ui.js b/src/ui.js index c919cbb..f9716fa 100644 --- a/src/ui.js +++ b/src/ui.js @@ -495,7 +495,7 @@ function MeterCard({ value, balance, currency, now, t, position }) { if (value.tokens.hit > 0) { notes.push(t('meter.cacheSaved', { amount: formatMoney(saved, currency), percent: hitShare })) } else if (promptTokens > 0) { - notes.push(t('meter.cacheCold', { factor: CACHE_DISCOUNT })) + notes.push(t('meter.cacheCold', { factor: cacheDiscountOf(value.models.map(entry => entry.model)) })) } if (clock.tariff === 'flat') { notes.push(t('meter.ifNewRates', { diff --git a/tests/tariff.spec.mjs b/tests/tariff.spec.mjs index 93dc9a7..8e14fe2 100644 --- a/tests/tariff.spec.mjs +++ b/tests/tariff.spec.mjs @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest' import { CACHE_DISCOUNT, CURRENCY_SYMBOL, PEAK_WINDOWS_UTC, RATES, TIME_OF_USE_FROM, - WEEKEND_OFFPEAK_FROM, bucketCostOf, costOf, formatCountdown, formatMoney, + WEEKEND_OFFPEAK_FROM, bucketCostOf, cacheDiscountOf, costOf, formatCountdown, formatMoney, formatTokens, isBeijingWeekend, nextTariffChange, tariffAt, tariffSchedule, } from '../lib/core.js' @@ -263,3 +263,22 @@ describe('display helpers', () => { expect(formatCountdown(-1)).toBe('0s') }) }) + +describe('the cache tip speaks about the session\'s own model', () => { + const cardWide = CACHE_DISCOUNT + + it('quotes Flash its own discount, not the card-wide minimum', () => { + // ¥1 miss against ¥0.02 hit, $0.15 against $0.003: 50x in both tables. + expect(cacheDiscountOf(['deepseek-flash'])).toBe(50) + expect(cardWide).toBe(30) + }) + + it('takes the smaller discount when a session mixed models', () => { + expect(cacheDiscountOf(['deepseek-flash', 'deepseek-v4-pro'])).toBe(30) + }) + + it('falls back to the card-wide figure for a model the live card does not price', () => { + expect(cacheDiscountOf(['mystery-model'])).toBe(cardWide) + expect(cacheDiscountOf([])).toBe(cardWide) + }) +})