Skip to content
dsh.fish
Bundle

@dsh-external/ui-usage-stats

Settings page for workspace/session/period/model token usage and DeepSeek cost

Source
xiyue718
License
BSD-3-Clause
Updated
Updated 13 hours ago

Readme

[中文](./README.md) | [English](./README_EN.md)

# @dsh-external/ui-usage-stats

## 介绍

`ui-usage-stats` 是 DSH Web 客户端的“用量统计”插件。它在“设置”页面中新增“用量统计”页面,按 **工作区 → 会话 → 时段 → 模型** 展示 token 用量与费用,并提供每一层的总计、会话类型筛选、分叉去重、持久化缓存和增量计算能力。

## 安装

### dsh 命令安装(项目官方方式)

如果你已安装 `dsh` CLI,可以按项目官方教程使用 `dsh plugin` 命令安装:

```bash
# 从本地插件目录安装
dsh plugin --profile web add C:/Users/<user>/.dsh/plugins/ui-usage-stats

# 或从 GitHub 仓库安装
dsh plugin --profile web add github:xiyue718/dsh-ui-usage-stats
```

安装后启动:

```bash
dsh --profile web
```

查看组合配置:

```bash
dsh --profile web --dump-config
```

详细命令说明见项目文档:`docs/user/develop/basic/publish.md`。

构建产物:host 为**自包含**的 `lib/index.js`,client 为 `lib/client.js`,打包文件为 `dsh-external-ui-usage-stats-0.1.0.tgz`。

安装前先构建一次:`DSH_CHECKOUT=<dsh 源码 checkout> bash scripts/build.sh`。官方渠道把插件装进 profile 后,宿主只从插件自身目录解析运行期依赖,所以 host 半由 tsdown 打包成自包含模块(内联 schemastery、zod 与 `@deepseek-ai/dsh-*` helper);未打包的 `lib/index.js` 会让该行以 `failed to import` 停用。

## 使用

1. 启动 DSH Web 客户端。
2. 打开“设置”面板。
3. 点击左侧或导航中的“用量统计”。
4. 页面顶部会显示 DeepSeek 账户余额。
5. 等待统计加载完成,按工作区查看各会话、各时段、各模型的用量和费用。
6. 使用页面顶部的“总计”查看所有工作区汇总。
7. 在刷新行最右侧勾选或取消会话类型,页面只显示当前选中的类型。
8. 点击“刷新”会重新检查所有会话的 revision,仅重算更新过的会话,同时刷新余额。

## 功能

- 设置页新增入口:设置 → 用量统计。
- 页面顶部余额显示:通过 DeepSeek `GET /user/balance` 查询账户余额,展示币种、总余额、赠送余额和充值余额。
- 展示字段:
  - 工作区路径
  - 会话 ID
  - 会话标题
  - 高峰/空闲时段
  - 模型
  - 未命中缓存 tokens(缓存未命中输入)
  - 命中缓存 tokens(缓存命中输入)
  - 输出 tokens
  - 费用(人民币)
- 层级总计:
  - 页面顶部“总计”:汇总所有工作区的用量与费用
  - 工作区总计
  - 会话总计
  - 时段总计
  - 模型行本身即为最细粒度
- 会话类型筛选:
  - 刷新行最右侧提供四个复选框:普通用户会话、子代理会话、分叉会话、其他会话
  - 默认只勾选“普通用户会话”
  - 页面仅展示当前选中类型对应的数据
  - 筛选状态通过项目 storage domain 持久化保存,刷新/重开页面后保持
- 聊天窗口统计行:
  - 自动将“缓存命中 xx%”替换为按真实 token 比例计算的“缓存命中 xx.xx%”,保留两位小数
  - 通过插件侧组件读取 tokenUsage 投影计算,不是简单补 `.00`,也不改动项目核心文件
