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
25 changes: 18 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@ The **tool-part TTFT fallback** (capturing `tool.pending` as first-response time

## Features

- **Cache hit rate**: session total + **per-turn** rate with trend (↑ / ↓ / `-`) on the main block
- **Cache hit rate**: active provider/model lineage total + **per-turn** rate with trend (↑ / ↓ / `-`) on the main block
- **Token breakdown**: cache read / write / miss / output (aligned rows with visual-cache)
- **Cost**: session cost with multi-currency config (`USD`, `CNY`, `EUR`, `GBP`, `JPY`); per-million rates and cache savings from provider config; **dynamic pricing** for time-of-day tiers (DeepSeek peak/off-peak) and context tiers (`context_over_200k`, e.g. GPT-5.6)
- **Cost**: per-message model rates with multi-currency config (`USD`, `CNY`, `EUR`, `GBP`, `JPY`); read savings, write premium, and net cache value; **dynamic pricing** for time-of-day tiers (DeepSeek peak/off-peak) and context tiers (`context_over_200k`, e.g. GPT-5.6)
- **Sub-agents**: **Agents** section rolls up **child sessions only** (scope labeled in UI); each row shows model name + session ID suffix with **vendor-tinted** label (cost in muted gray)
- **Main + Agents**: main block always shown; **Agents** section when sub-agents exist (foldable)
- **Collapsible sections**: Detail / Model (and Agents); theme-adaptive hit bar colors
- **Model lineages**: separate provider/model buckets; model switches show `switch` or `warming` instead of a cross-model trend
- **Cache TTL**: active and recent model lineages have independent timers
- **Compaction filtering**: `summary: true` and `agent: "compaction"` messages do not change interactive metrics
- **Collapsible sections**: Detail / Model / Models (and Agents); theme-adaptive hit bar colors
- **i18n**: `display.lang` — `en` / `zh` / `auto` via config (no slash commands yet)
- **Timeline** (optional): daily JSONL per assistant turn for `jq` / scripts

Expand Down Expand Up @@ -76,7 +79,7 @@ Create or edit `~/.config/opencode/tui.json` / `tui.jsonc`:
}
```

Local development: use `"./plugins/opencode-cache-hit"` instead of the npm name.
Local development: use an absolute path to the checkout, such as `"/path/to/opencode-cache-hit"`, instead of the npm name. OpenCode loads the package root and its `index.tsx` entry.

Copy `cache-hit.config.example.json` → `~/.config/opencode/cache-hit.json` (recommended) or next to the plugin root. **Restart OpenCode** after changing plugin code or config.

Expand Down Expand Up @@ -132,9 +135,17 @@ Supported display currencies in config: `USD`, `CNY`, `EUR`, `GBP`, `JPY` (see `

**Agents** totals sum **child sessions only**, not the main session (see `agentsScopeHint`). Main session metrics stay in the block above; collapse **Agents** to save space. Per-child rows use the same model slug as the main **Model** line (truncated when the sidebar is narrow); see [docs/en/design.md](docs/en/design.md) § Sub-agent row display.

### Model lineages and metric history

The main Hit, Total Hit, and cache TTL values use the active provider/model lineage. Cost, read savings, write premium, and net cache value sum eligible messages with each message's provider/model rates. The foldable **Models** section shows recent lineages separately.

The plugin excludes assistant messages with `summary: true` or `agent: "compaction"` from interactive metrics. Timeline JSONL keeps these rows when `timeline.logSummaryMessages` is enabled and marks them with `skippedForMetrics: true`.

The plugin requests main-session history directly with a limit of 10,000 messages. It reports a capped or unavailable source internally and falls back to the live TUI mirror when needed. The live mirror can contain only the 100 most recent messages. Streaming speed and TTFT use this live mirror so they can update during a call. If a message has no matching rate, the cost row keeps OpenCode's reported blended cost instead of showing a partial recomputation.

### Timeline logs (`timeline`, default off)

Per assistant turn → JSONL (tokens, cache, cost, TTFT, per-tool `toolDurations`). [docs/en/timeline.md](docs/en/timeline.md) · [中文](docs/zh-CN/timeline.md).
Per assistant turn → JSONL (tokens, cache, cost, TTFT, per-tool `toolDurations`, and `skippedForMetrics`). [docs/en/timeline.md](docs/en/timeline.md) · [中文](docs/zh-CN/timeline.md).

