@jack-yang/opencode-tps-meter 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jack Yang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -21,7 +21,7 @@ TPS 42.5 | AVG 38.2 | TTFT 0.8s ⠒⠑⠂
21
21
  - **TPS** — current tokens per second (live, color-coded)
22
22
  - **AVG** — session-wide cumulative average from completed messages (color-coded)
23
23
  - **TTFT** — time to first token, shown immediately on first token, persists until next generation (color-coded)
24
- - **Thinking stream** — while reasoning streams, a FIFO particle stream (newest dot on the left, fading right, color shifts over time) appears to the right of TTFT. Only rendered while thinking — no space is reserved when idle.
24
+ - **Thinking stream** — while reasoning streams, a FIFO particle stream (newest dot on the left, fading right, color shifts over time) appears to the right of TTFT. Each cell has its own color (a gradient fading to muted). Only rendered while thinking — no space is reserved when idle.
25
25
 
26
26
  ## Color Tiers
27
27
 
@@ -159,28 +159,34 @@ All options are optional — defaults are sane.
159
159
  | Option | Default | Description |
160
160
  |---|---|---|
161
161
  | `spinner.theme` | `"tech"` | Color preset: `"tech"` (cyan→blue→purple) or `"red"` (KITT-style red). `"none"` disables the thinking stream. |
162
- | `spinner.colors` | _from theme_ | Custom gradient (hex strings); overrides `theme`. |
163
- | `spinner.intervalMs` | `100` | Particle stream frame interval (ms). |
164
- | `spinner.cells` | `6` | Number of cells (width) of the thinking stream. |
162
+ | `spinner.colors` | _from theme_ | Custom gradient (hex strings `#rgb` / `#rrggbb` / `#rrggbbaa`); overrides `theme`. Invalid entries are ignored. |
163
+ | `spinner.intervalMs` | `100` | Particle stream frame interval (ms), clamped to 40–1000. |
164
+ | `spinner.cells` | `6` | Number of cells (width) of the thinking stream, clamped to 2–16. |
165
165
  | `tiers.tps` | `{20,50,100}` | TPS/AVG thresholds (slow/normal/fast, in TPS); above `fast` is "faster". |
166
166
  | `tiers.ttft` | `{10000,20000}` | TTFT thresholds in ms (fast/ok); above `ok` is "slow". |
167
+
168
+ Tier values must be positive and ascending; otherwise the defaults are used.
167
169
  | `slot` | `"prompt.footer.status"` | opencode 2.x only: which TUI slot the meter renders into. |
168
170
 
169
171
  ## How It Works
170
172
 
171
173
  ### Live TPS
172
174
 
173
- Tracks text/reasoning delta events in a rolling 5-second window. Uses wall-clock duration (`now - oldest sample`) instead of inter-arrival time sums to avoid inflation from network buffering.
175
+ Tracks text, reasoning and tool-argument delta events in a rolling 5-second window (so a model writing a large file through a tool call still shows a speed). Uses wall-clock duration (`now - oldest sample`) instead of inter-arrival time sums to avoid inflation from network buffering.
174
176
 
175
- Token estimation uses `ceil(byteLength / 4)` with a **calibration factor**: when a message completes, the estimated token count is compared against actual `tokens.output + tokens.reasoning` from the message metadata, and a running median ratio corrects future estimates.
177
+ Token estimation uses `ceil(byteLength / 4)` with a **calibration factor**: when a message completes, the estimated token count is compared against the actual `tokens.output` (+ `tokens.reasoning` when reasoning was streamed) from the message metadata, and a running median ratio corrects future estimates.
176
178
 
177
179
  ### AVG
178
180
 
179
- Session-wide cumulative average from all completed messages: `sum(actual tokens) / sum(generation durations)`. Updates when a message completes. Generation duration = `last delta - first delta` per message (excludes tool execution time and TTFT).
181
+ Session-wide cumulative average from all completed messages: `sum(actual tokens) / sum(generation durations)`. Updates when a message completes. Generation duration = `last delta - first delta` per step, where deltas include text, reasoning and tool-call arguments (excludes tool execution time and TTFT).
182
+
183
+ To avoid inflated numbers:
184
+ - reasoning tokens are only counted when the reasoning was actually streamed (some models report reasoning tokens without streaming them);
185
+ - a step that made tool calls but whose arguments were not streamed (older opencode versions) is excluded from AVG and calibration instead of being guessed.
180
186
 
181
187
  ### TTFT
182
188
 
