dsh-tokenlens 1.7.0 → 1.7.1
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 +10 -1
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/client.js +116 -59
- package/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,12 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
**版本记录约定**:同一发布日只保留一个章节,标题取当日**最后一个**已发布版本号;当日多次发布的改动合并到该章节,并在标题下注明当日实际发布过的版本号(npm 上仍可安装这些独立版本)。若当日已有发布,后续小改动不再单独增加版本号,而是并入下一篇日志。这样日志按天规整、不出现同日多个小版本。
|
|
6
6
|
|
|
7
|
+
## 1.7.1 — 2026-11-06
|
|
8
|
+
|
|
9
|
+
(当日已发布版本:v1.6.5 / v1.6.6 / v1.6.7 / v1.7.0 / v1.7.1)
|
|
10
|
+
|
|
11
|
+
- **fix(ui): 未最大化窗口时芯片悬停导致整个对话区闪烁**——`Tooltip` 的气泡锚在芯片右缘,窗口变窄时会溢出 dock,撑出**对话区底部的横向滚动条**;滚动条又挤压布局、导致 hover 被反复触发,于是整片区域持续重绘。芯片改用**原生 `title`**:不产生任何 DOM,不可能影响布局。v1.7.0 只去掉原生 `title` 保留 Tooltip,方向反了——真正的成因是 Tooltip 本身
|
|
12
|
+
- **feat(ui): TPS 趋势图由柱状改为曲线**——速率是连续量,折线比逐日立柱更易读。改为单条 `polyline` 曲线 + 淡色面积填充;每天一个透明命中列承载 `title`,鼠标移上去显示「日期 · 速率(输出 ÷ 耗时)」
|
|
13
|
+
- fix(ui): **TPS 卡片宽度与「明细」卡片一致**(`flex: 1 1 100%`)——此前它没有 flex 规则,宽度收缩到内容宽
|
|
14
|
+
- 测试:363 → 364(芯片不再包 Tooltip、标题为原生字符串、速度卡片满宽、曲线为 polyline+area 且不填充线体、透明命中列携带日期与速率)
|
|
15
|
+
|
|
7
16
|
## 1.7.0 — 2026-11-06
|
|
8
17
|
|
|
9
18
|
(当日已发布版本:v1.6.5 / v1.6.6 / v1.6.7 / v1.7.0)
|
|
10
19
|
|
|
11
20
|
- **fix(ui): 弹窗改为 portal 到 `<body>`,恢复视口居中**——DSH 0.2.2-rc.1 里 `conversation.input.overlay` 槽位的祖先建立了包含块(transform / filter / contain),`position: fixed` 因此相对「对话区」解析,弹窗只能贴在对话区底部。改用 `react-dom` 的 `createPortal` 挂到 `document.body`,彻底脱离该祖先链。`react-dom` 缺失时(含 Node 测试环境)自动回退为原地渲染,绝不抛错
|
|
12
|
-
- **fix(ui):
|
|
21
|
+
- **fix(ui): 芯片悬停不再让整个对话区闪烁**(v1.7.1 进一步修正为改用原生 `title`)——v1.7.0 曾移除冗余的原生 `title` 并保留平台 `Tooltip`,但窄窗口下成因其实是 Tooltip 气泡溢出
|
|
13
22
|
- **feat(ui): 新增「输出速度(TPS)」趋势图**,位于明细下方、独占整行——输出 Token ÷ 模型自身耗时(`step/start` → `assistant/message`)。工具调用发生在消息之后,因此等待时间不被计入;输出 Token 含思考 Token,弹窗提示已说明
|
|
14
23
|
- feat(stats): Host 侧新增 `speed` 序列(顶层为全 Harness 合计,每个 provider 行另有自己的序列,跟随作用域切换);按天聚合 `{day, outputTokens, seconds, tps, steps}`,无正耗时的日期直接跳过而不是给出无限速率。缓存 schema 升至 6,首次会重扫
|
|
15
24
|
- **fix(stats): 「最近X天」封顶 30 天**——此前 provider 首次出现超过 30 天时会报「最近90天」,而同期所有其他数字与图表都只覆盖 30 天。现在 `effectiveDays` 封顶 30,且 `sinceEffective` 在超窗时改用 30 天桶,保证**标签与数字描述同一区间**;`daily` 序列尾部由 120 天收敛为 30 天
|
package/README.en.md
CHANGED
|
@@ -144,7 +144,7 @@ Host bridge route: `/api/tokenlens`; agent tool: `tokenlens_usage`.
|
|
|
144
144
|
|
|
145
145
|
## Development
|
|
146
146
|
|
|
147
|
-
Node.js ≥ 24, zero runtime dependencies: `node --test` runs the
|
|
147
|
+
Node.js ≥ 24, zero runtime dependencies: `node --test` runs the 364 tests.
|
|
148
148
|
|
|
149
149
|
Release: `node scripts/bump-version.mjs <version> "<note>"` updates `package.json`, `PLUGIN_VERSION` and `CHANGELOG.md`; see [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md).
|
|
150
150
|
|
package/README.md
CHANGED
|
@@ -445,7 +445,7 @@ Host 半注册 `tokenlens_usage`,参数 `action`(默认 `snapshot`)与可
|
|
|
445
445
|
|
|
446
446
|
## 验证
|
|
447
447
|
|
|
448
|
-
- `node --test`:**
|
|
448
|
+
- `node --test`:**364 个自动化用例**,覆盖 Host 数据层、供应商适配器(含小米 MiMo 控制台 Cookie 适配器、Command Code 月度额度反推)、统计折叠、各 harness 读取、Design Plan 周期账本与额度计价、TPS 速度序列、窗口锚点切片、工作区自动识别、插件集成与浏览器端渲染 / 交互。
|
|
449
449
|
- 在真实环境中验证过 **5 个 harness** 的数据源;本机统计与独立的 SQLite 查询、独立的日志解析逐项对账(差额为 0),跨 harness 合并与供应商映射算术自洽。
|
|
450
450
|
- 供应商接口按各官方文档与参考客户端行为验证;未配置凭据时 chip 静默隐藏,接口异常时按可重试错误降级。
|
|
451
451
|
|
package/client.js
CHANGED
|
@@ -231,6 +231,7 @@ window.__ModuleLoader__.load({
|
|
|
231
231
|
statsSpeedTitle: '输出速度(TPS)',
|
|
232
232
|
statsSpeedHint: '输出 Token ÷ 模型自身耗时(step 开始到消息返回);不含工具调用等待,但输出含思考 Token',
|
|
233
233
|
statsSpeedTotal: '均值 {tps} tok/s',
|
|
234
|
+
statsSpeedPeak: '峰值 {tps} tok/s',
|
|
234
235
|
statsSpeedEmpty: '暂无可计算的速度样本(需要 step 计时数据)',
|
|
235
236
|
statsSpeedBarTitle: '{date} · {tps} tok/s({tokens} 输出 ÷ {seconds}s)',
|
|
236
237
|
statsLast7Days: '最近7天',
|
|
@@ -506,6 +507,7 @@ window.__ModuleLoader__.load({
|
|
|
506
507
|
statsSpeedHint:
|
|
507
508
|
'Output tokens ÷ the model’s own request time (step start to message); tool waits excluded, reasoning tokens included',
|
|
508
509
|
statsSpeedTotal: 'avg {tps} tok/s',
|
|
510
|
+
statsSpeedPeak: 'peak {tps} tok/s',
|
|
509
511
|
statsSpeedEmpty: 'No speed samples yet (needs step timing data)',
|
|
510
512
|
statsSpeedBarTitle: '{date} · {tps} tok/s ({tokens} output ÷ {seconds}s)',
|
|
511
513
|
statsLast7Days: 'Last 7 days',
|
|
@@ -3440,6 +3442,11 @@ window.__ModuleLoader__.load({
|
|
|
3440
3442
|
.dsh-tokenlens-chart-layout-row>.dsh-tokenlens-capacity{flex:0.85 1 260px}
|
|
3441
3443
|
.dsh-tokenlens-chart-layout-row>.dsh-tokenlens-stats{flex:1.25 1 330px;min-width:0}
|
|
3442
3444
|
.dsh-tokenlens-chart-layout-row>.dsh-tokenlens-details{flex:1 1 100%;min-width:0}
|
|
3445
|
+
/* v1.7.1: the TPS trend is a full-width row, exactly like the details card. */
|
|
3446
|
+
.dsh-tokenlens-chart-layout-row>.dsh-tokenlens-section[data-speed-section]{flex:1 1 100%;min-width:0}
|
|
3447
|
+
.dsh-tokenlens-speed-svg{display:block;width:100%;height:96px;overflow:visible}
|
|
3448
|
+
.dsh-tokenlens-speed-axis{display:flex;justify-content:space-between;gap:6px;color:var(--dsw-alias-label-tertiary);font-size:10px;line-height:1.2;font-variant-numeric:tabular-nums;min-width:0}
|
|
3449
|
+
.dsh-tokenlens-speed-axis>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
3443
3450
|
@media (max-width:680px){.dsh-tokenlens-chart-layout-row>*{flex-basis:100%!important}}
|
|
3444
3451
|
.dsh-tokenlens-details-charts{min-width:0}
|
|
3445
3452
|
/* Data-mode 明细: three FULL-WIDTH blocks stacked vertically (v0.30.0 layout). */
|
|
@@ -3923,29 +3930,28 @@ window.__ModuleLoader__.load({
|
|
|
3923
3930
|
const snapshot = normalizeSnapshot(state.snapshot)
|
|
3924
3931
|
const brand = providerBrand(state, snapshot)
|
|
3925
3932
|
const tooltip = chipTooltip(snapshot, state.providerId, t)
|
|
3933
|
+
// v1.7.1: a NATIVE title, not the platform Tooltip. On an unmaximized
|
|
3934
|
+
// window the Tooltip's bubble rendered at the chip's right edge, overflowed
|
|
3935
|
+
// the dock and produced a horizontal scrollbar on the conversation area;
|
|
3936
|
+
// that scrollbar shifted the layout, which re-triggered the hover and
|
|
3937
|
+
// flickered the whole area. A native title draws nothing in the DOM, so it
|
|
3938
|
+
// cannot affect layout at all. The text is unchanged.
|
|
3926
3939
|
return h(
|
|
3927
3940
|
'span',
|
|
3928
3941
|
{ className: 'dsh-tokenlens-anchor', 'data-composer-stat': 'tokenlens' },
|
|
3929
3942
|
h(
|
|
3930
|
-
|
|
3931
|
-
{
|
|
3932
|
-
|
|
3933
|
-
'
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
'aria-haspopup': 'dialog',
|
|
3943
|
-
onClick: () => openDialog(sessionId),
|
|
3944
|
-
},
|
|
3945
|
-
h('span', { className: 'dsh-tokenlens-dot', 'data-level': chipLevel(snapshot, state), 'aria-hidden': true }),
|
|
3946
|
-
h('span', { className: 'dsh-tokenlens-brand' }, brand),
|
|
3947
|
-
chipPieces(snapshot, state.providerId, t),
|
|
3948
|
-
),
|
|
3943
|
+
'button',
|
|
3944
|
+
{
|
|
3945
|
+
type: 'button',
|
|
3946
|
+
className: 'dsh-tokenlens-chip',
|
|
3947
|
+
title: tooltip,
|
|
3948
|
+
'aria-label': t('chipHint'),
|
|
3949
|
+
'aria-haspopup': 'dialog',
|
|
3950
|
+
onClick: () => openDialog(sessionId),
|
|
3951
|
+
},
|
|
3952
|
+
h('span', { className: 'dsh-tokenlens-dot', 'data-level': chipLevel(snapshot, state), 'aria-hidden': true }),
|
|
3953
|
+
h('span', { className: 'dsh-tokenlens-brand' }, brand),
|
|
3954
|
+
chipPieces(snapshot, state.providerId, t),
|
|
3949
3955
|
),
|
|
3950
3956
|
)
|
|
3951
3957
|
}
|
|
@@ -3965,25 +3971,23 @@ window.__ModuleLoader__.load({
|
|
|
3965
3971
|
'span',
|
|
3966
3972
|
{ className: 'dsh-tokenlens-anchor', 'data-composer-stat': 'tokenlens' },
|
|
3967
3973
|
h(
|
|
3968
|
-
|
|
3969
|
-
{
|
|
3970
|
-
|
|
3971
|
-
'
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
h('span', { className: 'dsh-tokenlens-metric' }, label),
|
|
3986
|
-
),
|
|
3974
|
+
'button',
|
|
3975
|
+
{
|
|
3976
|
+
type: 'button',
|
|
3977
|
+
className: 'dsh-tokenlens-chip',
|
|
3978
|
+
// v1.7.1: native title, for the same reason as the normal chip — a
|
|
3979
|
+
// Tooltip bubble here could overflow a narrow dock and add a
|
|
3980
|
+
// horizontal scrollbar that made the conversation area flicker.
|
|
3981
|
+
title: label,
|
|
3982
|
+
'aria-label': label,
|
|
3983
|
+
'aria-haspopup': 'dialog',
|
|
3984
|
+
...(needsConfig ? { 'data-needs-config': 'true' } : {}),
|
|
3985
|
+
...(statsOnly ? { 'data-stats-only': 'true' } : {}),
|
|
3986
|
+
onClick: () => openDialog(sessionId),
|
|
3987
|
+
},
|
|
3988
|
+
h('span', { className: 'dsh-tokenlens-dot', 'data-level': statsOnly ? 'normal' : 'warning', 'aria-hidden': true }),
|
|
3989
|
+
h('span', { className: 'dsh-tokenlens-brand' }, brand),
|
|
3990
|
+
h('span', { className: 'dsh-tokenlens-metric' }, label),
|
|
3987
3991
|
),
|
|
3988
3992
|
)
|
|
3989
3993
|
}
|
|
@@ -6088,36 +6092,89 @@ window.__ModuleLoader__.load({
|
|
|
6088
6092
|
h('span', { key: 'hint', className: 'dsh-tokenlens-hint' }, t('statsSpeedEmpty')),
|
|
6089
6093
|
)
|
|
6090
6094
|
: (() => {
|
|
6095
|
+
// v1.7.1: a CURVE, not bars — the trend is a rate, and a line reads
|
|
6096
|
+
// it far better than per-day columns. The SVG scales to the card
|
|
6097
|
+
// width; `preserveAspectRatio="none"` lets it fill without keeping
|
|
6098
|
+
// the viewBox ratio, and the per-day hover targets are transparent
|
|
6099
|
+
// columns so nothing is distorted.
|
|
6100
|
+
const width = 320
|
|
6101
|
+
const height = 90
|
|
6102
|
+
const pad = 4
|
|
6091
6103
|
const max = Math.max(...byDay.map((entry) => entry.tps ?? 0), 1)
|
|
6092
|
-
const
|
|
6093
|
-
|
|
6094
|
-
const
|
|
6095
|
-
|
|
6096
|
-
|
|
6104
|
+
const stepX = byDay.length > 1 ? (width - pad * 2) / (byDay.length - 1) : 0
|
|
6105
|
+
const points = byDay.map((entry, index) => {
|
|
6106
|
+
const x = byDay.length > 1 ? pad + stepX * index : width / 2
|
|
6107
|
+
const y = height - pad - ((entry.tps ?? 0) / max) * (height - pad * 2)
|
|
6108
|
+
return { x, y }
|
|
6109
|
+
})
|
|
6110
|
+
const line = points.map((point) => `${point.x.toFixed(1)},${point.y.toFixed(1)}`).join(' ')
|
|
6111
|
+
const area = `${pad.toFixed(1)},${(height - pad).toFixed(1)} ${line} ${(width - pad).toFixed(1)},${(height - pad).toFixed(1)}`
|
|
6112
|
+
const columnWidth = byDay.length > 1 ? stepX : width - pad * 2
|
|
6113
|
+
const columns = byDay.map((entry, index) =>
|
|
6114
|
+
h(
|
|
6115
|
+
'rect',
|
|
6097
6116
|
{
|
|
6098
|
-
key: `
|
|
6099
|
-
|
|
6117
|
+
key: `hit-${entry.day}-${index}`,
|
|
6118
|
+
x: (points[index].x - columnWidth / 2).toFixed(1),
|
|
6119
|
+
y: 0,
|
|
6120
|
+
width: Math.max(1, columnWidth).toFixed(1),
|
|
6121
|
+
height: String(height),
|
|
6122
|
+
fill: 'transparent',
|
|
6100
6123
|
'data-speed-day': entry.day,
|
|
6101
|
-
'data-speed-tps': String(tps),
|
|
6102
|
-
|
|
6124
|
+
'data-speed-tps': String(entry.tps ?? 0),
|
|
6125
|
+
},
|
|
6126
|
+
h(
|
|
6127
|
+
'title',
|
|
6128
|
+
{ key: 'tip' },
|
|
6129
|
+
t('statsSpeedBarTitle', {
|
|
6103
6130
|
date: entry.day,
|
|
6104
|
-
tps: tps.toFixed(1),
|
|
6131
|
+
tps: (entry.tps ?? 0).toFixed(1),
|
|
6105
6132
|
tokens: formatTokensPrecise(entry.outputTokens),
|
|
6106
6133
|
seconds: entry.seconds.toFixed(1),
|
|
6107
6134
|
}),
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6135
|
+
),
|
|
6136
|
+
),
|
|
6137
|
+
)
|
|
6138
|
+
const svg = h(
|
|
6139
|
+
'svg',
|
|
6140
|
+
{
|
|
6141
|
+
key: 'svg',
|
|
6142
|
+
className: 'dsh-tokenlens-speed-svg',
|
|
6143
|
+
'data-speed-plot': 'true',
|
|
6144
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
6145
|
+
preserveAspectRatio: 'none',
|
|
6146
|
+
role: 'img',
|
|
6147
|
+
'aria-label': t('statsSpeedTitle'),
|
|
6148
|
+
},
|
|
6149
|
+
h('polygon', {
|
|
6150
|
+
key: 'area',
|
|
6151
|
+
className: 'dsh-tokenlens-speed-area',
|
|
6152
|
+
points: area,
|
|
6153
|
+
fill: 'rgba(46,158,91,.18)',
|
|
6154
|
+
'aria-hidden': 'true',
|
|
6155
|
+
}),
|
|
6156
|
+
h('polyline', {
|
|
6157
|
+
key: 'line',
|
|
6158
|
+
className: 'dsh-tokenlens-speed-line',
|
|
6159
|
+
points: line,
|
|
6160
|
+
fill: 'none',
|
|
6161
|
+
stroke: '#2e9e5b',
|
|
6162
|
+
'stroke-width': '1.6',
|
|
6163
|
+
'stroke-linecap': 'round',
|
|
6164
|
+
'stroke-linejoin': 'round',
|
|
6165
|
+
'vector-effect': 'non-scaling-stroke',
|
|
6166
|
+
'aria-hidden': 'true',
|
|
6167
|
+
}),
|
|
6168
|
+
columns,
|
|
6169
|
+
)
|
|
6170
|
+
const axis = h(
|
|
6117
6171
|
'div',
|
|
6118
|
-
{ key: '
|
|
6119
|
-
h('
|
|
6172
|
+
{ key: 'axis', className: 'dsh-tokenlens-speed-axis', 'aria-hidden': 'true' },
|
|
6173
|
+
h('span', { key: 'first' }, byDay[0].day),
|
|
6174
|
+
byDay.length > 1 ? h('span', { key: 'peak' }, t('statsSpeedPeak', { tps: max.toFixed(1) })) : null,
|
|
6175
|
+
byDay.length > 1 ? h('span', { key: 'last' }, byDay[byDay.length - 1].day) : null,
|
|
6120
6176
|
)
|
|
6177
|
+
return h('div', { key: 'plot', className: 'dsh-tokenlens-speed-plot' }, svg, axis)
|
|
6121
6178
|
})()
|
|
6122
6179
|
return h(
|
|
6123
6180
|
'div',
|
package/index.js
CHANGED
|
@@ -84,7 +84,7 @@ export const inject = []
|
|
|
84
84
|
export const PLUGIN_NAME = 'dsh-tokenlens'
|
|
85
85
|
|
|
86
86
|
/** Plugin version; mirrors package.json (kept here so diagnostics need no fs read). */
|
|
87
|
-
export const PLUGIN_VERSION = '1.7.
|
|
87
|
+
export const PLUGIN_VERSION = '1.7.1'
|
|
88
88
|
|
|
89
89
|
/** Documented defaults; every field of the raw row config is optional. */
|
|
90
90
|
const DEFAULTS = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-tokenlens",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "dsh-TokenLens (词元透镜) — provider-aware plan capacity plus local token statistics (today/7d/30d/all-time, by harness/provider/model/workspace) for DSH.",
|
|
6
6
|
"license": "MIT",
|