dsh-recent-usage-metrics 0.3.4 → 0.3.6
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 +15 -0
- package/README.en.md +2 -1
- package/README.md +2 -1
- package/client/client.js +150 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## 0.3.6
|
|
2
|
+
|
|
3
|
+
- `#` 表头的提示加了一行颜色说明:黄色表示缓存命中过低,蓝色表示这一步切换了模型。
|
|
4
|
+
颜色用色块画出来而不是写成文字,读者按颜色对号即可,不必在脑内翻译一个词。
|
|
5
|
+
色块与行高亮取同一组主题令牌,两者不会各自漂移。
|
|
6
|
+
|
|
7
|
+
## 0.3.5
|
|
8
|
+
|
|
9
|
+
- 修复:模型切换或缓存偏低时,高亮只染了数字列,时间与速度列仍是灰色——那三列各自设置了
|
|
10
|
+
颜色,把行级颜色挡掉了。现在高亮会覆盖所有单元格,整行才真的变色。
|
|
11
|
+
- 总用时列新增悬浮提示,说明工具调用的用时:`工具调用 3 次 · 串行 12.4 s · 并行合并 5.1 s`。
|
|
12
|
+
两个口径都报,因为差值正是并行省下的时间。无工具时写「无工具调用」,
|
|
13
|
+
压缩行写明「压缩上下文不计工具时间」。
|
|
14
|
+
- 总用时的表头提示补上一句「不含工具调用时间」,把口径歧义说清。
|
|
15
|
+
|
|
1
16
|
## 0.3.4
|
|
2
17
|
|
|
3
18
|
- 实时行也能显示模型名了:形态与编号行相同——本步窗口内有 `request/header` 判为切换、
|
package/README.en.md
CHANGED
|
@@ -152,7 +152,8 @@ Every drawer column and every header carries a hint:
|
|
|
152
152
|
| Clock column | the full timestamp `11-06 10:47:41.828` |
|
|
153
153
|
| Speed column | the model that served that row |
|
|
154
154
|
| Tokens column | input tokens (with thousands separators, e.g. `370,683 tok`) and cache hit rate |
|
|
155
|
-
|
|
|
155
|
+
| Time column | how much of the request was tool time, both summed and parallel-merged |
|
|
156
|
+
| Headers | what the column measures (the `#` header draws the four state glyphs and swatches for the two row colours) |
|
|
156
157
|
|
|
157
158
|
**Row highlights:**
|
|
158
159
|
|
package/README.md
CHANGED
|
@@ -136,7 +136,8 @@ DSH 的极客级请求指标。它在 **设置 → 通用 → 性能与用量**
|
|
|
136
136
|
| 时间列 | 完整时间戳 `11-06 10:47:41.828` |
|
|
137
137
|
| 速度列 | 该行使用的模型 |
|
|
138
138
|
| 输出 Token 列 | 输入 token(带千分位,如 `370,683 tok`)与缓存命中率 |
|
|
139
|
-
|
|
|
139
|
+
| 总用时列 | 工具调用的用时(串行累加与并行合并两个口径);无工具时说明没有调用 |
|
|
140
|
+
| 表头 | 该列的含义(`#` 一列用图标展示四种状态,并用色块说明两种行高亮的颜色) |
|
|
140
141
|
|
|
141
142
|
**行高亮:**
|
|
142
143
|
|
package/client/client.js
CHANGED
|
@@ -114,14 +114,20 @@ window.__ModuleLoader__.load({
|
|
|
114
114
|
hoursAgo: '{count} 小时前',
|
|
115
115
|
// Column-header hints. `{bolt}` / `{tools}` / `{compact}` are replaced by the glyphs
|
|
116
116
|
// themselves, which is why these are rendered by the tooltip component and not by title.
|
|
117
|
-
hintIndex:
|
|
117
|
+
hintIndex:
|
|
118
|
+
'行号:1–10 为已完成的最近记录,{bolt} 为模型输出中,{tools} 为工具调用中,{compact} 为压缩上下文中',
|
|
119
|
+
hintColour: '整行黄色表示缓存命中过低,蓝色表示这一步切换了模型',
|
|
118
120
|
hintTime: '请求发出的时刻(本地时间)',
|
|
119
121
|
hintTtft: '从发出到收到第一个 token 的耗时',
|
|
120
122
|
hintSpeed: '模型生成速度 = 输出 token ÷ 解码时长',
|
|
121
123
|
hintTokens: '模型本次生成的 token 数',
|
|
122
|
-
hintTotal: '
|
|
124
|
+
hintTotal: '请求耗时:从发出请求一直到模型输出结束,不含工具调用时间',
|
|
123
125
|
hintAmortized: '输出 token ÷ 总用时,包含请求时间和响应时间,更加能表明本地到模型输出完整链路的速度',
|
|
124
126
|
tokensHint: '输入 {input} · 缓存命中 {hit}',
|
|
127
|
+
toolHintOne: '工具调用 {count} 次 · {total}',
|
|
128
|
+
toolHintTwo: '工具调用 {count} 次 · 串行 {total} · 并行合并 {wall}',
|
|
129
|
+
toolHintNone: '无工具调用',
|
|
130
|
+
toolHintCompaction: '压缩上下文不计工具时间',
|
|
125
131
|
}
|
|
126
132
|
|
|
127
133
|
const en = {
|
|
@@ -172,14 +178,20 @@ window.__ModuleLoader__.load({
|
|
|
172
178
|
secondsAgo: '{count}s ago',
|
|
173
179
|
minutesAgo: '{count}m ago',
|
|
174
180
|
hoursAgo: '{count}h ago',
|
|
175
|
-
hintIndex:
|
|
181
|
+
hintIndex:
|
|
182
|
+
'Row number: 1–10 are the most recent finished records; {bolt} is a model reply, {tools} is a tool call, {compact} is a context compaction',
|
|
183
|
+
hintColour: 'A yellow row means a low cache hit; a blue row means this step switched models',
|
|
176
184
|
hintTime: 'When the request was issued (local time)',
|
|
177
185
|
hintTtft: 'Time from issuing the request to receiving its first token',
|
|
178
186
|
hintSpeed: 'Generation speed = output tokens ÷ decode time',
|
|
179
187
|
hintTokens: 'Tokens this reply generated',
|
|
180
|
-
hintTotal: 'Request time: from issuing the request until the model finished replying',
|
|
188
|
+
hintTotal: 'Request time: from issuing the request until the model finished replying, not counting tool calls',
|
|
181
189
|
hintAmortized: 'Output tokens ÷ total time, including both queueing and response — the throughput of the whole local-to-model path',
|
|
182
190
|
tokensHint: 'Input {input} · cache hit {hit}',
|
|
191
|
+
toolHintOne: 'Tool calls: {count} · {total}',
|
|
192
|
+
toolHintTwo: 'Tool calls: {count} · serial {total} · parallel-merged {wall}',
|
|
193
|
+
toolHintNone: 'No tool calls',
|
|
194
|
+
toolHintCompaction: 'A compaction spends no time on tools',
|
|
183
195
|
}
|
|
184
196
|
|
|
185
197
|
const STYLE_TEXT = `
|
|
@@ -450,6 +462,26 @@ window.__ModuleLoader__.load({
|
|
|
450
462
|
color: var(--dsw-alias-label-tertiary);
|
|
451
463
|
text-align: left;
|
|
452
464
|
}
|
|
465
|
+
/*
|
|
466
|
+
* A highlighted row colours EVERY cell, not just the ones that inherit.
|
|
467
|
+
*
|
|
468
|
+
* The index, clock and primary cells each set their own colour, so without this they stay
|
|
469
|
+
* grey inside a blue or yellow row and the highlight reads as a stray word rather than as a
|
|
470
|
+
* whole row. Their own rules stay in place for every unhighlighted row.
|
|
471
|
+
*
|
|
472
|
+
* The specificity is deliberately higher than each of those cell rules: one class plus two
|
|
473
|
+
* attribute selectors beats a plain class selector.
|
|
474
|
+
*/
|
|
475
|
+
.dsh-rum-item[data-switched="true"] .dsh-rum-col,
|
|
476
|
+
.dsh-rum-item[data-switched="true"] .dsh-rum-index,
|
|
477
|
+
.dsh-rum-item[data-switched="true"] .dsh-rum-clock {
|
|
478
|
+
color: inherit;
|
|
479
|
+
}
|
|
480
|
+
.dsh-rum-item[data-lowcache="true"] .dsh-rum-col,
|
|
481
|
+
.dsh-rum-item[data-lowcache="true"] .dsh-rum-index,
|
|
482
|
+
.dsh-rum-item[data-lowcache="true"] .dsh-rum-clock {
|
|
483
|
+
color: inherit;
|
|
484
|
+
}
|
|
453
485
|
/*
|
|
454
486
|
* The index cells are flex boxes so a row number and the streaming dot share one alignment
|
|
455
487
|
* rule. With inline content, a 6px dot aligned on the text baseline sits visibly lower than
|
|
@@ -611,6 +643,39 @@ window.__ModuleLoader__.load({
|
|
|
611
643
|
.dsh-rum-tip .dsh-rum-tip-icon {
|
|
612
644
|
color: var(--dsw-alias-brand-primary);
|
|
613
645
|
}
|
|
646
|
+
/*
|
|
647
|
+
* The index hint is two lines: what the glyphs mean, then what the row colours mean.
|
|
648
|
+
*
|
|
649
|
+
* The colour line leads with two swatches instead of naming the hues, so a reader matches a
|
|
650
|
+
* row against the legend by colour rather than by translating a word. The swatches take the
|
|
651
|
+
* same tokens the rows take, so the two can never disagreement.
|
|
652
|
+
*/
|
|
653
|
+
.dsh-rum-tip-body {
|
|
654
|
+
display: block;
|
|
655
|
+
}
|
|
656
|
+
.dsh-rum-tip-line {
|
|
657
|
+
display: block;
|
|
658
|
+
}
|
|
659
|
+
.dsh-rum-tip-line + .dsh-rum-tip-line {
|
|
660
|
+
margin-top: 5px;
|
|
661
|
+
}
|
|
662
|
+
.dsh-rum-tip-swatch {
|
|
663
|
+
display: inline-block;
|
|
664
|
+
width: 8px;
|
|
665
|
+
height: 8px;
|
|
666
|
+
border-radius: 2px;
|
|
667
|
+
margin-right: 3px;
|
|
668
|
+
vertical-align: 0;
|
|
669
|
+
}
|
|
670
|
+
.dsh-rum-tip-swatch + .dsh-rum-tip-swatch {
|
|
671
|
+
margin-left: 6px;
|
|
672
|
+
}
|
|
673
|
+
.dsh-rum-tip-swatch[data-hue="lowcache"] {
|
|
674
|
+
background: var(--dsw-alias-state-warn-primary, var(--dsw-alias-state-warn-label));
|
|
675
|
+
}
|
|
676
|
+
.dsh-rum-tip-swatch[data-hue="switched"] {
|
|
677
|
+
background: var(--dsw-alias-state-business-primary, var(--dsw-alias-brand-primary));
|
|
678
|
+
}
|
|
614
679
|
.dsh-rum-empty {
|
|
615
680
|
flex: 1 1 auto;
|
|
616
681
|
padding: 14px 6px 16px;
|
|
@@ -1084,6 +1149,16 @@ window.__ModuleLoader__.load({
|
|
|
1084
1149
|
provider: null,
|
|
1085
1150
|
context: numberOrNull(stats.context),
|
|
1086
1151
|
cacheRead: numberOrNull(stats.cacheRead),
|
|
1152
|
+
/*
|
|
1153
|
+
* Tool time, for the total column hint. `tool` sums each call from its dispatch to its
|
|
1154
|
+
* result, so three parallel 10 s tools report 30 s; `toolWall` unions the intervals, so
|
|
1155
|
+
* the same three report 10 s. Both are reported because the gap between them is exactly
|
|
1156
|
+
* how much the parallelism bought.
|
|
1157
|
+
*/
|
|
1158
|
+
toolMs: numberOrNull(stats.tool),
|
|
1159
|
+
toolWallMs: numberOrNull(stats.toolWall),
|
|
1160
|
+
/** How many calls the step made, from the fold's own call list. */
|
|
1161
|
+
toolCalls: Array.isArray(stats.calls) ? stats.calls.length : null,
|
|
1087
1162
|
cacheHit: numberOrNull(stats.cacheHit),
|
|
1088
1163
|
}
|
|
1089
1164
|
}
|
|
@@ -1824,6 +1899,42 @@ window.__ModuleLoader__.load({
|
|
|
1824
1899
|
return provider === null ? model : `${provider} / ${model}`
|
|
1825
1900
|
}
|
|
1826
1901
|
|
|
1902
|
+
/**
|
|
1903
|
+
* The total column's hint: how much of the request was tool time.
|
|
1904
|
+
*
|
|
1905
|
+
* Two figures, because they answer different questions. `tool` adds up each call, so it
|
|
1906
|
+
* says how much tool work there was; `toolWall` unions the intervals, so it says how much
|
|
1907
|
+
* wall time the tools occupied. Three parallel 10 s calls are 30 s of work in 10 s of
|
|
1908
|
+
* elapsed time, and the gap is what parallelism bought.
|
|
1909
|
+
*
|
|
1910
|
+
* Neither is part of the total above, which is model time only — the header says so.
|
|
1911
|
+
*/
|
|
1912
|
+
function toolHintText(row, t) {
|
|
1913
|
+
if (row.compaction === true) return t('toolHintCompaction')
|
|
1914
|
+
const calls = numberOrNull(row.toolCalls)
|
|
1915
|
+
const serial = numberOrNull(row.toolMs)
|
|
1916
|
+
if (serial === null && calls === null) return t('toolHintNone')
|
|
1917
|
+
if (serial === null) return t('toolHintNone')
|
|
1918
|
+
const wall = numberOrNull(row.toolWallMs)
|
|
1919
|
+
const count = calls === null ? 0 : calls
|
|
1920
|
+
if (wall === null || wall === serial) {
|
|
1921
|
+
return t('toolHintOne', { count, total: secondsValue(serial, t) })
|
|
1922
|
+
}
|
|
1923
|
+
return t('toolHintTwo', {
|
|
1924
|
+
count,
|
|
1925
|
+
total: secondsValue(serial, t),
|
|
1926
|
+
wall: secondsValue(wall, t),
|
|
1927
|
+
})
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
/** A duration as the hint spells it: seconds, one decimal, no unit label of its own. */
|
|
1931
|
+
function secondsValue(ms, t) {
|
|
1932
|
+
if (ms === null || ms === undefined || !Number.isFinite(ms)) return t('none')
|
|
1933
|
+
const seconds = ms / 1000
|
|
1934
|
+
const text = seconds < 100 ? seconds.toFixed(1) : String(Math.round(seconds))
|
|
1935
|
+
return `${text} s`
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1827
1938
|
/** The tokens column's hint: the input side, which the cell itself does not show. */
|
|
1828
1939
|
function tokensHintText(row, t) {
|
|
1829
1940
|
const none = t('none')
|
|
@@ -1864,7 +1975,23 @@ window.__ModuleLoader__.load({
|
|
|
1864
1975
|
else if (token === '{compact}') nodes.push(glyph(CompactionIcon))
|
|
1865
1976
|
else nodes.push(token)
|
|
1866
1977
|
}
|
|
1867
|
-
|
|
1978
|
+
/*
|
|
1979
|
+
* The colour legend is its own line, with the two highlights drawn as swatches rather than
|
|
1980
|
+
* named. A reader can then match a row to the legend by colour instead of translating a
|
|
1981
|
+
* word, and the swatches use the same tokens the rows do, so the two cannot drift.
|
|
1982
|
+
*/
|
|
1983
|
+
return h(
|
|
1984
|
+
'span',
|
|
1985
|
+
{ className: 'dsh-rum-tip-body' },
|
|
1986
|
+
h('span', { className: 'dsh-rum-tip-line' }, ...nodes),
|
|
1987
|
+
h(
|
|
1988
|
+
'span',
|
|
1989
|
+
{ className: 'dsh-rum-tip-line' },
|
|
1990
|
+
h('span', { className: 'dsh-rum-tip-swatch', 'data-hue': 'lowcache' }),
|
|
1991
|
+
h('span', { className: 'dsh-rum-tip-swatch', 'data-hue': 'switched' }),
|
|
1992
|
+
t('hintColour'),
|
|
1993
|
+
),
|
|
1994
|
+
)
|
|
1868
1995
|
}
|
|
1869
1996
|
|
|
1870
1997
|
/**
|
|
@@ -3787,7 +3914,13 @@ window.__ModuleLoader__.load({
|
|
|
3787
3914
|
h('span', { className: 'dsh-rum-col', role: 'cell' }, tokensText(live.tokens, live.estimated === true, t)),
|
|
3788
3915
|
h(
|
|
3789
3916
|
'span',
|
|
3790
|
-
{
|
|
3917
|
+
{
|
|
3918
|
+
className: 'dsh-rum-col',
|
|
3919
|
+
role: 'cell',
|
|
3920
|
+
/* Same hint as the numbered rows, so the meaning never changes. */
|
|
3921
|
+
onMouseEnter: (event) => hint.show(toolHintText(live, t), event),
|
|
3922
|
+
onMouseLeave: hint.hide,
|
|
3923
|
+
},
|
|
3791
3924
|
secondsText(
|
|
3792
3925
|
live.compaction !== null && live.compaction !== undefined
|
|
3793
3926
|
? compactionSpan(live.compaction, now)
|
|
@@ -3902,7 +4035,17 @@ window.__ModuleLoader__.load({
|
|
|
3902
4035
|
),
|
|
3903
4036
|
h(
|
|
3904
4037
|
'span',
|
|
3905
|
-
{
|
|
4038
|
+
{
|
|
4039
|
+
className: 'dsh-rum-col',
|
|
4040
|
+
role: 'cell',
|
|
4041
|
+
/*
|
|
4042
|
+
* The hint explains how much of the request was tool time. The figure
|
|
4043
|
+
* in the cell deliberately excludes it, which is exactly why the two
|
|
4044
|
+
* need to be shown together.
|
|
4045
|
+
*/
|
|
4046
|
+
onMouseEnter: (event) => hint.show(toolHintText(entry.row, t), event),
|
|
4047
|
+
onMouseLeave: hint.hide,
|
|
4048
|
+
},
|
|
3906
4049
|
secondsText(
|
|
3907
4050
|
/*
|
|
3908
4051
|
* A running compaction counts up from its own start, exactly as the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-recent-usage-metrics",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"description": "Geek level for the Performance & usage setting: a live reading of the newest request (first-token latency, speed, tokens, total, amortised) plus a drawer listing the last ten, all derived from the shipped Statistics view data",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|