```json
"timeline": {
Expand Down Expand Up @@ -168,7 +179,7 @@ Retention details: [Rotation and retention](docs/en/timeline.md#rotation-and-ret

### Cache TTL (`cacheTTL`, default on)

Shows how long the prompt cache has been alive. Color changes when exceeding TTL:
Shows how long the active provider/model prompt cache has been alive. Each model lineage has an independent timer. Color changes when exceeding TTL:

- Green: elapsed < TTL
- Yellow: TTL ≤ elapsed < 2×TTL
Expand Down Expand Up @@ -254,7 +265,7 @@ Per-model rules support two forms (explicit config wins over the built-in DeepSe
- `levels`: absolute rates per level, e.g. `{"peak": {"input": 0.44, "output": 0.88, "cacheRead": 0.01}, "offpeak": {"input": 0.22, ...}}`. Cache rates may be written as flat `cacheRead`/`cacheWrite` (or `cache_read`/`cache_write`) or nested `cache: {"read": …, "write": …}` (both are accepted; flat wins if both present). Default unit is **USD per 1M** (same as `state.provider`). To write prices in another currency, set `"currency": "CNY"` and either make it match the display `cost.currency` (converted via `cost.rate`) or provide the per-rule `"rate"` (USD → that currency, e.g. `"rate": 1.08` for EUR). If the currency cannot be converted (no `rate`, currency ≠ display currency), a warning is logged to stderr and the values are treated as USD. `multipliers` are ratios and have no currency.
- `contextThreshold`: per-model override of the global threshold (wins over the runtime tier size from `state.provider`).

Rates shown in the sidebar switch automatically at schedule boundaries (no polling). The `peak`/`off-peak` badge on the rate row appears **only when the model actually prices that level** (the level exists in its explicit `levels`/`multipliers`, or the built-in DeepSeek default applies); plain static models and unpriced levels (e.g. a peak-only rule at an off-peak moment) show no badge. Session cost shown is recomputed per message from its request time + context tier when dynamic rules apply (marked `≈`); otherwise OpenCode's own `msg.cost` is used. Sub-agent rows use their **session creation time** (`session.list`) for time-of-day pricing (marked `≈` on the Agents total when any child was recomputed).
Rates shown in the sidebar switch automatically at schedule boundaries (no polling). The `peak`/`off-peak` badge on the rate row appears **only when the model actually prices that level** (the level exists in its explicit `levels`/`multipliers`, or the built-in DeepSeek default applies); plain static models and unpriced levels (e.g. a peak-only rule at an off-peak moment) show no badge. Session cost shown is recomputed per message from its provider/model, request time, and context tier when dynamic rules apply (marked `≈`); otherwise OpenCode's own `msg.cost` is used. If a message has no matching rate, the cost row keeps OpenCode's reported blended cost instead of showing a partial recomputation. Read savings, write premium, and net cache value use the same per-message rates. Sub-agent rows use their **session creation time** (`session.list`) for time-of-day pricing (marked `≈` on the Agents total when any child was recomputed).

> [!NOTE]
> **Migration (weekday-aware schedules).** Schedules now support an optional `days` field (ISO weekday, 1=Monday … 7=Sunday; omitted = every day) and a catch-all **fallback level** (a level with empty `windows`). DeepSeek's official peak is **Monday–Friday** 09:00-12:00 / 14:00-18:00 Beijing time; weekends are off-peak. The built-in default schedule is weekday-aware, and new configs written from the examples above are too. **Legacy configs without `days` keep the old behavior — weekends are still billed as peak.** To pick up the fix, add `"days": [1,2,3,4,5]` to your `peak` windows (or use the new default schedule with an `offpeak` fallback). If you resolve a DeepSeek model while your configured schedule has windowed levels but no `days`, the plugin logs a one-time hint to stderr.
Expand Down
25 changes: 18 additions & 7 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@ OpenCode **TUI 侧边栏插件**:展示 prompt cache 命中率、token 用量

## 功能一览

- **命中率**:会话累计 + **单轮**命中率与趋势(↑ / ↓ / `-`)
- **命中率**:当前 provider/model lineage 的累计命中率 + **单轮**命中率与趋势(↑ / ↓ / `-`)
- **Token 明细**:缓存读 / 写 / 未命中 / 输出(对齐 visual-cache 的行布局)
- **费用**:多币种配置(`USD` / `CNY` / `EUR` / `GBP` / `JPY`);从 provider 配置读取百万 token 单价及缓存节省;**动态计价**:支持按时段(DeepSeek 高峰/空闲)与按上下文分档(`context_over_200k`,如 GPT-5.6
- **费用**:按消息使用对应模型单价;展示读取节省、写入溢价和缓存净值;支持多币种配置(`USD` / `CNY` / `EUR` / `GBP` / `JPY`)**动态计价**(DeepSeek 高峰/空闲`context_over_200k` 上下文分档
- **子 agent**:**Agents** 段仅汇总**子 session**(UI 有范围标注);每行显示模型名 + session ID 后缀,**label 按厂商近似品牌色**,金额为灰色
- **主 + Agents**:主块始终显示;有子 agent 时出现可折叠的 **Agents** 段
- **可折叠段落**:Detail / Model(以及 Agents);主题自适应的命中率条颜色
- **模型 lineage**:按 provider/model 分桶;模型切换显示 `switch` 或 `warming`,不跨模型比较趋势
- **缓存 TTL**:当前及最近模型 lineage 使用独立计时器
- **压缩过滤**:`summary: true` 和 `agent: "compaction"` 不会改变交互指标
- **可折叠段落**:Detail / Model / Models(以及 Agents);主题自适应的命中率条颜色
- **国际化**:`display.lang` — `en` / `zh` / `auto`(配置文件,暂无斜杠命令)
- **时间轴**(可选):按天 JSONL,每 assistant 轮次一条,可用于 `jq` / 脚本分析

Expand Down Expand Up @@ -76,7 +79,7 @@ OpenCode **TUI 侧边栏插件**:展示 prompt cache 命中率、token 用量
}
```

