dsh-recent-usage-metrics 0.2.2 → 0.2.3

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # 更新记录
2
2
 
3
+ ## 0.2.3
4
+
5
+ - 中断的行现在显示它跑了多久:被手动停止的请求没有 model,总用时回退到 wall
6
+ (endTime - startTime),刷新后仍是同一个数。
7
+ - 「中断」标记移到速度列。首字若已返回就照常显示实测值,总用时也照常显示——
8
+ 缺的只是生成速度,由速度列说明原因。
9
+ - 中断行不计入底部平均值,但标题仍数满十行。
10
+
3
11
  ## 0.2.2
4
12
 
5
13
  - 修复:切换模型后抽屉卡在旧的一轮。数据源的 target 曾被缓存,而会话重建
package/README.en.md CHANGED
@@ -412,6 +412,19 @@ carries `ttft`, `model`, `decode`, `tps`, `output` and `wall`.
412
412
  | **Time** | **`model`** | **model time, not the Statistics view's total** |
413
413
  | Amortised | `output ÷ model` | this plugin's own figure |
414
414
 
415
+ **Interrupted rows:**
416
+
417
+ A request that was stopped never produced an `assistant/message`, so `model`, `decode` and
418
+ `tps` are all null. That row's **total falls back to `wall`** (`endTime − startTime`) — how
419
+ long the request actually ran before it was stopped. It is a log fact, so it survives a reload
420
+ unchanged, which is exactly what that column is for here.
421
+
422
+ The word **"interrupted" sits in the speed column**, not the first-token column: a request that
423
+ already streamed keeps its measured first-token time and still shows its total; the only figure
424
+ missing is the generation rate, so that is the column that says why. Interrupted rows are
425
+ excluded from the footer average — their total is wall-clock time, and folding it into the mean
426
+ would pull it toward a number describing neither case — but the caption still counts all ten.
427
+
415
428
  **Why the total is `model` and not `wall`:**
416
429
 
417
430
  The Statistics view's total column is `wall = endTime − startTime`, which **includes the step's
package/README.md CHANGED
@@ -370,6 +370,16 @@ const target = uiConversation.binding(sessionId).target('stats')
370
370
  | **总用时** | **`model`** | **模型用时,不是统计页的「总用时」列** |
371
371
  | 折算速度 | `output ÷ model` | 本插件独有 |
372
372
 
373
+ **中断的行:**
374
+
375
+ 被手动停止的请求没有 `assistant/message`,因此 `model`、`decode`、`tps` 全为 null。
376
+ 那一行的**总用时回退到 `wall`**(`endTime − startTime`),也就是这条请求从发出到被
377
+ 停止实际过了多久——它是日志里的事实,刷新多少次都不变,正是这个场合要的数字。
378
+
379
+ **「中断」写在速度列**,不在首字列:首字若已经返回就照样显示实测值,总用时也照常显示;
380
+ 缺的只是生成速度,所以由速度列说明原因。中断行不计入底部的平均值——它的总用时是墙钟
381
+ 时间,混进均值会拉向一个既不代表完成、也不代表中断的数;但平均行的标题仍数满十行。
382
+
373
383
  **总用时为何取 `model` 而不是 `wall`:**
374
384
 
375
385
  统计页的「总用时」列是 `wall = endTime − startTime`,它**把工具调用也算了进去**。
