跳到主要内容

dshcost

已验证

@stuchenxj/dshcost · v1.0.5 · MIT · Web 界面

Session cost readout for the DeepSeek Harness Web GUI: prices every model call at DeepSeek's official peak/off-peak rates and shows the running session cost under the composer.

安装

dsh plugin add @stuchenxj/dshcost

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

源码

标签

作者

说明文档

dshcost

A publishable Cordis plugin for the DeepSeek Harness Web GUI that records and displays the current session's model consumption and estimated cost in real time, right under the composer.

It prices every model call at DeepSeek's official peak/off-peak rates and shows a compact readout like:

💰 本会话费用 ¥0.0123 · 模型 deepseek-v4-flash · 输入 12.3K · 缓存 20.1K · 输出 1.2K · 6 次调用

With the UI set to English the readout reads:

💰 Session cost:¥0.0123 model:deepseek-v4-flash input:12.3K cache:20.1K output:1.2K 6 calls

Hovering the readout shows the per-model breakdown and the full token usage.

Localization

The readout follows the DSH UI language through the client locale service (@deepseek-ai/dsh-client-locale): zh → 中文, en → English, and only those two are shipped — any other locale falls back to Chinese. Switching DSH's language (Settings → General → Language) updates the readout instantly without a reload. If the locale service is absent from the composition, the client falls back to the Chinese dictionary.

How it works

  • Host half (lib/index.js) registers a session projection unit sessionCost with ctx.sessionProjections. It replays the session log's provider-usage records (assistant/chunk usage samples plus the final assistant/message usage per step), tracks the model from request/header / message source, and estimates cost from:
    • token buckets (input + cache-write billed as input, cache-read, output),
    • the model's price table,
    • the call time in Beijing to pick peak vs. off-peak pricing.
  • Client half (lib/client.js) is a web module that registers a read-only cell in the conversation.composer.dock slot and reads the projection reactively via the slot's standard useProjection('sessionCost') prop — no polling, no Client→Host RPC.

Because the data lives in a durable session projection, the readout survives process restarts: after a restart the projection refolds from the session log (or a checkpoint), so history is not lost.

Pricing

Prices in CNY per million tokens (DeepSeek official peak/off-peak pricing, effective 2026-08-17):

Model input peak / off cache read peak / off output peak / off
deepseek-v4-flash 3.0 / 1.5 0.1 / 0.05 9.0 / 4.5
deepseek-v4-pro 9.0 / 4.5 0.3 / 0.15 27.0 / 13.5

Peak hours = Beijing 09:00–12:00 and 14:00–18:00; the rest is off-peak (half price). Cache writes are billed as input. Model ids with a version suffix (deepseek-v4-flash-0731) resolve to the base model price. Calls from models not in the table are counted as unknownCalls and shown as "价格未知".

These are estimates for display, not billing records.

Install & mount

The package is published on npm as @stuchenxj/dshcost and is also a profile bundle (its manifest declares dsh.bundle.patch).

[!WARNING] Do NOT run npm install directly inside $DSH_HOME/profiles/node_modules — that directory has no package.json/lockfile, so npm treats the whole runtime dependency tree as extraneous and prunes it (this has wiped 500+ packages in practice). Install into a temp dir and copy the package in:

tmp=$(mktemp -d) && cd "$tmp" && npm i @stuchenxj/dshcost
# then copy "$tmp/node_modules/@stuchenxj/dshcost" into $DSH_HOME/profiles/node_modules/

Option A — as a bundle (add to a profile's dsh.profile.bundles):

// $DSH_HOME/profiles/web/package.json
{
  "dsh": {
    "profile": {
      "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "@stuchenxj/dshcost"]
    }
  }
}

Option B — as a plugin row in the profile patch:

# $DSH_HOME/profiles/web/cordis.patch.yml
- insert:
    - id: dshcost
      name: '@stuchenxj/dshcost'

[!IMPORTANT] The client bundle registers via __ModuleLoader__.load({ id: "dshcost" }). That string must equal the composition row id (the boot-graph entry name) exactly — no @scope. The row name is only the npm package name used to resolve the package; the loader does NOT key off it. A mismatch (e.g. an old client.js registering @stuchenxj/dshcost) fails the whole plugin tree with client-modules: bundle ... loaded without registering "dshcost". Keep the row as id: dshcost + name: '@stuchenxj/dshcost' and never rename the id.

Install the package where the profile can resolve it (its node_modules, or the flat $DSH_HOME/profiles/node_modules fallback), then restart (bundle) or let patch HMR apply it (row) and reload the page. Requires ctx.sessionProjections (present in the standard Web profile via @deepseek-ai/dsh-session-projection).

Development

  • lib/index.js — Host half: the sessionCost projection unit (pure fold, plain ESM, depends only on zod).
  • lib/client.js — Client half: the conversation.composer.dock readout (plain JS, window.__ModuleLoader__ format, requires only react).

Edit lib/index.js / lib/client.js, no build step. Bump stateVersion in the projection unit if you change the fold state shape (stale checkpoints then refold).

License

MIT