跳到主要内容

dsh-ui-cost-meter

已验证

dsh-ui-cost-meter · v1.1.1 · MIT · Web 界面

DeepSeek Harness Web GUI plugin: real-time spend for the current session, priced from per-model token rates and shown as a pill under the composer

安装

dsh plugin add dsh-ui-cost-meter

用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。

源码

标签

说明文档

dsh-ui-cost-meter

English | 日本語

Real-time spend for the DeepSeek Harness Web GUI: one pill under the composer, beside the shipped turn/token pills, that says how much the session has cost so far.

⏱ 1 turns 71 steps · 285 tok/s    🗄 7.3M tok · Cache hit 99%    $ 0.42

The amount rides the session projection seam, so it is whole-log (paging and compaction cannot change it), it survives reloads, and it is priced from the durable provider usage the harness already logs. The pill is shaped like the shipped stats pills: clicking it opens a breakdown panel that repeats their dialog's title, rule, and label/value rows, naming each cache bucket, the unpriced tokens, and the route that priced them.

Install

dsh plugin --profile web add dsh-ui-cost-meter

Under a source checkout, add link:<path> works the same way. The plugin declares its own bundle patch, so the row appears in the profile without editing cordis.patch.yml.

Configure

Every field lives on the plugin row, and every price is per one million tokens in whatever currency symbol names. The example below prices the Command Code DeepSeek Flash routes — deepseek/deepseek-v4-flash and deepseek/deepseek-v4.1-flash share the V4.1 Flash price, and deepseek/deepseek-v4-flash-fast and deepseek/deepseek-v4.1-flash-fast each carry what their own model page publishes — and DeepSeek V4 Pro at DeepSeek's off-peak list price. Replace them with the rates you actually pay.

- id: cost-meter
  name: dsh-ui-cost-meter
  config:
    symbol: '$'
    rates:
      opencode-go/deepseek-flash:
        input: 0.15
        output: 0.6
        cacheRead: 0.003
        cacheWrite: 0
      opencode-go/deepseek-v4-pro:
        input: 0.66
        output: 1.98
        cacheRead: 0.022
        cacheWrite: 0
      deepseek-official/deepseek-flash:
        input: 0.15
        output: 0.6
        cacheRead: 0.003
        cacheWrite: 0
      deepseek-official/deepseek-v4-pro:
        input: 0.66
        output: 1.98
        cacheRead: 0.022
        cacheWrite: 0
      command-code/deepseek/deepseek-v4.1-flash:
        input: 0.15
        output: 0.6
        cacheRead: 0.003
        cacheWrite: 0
      command-code/deepseek/deepseek-v4-flash:
        input: 0.15
        output: 0.6
        cacheRead: 0.003
        cacheWrite: 0
      command-code/deepseek/deepseek-v4.1-flash-fast:
        input: 0.16
        output: 0.58
        cacheRead: 0.02
        cacheWrite: 0
      command-code/deepseek/deepseek-v4-flash-fast:
        input: 0.28
        output: 0.56
        cacheRead: 0.07
        cacheWrite: 0

One rate per route can only carry one number, so that is what the example shows: the off-peak price. Peak is exactly double for DeepSeek's list price and for Command Code's V4.1 Flash and V4 Flash, and covers 01:00-04:00 and 06:00-10:00 UTC, Monday through Friday; Command Code bills V4 Flash Fast flat, and doubles everything on V4.1 Flash Fast except the cache read. No provider here bills a cache write, so cacheWrite stays 0. Command Code also bills the deepseek-v4-flash-vision-exp id at the Flash price.

Command Code's GOAT plan is not a per-token invoice for these models either: the $10/month plan grants $60 of DeepSeek V4.1 Flash usage credits a month, and V4.1 Flash Fast draws on the same allowance, so the pill these rates produce reads as credits burned rather than an amount owed. OpenCode Go's $10/month plan likewise meters its rates against the plan's limits.

Sources: DeepSeek Models & Pricing, OpenCode Go, Command Code pricing, DeepSeek V4.1 Flash Fast, DeepSeek V4 Flash Fast, the V4.1 Flash Fast announcement.

Field Meaning
symbol Prefixed to every amount. Default $.
rates Unit prices keyed by "<provider>/<model>", then "<model>". The route key wins.
fallback Unit prices for every route no key matches. Omit it to price nothing by default.

A route with no matching rate is not guessed at: its tokens are counted as unpriced, contribute nothing to the total, and make the pill mark its amount with a trailing +. The panel then names how many tokens were left unpriced, so a missing rate is visible instead of silently wrong.

Where the numbers come from

The host half registers the costMeter session projection. It folds the durable log: a request/header sets the route, and each settled assistant/message (or assistant/attempt, which preserves a billed attempt that left no surface message) contributes the provider usage embedded in its stream. A re-reported sample for the same turn and step replaces the earlier one; llm/retry-started closes that slot so a retried attempt adds instead. Input, output, cache-read, and cache-write tokens are then priced by the route's rates.

The client half registers one entry on the conversation.composer.dock slot and renders it into the shipped stats row ([data-composer-stats]) with a React portal, so the cost pill shares that row's font, spacing, and vertical rhythm instead of starting a row of its own. Without a stats row to join (a session that has produced no steps or tokens yet) it falls back to drawing its own centered row.

The pill and its panel carry the shell's own declarations — the same --dsw-* tokens, sizes, radius, and two-column row grid as the shipped pills and their dialog — because a third-party bundle can only reach the platform modules the shell seeds, not its CSS modules.

Caveats

  • Not a billing record. The figures are the harness's own token accounting times the rates you configured. The provider's invoice is the authority; a wrong rate in rates is a wrong pill.
  • One rate per route. A provider that changes its price mid-session, or a route whose price depends on the request size, needs a rate that reflects what you actually pay.
  • Currency is yours to name. Nothing here converts between currencies.

Development

bun install
bun run build     # lib/index.js (host half) and lib/client.js (browser half)
bun test          # pricing fold, formatting, and the built bundle
bun run check     # type check

lib/ is generated and ignored by git. build.ts bundles the host half as ESM (with zod and @deepseek-ai/schemastery left external) and the browser half as CJS wrapped in the window.__ModuleLoader__.load({ id, factory }) loader format the client module system serves.

License

MIT