package/client/client.js CHANGED
@@ -880,6 +880,12 @@ window.__ModuleLoader__.load({
880
880
  function readingOfStatsStep(entry) {
881
881
  const stats = entry.metrics
882
882
  const ttftMs = numberOrNull(stats.ttft)
883
+ /*
884
+ * A step the fold marks aborted ended without a settled reply: the user stopped it, or the
885
+ * upstream failed mid-request. Either way there is no `messageTime`, so `model`, `decode`
886
+ * and `tps` are all null and the row would be entirely blank.
887
+ */
888
+ const interrupted = stats.aborted === true || stats.interrupted === true
883
889
  /*
884
890
  * The drawer's "total" is the Statistics view's MODEL time, not its wall time.
885
891
  *
@@ -888,17 +894,15 @@ window.__ModuleLoader__.load({
888
894
  * The drawer answers "how long did this request take to answer", so it reports `model` —
889
895
  * and that figure is final the moment the reply lands, which is why it must not climb
890
896
  * while tools run.
897
+ *
898
+ * An interrupted step has no `model` at all, so it falls back to `wall`. That is the
899
+ * figure the user actually wants there: how long the request ran before it was stopped.
900
+ * It is a log fact — `endTime - startTime` — so it survives a reload unchanged, unlike
901
+ * the live reading, which is recomputed and lost.
891
902
  */
892
- const totalMs = numberOrNull(stats.model)
903
+ const totalMs = numberOrNull(stats.model) ?? (interrupted ? numberOrNull(stats.wall) : null)
893
904
  const tokens = numberOrNull(stats.output)
894
905
  const speed = numberOrNull(stats.tps)
895
- /*
896
- * A step the fold marks interrupted or aborted never produced a settled reply: a
897
- * cancelled request has no first token to subtract from, so its ttft is null and the
898
- * drawer used to print the same dash a not-yet-started step would. The flag lets the
899
- * cell say what actually happened instead.
900
- */
901
- const interrupted = stats.interrupted === true || stats.aborted === true
902
906
  return {
903
907
  key: `${entry.turn}:${entry.step}`,
904
908
  at: numberOrNull(stats.startTime) ?? numberOrNull(stats.endTime),
@@ -2020,13 +2024,19 @@ window.__ModuleLoader__.load({
2020
2024
  * Average of the rows on screen, computed over the ones that actually carry each
2021
2025
  * figure (an interrupted request may have no TTFT, a placeholder no tokens). Returns
2022
2026
  * null for a column with no samples so the row can print a dash instead of a fake 0.
2027
+ *
2028
+ * Interrupted rows are excluded from every mean. Their total is wall-clock time — it
2029
+ * includes however long the request ran before being stopped — so averaging it against
2030
+ * model times would pull the figures toward a number that describes neither case. The
2031
+ * row still counts toward `count`, because the caption names the list, not the sample.
2023
2032
  */
2024
2033
  function averageOf(rows) {
2025
2034
  if (rows.length === 0) return null
2035
+ const measurable = rows.filter((row) => row.interrupted !== true)
2026
2036
  const mean = (pick) => {
2027
2037
  let sum = 0
2028
2038
  let count = 0
2029
- for (const row of rows) {
2039
+ for (const row of measurable) {
2030
2040
  const value = pick(row)
2031
2041
  if (value === null || value === undefined || !Number.isFinite(value)) continue
2032
2042
  sum += value
@@ -2036,6 +2046,7 @@ window.__ModuleLoader__.load({
2036
2046
  }
2037
2047
  return {
2038
2048
  count: rows.length,
2049
+ measured: measurable.length,
2039
2050
  ttftMs: mean((row) => row.ttftMs),
2040
2051
  speed: mean((row) => row.speed),
2041
2052
  tokens: mean((row) => row.tokens),
@@ -2043,8 +2054,8 @@ window.__ModuleLoader__.load({
2043
2054
  amortized: mean((row) => row.amortized),
2044
2055
  // A column is "estimated" when any sample in it was; the tilde then still means
2045
2056
  // the same thing it does on a single row.
2046
- estimated: rows.some((row) => row.estimated === true),
2047
- anyEstimated: rows.some((row) => row.estimated === true),
2057
+ estimated: measurable.some((row) => row.estimated === true),
2058
+ anyEstimated: measurable.some((row) => row.estimated === true),
2048
2059
  }
2049
2060
  }
2050
2061
 
@@ -2812,18 +2823,24 @@ window.__ModuleLoader__.load({
2812
2823
  'span',
2813
2824
  { className: 'dsh-rum-col', role: 'cell' },
2814
2825
  /*
2815
- * A cancelled step has no first token to report, so the column would
2816
- * otherwise show the same dash as a step that never started. Say what
2817
- * happened instead; it is the one figure the row can honestly give.
2826
+ * The first-token column reports whatever was actually measured. An
2827
+ * interrupted request that had already produced its first token shows
2828
+ * that real figure; one stopped before any token shows the dash.
2818
2829
  */
2819
- entry.row.interrupted === true
2820
- ? t('interrupted')
2821
- : secondsText(entry.row.ttftMs, t),
2830
+ secondsText(entry.row.ttftMs, t),
2822
2831
  ),
2823
2832
  h(
2824
2833
  'span',
2825
2834
  { className: 'dsh-rum-col dsh-rum-primary', role: 'cell' },
2826
- speedText(entry.row.speed, false, t),
2835
+ /*
2836
+ * A cancelled step never settled, so it has no generation speed. The
2837
+ * column says what happened instead of the same dash a step that has
2838
+ * not started would show — the speed column is where "this produced no
2839
+ * rate" belongs, while first-token and total still carry real figures.
2840
+ */
2841
+ entry.row.interrupted === true
2842
+ ? t('interrupted')
2843
+ : speedText(entry.row.speed, false, t),
2827
2844
  ),
2828
2845
  h(
2829
2846
  'span',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-recent-usage-metrics",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
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",