本地开发:将 npm 名称替换为 `"./plugins/opencode-cache-hit"`。
本地开发:将 npm 名称替换为 checkout 的绝对路径,例如 `"/path/to/opencode-cache-hit"`。OpenCode 会加载包根目录及其 `index.tsx` 入口

复制 `cache-hit.config.example.json` → `~/.config/opencode/cache-hit.json`(推荐)或放在插件根目录旁。更改插件代码或配置后**重启 OpenCode**。

Expand Down Expand Up @@ -132,9 +135,17 @@ OpenCode **TUI 侧边栏插件**:展示 prompt cache 命中率、token 用量

**Agents** 段仅汇总**子 session**,不含主 session(详见 `agentsScopeHint`)。主 session 指标始终在上方块中;折叠 **Agents** 可节省空间。各子 session 行与主块 **Model** 行同源模型名(侧栏窄时会截断);详见 [docs/zh-CN/design.md](docs/zh-CN/design.md)「子 session 行展示」。

### 模型 lineage 与指标历史

主 Hit、Total Hit 和缓存 TTL 使用当前 provider/model lineage。费用、读取节省、写入溢价和缓存净值按每条消息的 provider/model 单价汇总。可折叠的 **Models** 段展示最近的 lineage。

插件会从交互指标中排除 `summary: true` 或 `agent: "compaction"` 的 assistant 消息。启用 `timeline.logSummaryMessages` 时,JSONL 仍保留这些行,并标记 `skippedForMetrics: true`。

插件直接请求主 session 历史,最多 10,000 条消息。数据源达到上限或不可用时会回退到 TUI 实时镜像;该镜像最多包含最近 100 条消息。流式速度和 TTFT 使用实时镜像,因此可在调用过程中更新。如果某条消息没有匹配单价,费用行保留 OpenCode 的混合会话费用,不显示不完整的重算结果。

### 时间轴日志(`timeline`,默认关闭)

每 assistant 轮次 → JSONL(tokens、cache、cost、TTFT各工具 `toolDurations`)。详见 [docs/zh-CN/timeline.md](docs/zh-CN/timeline.md)。
每 assistant 轮次 → JSONL(tokens、cache、cost、TTFT各工具 `toolDurations`、`skippedForMetrics`)。详见 [docs/zh-CN/timeline.md](docs/zh-CN/timeline.md)。

