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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` 不依赖任何包。价目表、档位时钟、成本折叠都是纯函数,内部不读时钟,所以历史会话按它当时真实的档位回算。
Expand Down
15 changes: 15 additions & 0 deletions docs/index.html

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

2 changes: 1 addition & 1 deletion docs/pricing.json

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

17 changes: 16 additions & 1 deletion lib/client.js

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

15 changes: 15 additions & 0 deletions lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: '¥' }

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
2 changes: 1 addition & 1 deletion src/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', {
Expand Down
21 changes: 20 additions & 1 deletion tests/tariff.spec.mjs
Original file line number Diff line number Diff line change
@@ -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'

Expand Down Expand Up @@ -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)
})
})
Loading