dsh-recent-usage-metrics 0.2.2 → 0.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/CHANGELOG.md +25 -0
- package/README.en.md +65 -54
- package/README.md +49 -47
- package/client/client.js +907 -54
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
状态从三种扩到四种,加入上下文压缩的显示,并给抽屉补上悬浮提示。
|
|
6
|
+
|
|
7
|
+
- **压缩中**:状态栏显示图标、开始时刻、「压缩上下文中」与实时秒数;抽屉第 0 行同步计时。
|
|
8
|
+
图标闪动,速度列写「压缩上下文」,首字列固定为破折号。
|
|
9
|
+
- **压缩完成**:图标停止闪动,文案改为「压缩完成」,保持到下一条请求发出。该行进入
|
|
10
|
+
抽屉 #1,补上输出 Token 与折算速度(output ÷ wall),但**不计入底部平均值**。
|
|
11
|
+
- 压缩没有首字与生成速度,这两项数据源里本就不存在;总用时取 wall,是日志事实,
|
|
12
|
+
刷新后不变。
|
|
13
|
+
- 压缩用哪个模型:优先显式配置,否则跟随最后一次请求(复用 KV 缓存)。压缩结束后
|
|
14
|
+
compaction/summary 会记录真实模型并显示;压缩期间尚无此记录,显示对话模型名。
|
|
15
|
+
- **悬浮提示**:时间列显示完整时间戳,速度列显示模型,输出 Token 列显示输入 token
|
|
16
|
+
与缓存命中率;七个表头各自说明该列含义,# 列的说明直接画出四种状态的图标。
|
|
17
|
+
- 提示用自建浮层而非 title,因为 title 只能显示纯文本,而 # 列的说明需要图标。
|
|
18
|
+
- 压缩阶段的状态栏预留空闲态宽度,切换时整体尺寸与图标位置都不变。
|
|
19
|
+
|
|
20
|
+
## 0.2.3
|
|
21
|
+
|
|
22
|
+
- 中断的行现在显示它跑了多久:被手动停止的请求没有 model,总用时回退到 wall
|
|
23
|
+
(endTime - startTime),刷新后仍是同一个数。
|
|
24
|
+
- 「中断」标记移到速度列。首字若已返回就照常显示实测值,总用时也照常显示——
|
|
25
|
+
缺的只是生成速度,由速度列说明原因。
|
|
26
|
+
- 中断行不计入底部平均值,但标题仍数满十行。
|
|
27
|
+
|
|
3
28
|
## 0.2.2
|
|
4
29
|
|
|
5
30
|
- 修复:切换模型后抽屉卡在旧的一轮。数据源的 target 曾被缓存,而会话重建
|
package/README.en.md
CHANGED
|
@@ -95,75 +95,67 @@ Two further details make the edges actually coincide:
|
|
|
95
95
|
Click the pill to open the drawer; click it again, or press Escape, or click outside, to
|
|
96
96
|
close it. There is no hover-open and no tooltip.
|
|
97
97
|
|
|
98
|
-
##
|
|
98
|
+
## Four states
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
The plugin sorts "what is happening right now" into four phases, each with its own glyph and
|
|
101
|
+
wording:
|
|
101
102
|
|
|
102
|
-
| State | Decided by | Pill icon | Drawer header |
|
|
103
|
-
| --- | --- | --- | --- |
|
|
104
|
-
| **Model writing** | `model` is `null` | ⚡ pulsing
|
|
105
|
-
| **Tools running** | `model` set, `endTime` `null` |
|
|
106
|
-
| **
|
|
103
|
+
| State | Decided by | Pill icon | Drawer header |
|
|
104
|
+
| --- | --- | --- | --- |
|
|
105
|
+
| **Model writing** | `model` is `null` | ⚡ pulsing | `Streaming` |
|
|
106
|
+
| **Tools running** | `model` set, `endTime` `null` | T pulsing | `Calling tools` |
|
|
107
|
+
| **Compacting** | newest compaction `running` | ≡ pulsing | `Compacting context` |
|
|
108
|
+
| **Compacted** | compaction finished, no request since | ≡ still | `Context compacted` |
|
|
109
|
+
| **Idle** | otherwise | ⚡ still | `Last N` |
|
|
107
110
|
|
|
108
|
-
|
|
111
|
+
When a compaction finishes the pill says "Context compacted" and stops pulsing, and it stays
|
|
112
|
+
that way until the next request starts — at which point the writing phase takes over again.
|
|
109
113
|
|
|
110
|
-
|
|
111
|
-
have all returned. Keying off `running` (which `step/end` clears) therefore kept the plugin in
|
|
112
|
-
"streaming" for the entire tool phase after the reply had already finished: the bolt kept
|
|
113
|
-
pulsing and the live row kept occupying an eleventh row.
|
|
114
|
+
### Compaction is its own activity
|
|
114
115
|
|
|
115
|
-
|
|
116
|
+
It belongs to no step: it is opened by `compaction/start` and closed by `compaction/end`, it
|
|
117
|
+
lives on the turn's `compactions[]`, it runs between two steps, and it can take tens of
|
|
118
|
+
seconds. No step is running during that time, so the pill used to keep showing the previous
|
|
119
|
+
step's figures and the pause looked like a hang.
|
|
116
120
|
|
|
117
|
-
|
|
118
|
-
|
|
121
|
+
It has no first token, no generation rate and no streamed token count — all three belong to a
|
|
122
|
+
settled reply. So the drawer's columns are filled like this:
|
|
119
123
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
three states distinguishable at a glance.
|
|
124
|
+
| Column | While compacting | Once compacted |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| First token | `—` | `—` |
|
|
127
|
+
| Speed | `Compaction` | `Compaction` |
|
|
128
|
+
| Tokens | `—` | the real figure (e.g. `4.9k tok`) |
|
|
129
|
+
| Time | counts up live | frozen at `wall` |
|
|
130
|
+
| Amortised | `—` | `output ÷ wall` |
|
|
128
131
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
The total is `wall` (`endTime − startTime`): a log fact, so a reload shows the same span.
|
|
133
|
+
A compaction row is excluded from the footer average — its total is wall-clock time, and
|
|
134
|
+
folding it into the mean would pull it toward a number describing neither case.
|
|
132
135
|
|
|
133
|
-
###
|
|
136
|
+
### Which model compacts
|
|
134
137
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
+
The summarizer picks, in order: an explicitly configured `summarizationProvider/Model` → **the
|
|
139
|
+
model the last request used** → the Agent's own model. The default is the second, because
|
|
140
|
+
replaying the whole conversation is only cheap when it hits the same provider's KV cache.
|
|
138
141
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
(`#303136f0` in dark mode, i.e. nearly opaque, the same surface behind the native
|
|
143
|
-
numbers), the `--dsw-menu-backdrop-filter` 40px/150% blur, `--dsw-elevation-prominent`
|
|
144
|
-
stroke and shadow, `--dsw-radius-lg`, and 16px padding. It lists the last ten requests,
|
|
145
|
-
newest first, with no explanatory footer:
|
|
142
|
+
`compaction/summary` records the model actually used, so **after** a compaction the real name
|
|
143
|
+
is shown; **while** one runs the log has no such record yet, so the chat model is named, and
|
|
144
|
+
the generic wording is the last resort.
|
|
146
145
|
|
|
147
|
-
|
|
148
|
-
| --- | --- | --- | --- | --- | --- | --- |
|
|
149
|
-
| 1 | 18:42:07 | 2.3 s | 445 tok/s | 1024 tok | 4.6 s | 223 tok/s |
|
|
146
|
+
### Hover hints
|
|
150
147
|
|
|
151
|
-
|
|
152
|
-
view. It is not the end time: a row stamped 18:42:07 with a 4.6 s total actually finished
|
|
153
|
-
at 18:42:12, so an end stamp would read as "it finished then" and would also order the list
|
|
154
|
-
by completion rather than by issue.
|
|
148
|
+
Every drawer column and every header carries a hint:
|
|
155
149
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
150
|
+
| Where | Content |
|
|
151
|
+
| --- | --- |
|
|
152
|
+
| Clock column | the full timestamp `11-06 10:47:41.828` |
|
|
153
|
+
| Speed column | the model that served that row |
|
|
154
|
+
| Tokens column | input tokens and cache hit rate |
|
|
155
|
+
| Headers | what the column measures (the `#` header draws the four state glyphs) |
|
|
161
156
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
row — the average of the visible requests, in the same grid so each mean lands under its
|
|
165
|
-
column. A column with no samples (say no TTFT on an interrupted request) prints a dash
|
|
166
|
-
rather than a misleading zero.
|
|
157
|
+
The hints are a real overlay rather than `title`: `title` can only show plain text, and the
|
|
158
|
+
`#` header has to draw glyphs.
|
|
167
159
|
|
|
168
160
|
## How the numbers are measured
|
|
169
161
|
|
|
@@ -412,6 +404,25 @@ carries `ttft`, `model`, `decode`, `tps`, `output` and `wall`.
|
|
|
412
404
|
| **Time** | **`model`** | **model time, not the Statistics view's total** |
|
|
413
405
|
| Amortised | `output ÷ model` | this plugin's own figure |
|
|
414
406
|
|
|
407
|
+
**Interrupted rows:**
|
|
408
|
+
|
|
409
|
+
A request that was stopped never produced an `assistant/message`, so `model`, `decode` and
|
|
410
|
+
`tps` are all null. That row's **total falls back to `wall`** (`endTime − startTime`) — how
|
|
411
|
+
long the request actually ran before it was stopped. It is a log fact, so it survives a reload
|
|
412
|
+
unchanged, which is exactly what that column is for here.
|
|
413
|
+
|
|
414
|
+
**The first-token column falls back to `wall` too**, in two shapes: if the upstream never returned a
|
|
415
|
+
token, the first-token figure is the wait itself and equals the total; if a token had arrived,
|
|
416
|
+
`ttft` is already fixed and the row keeps that measurement, so the first-token figure is smaller
|
|
417
|
+
than the total — which is how a reader tells "it started answering and was cut off" from "it never
|
|
418
|
+
got to answer".
|
|
419
|
+
|
|
420
|
+
The word **"interrupted" sits in the speed column**, not the first-token column: both the
|
|
421
|
+
first-token figure and the total are real readings, and the only missing figure is the generation
|
|
422
|
+
rate, so that is the column that says why. Interrupted rows are
|
|
423
|
+
excluded from the footer average — their total is wall-clock time, and folding it into the mean
|
|
424
|
+
would pull it toward a number describing neither case — but the caption still counts all ten.
|
|
425
|
+
|
|
415
426
|
**Why the total is `model` and not `wall`:**
|
|
416
427
|
|
|
417
428
|
The Statistics view's total column is `wall = endTime − startTime`, which **includes the step's
|
package/README.md
CHANGED
|
@@ -85,68 +85,60 @@ DSH 的极客级请求指标。它在 **设置 → 通用 → 性能与用量**
|
|
|
85
85
|
|
|
86
86
|
点击气泡打开抽屉;再次点击、按 Esc、或点击外部即可关闭。没有悬停打开,也没有悬停提示。
|
|
87
87
|
|
|
88
|
-
##
|
|
88
|
+
## 四种状态
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
插件把「现在在干什么」分成四段,每段有自己的图标与文案:
|
|
91
91
|
|
|
92
|
-
| 状态 | 判定 | 状态栏图标 | 抽屉右上角 |
|
|
93
|
-
| --- | --- | --- | --- |
|
|
94
|
-
| **模型输出中** | `model` 为 `null` | ⚡
|
|
95
|
-
| **工具调用中** | `model` 有值、`endTime` 为 `null` |
|
|
96
|
-
|
|
|
92
|
+
| 状态 | 判定 | 状态栏图标 | 抽屉右上角 |
|
|
93
|
+
| --- | --- | --- | --- |
|
|
94
|
+
| **模型输出中** | `model` 为 `null` | ⚡ 脉冲 | `生成中` |
|
|
95
|
+
| **工具调用中** | `model` 有值、`endTime` 为 `null` | T 脉冲 | `调用工具中` |
|
|
96
|
+
| **上下文压缩中** | 最近一次压缩 `running` | ≡ 脉冲 | `上下文压缩中` |
|
|
97
|
+
| **上下文压缩完成** | 压缩已结束、之后没有新请求 | ≡ 静止 | `上下文压缩完成` |
|
|
98
|
+
| **空闲** | 其余 | ⚡ 静止 | `最近 N 条` |
|
|
97
99
|
|
|
98
|
-
|
|
100
|
+
压缩完成后状态栏显示「压缩完成」并停止闪动,直到下一条请求发出——那时自动回到模型输出状态。
|
|
99
101
|
|
|
100
|
-
|
|
101
|
-
若只看 `running`(它由 `step/end` 清零),模型回复早已结束、工具还在跑的整段时间里,
|
|
102
|
-
插件都会停在「生成中」:闪电一直闪、实时行一直占着第 11 行。
|
|
102
|
+
### 压缩是一种独立的活动
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
压缩不属于任何一步:它由 `compaction/start` 与 `compaction/end` 界定,挂在回合的
|
|
105
|
+
`compactions[]` 上,跑在两步之间,可以持续几十秒。在这段时间里没有步骤在跑,
|
|
106
|
+
所以之前只能沿用上一步的数字,看起来像是卡住了。
|
|
105
107
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
它没有首字、没有生成速度,也没有流式 token —— 那三项都依附于一条已结算的回复。
|
|
109
|
+
因此抽屉里那几列,压缩行是这样处理的:
|
|
108
110
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
| 列 | 压缩期间 | 压缩完成后 |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| 首字 | `—` | `—` |
|
|
114
|
+
| 速度 | `压缩上下文` | `压缩上下文` |
|
|
115
|
+
| 输出 Token | `—` | 实际值(如 `4.9k tok`) |
|
|
116
|
+
| 总用时 | 实时递增 | 冻结为 `wall` |
|
|
117
|
+
| 折算速度 | `—` | `output ÷ wall` |
|
|
116
118
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
T 的字形特意收窄,避免比闪电看起来更重。
|
|
119
|
+
总用时取 `wall`(`endTime − startTime`):这是日志事实,刷新后仍是同一个数。
|
|
120
|
+
压缩行不计入底部平均值——它的总用时是墙钟时间,混进均值会拉偏。
|
|
120
121
|
|
|
121
|
-
###
|
|
122
|
+
### 压缩用哪个模型
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
不会因为缺少折叠而误判为空闲。
|
|
124
|
+
压缩的模型按顺序取:显式配置的 `summarizationProvider/Model` → **最后一次请求实际用的模型**
|
|
125
|
+
→ Agent 自身的模型。默认走第二条,因为重放整段对话时复用同一个模型才能命中 KV 缓存。
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
`--dsw-specific-menu` 底色(深色模式下是 `#303136f0`,即近乎不透明,正是原生数字背后的那层)、
|
|
130
|
-
`--dsw-menu-backdrop-filter` 的 40px/150% 模糊、`--dsw-elevation-prominent` 描边与阴影、
|
|
131
|
-
`--dsw-radius-lg`、以及 16px 内边距。它按最新在上的顺序列出最近十次请求,底部不加说明文字:
|
|
127
|
+
`compaction/summary` 会记录实际用的模型,所以**压缩结束后**显示的是真实模型名;
|
|
128
|
+
**压缩期间**日志里还没有这个信息,此时显示对话模型名,拿不到则写「使用对话模型」。
|
|
132
129
|
|
|
133
|
-
|
|
134
|
-
| --- | --- | --- | --- | --- | --- | --- |
|
|
135
|
-
| 1 | 18:42:07 | 2.3 s | 445 tok/s | 1024 tok | 4.6 s | 223 tok/s |
|
|
130
|
+
### 悬浮提示
|
|
136
131
|
|
|
137
|
-
|
|
138
|
-
否则一行写着 18:42:07、总用时 4.6 秒,实际却是 18:42:12 才完成,读起来会误解成「那一刻就结束了」;
|
|
139
|
-
而且列表会变成按完成时间排序,而不是按发起时间。
|
|
132
|
+
抽屉的每一列与表头都有提示:
|
|
140
133
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
134
|
+
| 位置 | 内容 |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| 时间列 | 完整时间戳 `11-06 10:47:41.828` |
|
|
137
|
+
| 速度列 | 该行使用的模型 |
|
|
138
|
+
| 输出 Token 列 | 输入 token 与缓存命中率 |
|
|
139
|
+
| 表头 | 该列的含义(`#` 一列用图标展示四种状态) |
|
|
145
140
|
|
|
146
|
-
|
|
147
|
-
历史下方有一条分隔线,线下面是汇总行——对当前可见请求取平均值,复用同一套 grid,
|
|
148
|
-
因此每个均值都落在它所汇总的那一列下面。完全没有样本的列(比如中断请求没有首字时间)
|
|
149
|
-
打印破折号,而不是容易误解的 0。
|
|
141
|
+
提示是自建的浮层而不是 `title`:`title` 只能显示纯文本,而 `#` 列的说明要画出图标。
|
|
150
142
|
|
|
151
143
|
## 数字是怎么算出来的
|
|
152
144
|
|
|
@@ -370,6 +362,16 @@ const target = uiConversation.binding(sessionId).target('stats')
|
|
|
370
362
|
| **总用时** | **`model`** | **模型用时,不是统计页的「总用时」列** |
|
|
371
363
|
| 折算速度 | `output ÷ model` | 本插件独有 |
|
|
372
364
|
|
|
365
|
+
**中断的行:**
|
|
366
|
+
|
|
367
|
+
被手动停止的请求没有 `assistant/message`,因此 `model`、`decode`、`tps` 全为 null。
|
|
368
|
+
那一行的**总用时回退到 `wall`**(`endTime − startTime`),也就是这条请求从发出到被
|
|
369
|
+
停止实际过了多久——它是日志里的事实,刷新多少次都不变,正是这个场合要的数字。
|
|
370
|
+
|
|
371
|
+
**首字也回退到 `wall`**,分两种情况:上游一直没返回 token,那么首字就是它等待的时长,
|
|
372
|
+
缺的只是生成速度,所以由速度列说明原因。中断行不计入底部的平均值——它的总用时是墙钟
|
|
373
|
+
时间,混进均值会拉向一个既不代表完成、也不代表中断的数;但平均行的标题仍数满十行。
|
|
374
|
+
|
|
373
375
|
**总用时为何取 `model` 而不是 `wall`:**
|
|
374
376
|
|
|
375
377
|
统计页的「总用时」列是 `wall = endTime − startTime`,它**把工具调用也算了进去**。
|