```json
"timeline": {
Expand Down Expand Up @@ -168,7 +179,7 @@ jq -r 'select(.rootSessionId=="YOUR_ROOT") | [.created,.scope,.hitPercent,.cost]

### 缓存 TTL(`cacheTTL`,默认开启)

显示 prompt cache 已存活时间。超过 TTL 时颜色变化:
显示当前 provider/model prompt cache 已存活时间。每个模型 lineage 使用独立计时器。超过 TTL 时颜色变化:

- 绿色:已存活 < TTL
- 黄色:TTL ≤ 已存活 < 2×TTL
Expand Down Expand Up @@ -254,7 +265,7 @@ jq -r 'select(.rootSessionId=="YOUR_ROOT") | [.created,.scope,.hitPercent,.cost]
- `levels`:各时段档的绝对单价,如 `{"peak": {"input": 0.44, "output": 0.88, "cacheRead": 0.01}, "offpeak": {"input": 0.22, ...}}`。缓存单价可写为扁平 `cacheRead`/`cacheWrite`(或 `cache_read`/`cache_write`),也可写为嵌套 `cache: {"read": …, "write": …}`(两种都接受;同时存在时扁平优先)。默认单位为 **USD/百万 token**(与 `state.provider` 一致)。想用其他币种写价时设 `"currency": "CNY"`:要么与展示币种 `cost.currency` 一致(按 `cost.rate` 换算),要么提供模型级 `"rate"`(USD → 该币种,如 EUR 填 1.08)。无法换算时(无 `rate` 且币种 ≠ 展示币种)向 stderr 告警并按 USD 处理。`multipliers` 是倍率,无币种概念。
- `contextThreshold`:覆盖全局阈值的模型级配置(优先于 `state.provider` 的运行时档位阈值)。

侧边栏单价会在时段边界自动切换(无需轮询)。单价行的 `peak`/`offpeak` 徽标**仅在该模型对当前时段档有定价时**显示(当前档存在于其显式 `levels`/`multipliers` 中,或内置 DeepSeek 默认生效);纯静态价模型、以及未定价的档(如只配 peak 的模型在 offpeak 时刻)不标注。会话成本在动态规则生效时按每条消息的请求时刻 + 上下文档位重算(标注 `≈`);否则使用 OpenCode 自身的 `msg.cost`。子 agent 行按其**会话创建时刻**(`session.list`)做时段计价(任一子会话被重算时 Agents 合计标注 `≈`)。
侧边栏单价会在时段边界自动切换(无需轮询)。单价行的 `peak`/`offpeak` 徽标**仅在该模型对当前时段档有定价时**显示(当前档存在于其显式 `levels`/`multipliers` 中,或内置 DeepSeek 默认生效);纯静态价模型、以及未定价的档(如只配 peak 的模型在 offpeak 时刻)不标注。会话成本在动态规则生效时按每条消息的 provider/model、请求时刻和上下文档位重算(标注 `≈`);否则使用 OpenCode 自身的 `msg.cost`。如果有消息缺少单价,则费用行保留 OpenCode 自身的混合 `msg.cost`,不显示不完整的重算结果。读取节省、写入溢价和缓存净值使用相同的逐消息单价。子 agent 行按其**会话创建时刻**(`session.list`)做时段计价(任一子会话被重算时 Agents 合计标注 `≈`)。

> [!NOTE]
> **迁移提示(星期感知 schedule)**。schedule 现支持可选 `days` 字段(ISO 星期,1=周一 … 7=周日;省略 = 每天)与「**回退档**」(`windows` 为空的 level)。DeepSeek 官方高峰为**周一至周五**北京时间 09:00-12:00 / 14:00-18:00,周末为空闲。内置默认 schedule 已是星期感知写法,上方示例亦然;**未写 `days` 的旧配置保持原语义——周末仍按高峰计费**。要修复,请为 `peak` 窗口补 `"days": [1,2,3,4,5]`(或改用含 `offpeak` 回退档的新默认 schedule)。若你解析 DeepSeek 模型时配置的 schedule 存在窗口级档但均未写 `days`,插件会向 stderr 输出一次性提示。
Expand Down
Loading
Loading