183
- Time from message creation (`info.time.created`) to the first text/reasoning delta. Displayed immediately when the first token arrives, and persists until the next generation starts.
189
+ Time from step start (`info.time.created`) to the first output delta (text, reasoning or tool arguments). Displayed immediately when the first token arrives, and persists until the next generation starts.
184
190
 
185
191
  ## Development
186
192
 
@@ -188,10 +194,11 @@ Time from message creation (`info.time.created`) to the first text/reasoning del
188
194
  git clone https://github.com/yangwudong/opencode-tps-meter.git
189
195
  cd opencode-tps-meter
190
196
  npm install
191
- npm test
197
+ npm test # unit tests
198
+ npm run typecheck # tsc, 0 errors expected
192
199
  ```
193
200
 
194
- Pure measurement functions (`measure.ts`) have 29 unit tests. The TUI plugin (`tui.tsx`) is a standalone single file (all functions inline) for direct deployment.
201
+ Pure measurement logic lives in `measure.ts` (67 unit tests). The TUI plugin (`tui.tsx`) is a standalone single file for direct deployment; it embeds an identical copy of the pure region — edit `measure.ts`, then run `npm run sync-region` (`test/sync.test.ts` fails if they drift).
195
202
 
196
203
  ## Requirements
197
204
 
@@ -199,4 +206,4 @@ Pure measurement functions (`measure.ts`) have 29 unit tests. The TUI plugin (`t
199
206
 
200
207
  ## License
201
208
 
202
- MIT
209
+ [MIT](./LICENSE)
package/README.zh-CN.md CHANGED
@@ -21,7 +21,7 @@ TPS 42.5 | AVG 38.2 | TTFT 0.8s ⠒⠑⠂
21
21
  - **TPS** — 当前每秒 token 数(实时跳动,带颜色)
22
22
  - **AVG** — 整个会话的累计平均速度(消息完成后更新,带颜色)
23
23
  - **TTFT** — 首个 token 的等待时间,第一个 token 到达即显示,保留到下次生成(带颜色)
24
- - **思考粒子条** — 推理流式输出时,TTFT 右侧出现一个 FIFO 粒子流(左侧最新、向右渐暗,颜色随时间渐变)。仅在思考时渲染,空闲时不占位。
24
+ - **思考粒子条** — 推理流式输出时,TTFT 右侧出现一个 FIFO 粒子流(左侧最新、向右渐暗,颜色随时间渐变)。每一格单独着色(渐变淡出到 muted)。仅在思考时渲染,空闲时不占位。
25
25
 
26
26
  ## 颜色等级
27
27
 
@@ -159,11 +159,13 @@ curl -o ~/.config/opencode/tps-meter.tsx https://raw.githubusercontent.com/yangw
159
159
  | 配置项 | 默认值 | 说明 |
160
160
  |---|---|---|
161
161
  | `spinner.theme` | `"tech"` | 配色预设:`"tech"`(青→蓝→紫)或 `"red"`(KITT 风格红)。`"none"` 关闭思考粒子流。 |
162
- | `spinner.colors` | _随 theme_ | 自定义渐变(十六进制字符串数组);覆盖 theme。 |
163
- | `spinner.intervalMs` | `100` | 粒子流帧间隔(毫秒)。 |
164
- | `spinner.cells` | `6` | 粒子流格数(宽度)。 |
162
+ | `spinner.colors` | _随 theme_ | 自定义渐变(十六进制字符串 `#rgb` / `#rrggbb` / `#rrggbbaa`);覆盖 theme。无效项会被忽略。 |
163
+ | `spinner.intervalMs` | `100` | 粒子流帧间隔(毫秒),限制在 40–1000。 |
164
+ | `spinner.cells` | `6` | 粒子流格数(宽度),限制在 2–16。 |
165
165
  | `tiers.tps` | `{20,50,100}` | TPS/AVG 阈值(慢/正常/快,单位 TPS);超过 fast 为"很快"。 |
166
166
  | `tiers.ttft` | `{10000,20000}` | TTFT 阈值(毫秒,快/正常);超过 ok 为"慢"。 |
167
+
168
+ 阈值必须为正数且递增,否则使用默认值。
167
169
  | `slot` | `"prompt.footer.status"` | 仅 opencode 2.x:仪表渲染到的 TUI slot。 |
168
170
 
169
171
  > 一条命令安装(`opencode plugin ...`)使用默认值;如需自定义,改用上面的元组/对象形式。
@@ -172,17 +174,21 @@ curl -o ~/.config/opencode/tps-meter.tsx https://raw.githubusercontent.com/yangw
172
174
 
