dsh-desktop-statusbar 2.0.0 → 2.0.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/README.en.md +3 -3
- package/README.md +3 -3
- package/docs/settings.png +0 -0
- package/lib/client.js +81 -9
- package/lib/index.js +29 -1
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -27,9 +27,9 @@ Replaces the stats line under the message input in the DSH desktop app with a co
|
|
|
27
27
|
| Field | Content |
|
|
28
28
|
| --- | --- |
|
|
29
29
|
| Session status | Context occupancy: system prompt / tool definitions / messages |
|
|
30
|
-
| Combined hit |
|
|
31
|
-
| TTFT |
|
|
32
|
-
| TPS |
|
|
30
|
+
| Combined hit | This turn / highest / lowest cache hit rate |
|
|
31
|
+
| TTFT | This turn / fastest / slowest first-token delay |
|
|
32
|
+
| TPS | This turn / fastest / slowest output speed |
|
|
33
33
|
| Run time | Model time / tool calls |
|
|
34
34
|
| This turn cost | Token breakdown for this turn |
|
|
35
35
|
| Session cost | Cumulative token breakdown for the whole session |
|
package/README.md
CHANGED
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
| 字段 | 内容 |
|
|
28
28
|
| --- | --- |
|
|
29
29
|
| 会话状态 | 上下文占用:系统提示词 / 工具定义 / 对话消息 |
|
|
30
|
-
| 综合命中 | 最高 / 最低缓存命中率 |
|
|
31
|
-
| 首字平均 | 最快 / 最慢首字延迟 |
|
|
32
|
-
| 输出速度 | 最快 / 最慢输出速度 |
|
|
30
|
+
| 综合命中 | 本轮 / 最高 / 最低缓存命中率 |
|
|
31
|
+
| 首字平均 | 本轮 / 最快 / 最慢首字延迟 |
|
|
32
|
+
| 输出速度 | 本轮 / 最快 / 最慢输出速度 |
|
|
33
33
|
| 运行用时 | 模型用时 / 工具调用 |
|
|
34
34
|
| 本轮费用 | 本轮会话的 token 明细 |
|
|
35
35
|
| 总计费用 | 整个会话累计的 token 明细 |
|
package/docs/settings.png
CHANGED
|
Binary file
|
package/lib/client.js
CHANGED
|
@@ -40,6 +40,8 @@ window.__ModuleLoader__.load({
|
|
|
40
40
|
* v6 起:`segments` 存全部段的有序列表(顺序对整份列表生效),`hidden` 存未勾选的段。
|
|
41
41
|
*/
|
|
42
42
|
const CONFIG_VERSION = 6;
|
|
43
|
+
/** 插件版本:显示在设置页底部,用来确认页面加载的是哪一版客户端代码。 */
|
|
44
|
+
const VERSION = "2.0.1";
|
|
43
45
|
/** 官方参考价的版本:官方调价时改这个数字,老价格库会自动并入新参考价。 */
|
|
44
46
|
const PRICE_VERSION = 2;
|
|
45
47
|
|
|
@@ -447,12 +449,15 @@ window.__ModuleLoader__.load({
|
|
|
447
449
|
f_tokensTurn: "本轮用量",
|
|
448
450
|
tipModelTime: "模型用时",
|
|
449
451
|
tipToolTime: "工具调用",
|
|
452
|
+
tipTtftTurn: "本轮平均首字",
|
|
450
453
|
tipTtftFast: "最快首字延迟",
|
|
451
454
|
tipTtftSlow: "最慢首字延迟",
|
|
455
|
+
tipTpsTurn: "本轮平均速度",
|
|
452
456
|
tipTpsFast: "最快输出速度",
|
|
453
457
|
tipTpsSlow: "最慢输出速度",
|
|
454
458
|
tipTopUp: "充值余额",
|
|
455
459
|
tipGranted: "赠金余额",
|
|
460
|
+
tipHitTurn: "本轮缓存命中率",
|
|
456
461
|
tipHitHigh: "最高缓存命中率",
|
|
457
462
|
tipHitLow: "最低缓存命中率",
|
|
458
463
|
contextAria: "上下文已用 {percent}",
|
|
@@ -533,12 +538,15 @@ window.__ModuleLoader__.load({
|
|
|
533
538
|
f_tokensTurn: "This turn usage",
|
|
534
539
|
tipModelTime: "Model time",
|
|
535
540
|
tipToolTime: "Tool calls",
|
|
541
|
+
tipTtftTurn: "Average TTFT this turn",
|
|
536
542
|
tipTtftFast: "Fastest TTFT",
|
|
537
543
|
tipTtftSlow: "Slowest TTFT",
|
|
544
|
+
tipTpsTurn: "Average TPS this turn",
|
|
538
545
|
tipTpsFast: "Fastest TPS",
|
|
539
546
|
tipTpsSlow: "Slowest TPS",
|
|
540
547
|
tipTopUp: "Top-up balance",
|
|
541
548
|
tipGranted: "Granted balance",
|
|
549
|
+
tipHitTurn: "Cache hit rate this turn",
|
|
542
550
|
tipHitHigh: "Highest cache hit rate",
|
|
543
551
|
tipHitLow: "Lowest cache hit rate",
|
|
544
552
|
contextAria: "{percent} of context used",
|
|
@@ -999,12 +1007,52 @@ window.__ModuleLoader__.load({
|
|
|
999
1007
|
return panel === null ? null : { kind: "tokens", title: title, panel: panel };
|
|
1000
1008
|
}
|
|
1001
1009
|
|
|
1002
|
-
/** 毫秒 →
|
|
1010
|
+
/** 毫秒 → 面板里的时长文本(精确到秒);0 或缺数据给横杠。 */
|
|
1003
1011
|
function tipDuration(ms) {
|
|
1004
|
-
const text =
|
|
1012
|
+
const text = formatDurationFine(ms);
|
|
1005
1013
|
return text === null ? "-" : text;
|
|
1006
1014
|
}
|
|
1007
1015
|
|
|
1016
|
+
/** 时长精确到秒:1h23m45s / 23m45s / 45s。底栏那条用 formatDuration(省掉秒,宽度有限)。 */
|
|
1017
|
+
function formatDurationFine(ms) {
|
|
1018
|
+
const n = Number(ms) || 0;
|
|
1019
|
+
if (n <= 0) return null;
|
|
1020
|
+
const whole = Math.round(n / 1000);
|
|
1021
|
+
if (whole < 60) return whole + "s";
|
|
1022
|
+
const minutes = Math.floor(whole / 60);
|
|
1023
|
+
if (minutes < 60) return minutes + "m" + (whole % 60) + "s";
|
|
1024
|
+
return Math.floor(minutes / 60) + "h" + (minutes % 60) + "m" + (whole % 60) + "s";
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
/** 本轮缓存命中率:这一轮的输入 token 里命中缓存的比例;本轮还没有调用给横杠。
|
|
1028
|
+
读插件自己的 desktopStatusbarUsage 投影 —— 官方 tokenUsage 只有会话累计值。
|
|
1029
|
+
注意字段是短名(input / cacheRead / cacheWrite),与官方投影的 *Tokens 不同名。 */
|
|
1030
|
+
function turnHitRate(sessionUsage) {
|
|
1031
|
+
const current = sessionUsage === undefined || sessionUsage === null ? null : sessionUsage.current;
|
|
1032
|
+
if (current === null || current === undefined) return "-";
|
|
1033
|
+
const denominator = (current.input || 0) + (current.cacheRead || 0) + (current.cacheWrite || 0);
|
|
1034
|
+
if (denominator <= 0) return "-";
|
|
1035
|
+
return hitRateText(((current.cacheRead || 0) / denominator) * 100);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/** 本轮平均首字;本轮还没有样本给横杠。 */
|
|
1039
|
+
function turnAverageTtft(timing) {
|
|
1040
|
+
if (timing === null || timing === undefined) return "-";
|
|
1041
|
+
const samples = timing.turnTtftSamples || 0;
|
|
1042
|
+
if (samples <= 0) return "-";
|
|
1043
|
+
const text = formatSeconds(timing.turnTtftMs / samples);
|
|
1044
|
+
return text === null ? "-" : text + "s";
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/** 本轮平均输出速度;本轮还没有可测样本给横杠。 */
|
|
1048
|
+
function turnAverageTps(timing, t) {
|
|
1049
|
+
if (timing === null || timing === undefined) return "-";
|
|
1050
|
+
const decodeMs = timing.turnDecodeMs || 0;
|
|
1051
|
+
const tokens = timing.turnDecodeTokens || 0;
|
|
1052
|
+
if (decodeMs <= 0 || tokens <= 0) return "-";
|
|
1053
|
+
return formatThroughput(tokens / (decodeMs / 1000)) + t("unitSpeed");
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1008
1056
|
/** 首字极值(毫秒)→ "9.3s";没有样本给横杠。 */
|
|
1009
1057
|
function tipSeconds(mark) {
|
|
1010
1058
|
if (mark === null || mark === undefined) return "-";
|
|
@@ -1126,6 +1174,7 @@ window.__ModuleLoader__.load({
|
|
|
1126
1174
|
kind: "rows",
|
|
1127
1175
|
title: t("segTtft"),
|
|
1128
1176
|
rows: [
|
|
1177
|
+
{ label: t("tipTtftTurn"), value: turnAverageTtft(timing) },
|
|
1129
1178
|
{ label: t("tipTtftFast"), value: tipSeconds(timing === null ? null : timing.fastestTtft) },
|
|
1130
1179
|
{ label: t("tipTtftSlow"), value: tipSeconds(timing === null ? null : timing.slowestTtft) }
|
|
1131
1180
|
]
|
|
@@ -1147,6 +1196,7 @@ window.__ModuleLoader__.load({
|
|
|
1147
1196
|
kind: "rows",
|
|
1148
1197
|
title: t("segCacheHit"),
|
|
1149
1198
|
rows: [
|
|
1199
|
+
{ label: t("tipHitTurn"), value: turnHitRate(src.sessionUsage) },
|
|
1150
1200
|
{ label: t("tipHitHigh"), value: extremes === null ? "-" : extremes.high },
|
|
1151
1201
|
{ label: t("tipHitLow"), value: extremes === null ? "-" : extremes.low }
|
|
1152
1202
|
]
|
|
@@ -1165,6 +1215,7 @@ window.__ModuleLoader__.load({
|
|
|
1165
1215
|
kind: "rows",
|
|
1166
1216
|
title: t("segTps"),
|
|
1167
1217
|
rows: [
|
|
1218
|
+
{ label: t("tipTpsTurn"), value: turnAverageTps(timing, t) },
|
|
1168
1219
|
{ label: t("tipTpsFast"), value: tipSpeed(timing === null ? null : timing.fastestTps, t) },
|
|
1169
1220
|
{ label: t("tipTpsSlow"), value: tipSpeed(timing === null ? null : timing.slowestTps, t) }
|
|
1170
1221
|
]
|
|
@@ -1475,6 +1526,8 @@ window.__ModuleLoader__.load({
|
|
|
1475
1526
|
".dsb-settings .dsb-action:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.1))}",
|
|
1476
1527
|
".dsb-settings .dsb-action-sm{height:24px;padding:0 8px;font-size:12px}",
|
|
1477
1528
|
".dsb-settings .dsb-foot{display:flex;align-items:center;gap:12px;flex-wrap:wrap}",
|
|
1529
|
+
/* 版本号贴右侧、与按钮底边对齐 */
|
|
1530
|
+
".dsb-settings .dsb-version{margin-left:auto;align-self:flex-end;font-size:11px;line-height:1;color:var(--dsw-alias-label-tertiary);opacity:.75}",
|
|
1478
1531
|
".dsb-settings .dsb-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap}"
|
|
1479
1532
|
].join("");
|
|
1480
1533
|
|
|
@@ -1878,21 +1931,38 @@ window.__ModuleLoader__.load({
|
|
|
1878
1931
|
const currencyRef = react.useRef(null);
|
|
1879
1932
|
const editPopRef = react.useRef(null);
|
|
1880
1933
|
|
|
1881
|
-
/*
|
|
1882
|
-
|
|
1934
|
+
/* 编辑浮层打开后按需滚动:block:"nearest" 只补上被遮住的那一段,
|
|
1935
|
+
本来就在视野里就一动不动(旧版用 "end" 会平白往上滚,明明下面有地方)。 */
|
|
1883
1936
|
react.useEffect(() => {
|
|
1884
1937
|
if (editing === null) return;
|
|
1885
1938
|
const node = editPopRef.current;
|
|
1886
1939
|
if (node === null || node === undefined || typeof node.scrollIntoView !== "function") return;
|
|
1887
|
-
node.scrollIntoView({ block: "
|
|
1940
|
+
node.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
1888
1941
|
}, [editing]);
|
|
1889
1942
|
|
|
1890
|
-
/*
|
|
1943
|
+
/* 编辑浮层:点别处收起并丢弃草稿。判断"里面"上三重保险 —— 浮层节点、价格卡节点,
|
|
1944
|
+
以及鼠标坐标是否落在卡片或币种下拉的矩形内。切币种时点在卡片内的控件上,
|
|
1945
|
+
无论 DOM 结构怎么变都不该把编辑区收掉。 */
|
|
1891
1946
|
react.useEffect(() => {
|
|
1892
1947
|
if (editing === null) return undefined;
|
|
1893
|
-
const
|
|
1948
|
+
const inside = (event) => {
|
|
1949
|
+
const target = event.target;
|
|
1950
|
+
if (target !== null && target !== undefined && typeof target.closest === "function"
|
|
1951
|
+
&& target.closest(".dsb-model") !== null) return true;
|
|
1894
1952
|
const node = editPopRef.current;
|
|
1895
|
-
if (node !== null && node !== undefined && typeof node.contains === "function" && node.contains(
|
|
1953
|
+
if (node !== null && node !== undefined && typeof node.contains === "function" && node.contains(target)) return true;
|
|
1954
|
+
const x = event.clientX;
|
|
1955
|
+
const y = event.clientY;
|
|
1956
|
+
if (typeof x !== "number" || typeof y !== "number") return false;
|
|
1957
|
+
return [node, currencyRef.current].some((one) => {
|
|
1958
|
+
if (one === null || one === undefined || typeof one.getBoundingClientRect !== "function") return false;
|
|
1959
|
+
const box = one.getBoundingClientRect();
|
|
1960
|
+
return box.width > 0 && box.height > 0
|
|
1961
|
+
&& x >= box.left && x <= box.right && y >= box.top && y <= box.bottom;
|
|
1962
|
+
});
|
|
1963
|
+
};
|
|
1964
|
+
const onDocClick = (event) => {
|
|
1965
|
+
if (inside(event)) return;
|
|
1896
1966
|
cancelEdit();
|
|
1897
1967
|
};
|
|
1898
1968
|
document.addEventListener("click", onDocClick);
|
|
@@ -2280,7 +2350,9 @@ window.__ModuleLoader__.load({
|
|
|
2280
2350
|
modelOrder: Object.keys(DEFAULT_PRICES)
|
|
2281
2351
|
})
|
|
2282
2352
|
}, t("reset")),
|
|
2283
|
-
h("div", { className: "dsb-actions", key: "__actions" }, holidayActions)
|
|
2353
|
+
h("div", { className: "dsb-actions", key: "__actions" }, holidayActions),
|
|
2354
|
+
/* 版本标记贴右、与行内底边对齐:改了客户端代码要刷新页面才生效,这行字让"跑的是哪版"一眼可见 */
|
|
2355
|
+
h("span", { className: "dsb-version", key: "__version" }, "dsh-desktop-statusbar " + VERSION)
|
|
2284
2356
|
]));
|
|
2285
2357
|
|
|
2286
2358
|
return h("div", { className: "dsb-settings" }, sections);
|
package/lib/index.js
CHANGED
|
@@ -340,6 +340,12 @@ const timingViewSchema = z.object({
|
|
|
340
340
|
slowestTtft: timingMark.nullable(),
|
|
341
341
|
fastestTps: timingMark.nullable(),
|
|
342
342
|
slowestTps: timingMark.nullable(),
|
|
343
|
+
/* 本轮累计:气泡里"本轮平均首字 / 本轮平均速度"要用(官方投影只有会话累计值) */
|
|
344
|
+
turn: z.number().nullable(),
|
|
345
|
+
turnTtftMs: z.number().nonnegative(),
|
|
346
|
+
turnTtftSamples: z.number().int().nonnegative(),
|
|
347
|
+
turnDecodeMs: z.number().nonnegative(),
|
|
348
|
+
turnDecodeTokens: z.number().nonnegative(),
|
|
343
349
|
}).strict()
|
|
344
350
|
|
|
345
351
|
const sessionTimingSchema = timingViewSchema.extend({ openStep: timingStep.nullable() }).strict()
|
|
@@ -417,12 +423,25 @@ const sessionTimingProjection = {
|
|
|
417
423
|
slowestTtft: null,
|
|
418
424
|
fastestTps: null,
|
|
419
425
|
slowestTps: null,
|
|
426
|
+
turn: null,
|
|
427
|
+
turnTtftMs: 0,
|
|
428
|
+
turnTtftSamples: 0,
|
|
429
|
+
turnDecodeMs: 0,
|
|
430
|
+
turnDecodeTokens: 0,
|
|
420
431
|
openStep: null,
|
|
421
432
|
}),
|
|
422
433
|
apply: (state, event) => {
|
|
423
434
|
if (event.type === 'step/start') {
|
|
435
|
+
/* 换轮就把本轮累计清零:气泡里的"本轮平均…"只算当前这一轮 */
|
|
436
|
+
const turn = event.data.turn
|
|
437
|
+
const sameTurn = state.turn === turn
|
|
424
438
|
return {
|
|
425
439
|
...state,
|
|
440
|
+
turn: turn,
|
|
441
|
+
turnTtftMs: sameTurn ? state.turnTtftMs : 0,
|
|
442
|
+
turnTtftSamples: sameTurn ? state.turnTtftSamples : 0,
|
|
443
|
+
turnDecodeMs: sameTurn ? state.turnDecodeMs : 0,
|
|
444
|
+
turnDecodeTokens: sameTurn ? state.turnDecodeTokens : 0,
|
|
426
445
|
openStep: {
|
|
427
446
|
turn: event.data.turn, step: event.data.step, startTime: event.time,
|
|
428
447
|
firstTokenTime: null, firstTokenTool: false,
|
|
@@ -451,6 +470,8 @@ const sessionTimingProjection = {
|
|
|
451
470
|
next.samples = state.samples + 1
|
|
452
471
|
next.fastestTtft = pickSmaller(state.fastestTtft, { value: ttft, at: event.time, model })
|
|
453
472
|
next.slowestTtft = pickLarger(state.slowestTtft, { value: ttft, at: event.time, model })
|
|
473
|
+
next.turnTtftMs = state.turnTtftMs + ttft
|
|
474
|
+
next.turnTtftSamples = state.turnTtftSamples + 1
|
|
454
475
|
const output = stepOutputTokens(event.data.usage)
|
|
455
476
|
const decode = Math.max(0, event.time - first.time)
|
|
456
477
|
/* 纯工具调用的步骤不进速度极值:分片时间戳挤在同一刻,算出来是假值(首字照旧统计) */
|
|
@@ -458,6 +479,8 @@ const sessionTimingProjection = {
|
|
|
458
479
|
const speed = { value: output / (decode / 1000), at: event.time, model }
|
|
459
480
|
next.fastestTps = pickLarger(state.fastestTps, speed)
|
|
460
481
|
next.slowestTps = pickSmaller(state.slowestTps, speed)
|
|
482
|
+
next.turnDecodeMs = state.turnDecodeMs + decode
|
|
483
|
+
next.turnDecodeTokens = state.turnDecodeTokens + output
|
|
461
484
|
}
|
|
462
485
|
return next
|
|
463
486
|
}
|
|
@@ -475,9 +498,14 @@ const sessionTimingProjection = {
|
|
|
475
498
|
slowestTtft: state.slowestTtft,
|
|
476
499
|
fastestTps: state.fastestTps,
|
|
477
500
|
slowestTps: state.slowestTps,
|
|
501
|
+
turn: state.turn,
|
|
502
|
+
turnTtftMs: state.turnTtftMs,
|
|
503
|
+
turnTtftSamples: state.turnTtftSamples,
|
|
504
|
+
turnDecodeMs: state.turnDecodeMs,
|
|
505
|
+
turnDecodeTokens: state.turnDecodeTokens,
|
|
478
506
|
}),
|
|
479
507
|
},
|
|
480
|
-
stateVersion:
|
|
508
|
+
stateVersion: 3,
|
|
481
509
|
}
|
|
482
510
|
|
|
483
511
|
/* ---------------------------------------------------------------- 余额查询 */
|