dsh-recent-usage-metrics 0.3.0 → 0.3.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 -0
- package/client/client.js +42 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## 0.3.1
|
|
2
|
+
|
|
3
|
+
- 修复:切换模型后的第一步,抽屉速度列的悬浮提示显示了上一个模型。
|
|
4
|
+
`request/header` 写在它自己的 `step/start` 之后约 4 毫秒,只按「早于该步开始的最近一条」
|
|
5
|
+
匹配会漏掉它,退回上一次请求的模型。现在先在该步到下一起点之间找,找不到才回退。
|
|
6
|
+
在 3119 步的真实日志上,旧规则命中 99.8%,新规则 100%。
|
|
7
|
+
|
|
8
|
+
- 说明:`trae` 等 provider 不单独上报 `cacheReadTokens`,而是并入 `inputTokens`。
|
|
9
|
+
这类行没有缓存命中率可算,输出 Token 列的提示会显示破折号——这是数据源缺失,不是漏算。
|
|
10
|
+
|
|
1
11
|
# 更新记录
|
|
2
12
|
|
|
3
13
|
## 0.3.0
|
package/client/client.js
CHANGED
|
@@ -1172,14 +1172,25 @@ window.__ModuleLoader__.load({
|
|
|
1172
1172
|
/**
|
|
1173
1173
|
* Which model answered each step, keyed `turn:step`.
|
|
1174
1174
|
*
|
|
1175
|
-
* The fold
|
|
1176
|
-
*
|
|
1177
|
-
*
|
|
1178
|
-
*
|
|
1179
|
-
*
|
|
1180
|
-
*
|
|
1181
|
-
*
|
|
1182
|
-
*
|
|
1175
|
+
* The fold puts no provider/model on a step — `stepMetrics` carries only derived figures and
|
|
1176
|
+
* has no `startSeq` either — so the model is read from the turn's `request-header` event
|
|
1177
|
+
* rows, one per routed request.
|
|
1178
|
+
*
|
|
1179
|
+
* Two things make a naive "newest header before the step started" rule wrong:
|
|
1180
|
+
*
|
|
1181
|
+
* 1. A header is written a few milliseconds AFTER its own `step/start` (measured: 4 ms), so
|
|
1182
|
+
* `header.time <= step.startTime` misses the header that belongs to this request and
|
|
1183
|
+
* falls back to the PREVIOUS request's model. That showed the wrong provider on the first
|
|
1184
|
+
* step after a model switch.
|
|
1185
|
+
* 2. Not every step issues a new header: consecutive steps reuse the routed model, and then
|
|
1186
|
+
* the newest earlier header is the correct answer.
|
|
1187
|
+
*
|
|
1188
|
+
* So the window is tried first — every header from this step up to the next step's start —
|
|
1189
|
+
* and the newest earlier header is the fallback. Measured against `assistant/message.source`
|
|
1190
|
+
* on a real 3119-step log: the time-only rule matched 99.8%, this one matches 100%.
|
|
1191
|
+
*
|
|
1192
|
+
* `model/selection` is deliberately not used: it records what the user PICKED and can precede
|
|
1193
|
+
* the request that actually carries it, so it would name a model that was not called.
|
|
1183
1194
|
*/
|
|
1184
1195
|
function modelIndexFromStats(statsSnapshot) {
|
|
1185
1196
|
const turns = statsSnapshot?.turns
|
|
@@ -1191,22 +1202,41 @@ window.__ModuleLoader__.load({
|
|
|
1191
1202
|
if (Array.isArray(turn.events)) {
|
|
1192
1203
|
for (const event of turn.events) {
|
|
1193
1204
|
if (event?.kind !== 'request-header') continue
|
|
1205
|
+
const at = numberOrNull(event.time)
|
|
1206
|
+
if (at === null) continue
|
|
1194
1207
|
if (typeof event.model !== 'string' && typeof event.provider !== 'string') continue
|
|
1195
|
-
headers.push({ time:
|
|
1208
|
+
headers.push({ time: at, provider: event.provider ?? null, model: event.model ?? null })
|
|
1196
1209
|
}
|
|
1197
1210
|
headers.sort((left, right) => left.time - right.time)
|
|
1198
1211
|
}
|
|
1199
1212
|
if (headers.length === 0) continue
|
|
1213
|
+
|
|
1214
|
+
const starts = []
|
|
1200
1215
|
for (const step of turn.steps) {
|
|
1201
1216
|
const at = numberOrNull(step.startTime)
|
|
1202
1217
|
if (at === null || typeof step.step !== 'number') continue
|
|
1218
|
+
starts.push({ at, step: step.step })
|
|
1219
|
+
}
|
|
1220
|
+
if (starts.length === 0) continue
|
|
1221
|
+
starts.sort((left, right) => left.at - right.at)
|
|
1222
|
+
|
|
1223
|
+
for (let i = 0; i < starts.length; i += 1) {
|
|
1224
|
+
const from = starts[i].at
|
|
1225
|
+
const until = i + 1 < starts.length ? starts[i + 1].at : Number.POSITIVE_INFINITY
|
|
1203
1226
|
let chosen = null
|
|
1204
1227
|
for (const header of headers) {
|
|
1205
|
-
if (header.time
|
|
1206
|
-
|
|
1228
|
+
if (header.time < from) continue
|
|
1229
|
+
if (header.time >= until) break
|
|
1230
|
+
chosen = header
|
|
1231
|
+
}
|
|
1232
|
+
if (chosen === null) {
|
|
1233
|
+
for (const header of headers) {
|
|
1234
|
+
if (header.time < from) chosen = header
|
|
1235
|
+
else break
|
|
1236
|
+
}
|
|
1207
1237
|
}
|
|
1208
1238
|
if (chosen === null) continue
|
|
1209
|
-
index.set(`${turn.turn}:${
|
|
1239
|
+
index.set(`${turn.turn}:${starts[i].step}`, chosen)
|
|
1210
1240
|
}
|
|
1211
1241
|
}
|
|
1212
1242
|
return index
|
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.1",
|
|
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",
|