- 排序:工作区和会话按最近修改时间(会话最后活动时间)倒序排列,不再按费用排序。
- 收缩:工作区、会话、时段标题均可点击展开/折叠,默认折叠,减少页面渲染量。
- 性能:host 侧并行读取会话日志,并直接从原始 JSONL 聚合 token,避免完整日志重放。
- 持久化缓存:每个会话的统计结果通过项目 storage domain 持久化保存;只有持久化 revision 变化的会话会重新读取并计算,未变化的会话直接复用缓存结果。
- 分叉去重:分叉会话继承自原会话的 `seedLength` 前缀聊天记录不会重复计入,只统计分叉后自己产生的用量。
- 点击“刷新”会重新检查所有会话的 revision,仅重算更新过的会话。

### 价格计算

按照 DeepSeek 官方峰谷定价,高峰时段为北京时间 9:00-12:00、14:00-18:00,其余为空闲时段。周六和周日全天执行谷价(空闲时段),不再设置峰价。

| 模型 | 时段 | 百万 tokens 输入(缓存命中) | 百万 tokens 输入(缓存未命中) | 百万 tokens 输出 |
|---|---|---|---|---|
| deepseek-v4-flash | 空闲 | 0.05 元 | 1.5 元 | 4.5 元 |
| deepseek-v4-flash | 高峰 | 0.10 元 | 3.0 元 | 9.0 元 |
| deepseek-v4-flash-vision-exp | 空闲 | 0.05 元 | 1.5 元 | 4.5 元 |
| deepseek-v4-flash-vision-exp | 高峰 | 0.10 元 | 3.0 元 | 9.0 元 |
| deepseek-v4-pro | 空闲 | 0.15 元 | 4.5 元 | 13.5 元 |
| deepseek-v4-pro | 高峰 | 0.30 元 | 9.0 元 | 27.0 元 |

费用公式:

```text
费用 = 未命中缓存 tokens / 1_000_000 × 未命中单价
     + 命中缓存 tokens / 1_000_000 × 命中单价
     + 输出 tokens / 1_000_000 × 输出单价
```

未在上表中的模型显示费用为 `0`。

### Host API

```http
GET /@dsh-external/ui-usage-stats/api/stats
```

```http
GET  /@dsh-external/ui-usage-stats/api/filters
POST /@dsh-external/ui-usage-stats/api/filters
```

```http
GET /@dsh-external/ui-usage-stats/api/balance
```

## 原理

插件由 host 和 client 两部分组成。

Host 侧通过 `sessionPersistence.readRaw` 或 `sessionQuery.readSession` 读取会话日志,只关注 `assistant/message` 事件中的模型 `usage` 数据,按模型和高峰/空闲时段聚合未命中缓存、命中缓存、输出 tokens,再根据 DeepSeek 官方价格计算费用。高峰时段为北京时间 9:00-12:00、14:00-18:00;周六和周日全天按空闲时段计价。分叉会话通过 `header.seedLength` 跳过继承自原会话的聊天记录,避免重复统计。

余额查询通过 DeepSeek `GET https://api.deepseek.com/user/balance` 实现,API Key 从项目凭证服务读取 `DEEPSEEK_API_KEY`,响应中的 `balance_infos` 会映射为页面顶部的余额展示。

统计结果会以会话为单位缓存到 storage domain,缓存键为会话的持久化 revision;请求 `/api/stats` 时,只有 revision 变化的会话会重新读取日志并计算,未变化的会话直接复用缓存。多个会话使用并发数为 4 的 mapLimit 并行处理。最后按工作区路径分组,并按最近活动时间排序返回树形结果。

Client 侧渲染“用量统计”页面,调用 `/api/stats` 获取数据,按工作区、会话、时段、模型逐层展示,并提供折叠、总计、会话类型筛选和刷新操作;页面顶部调用 `/api/balance` 展示余额。聊天窗口中的百分比修正由插件侧组件读取 tokenUsage 投影计算后显示为两位小数。

Install

dsh plugin --profile web add github:xiyue718/dsh-ui-usage-stats

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source