173
175
  ### 实时 TPS
174
176
 
175
- 在 5 秒滚动窗口内追踪文本/推理的 delta 事件。使用壁钟时长(`当前时间 - 最早样本时间`)而非间隔时间累加,避免网络批量传输导致的 TPS 虚高。
177
+ 在 5 秒滚动窗口内追踪文本、推理和工具参数的 delta 事件(模型通过工具调用写大文件时也有速度显示)。使用壁钟时长(`当前时间 - 最早样本时间`)而非间隔时间累加,避免网络批量传输导致的 TPS 虚高。
176
178
 
177
- Token 估算使用 `ceil(字节数 / 4)`,并带有**校准因子**:消息完成时,将估算的 token 数与实际的 `tokens.output + tokens.reasoning` 对比,用中位数比率自动修正后续估算。
179
+ Token 估算使用 `ceil(字节数 / 4)`,并带有**校准因子**:消息完成时,将估算的 token 数与实际的 `tokens.output`(推理确实流式输出时再加上 `tokens.reasoning`)对比,用中位数比率自动修正后续估算。
178
180
 
179
181
  ### AVG
180
182
 
181
- 所有已完成消息的会话级累计平均值:`总实际 token 数 / 总生成时长`。消息完成时更新。生成时长 = 每条消息的 `最后 delta - 首个 delta`(不含工具执行时间和 TTFT)。
183
+ 所有已完成消息的会话级累计平均值:`总实际 token 数 / 总生成时长`。消息完成时更新。生成时长 = 每个 step 的 `最后 delta - 首个 delta`,delta 包括文本、推理和工具调用参数(不含工具执行时间和 TTFT)。
184
+
185
+ 为避免数值虚高:
186
+ - 只有推理内容确实流式输出时才计入 reasoning token(有些模型只上报 reasoning token 而不流式输出推理);
187
+ - 发起了工具调用、但工具参数没有流式输出的 step(旧版 opencode)直接不计入 AVG 和校准,而不是去猜。
182
188
 
183
189
  ### TTFT
184
190
 
185
- 从消息创建(`info.time.created`)到第一个文本/推理 delta 的时间。第一个 token 到达时立即显示,保留到下次生成开始。
191
+ 从 step 开始(`info.time.created`)到第一个输出 delta(文本、推理或工具参数)的时间。第一个 token 到达时立即显示,保留到下次生成开始。
186
192
 
187
193
  ## 开发
188
194
 
@@ -190,10 +196,11 @@ Token 估算使用 `ceil(字节数 / 4)`,并带有**校准因子**:消息完
190
196
  git clone https://github.com/yangwudong/opencode-tps-meter.git
191
197
  cd opencode-tps-meter
192
198
  npm install
193
- npm test
199
+ npm test # 单元测试
200
+ npm run typecheck # tsc,应为 0 错误
194
201
  ```
195
202
 
196
- 纯计算函数(`measure.ts`)有 29 个单元测试。TUI 插件(`tui.tsx`)是独立的单文件(所有函数内联),可直接部署。
203
+ 纯计算逻辑在 `measure.ts`(67 个单元测试)。TUI 插件(`tui.tsx`)是独立的单文件,可直接部署;它内嵌了一份完全相同的纯计算区——修改 `measure.ts` 后运行 `npm run sync-region`(两份不一致时 `test/sync.test.ts` 会失败)。
197
204
 
198
205
  ## 环境要求
199
206
 
@@ -201,4 +208,4 @@ npm test
201
208
 
202
209
  ## 许可证
203
210
 
204
- MIT
211
+ [MIT](./LICENSE)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@jack-yang/opencode-tps-meter",
4
- "version": "1.2.0",
4
+ "version": "1.3.0",
5
5
  "description": "Live TPS/AVG/TTFT meter plugin for OpenCode TUI with color-coded speed tiers",
6
6
  "type": "module",
7
7
  "exports": {
@@ -27,7 +27,9 @@
27
27
  },
28
28
  "scripts": {
29
29
  "test": "vitest run",
30
- "test:watch": "vitest"
30
+ "test:watch": "vitest",
31
+ "typecheck": "tsc --noEmit --jsx react-jsx --jsxImportSource @opentui/solid --module esnext --target esnext --moduleResolution bundler --skipLibCheck tui.tsx measure.ts",
32
+ "sync-region": "node scripts/sync-region.mjs"
31
33
  },
32
34
  "files": [
33
35
  "tui.tsx",