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
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,16 +148,16 @@ Costs are resolved with priority **user override file > builtin list price > fal

```json
{
"deepseek-v4-flash": {
"deepseek-flash": {
"offPeak": {
"cny": { "cacheMissInput": 1.5, "cacheHitInput": 0.05, "output": 4.5 },
"usd": { "cacheMissInput": 0.22, "cacheHitInput": 0.007, "output": 0.66 }
"cny": { "cacheMissInput": 1, "cacheHitInput": 0.02, "output": 4 },
"usd": { "cacheMissInput": 0.15, "cacheHitInput": 0.003, "output": 0.6 }
},
"peak": {
"cny": { "cacheMissInput": 3.0, "cacheHitInput": 0.10, "output": 9.0 },
"usd": { "cacheMissInput": 0.44, "cacheHitInput": 0.014, "output": 1.32 }
"cny": { "cacheMissInput": 2, "cacheHitInput": 0.04, "output": 8 },
"usd": { "cacheMissInput": 0.3, "cacheHitInput": 0.006, "output": 1.2 }
},
"verifiedAt": 1755100800000
"verifiedAt": 1788998400000
}
}
```
Expand Down
12 changes: 6 additions & 6 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,16 +168,16 @@ dsh web --profile web

```json
{
"deepseek-v4-flash": {
"deepseek-flash": {
"offPeak": {
"cny": { "cacheMissInput": 1.5, "cacheHitInput": 0.05, "output": 4.5 },
"usd": { "cacheMissInput": 0.22, "cacheHitInput": 0.007, "output": 0.66 }
"cny": { "cacheMissInput": 1, "cacheHitInput": 0.02, "output": 4 },
"usd": { "cacheMissInput": 0.15, "cacheHitInput": 0.003, "output": 0.6 }
},
"peak": {
"cny": { "cacheMissInput": 3.0, "cacheHitInput": 0.10, "output": 9.0 },
"usd": { "cacheMissInput": 0.44, "cacheHitInput": 0.014, "output": 1.32 }
"cny": { "cacheMissInput": 2, "cacheHitInput": 0.04, "output": 8 },
"usd": { "cacheMissInput": 0.3, "cacheHitInput": 0.006, "output": 1.2 }
},
"verifiedAt": 1755100800000
"verifiedAt": 1788998400000
}
}
```
Expand Down
50 changes: 25 additions & 25 deletions src/pricing/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,25 @@ export interface PricingSource {

/**
* 当前官方在售模型定价表(双币种 / 1M tokens,区分高峰/空闲时段)。
* 来源:官方定价页(2026-08-26 核验)
* 来源:官方定价页(2026-09-10 核验)
* - 中文页 https://api-docs.deepseek.com/zh-cn/quick_start/pricing(CNY 报价)
* - 英文页 https://api-docs.deepseek.com/quick_start/pricing(USD 报价)
* 高峰时段(北京时间周一至周五 09:00–12:00、14:00–18:00 = UTC 01:00–04:00、06:00–10:00)
* 价格为空闲时段的两倍。
*/
export const BUILTIN_PRICING: Record<string, ModelPricing> = {
'deepseek-v4-flash': {
offPeak: {
cny: { cacheMissInput: 1.5, cacheHitInput: 0.05, output: 4.5 },
usd: { cacheMissInput: 0.22, cacheHitInput: 0.007, output: 0.66 },
},
peak: {
cny: { cacheMissInput: 3.0, cacheHitInput: 0.10, output: 9.0 },
usd: { cacheMissInput: 0.44, cacheHitInput: 0.014, output: 1.32 },
},
const FLASH_PRICING: ModelPricing = {
offPeak: {
cny: { cacheMissInput: 1, cacheHitInput: 0.02, output: 4 },
usd: { cacheMissInput: 0.15, cacheHitInput: 0.003, output: 0.6 },
},
peak: {
cny: { cacheMissInput: 2, cacheHitInput: 0.04, output: 8 },
usd: { cacheMissInput: 0.3, cacheHitInput: 0.006, output: 1.2 },
},
}

export const BUILTIN_PRICING: Record<string, ModelPricing> = {
'deepseek-flash': FLASH_PRICING,
'deepseek-v4-pro': {
offPeak: {
cny: { cacheMissInput: 4.5, cacheHitInput: 0.15, output: 13.5 },
Expand All @@ -58,23 +60,21 @@ export const BUILTIN_PRICING: Record<string, ModelPricing> = {
usd: { cacheMissInput: 1.32, cacheHitInput: 0.044, output: 3.96 },
},
},
'deepseek-v4-flash-vision-exp': {
offPeak: {
cny: { cacheMissInput: 1.5, cacheHitInput: 0.05, output: 4.5 },
usd: { cacheMissInput: 0.22, cacheHitInput: 0.007, output: 0.66 },
},
peak: {
cny: { cacheMissInput: 3.0, cacheHitInput: 0.10, output: 9.0 },
usd: { cacheMissInput: 0.44, cacheHitInput: 0.014, output: 1.32 },
},
},
}

/** 内置表的核验日期(来源:官方定价页中/英文版,2026-08-26 抓取核验)。 */
export const BUILTIN_VERIFIED_AT = Date.parse('2026-08-26T00:00:00Z')
/**
* 旧模型名兼容:
* - `deepseek-v4-flash` / `deepseek-v4-flash-vision-exp` 已并入 `deepseek-flash`,
* 仍按同一价格表解析,避免历史会话突然变成「未定价模型」。
*/
BUILTIN_PRICING['deepseek-v4-flash'] = FLASH_PRICING
BUILTIN_PRICING['deepseek-v4-flash-vision-exp'] = FLASH_PRICING

/** 内置表的核验日期(来源:官方定价页中/英文版,2026-09-10 抓取核验)。 */
export const BUILTIN_VERIFIED_AT = Date.parse('2026-09-10T00:00:00Z')

/** 未收录模型回退:按 deepseek-v4-flash 刊例价估算并标记 ≈。 */
export const FALLBACK_PRICING: ModelPricing = BUILTIN_PRICING['deepseek-v4-flash']
/** 未收录模型回退:按 deepseek-flash 刊例价估算并标记 ≈。 */
export const FALLBACK_PRICING: ModelPricing = FLASH_PRICING

/** 前缀匹配(如带日期后缀的模型版本);精确匹配优先。 */
function matchEntry<T>(table: Record<string, T>, model: string): T | null {
Expand Down
13 changes: 7 additions & 6 deletions tests/core.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,18 @@ test('foldTurnUsage ignores malformed or negative usage samples', () => {
})

test('pricing matches the documented DeepSeek V4 prices (CNY + USD, peak/off-peak)', () => {
assert.deepEqual(PRICING['deepseek-v4-flash'], {
assert.deepEqual(PRICING['deepseek-flash'], {
offPeak: {
cny: { cacheMissInput: 1.5, cacheHitInput: 0.05, output: 4.5 },
usd: { cacheMissInput: 0.22, cacheHitInput: 0.007, output: 0.66 },
cny: { cacheMissInput: 1, cacheHitInput: 0.02, output: 4 },
usd: { cacheMissInput: 0.15, cacheHitInput: 0.003, output: 0.6 },
},
peak: {
cny: { cacheMissInput: 3.0, cacheHitInput: 0.10, output: 9.0 },
usd: { cacheMissInput: 0.44, cacheHitInput: 0.014, output: 1.32 },
cny: { cacheMissInput: 2, cacheHitInput: 0.04, output: 8 },
usd: { cacheMissInput: 0.3, cacheHitInput: 0.006, output: 1.2 },
},
})
assert.deepEqual(PRICING['deepseek-v4-flash-vision-exp'], PRICING['deepseek-v4-flash'])
assert.deepEqual(PRICING['deepseek-v4-flash'], PRICING['deepseek-flash'])
assert.deepEqual(PRICING['deepseek-v4-flash-vision-exp'], PRICING['deepseek-flash'])
assert.equal(cacheHitPercent({ uncachedInputTokens: 50, outputTokens: 0, cacheReadTokens: 50, cacheWriteTokens: 0 }), 50)
// 时刻未知 → 高峰价保守估算:1M 未命中输入 + 1M 输出(v4-pro 高峰 CNY = 9 + 27)
assert.equal(estimateCost({ uncachedInputTokens: 1_000_000, outputTokens: 1_000_000, cacheReadTokens: 0, cacheWriteTokens: 0 }, 'deepseek-v4-pro').cny, 36)
Expand Down
39 changes: 21 additions & 18 deletions tests/pricing.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ import {
tierAt,
} from '../lib/index.js'

/** 2026-08-26 内置刊例价(双币种 /1M,高峰/空闲)。 */
/** 2026-09-10 内置刊例价(双币种 /1M,高峰/空闲)。 */
const FLASH_OFF_PEAK = {
cny: { cacheMissInput: 1.5, cacheHitInput: 0.05, output: 4.5 },
usd: { cacheMissInput: 0.22, cacheHitInput: 0.007, output: 0.66 },
cny: { cacheMissInput: 1, cacheHitInput: 0.02, output: 4 },
usd: { cacheMissInput: 0.15, cacheHitInput: 0.003, output: 0.6 },
}
const FLASH_PEAK = {
cny: { cacheMissInput: 3.0, cacheHitInput: 0.10, output: 9.0 },
usd: { cacheMissInput: 0.44, cacheHitInput: 0.014, output: 1.32 },
cny: { cacheMissInput: 2, cacheHitInput: 0.04, output: 8 },
usd: { cacheMissInput: 0.3, cacheHitInput: 0.006, output: 1.2 },
}
const PRO_OFF_PEAK = {
cny: { cacheMissInput: 4.5, cacheHitInput: 0.15, output: 13.5 },
Expand All @@ -42,9 +42,10 @@ test('builtin source resolves exact and prefixed models, misses unknown', () =>
assert.deepEqual(source.resolve('deepseek-v4-pro').pricing.offPeak, PRO_OFF_PEAK)
assert.deepEqual(source.resolve('deepseek-v4-pro').pricing.peak, PRO_PEAK)
// 前缀匹配(带日期后缀的模型版本)
assert.equal(source.resolve('deepseek-v4-flash-2026-08-01').pricing.offPeak.cny.output, 4.5)
assert.equal(source.resolve('deepseek-v4-flash-2026-08-01').pricing.offPeak.usd.output, 0.66)
assert.equal(source.resolve('deepseek-v4-flash-2026-08-01').pricing.peak.usd.output, 1.32)
assert.equal(source.resolve('deepseek-flash-2026-09-01').pricing.offPeak.cny.output, 4)
assert.equal(source.resolve('deepseek-flash-2026-09-01').pricing.offPeak.usd.output, 0.6)
assert.equal(source.resolve('deepseek-flash-2026-09-01').pricing.peak.usd.output, 1.2)
assert.deepEqual(source.resolve('deepseek-flash').pricing, { offPeak: FLASH_OFF_PEAK, peak: FLASH_PEAK })
assert.deepEqual(source.resolve('deepseek-v4-flash-vision-exp').pricing.offPeak, FLASH_OFF_PEAK)
assert.deepEqual(source.resolve('deepseek-v4-flash-vision-exp').pricing.peak, FLASH_PEAK)
assert.equal(source.resolve('unknown-model').pricing, null)
Expand Down Expand Up @@ -106,12 +107,12 @@ test('costSplitAt bills in both official currencies per tier', () => {
const usage = { uncachedInputTokens: 1_000_000, outputTokens: 1_000_000, cacheReadTokens: 0, cacheWriteTokens: 0 }
const offPeakAt = Date.UTC(1970, 0, 1, 0, 0, 0) // 北京时间 08:00
const peakAt = Date.UTC(1970, 0, 1, 2, 0, 0) // 北京时间 10:00
// 空闲:flash 未命中 1.5 + 输出 4.5(CNY);0.22 + 0.66(USD)
assert.deepEqual(costSplitAt(usage, PRICING['deepseek-v4-flash'], offPeakAt, 'cny'), { input: 1.5, cacheRead: 0, output: 4.5, total: 6 })
assert.deepEqual(costSplitAt(usage, PRICING['deepseek-v4-flash'], offPeakAt, 'usd'), { input: 0.22, cacheRead: 0, output: 0.66, total: 0.88 })
// 空闲:flash 未命中 1 + 输出 4(CNY);0.15 + 0.6(USD)
assert.deepEqual(costSplitAt(usage, PRICING['deepseek-flash'], offPeakAt, 'cny'), { input: 1, cacheRead: 0, output: 4, total: 5 })
assert.deepEqual(costSplitAt(usage, PRICING['deepseek-flash'], offPeakAt, 'usd'), { input: 0.15, cacheRead: 0, output: 0.6, total: 0.75 })
// 高峰 = 空闲 × 2
assert.deepEqual(costSplitAt(usage, PRICING['deepseek-v4-flash'], peakAt, 'cny'), { input: 3, cacheRead: 0, output: 9, total: 12 })
assert.deepEqual(costSplitAt(usage, PRICING['deepseek-v4-flash'], peakAt, 'usd'), { input: 0.44, cacheRead: 0, output: 1.32, total: 1.76 })
assert.deepEqual(costSplitAt(usage, PRICING['deepseek-flash'], peakAt, 'cny'), { input: 2, cacheRead: 0, output: 8, total: 10 })
assert.deepEqual(costSplitAt(usage, PRICING['deepseek-flash'], peakAt, 'usd'), { input: 0.3, cacheRead: 0, output: 1.2, total: 1.5 })
})

test('parsePricingFile validates entries and accepts dual-currency + legacy shapes', () => {
Expand Down Expand Up @@ -239,12 +240,14 @@ test('file source serves prefix matches and refresh after rewrite', async () =>
})

test('estimateCost / pricingFor keep v0.1 compat semantics', () => {
assert.deepEqual(PRICING['deepseek-v4-flash'], { peak: FLASH_PEAK, offPeak: FLASH_OFF_PEAK })
assert.deepEqual(PRICING['deepseek-flash'], { peak: FLASH_PEAK, offPeak: FLASH_OFF_PEAK })
assert.deepEqual(PRICING['deepseek-v4-flash'], PRICING['deepseek-flash'])
// 时刻未知 → 按高峰价保守估算:1M 未命中输入 + 1M 输出(v4-pro 高峰 CNY = 9 + 27)
assert.equal(estimateCost({ uncachedInputTokens: 1_000_000, outputTokens: 1_000_000, cacheReadTokens: 0, cacheWriteTokens: 0 }, 'deepseek-v4-pro').cny, 36)
assert.equal(pricingFor('deepseek-flash').estimated, false)
assert.equal(pricingFor('deepseek-v4-flash').estimated, false)
assert.equal(pricingFor('unknown-model').estimated, true)
assert.equal(BUILTIN_VERIFIED_AT, Date.parse('2026-08-26T00:00:00Z'))
assert.equal(BUILTIN_VERIFIED_AT, Date.parse('2026-09-10T00:00:00Z'))
})

function responseRecorder() {
Expand Down Expand Up @@ -274,9 +277,9 @@ test('/pricing route exposes builtin + fallback + models snapshot', async () =>
const body = JSON.parse(recorder.body)
assert.equal(body.ok, true)
assert.equal(body.builtinVerifiedAt, BUILTIN_VERIFIED_AT)
assert.equal(body.fallback.pricing.offPeak.cny.output, 4.5)
assert.equal(body.fallback.pricing.offPeak.usd.output, 0.66)
assert.equal(body.fallback.pricing.peak.usd.output, 1.32)
assert.equal(body.fallback.pricing.offPeak.cny.output, 4)
assert.equal(body.fallback.pricing.offPeak.usd.output, 0.6)
assert.equal(body.fallback.pricing.peak.usd.output, 1.2)
const models = body.models.map((m) => m.model)
assert.ok(models.includes('deepseek-v4-flash'))
assert.ok(models.includes('deepseek-v4-pro'))
Expand Down
38 changes: 19 additions & 19 deletions tests/rounds.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import test from 'node:test'

import { apply, foldRounds, foldTurnUsage, tierAt } from '../lib/index.js'

/** 2026-08-17 内置刊例价(双币种 /1M):flash 高峰/空闲。 */
/** 2026-09-10 内置刊例价(双币种 /1M):flash 高峰/空闲。 */
const FLASH_PEAK = {
cny: { cacheMissInput: 3.0, cacheHitInput: 0.10, output: 9.0 },
usd: { cacheMissInput: 0.44, cacheHitInput: 0.014, output: 1.32 },
cny: { cacheMissInput: 2, cacheHitInput: 0.04, output: 8 },
usd: { cacheMissInput: 0.3, cacheHitInput: 0.006, output: 1.2 },
}
const FLASH_OFF_PEAK = {
cny: { cacheMissInput: 1.5, cacheHitInput: 0.05, output: 4.5 },
usd: { cacheMissInput: 0.22, cacheHitInput: 0.007, output: 0.66 },
cny: { cacheMissInput: 1, cacheHitInput: 0.02, output: 4 },
usd: { cacheMissInput: 0.15, cacheHitInput: 0.003, output: 0.6 },
}

/** 合成事件流工具:seq 自动递增,time 由调用方给定。 */
Expand Down Expand Up @@ -68,12 +68,12 @@ test('foldRounds computes per-round timing, TTFT, TPS, model attribution and cos
assert.equal(r1.cost.estimated, false)
// 轮 1 开始于 1970-01-01T00:00:01Z = 北京时间 08:00(空闲时段)→ 空闲价
assert.equal(tierAt(r1.startedAt), 'offPeak')
assert.equal(r1.cost.cny.input, (100 / 1_000_000) * 1.5)
assert.equal(r1.cost.cny.cacheRead, (30 / 1_000_000) * 0.05)
assert.equal(r1.cost.cny.output, (20 / 1_000_000) * 4.5)
assert.equal(r1.cost.usd.input, (100 / 1_000_000) * 0.22)
assert.equal(r1.cost.usd.cacheRead, (30 / 1_000_000) * 0.007)
assert.equal(r1.cost.usd.output, (20 / 1_000_000) * 0.66)
assert.equal(r1.cost.cny.input, (100 / 1_000_000) * 1)
assert.equal(r1.cost.cny.cacheRead, (30 / 1_000_000) * 0.02)
assert.equal(r1.cost.cny.output, (20 / 1_000_000) * 4)
assert.equal(r1.cost.usd.input, (100 / 1_000_000) * 0.15)
assert.equal(r1.cost.usd.cacheRead, (30 / 1_000_000) * 0.003)
assert.equal(r1.cost.usd.output, (20 / 1_000_000) * 0.6)

const r2 = rounds.find((r) => r.turn === 2)
assert.ok(r2, 'round 2 present')
Expand Down Expand Up @@ -101,11 +101,11 @@ test('foldRounds bills peak vs off-peak rounds by their start time', () => {
const offPeak = rounds.find((r) => r.turn === 2)
assert.equal(tierAt(peak.startedAt), 'peak')
assert.equal(tierAt(offPeak.startedAt), 'offPeak')
// 1M 未命中输入:高峰 = 空闲 × 2(CNY 3.0/1.5;USD 0.44/0.22)
assert.equal(peak.cost.cny.total, 3.0)
assert.equal(peak.cost.usd.total, 0.44)
assert.equal(offPeak.cost.cny.total, 1.5)
assert.equal(offPeak.cost.usd.total, 0.22)
// 1M 未命中输入:高峰 = 空闲 × 2(CNY 2/1;USD 0.3/0.15)
assert.equal(peak.cost.cny.total, 2)
assert.equal(peak.cost.usd.total, 0.3)
assert.equal(offPeak.cost.cny.total, 1)
assert.equal(offPeak.cost.usd.total, 0.15)
})

test('foldRounds tolerates missing turn/end (open round) and missing model', () => {
Expand Down Expand Up @@ -150,9 +150,9 @@ test('foldRounds marks unknown models and uses fallback pricing', () => {
assert.equal(rounds[0].cost.unknownModel, true)
assert.equal(rounds[0].cost.estimated, true)
assert.equal(rounds[0].cost.source, 'fallback')
// 轮开始于北京时间 08:00(空闲)→ 1M token × flash 空闲未命中价(CNY 1.5 / USD 0.22)
assert.equal(rounds[0].cost.cny.total, 1.5)
assert.equal(rounds[0].cost.usd.total, 0.22)
// 轮开始于北京时间 08:00(空闲)→ 1M token × flash 空闲未命中价(CNY 1 / USD 0.15)
assert.equal(rounds[0].cost.cny.total, 1)
assert.equal(rounds[0].cost.usd.total, 0.15)
})

test('foldTurnUsage stays compatible with the v0.1 semantics', () => {
Expand Down