dsh-context 0.47.0 → 0.48.0
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.md +1 -1
- package/lib/client.js +209 -163
- package/lib/index.d.ts +39 -1
- package/lib/index.js +109 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
[`dsh-context`](https://www.npmjs.com/package/dsh-context) provides full context lifecycle management features.
|
|
12
12
|
- **Context tab** — an UI context dashboard for DeepSeek Harness's context stats, composition, trend, events, and messages.
|
|
13
|
-
- **Context panel** — the same dashboard as a right-sidebar tab (dsh 0.1.5+): pick **Context** on the sidebar's guide page and the panel opens beside the chat.
|
|
13
|
+
- **Context panel** — the same dashboard as a right-sidebar tab (dsh 0.1.5-alpha.2+): pick **Context** on the sidebar's guide page and the panel opens beside the chat.
|
|
14
14
|
- **`/context` command** — the slash command shows the context model for current context composition and recent context evolution.
|
|
15
15
|
|
|
16
16
|
## Install / Update
|
package/lib/client.js
CHANGED
|
@@ -10,7 +10,6 @@ window.__ModuleLoader__.load({
|
|
|
10
10
|
//#region src/client/i18n.ts
|
|
11
11
|
const DICT_ZH = {
|
|
12
12
|
"tab": "上下文",
|
|
13
|
-
"sidebar.guideDescription": "查看这个会话的上下文构成、每步变化与消息列表。",
|
|
14
13
|
"cat.system": "系统提示词",
|
|
15
14
|
"cat.tools": "工具定义",
|
|
16
15
|
"cat.user": "用户消息",
|
|
@@ -32,15 +31,18 @@ window.__ModuleLoader__.load({
|
|
|
32
31
|
"stats.toolCalls": "工具调用",
|
|
33
32
|
"stats.images": "图片",
|
|
34
33
|
"stats.cost": "预估费用",
|
|
35
|
-
"stats.costTip": "按 DeepSeek 官方刊例价估算整个会话的累计费用:输入区分缓存命中/未命中,输出含思考;按请求时间区分高峰(北京时间工作日 9:00–12:00、14:00–18:00)与空闲时段(半价,周末全天均为空闲时段)。适用于 deepseek-v4-flash / deepseek-v4-pro(不限 provider
|
|
34
|
+
"stats.costTip": "按 DeepSeek 官方刊例价估算整个会话的累计费用:输入区分缓存命中/未命中,输出含思考;按请求时间区分高峰(北京时间工作日 9:00–12:00、14:00–18:00)与空闲时段(半价,周末全天均为空闲时段)。适用于 deepseek-v4.1-flash / deepseek-v4-pro(不限 provider;V4.1 Pro 上线前,V4 Pro 请求将被路由并按 V4.1 Flash 单价计费),价格写死在代码中,仅供参考。",
|
|
36
35
|
"stats.costPriceHead": "每百万 tokens 价格(高峰 | 空闲半价):",
|
|
37
36
|
"stats.costHit": "命中",
|
|
38
37
|
"stats.costMiss": "未命中",
|
|
39
38
|
"stats.costOut": "输出",
|
|
40
39
|
"timing.title": "耗时统计",
|
|
41
|
-
"timing.total": "
|
|
40
|
+
"timing.total": "活跃时长",
|
|
42
41
|
"timing.ttft": "模型等待",
|
|
43
42
|
"timing.gen": "模型生成",
|
|
43
|
+
"timing.reasoning": "模型思考",
|
|
44
|
+
"timing.text": "正文输出",
|
|
45
|
+
"timing.toolArgs": "工具参数",
|
|
44
46
|
"timing.tools": "工具执行",
|
|
45
47
|
"timing.other": "其他开销",
|
|
46
48
|
"timing.callTimes": "{n}次",
|
|
@@ -297,7 +299,6 @@ window.__ModuleLoader__.load({
|
|
|
297
299
|
};
|
|
298
300
|
const DICT_EN = {
|
|
299
301
|
"tab": "Context",
|
|
300
|
-
"sidebar.guideDescription": "Inspect this session's context composition, per-step history, and messages.",
|
|
301
302
|
"cat.system": "System Prompt",
|
|
302
303
|
"cat.tools": "Tool Schemas",
|
|
303
304
|
"cat.user": "User Messages",
|
|
@@ -319,7 +320,7 @@ window.__ModuleLoader__.load({
|
|
|
319
320
|
"stats.toolCalls": "Tool Calls",
|
|
320
321
|
"stats.images": "Images",
|
|
321
322
|
"stats.cost": "Cost",
|
|
322
|
-
"stats.costTip": "Rough cumulative cost of the whole session at DeepSeek’s list prices: input split by cache hit/miss, output includes reasoning; each request is priced at the peak (Beijing Time weekdays 09:00–12:00, 14:00–18:00) or half-price off-peak rate by its time — weekends bill at off-peak all day. Applies to deepseek-v4-flash / deepseek-v4-pro on any provider; prices are hardcoded, for reference only.",
|
|
323
|
+
"stats.costTip": "Rough cumulative cost of the whole session at DeepSeek’s list prices: input split by cache hit/miss, output includes reasoning; each request is priced at the peak (Beijing Time weekdays 09:00–12:00, 14:00–18:00) or half-price off-peak rate by its time — weekends bill at off-peak all day. Applies to deepseek-v4.1-flash / deepseek-v4-pro on any provider (until V4.1 Pro ships, Pro requests are routed to and billed at V4.1 Flash rates); prices are hardcoded, for reference only.",
|
|
323
324
|
"stats.costPriceHead": "Per-1M-token rates (peak | off-peak at half price):",
|
|
324
325
|
"stats.costHit": "hit",
|
|
325
326
|
"stats.costMiss": "miss",
|
|
@@ -328,6 +329,9 @@ window.__ModuleLoader__.load({
|
|
|
328
329
|
"timing.total": "Active Time",
|
|
329
330
|
"timing.ttft": "TTFT",
|
|
330
331
|
"timing.gen": "LLM Gen",
|
|
332
|
+
"timing.reasoning": "Thinking",
|
|
333
|
+
"timing.text": "Answer",
|
|
334
|
+
"timing.toolArgs": "Tool args",
|
|
331
335
|
"timing.tools": "Tool runs",
|
|
332
336
|
"timing.other": "Overhead",
|
|
333
337
|
"timing.callTimes": "{n} calls",
|
|
@@ -1136,6 +1140,14 @@ window.__ModuleLoader__.load({
|
|
|
1136
1140
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
|
|
1137
1141
|
}
|
|
1138
1142
|
/**
|
|
1143
|
+
* The OPTIONAL timing scalars (the generation split): a real non-negative
|
|
1144
|
+
* number passes, anything else — including absence — reads as undefined so the
|
|
1145
|
+
* field stays absent on the narrowed value (see `timingOf`).
|
|
1146
|
+
*/
|
|
1147
|
+
function optMsNumOf(value) {
|
|
1148
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1139
1151
|
* Cheap whole-value check for the pass-through path of `timelineOf`: absent
|
|
1140
1152
|
* timing passes; present timing must already be well-formed (every scalar
|
|
1141
1153
|
* numeric, every per-name row shaped) — anything else sends the payload down
|
|
@@ -1153,6 +1165,14 @@ window.__ModuleLoader__.load({
|
|
|
1153
1165
|
"toolsMs",
|
|
1154
1166
|
"toolCalls"
|
|
1155
1167
|
]) if (typeof t[k] !== "number") return false;
|
|
1168
|
+
for (const k of [
|
|
1169
|
+
"reasoningMs",
|
|
1170
|
+
"textMs",
|
|
1171
|
+
"toolArgMs"
|
|
1172
|
+
]) {
|
|
1173
|
+
const v = t[k];
|
|
1174
|
+
if (v !== void 0 && (typeof v !== "number" || !Number.isFinite(v) || v < 0)) return false;
|
|
1175
|
+
}
|
|
1156
1176
|
const tools = t.tools;
|
|
1157
1177
|
if (tools === null || typeof tools !== "object" || Array.isArray(tools)) return false;
|
|
1158
1178
|
for (const k in tools) {
|
|
@@ -1187,7 +1207,7 @@ window.__ModuleLoader__.load({
|
|
|
1187
1207
|
ms
|
|
1188
1208
|
};
|
|
1189
1209
|
}
|
|
1190
|
-
|
|
1210
|
+
const totals = {
|
|
1191
1211
|
wallMs: msNumOf(data.wallMs),
|
|
1192
1212
|
ttftMs: msNumOf(data.ttftMs),
|
|
1193
1213
|
genMs: msNumOf(data.genMs),
|
|
@@ -1196,6 +1216,13 @@ window.__ModuleLoader__.load({
|
|
|
1196
1216
|
toolCalls: msNumOf(data.toolCalls),
|
|
1197
1217
|
tools
|
|
1198
1218
|
};
|
|
1219
|
+
const reasoning = optMsNumOf(data.reasoningMs);
|
|
1220
|
+
if (reasoning !== void 0) totals.reasoningMs = reasoning;
|
|
1221
|
+
const textMs = optMsNumOf(data.textMs);
|
|
1222
|
+
if (textMs !== void 0) totals.textMs = textMs;
|
|
1223
|
+
const toolArgMs = optMsNumOf(data.toolArgMs);
|
|
1224
|
+
if (toolArgMs !== void 0) totals.toolArgMs = toolArgMs;
|
|
1225
|
+
return totals;
|
|
1199
1226
|
}
|
|
1200
1227
|
/**
|
|
1201
1228
|
* Narrow a delivered projection value to the plugin's `contextHeaders`
|
|
@@ -2453,22 +2480,20 @@ window.__ModuleLoader__.load({
|
|
|
2453
2480
|
return `${pct.toFixed(1)}%`;
|
|
2454
2481
|
}
|
|
2455
2482
|
/**
|
|
2456
|
-
* Whole-session durations for the timing card,
|
|
2457
|
-
* under a second, one-decimal seconds under a minute, then m/s and h/m.
|
|
2483
|
+
* Whole-session durations for the timing card, locale-free compact units: raw
|
|
2484
|
+
* ms under a second, one-decimal seconds under a minute, then m/s and h/m.
|
|
2458
2485
|
* Non-finite or non-positive input shows the dash (callers render their empty
|
|
2459
2486
|
* state anyway).
|
|
2460
2487
|
*/
|
|
2461
|
-
function fmtDuration(ms
|
|
2488
|
+
function fmtDuration(ms) {
|
|
2462
2489
|
if (!Number.isFinite(ms) || ms <= 0) return "—";
|
|
2463
2490
|
if (ms < 1e3) return `${Math.round(ms)}ms`;
|
|
2464
|
-
|
|
2465
|
-
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}${zh ? "秒" : "s"}`;
|
|
2491
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
2466
2492
|
const totalSec = Math.floor(ms / 1e3);
|
|
2467
2493
|
const m = Math.floor(totalSec / 60);
|
|
2468
2494
|
const s = totalSec % 60;
|
|
2469
|
-
if (ms < 36e5) return
|
|
2470
|
-
|
|
2471
|
-
return zh ? `${h}时${m % 60}分` : `${h}h ${m % 60}m`;
|
|
2495
|
+
if (ms < 36e5) return `${m}m${s}s`;
|
|
2496
|
+
return `${Math.floor(m / 60)}h${m % 60}m`;
|
|
2472
2497
|
}
|
|
2473
2498
|
//#endregion
|
|
2474
2499
|
//#region src/shared/imageTokens.ts
|
|
@@ -3924,7 +3949,7 @@ window.__ModuleLoader__.load({
|
|
|
3924
3949
|
className: "lc-stacked-seg" + (on ? " lc-stacked-seg-on" : "") + (pickKey !== void 0 ? " lc-stacked-seg-pick" : ""),
|
|
3925
3950
|
style: {
|
|
3926
3951
|
width: `${widths[i]}%`,
|
|
3927
|
-
|
|
3952
|
+
backgroundColor: p.color
|
|
3928
3953
|
},
|
|
3929
3954
|
onMouseEnter: () => {
|
|
3930
3955
|
if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
|
|
@@ -5662,9 +5687,9 @@ window.__ModuleLoader__.load({
|
|
|
5662
5687
|
}
|
|
5663
5688
|
/**
|
|
5664
5689
|
* Compact duration: `42s`, `3m05s`, `1h07m` (shared by both locales).
|
|
5665
|
-
* Deliberately distinct from format.ts's
|
|
5666
|
-
*
|
|
5667
|
-
*
|
|
5690
|
+
* Deliberately distinct from format.ts's `fmtDuration` (the timing card's
|
|
5691
|
+
* `12.3s` / `3m25s`): the inspector's caption column needs whole-second,
|
|
5692
|
+
* fixed-width text.
|
|
5668
5693
|
*/
|
|
5669
5694
|
function fmtDurationCompact(ms) {
|
|
5670
5695
|
if (!Number.isFinite(ms) || ms < 0) return "—";
|
|
@@ -6579,7 +6604,7 @@ window.__ModuleLoader__.load({
|
|
|
6579
6604
|
return function PluginInfo() {
|
|
6580
6605
|
const [latest, setLatest] = (0, react.useState)(null);
|
|
6581
6606
|
(0, react.useEffect)(() => {
|
|
6582
|
-
if ("0.
|
|
6607
|
+
if ("0.48.0".includes("-dev")) return;
|
|
6583
6608
|
let on = true;
|
|
6584
6609
|
fetchLatestVersion().then((v) => {
|
|
6585
6610
|
if (on && v) setLatest(v);
|
|
@@ -6588,8 +6613,8 @@ window.__ModuleLoader__.load({
|
|
|
6588
6613
|
on = false;
|
|
6589
6614
|
};
|
|
6590
6615
|
}, []);
|
|
6591
|
-
const update = latest !== null && isNewerVersion(latest, "0.
|
|
6592
|
-
const nameText = "dsh-context (v0.
|
|
6616
|
+
const update = latest !== null && isNewerVersion(latest, "0.48.0") ? latest : null;
|
|
6617
|
+
const nameText = "dsh-context (v0.48.0)";
|
|
6593
6618
|
const nameValue = [nameText];
|
|
6594
6619
|
if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6595
6620
|
className: "lc-pi-update",
|
|
@@ -7046,61 +7071,49 @@ window.__ModuleLoader__.load({
|
|
|
7046
7071
|
}
|
|
7047
7072
|
//#endregion
|
|
7048
7073
|
//#region src/client/cost.ts
|
|
7049
|
-
|
|
7074
|
+
/** The Flash-series rates (peak = 2× off-peak), per currency. */
|
|
7075
|
+
const FLASH_RATES = {
|
|
7050
7076
|
usd: {
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
out: 1.32
|
|
7056
|
-
},
|
|
7057
|
-
off: {
|
|
7058
|
-
hit: .007,
|
|
7059
|
-
miss: .22,
|
|
7060
|
-
out: .66
|
|
7061
|
-
}
|
|
7077
|
+
peak: {
|
|
7078
|
+
hit: .006,
|
|
7079
|
+
miss: .3,
|
|
7080
|
+
out: 1.2
|
|
7062
7081
|
},
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
out: 3.96
|
|
7068
|
-
},
|
|
7069
|
-
off: {
|
|
7070
|
-
hit: .022,
|
|
7071
|
-
miss: .66,
|
|
7072
|
-
out: 1.98
|
|
7073
|
-
}
|
|
7082
|
+
off: {
|
|
7083
|
+
hit: .003,
|
|
7084
|
+
miss: .15,
|
|
7085
|
+
out: .6
|
|
7074
7086
|
}
|
|
7075
7087
|
},
|
|
7076
7088
|
cny: {
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
out: 9
|
|
7082
|
-
},
|
|
7083
|
-
off: {
|
|
7084
|
-
hit: .05,
|
|
7085
|
-
miss: 1.5,
|
|
7086
|
-
out: 4.5
|
|
7087
|
-
}
|
|
7089
|
+
peak: {
|
|
7090
|
+
hit: .04,
|
|
7091
|
+
miss: 2,
|
|
7092
|
+
out: 8
|
|
7088
7093
|
},
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
out: 27
|
|
7094
|
-
},
|
|
7095
|
-
off: {
|
|
7096
|
-
hit: .15,
|
|
7097
|
-
miss: 4.5,
|
|
7098
|
-
out: 13.5
|
|
7099
|
-
}
|
|
7094
|
+
off: {
|
|
7095
|
+
hit: .02,
|
|
7096
|
+
miss: 1,
|
|
7097
|
+
out: 4
|
|
7100
7098
|
}
|
|
7101
7099
|
}
|
|
7102
7100
|
};
|
|
7103
7101
|
/**
|
|
7102
|
+
* Pro rides the Flash triple: between the V4.1 Flash launch and the V4.1 Pro
|
|
7103
|
+
* release, DeepSeek routes every Pro request to V4.1 Flash and bills it at
|
|
7104
|
+
* the Flash rates — point `pro` at the new table once V4.1 Pro is priced.
|
|
7105
|
+
*/
|
|
7106
|
+
const PRICES = {
|
|
7107
|
+
usd: {
|
|
7108
|
+
flash: FLASH_RATES.usd,
|
|
7109
|
+
pro: FLASH_RATES.usd
|
|
7110
|
+
},
|
|
7111
|
+
cny: {
|
|
7112
|
+
flash: FLASH_RATES.cny,
|
|
7113
|
+
pro: FLASH_RATES.cny
|
|
7114
|
+
}
|
|
7115
|
+
};
|
|
7116
|
+
/**
|
|
7104
7117
|
* Price the session's cumulative billed-token totals. Cache reads bill at
|
|
7105
7118
|
* the hit rate; uncached input AND cache writes bill at the miss rate;
|
|
7106
7119
|
* output (reasoning included) bills at the out rate. Null when nothing was
|
|
@@ -7135,7 +7148,7 @@ window.__ModuleLoader__.load({
|
|
|
7135
7148
|
*/
|
|
7136
7149
|
function sessionPrices(currency) {
|
|
7137
7150
|
return ["flash", "pro"].map((id) => ({
|
|
7138
|
-
family: id === "flash" ? "deepseek-v4-flash" : "deepseek-v4-pro",
|
|
7151
|
+
family: id === "flash" ? "deepseek-v4.1-flash" : "deepseek-v4-pro",
|
|
7139
7152
|
peak: PRICES[currency][id].peak,
|
|
7140
7153
|
off: PRICES[currency][id].off
|
|
7141
7154
|
}));
|
|
@@ -7330,90 +7343,139 @@ window.__ModuleLoader__.load({
|
|
|
7330
7343
|
* token, the wait) and LLM generation (first token → assistant message) —
|
|
7331
7344
|
* and the tool-execution slice, with the residue as overhead; a call whose
|
|
7332
7345
|
* stream recorded no token delta (legacy or aborted) stays unattributed and
|
|
7333
|
-
* lands in the residue.
|
|
7334
|
-
*
|
|
7335
|
-
*
|
|
7336
|
-
*
|
|
7337
|
-
*
|
|
7338
|
-
|
|
7346
|
+
* lands in the residue. When the host folded the generation split (see
|
|
7347
|
+
* TimingTotals), the generation slice expands into what was being decoded:
|
|
7348
|
+
* thinking, answer text, and tool-call arguments. The slice rows lead with
|
|
7349
|
+
* the true duration and qualify it with the call count on the secondary line —
|
|
7350
|
+
* except the decode slices, which count BLOCKS (zero to many per call, so a
|
|
7351
|
+
* call count would be a false tally) and therefore carry no qualifier. A slice
|
|
7352
|
+
* that never happened (zero time) is omitted entirely — the ring already skips
|
|
7353
|
+
* its arc, and a zero row would otherwise borrow the session's call count and
|
|
7354
|
+
* read as "no time, many calls". The donut and the rows sit side by side so
|
|
7355
|
+
* the head row stays half-height. Parallel tool calls each count, so the tools
|
|
7356
|
+
* figure can overlap — the ring clamps it into the post-model window while the
|
|
7357
|
+
* row numbers stay true.
|
|
7358
|
+
*/
|
|
7359
|
+
const COLOR = {
|
|
7360
|
+
ttft: "#3b82f6",
|
|
7361
|
+
reasoning: "#8b5cf6",
|
|
7362
|
+
text: "#ec4899",
|
|
7363
|
+
toolarg: "#f59e0b",
|
|
7364
|
+
tools: "#14b8a6",
|
|
7365
|
+
other: "#94a3b8"
|
|
7366
|
+
};
|
|
7339
7367
|
function makeStatsTiming(kit, Donut) {
|
|
7340
7368
|
const { t, fmt, fmtDuration, fmtShare } = kit;
|
|
7341
7369
|
const SliceList = makeSliceList(kit);
|
|
7342
7370
|
return function StatsTiming(props) {
|
|
7343
|
-
const lang = props.locale === "zh" ? "zh" : "en";
|
|
7344
7371
|
const [hoverKey, setHoverKey] = (0, react.useState)(null);
|
|
7345
7372
|
const timing = props.timing;
|
|
7346
7373
|
const wall = timing !== null && Number.isFinite(timing.wallMs) && timing.wallMs > 0 ? timing.wallMs : 0;
|
|
7347
7374
|
let segments = [];
|
|
7348
7375
|
let rows = [];
|
|
7349
7376
|
if (timing !== null && (wall > 0 || timing.calls > 0 || timing.toolCalls > 0)) {
|
|
7350
|
-
const
|
|
7351
|
-
const
|
|
7352
|
-
|
|
7353
|
-
|
|
7377
|
+
const callTimes = t("timing.callTimes", { n: fmt(timing.calls) });
|
|
7378
|
+
const modelSlices = [{
|
|
7379
|
+
key: "ttft",
|
|
7380
|
+
color: COLOR.ttft,
|
|
7381
|
+
label: t("timing.ttft"),
|
|
7382
|
+
ms: timing.ttftMs,
|
|
7383
|
+
times: callTimes
|
|
7384
|
+
}];
|
|
7385
|
+
const buckets = [
|
|
7386
|
+
[
|
|
7387
|
+
"reasoning",
|
|
7388
|
+
COLOR.reasoning,
|
|
7389
|
+
t("timing.reasoning"),
|
|
7390
|
+
timing.reasoningMs ?? 0
|
|
7391
|
+
],
|
|
7392
|
+
[
|
|
7393
|
+
"text",
|
|
7394
|
+
COLOR.text,
|
|
7395
|
+
t("timing.text"),
|
|
7396
|
+
timing.textMs ?? 0
|
|
7397
|
+
],
|
|
7398
|
+
[
|
|
7399
|
+
"toolarg",
|
|
7400
|
+
COLOR.toolarg,
|
|
7401
|
+
t("timing.toolArgs"),
|
|
7402
|
+
timing.toolArgMs ?? 0
|
|
7403
|
+
]
|
|
7404
|
+
];
|
|
7405
|
+
if (buckets.some(([, , , ms]) => ms > 0)) for (const [key, color, label, ms] of buckets) modelSlices.push({
|
|
7406
|
+
key,
|
|
7407
|
+
color,
|
|
7408
|
+
label,
|
|
7409
|
+
ms
|
|
7410
|
+
});
|
|
7411
|
+
else modelSlices.push({
|
|
7412
|
+
key: "gen",
|
|
7413
|
+
color: COLOR.reasoning,
|
|
7414
|
+
label: t("timing.gen"),
|
|
7415
|
+
ms: timing.genMs
|
|
7416
|
+
});
|
|
7417
|
+
const ttftRing = Math.min(timing.ttftMs, wall);
|
|
7418
|
+
const genRing = Math.max(0, Math.min(timing.genMs, wall - ttftRing));
|
|
7419
|
+
const modelRings = [ttftRing];
|
|
7420
|
+
let used = ttftRing;
|
|
7421
|
+
const genEnd = ttftRing + genRing;
|
|
7422
|
+
for (const slice of modelSlices.slice(1)) {
|
|
7423
|
+
const take = Math.max(0, Math.min(slice.ms, genEnd - used));
|
|
7424
|
+
modelRings.push(take);
|
|
7425
|
+
used += take;
|
|
7426
|
+
}
|
|
7427
|
+
const toolRing = Math.max(0, Math.min(timing.toolsMs, wall - ttftRing - genRing));
|
|
7428
|
+
const other = Math.max(0, wall - ttftRing - genRing - toolRing);
|
|
7354
7429
|
const share = (ms) => wall > 0 ? ms / wall : 0;
|
|
7355
7430
|
const countOf = (ms, times) => {
|
|
7356
|
-
const dur = fmtDuration(ms
|
|
7431
|
+
const dur = fmtDuration(ms);
|
|
7357
7432
|
if (times === void 0) return dur;
|
|
7358
7433
|
return ms > 0 ? `${dur} · ${times}` : times;
|
|
7359
7434
|
};
|
|
7360
|
-
const
|
|
7435
|
+
const modelSegments = modelSlices.map((slice, index) => ({
|
|
7436
|
+
key: slice.key,
|
|
7437
|
+
color: slice.color,
|
|
7438
|
+
value: share(modelRings[index])
|
|
7439
|
+
}));
|
|
7440
|
+
const toolSlice = {
|
|
7441
|
+
key: "tools",
|
|
7442
|
+
color: COLOR.tools,
|
|
7443
|
+
label: t("timing.tools"),
|
|
7444
|
+
ms: timing.toolsMs,
|
|
7445
|
+
times: t("timing.toolTimes", { n: fmt(timing.toolCalls) })
|
|
7446
|
+
};
|
|
7447
|
+
const otherSlice = {
|
|
7448
|
+
key: "other",
|
|
7449
|
+
color: COLOR.other,
|
|
7450
|
+
label: t("timing.other"),
|
|
7451
|
+
ms: other
|
|
7452
|
+
};
|
|
7361
7453
|
segments = [
|
|
7362
|
-
|
|
7363
|
-
key: "ttft",
|
|
7364
|
-
color: "#3b82f6",
|
|
7365
|
-
value: share(ttft)
|
|
7366
|
-
},
|
|
7367
|
-
{
|
|
7368
|
-
key: "gen",
|
|
7369
|
-
color: "#8b5cf6",
|
|
7370
|
-
value: share(gen)
|
|
7371
|
-
},
|
|
7454
|
+
...modelSegments,
|
|
7372
7455
|
{
|
|
7373
7456
|
key: "tools",
|
|
7374
|
-
color:
|
|
7457
|
+
color: COLOR.tools,
|
|
7375
7458
|
value: share(toolRing)
|
|
7376
7459
|
},
|
|
7377
7460
|
{
|
|
7378
7461
|
key: "other",
|
|
7379
|
-
color:
|
|
7462
|
+
color: COLOR.other,
|
|
7380
7463
|
value: share(other)
|
|
7381
7464
|
}
|
|
7382
7465
|
];
|
|
7466
|
+
const toRow = (slice) => ({
|
|
7467
|
+
key: slice.key,
|
|
7468
|
+
color: slice.color,
|
|
7469
|
+
label: slice.label,
|
|
7470
|
+
dim: slice.ms === 0,
|
|
7471
|
+
pct: fmtShare(slice.ms, wall),
|
|
7472
|
+
count: countOf(slice.ms, slice.times)
|
|
7473
|
+
});
|
|
7383
7474
|
rows = [
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
dim: timing.ttftMs === 0,
|
|
7389
|
-
pct: fmtShare(timing.ttftMs, wall),
|
|
7390
|
-
count: countOf(timing.ttftMs, callTimes)
|
|
7391
|
-
},
|
|
7392
|
-
{
|
|
7393
|
-
key: "gen",
|
|
7394
|
-
color: "#8b5cf6",
|
|
7395
|
-
label: t("timing.gen"),
|
|
7396
|
-
dim: timing.genMs === 0,
|
|
7397
|
-
pct: fmtShare(timing.genMs, wall),
|
|
7398
|
-
count: countOf(timing.genMs, callTimes)
|
|
7399
|
-
},
|
|
7400
|
-
{
|
|
7401
|
-
key: "tools",
|
|
7402
|
-
color: "#14b8a6",
|
|
7403
|
-
label: t("timing.tools"),
|
|
7404
|
-
dim: timing.toolsMs === 0,
|
|
7405
|
-
pct: fmtShare(timing.toolsMs, wall),
|
|
7406
|
-
count: countOf(timing.toolsMs, t("timing.toolTimes", { n: fmt(timing.toolCalls) }))
|
|
7407
|
-
},
|
|
7408
|
-
{
|
|
7409
|
-
key: "other",
|
|
7410
|
-
color: "#94a3b8",
|
|
7411
|
-
label: t("timing.other"),
|
|
7412
|
-
dim: other === 0,
|
|
7413
|
-
pct: fmtShare(other, wall),
|
|
7414
|
-
count: countOf(other)
|
|
7415
|
-
}
|
|
7416
|
-
];
|
|
7475
|
+
...modelSlices.map(toRow),
|
|
7476
|
+
toRow(toolSlice),
|
|
7477
|
+
toRow(otherSlice)
|
|
7478
|
+
].filter((row) => !row.dim);
|
|
7417
7479
|
}
|
|
7418
7480
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7419
7481
|
className: "lc-card lc-col-stats lc-col-donut",
|
|
@@ -7431,7 +7493,7 @@ window.__ModuleLoader__.load({
|
|
|
7431
7493
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Donut, {
|
|
7432
7494
|
segments,
|
|
7433
7495
|
size: 96,
|
|
7434
|
-
centerTop: wall > 0 ? fmtDuration(wall
|
|
7496
|
+
centerTop: wall > 0 ? fmtDuration(wall) : "—",
|
|
7435
7497
|
centerSub: t("timing.total"),
|
|
7436
7498
|
hoverKey,
|
|
7437
7499
|
onHoverKey: setHoverKey
|
|
@@ -8500,35 +8562,19 @@ window.__ModuleLoader__.load({
|
|
|
8500
8562
|
locale: activeLocale
|
|
8501
8563
|
}),
|
|
8502
8564
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsTokens, { usage }),
|
|
8503
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsTiming, {
|
|
8504
|
-
timing: data.timing ?? null,
|
|
8505
|
-
locale: activeLocale
|
|
8506
|
-
}),
|
|
8565
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsTiming, { timing: data.timing ?? null }),
|
|
8507
8566
|
inSidebar ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginInfo, {})
|
|
8508
8567
|
]
|
|
8509
8568
|
}),
|
|
8510
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
8569
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8511
8570
|
className: "lc-cols lc-cols-main",
|
|
8512
|
-
children:
|
|
8513
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8514
|
-
className: "lc-col",
|
|
8515
|
-
children: compositionCard
|
|
8516
|
-
}),
|
|
8517
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8518
|
-
className: "lc-col lc-col-browser",
|
|
8519
|
-
children: browserCard
|
|
8520
|
-
}),
|
|
8521
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8522
|
-
className: "lc-col",
|
|
8523
|
-
children: trendCard
|
|
8524
|
-
})
|
|
8525
|
-
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8571
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8526
8572
|
className: "lc-col",
|
|
8527
8573
|
children: [compositionCard, trendCard]
|
|
8528
8574
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8529
8575
|
className: "lc-col lc-col-browser",
|
|
8530
8576
|
children: browserCard
|
|
8531
|
-
})]
|
|
8577
|
+
})]
|
|
8532
8578
|
}),
|
|
8533
8579
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8534
8580
|
className: "lc-cols",
|
|
@@ -8676,7 +8722,7 @@ window.__ModuleLoader__.load({
|
|
|
8676
8722
|
//#endregion
|
|
8677
8723
|
//#region src/client/sidebar.ts
|
|
8678
8724
|
/**
|
|
8679
|
-
* The right Sidebar's Context tab (dsh 0.1.5-alpha.
|
|
8725
|
+
* The right Sidebar's Context tab (dsh 0.1.5-alpha.2+).
|
|
8680
8726
|
*
|
|
8681
8727
|
* The tab reuses the Context conversation-view component VERBATIM: the
|
|
8682
8728
|
* `sidebar.right.pane.tab` seat is session-scoped and delivers the same
|
|
@@ -8684,14 +8730,15 @@ window.__ModuleLoader__.load({
|
|
|
8684
8730
|
* `t` seat) the `conversation.view` seat does, so the panel and the tab are
|
|
8685
8731
|
* one component with one data path. The tab type contributes a guide entry, so
|
|
8686
8732
|
* the sidebar's guide page offers "Context" and picking it opens the panel —
|
|
8687
|
-
* the product's own path, exactly as the shipped Files type does
|
|
8733
|
+
* the product's own path, exactly as the shipped Files type does: a capsule of
|
|
8734
|
+
* glyph + title.
|
|
8688
8735
|
*
|
|
8689
|
-
* OPTIONAL BY CONTRACT. `ctx.sidebarRightTabs` and the seat
|
|
8690
|
-
*
|
|
8691
|
-
* plugin's hard injects stay `slots` + `locale`), so on
|
|
8692
|
-
* line the callback never fires, the plugin fiber never
|
|
8693
|
-
* registered. The registry is re-proved structurally and
|
|
8694
|
-
* registration is guarded: a foreign or hostile registry (a throwing
|
|
8736
|
+
* OPTIONAL BY CONTRACT. `ctx.sidebarRightTabs` and the seat ship only on the
|
|
8737
|
+
* 0.1.5 line (0.1.5-alpha.2+ supported); the registration therefore rides a
|
|
8738
|
+
* DEFERRED inject (the plugin's hard injects stay `slots` + `locale`), so on
|
|
8739
|
+
* every older supported line the callback never fires, the plugin fiber never
|
|
8740
|
+
* pends, and nothing is registered. The registry is re-proved structurally and
|
|
8741
|
+
* the whole registration is guarded: a foreign or hostile registry (a throwing
|
|
8695
8742
|
* `register`, a taken id/kind) leaves the sidebar without the tab instead of
|
|
8696
8743
|
* taking the browser down.
|
|
8697
8744
|
*
|
|
@@ -8705,13 +8752,13 @@ window.__ModuleLoader__.load({
|
|
|
8705
8752
|
* non-coexisting band, and a foreign plugin may well own `context`.
|
|
8706
8753
|
*/
|
|
8707
8754
|
const SIDEBAR_CONTEXT_KIND = "dsh-context";
|
|
8708
|
-
/** The guide
|
|
8755
|
+
/** The guide capsule's position: after the shipped Files entry (order 10). */
|
|
8709
8756
|
const GUIDE_ORDER = 20;
|
|
8710
8757
|
/**
|
|
8711
|
-
* The guide
|
|
8712
|
-
* and a primitives module that does not serve it — or whose namespace
|
|
8713
|
-
* on the read (an interop/mock shape does exactly that) — must cost the
|
|
8714
|
-
* never the whole tab.
|
|
8758
|
+
* The guide capsule's glyph, read defensively: the icon is OPTIONAL in the
|
|
8759
|
+
* entry, and a primitives module that does not serve it — or whose namespace
|
|
8760
|
+
* THROWS on the read (an interop/mock shape does exactly that) — must cost the
|
|
8761
|
+
* glyph, never the whole tab.
|
|
8715
8762
|
* @returns the icon component, or undefined to register the tab without one.
|
|
8716
8763
|
*/
|
|
8717
8764
|
function guideIcon() {
|
|
@@ -8745,7 +8792,6 @@ window.__ModuleLoader__.load({
|
|
|
8745
8792
|
guide: [{
|
|
8746
8793
|
order: GUIDE_ORDER,
|
|
8747
8794
|
title: () => t("tab"),
|
|
8748
|
-
description: () => t("sidebar.guideDescription"),
|
|
8749
8795
|
icon: guideIcon()
|
|
8750
8796
|
}]
|
|
8751
8797
|
});
|
|
@@ -8783,7 +8829,7 @@ window.__ModuleLoader__.load({
|
|
|
8783
8829
|
}
|
|
8784
8830
|
//#endregion
|
|
8785
8831
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/base.css.mjs
|
|
8786
|
-
const css$13 = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}[data-sidebar-right-open] .lc-root,[data-sidebar-right-float-host] .lc-root{height:auto;padding:
|
|
8832
|
+
const css$13 = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}[data-sidebar-right-open] .lc-root,[data-sidebar-right-float-host] .lc-root{height:auto;padding:12px;overflow:visible}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-root .lc-card{margin-bottom:7px;padding:7px 8px;container:lc-card/inline-size}.lc-root .lc-cols{gap:7px;margin-bottom:7px}.lc-card-title{flex-wrap:wrap;align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{margin-left:0}.lc-trend-adaptive{flex:none;margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;color:var(--dsw-alias-label-primary);box-shadow:var(--dsw-shadow-lv3,0 2px 8px #0000002e);pointer-events:none;opacity:0;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;padding:6px 10px;font-size:12px;position:absolute}.lc-events,.lc-fa-list{flex-direction:column;gap:2px;height:320px;display:flex;overflow-y:auto}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:min(360px,100%)}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head{container:lc-head-row/inline-size}.lc-head>.lc-card{flex:1 1 0;min-width:0}@container lc-head-row (width<=1045px){.lc-head>.lc-card{flex-basis:40%}}@container lc-head-row (width<=480px){.lc-head>.lc-card{flex-basis:100%}}.lc-head>.lc-card:first-child{flex-direction:column;display:flex}.lc-head>.lc-card:first-child .lc-stats{flex:1;align-content:stretch}.lc-head>.lc-card:first-child .lc-stat{justify-content:center}.lc-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:18px 0}.lc-error{flex-direction:column;align-items:center;gap:8px;padding:40px 16px;display:flex}.lc-error-msg{font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);overflow-wrap:anywhere;border-radius:6px;max-width:100%;padding:4px 8px;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;padding:4px 14px;font-size:12px}.lc-error-retry:hover{border-color:var(--dsw-alias-label-primary)}.lc-foot{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:12px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])),[data-conversation-scroll]:has(.lc-root,.lc-modal-backdrop)~[data-width-handle]{display:none}";
|
|
8787
8833
|
const tagId$13 = "dsh-context/base.css";
|
|
8788
8834
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$13) + "]") === null) {
|
|
8789
8835
|
const tag = document.createElement("style");
|
|
@@ -8838,7 +8884,7 @@ window.__ModuleLoader__.load({
|
|
|
8838
8884
|
}
|
|
8839
8885
|
//#endregion
|
|
8840
8886
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/trendChart.css.mjs
|
|
8841
|
-
const css$8 = ".lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-q3{top:41px}.lc-axis-mid{top:69px}.lc-axis-q1{top:97px}.lc-axis-bot{top:125px}.lc-chart-wrap{flex:1;min-width:0;position:relative}.lc-chart-scroll{overscroll-behavior-x:contain;scrollbar-gutter:stable;padding-bottom:var(--dsh-scrollbar-width,8px);overflow:auto hidden}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l2);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-q3{top:46px}.lc-grid-mid{top:74px}.lc-grid-q1{top:102px}.lc-grid-zero{border-top-style:solid;border-top-color:#80808080;top:130px}.lc-bar{cursor:pointer;outline-offset:1px;width:14px;height:100%;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), outline-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;outline:1px dashed #0000;flex:none;align-items:flex-end;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;box-sizing:border-box;overflow-wrap:break-word;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:100%;color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;flex-direction:column;gap:2px;padding:3px 8px;font-size:12px;display:flex;position:absolute;bottom:calc(100% + 4px);left:0;box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline-color:var(--dsw-alias-brand-primary)}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-cat-seg{transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-chart[data-catdim] .lc-cat-seg{opacity:.35}.lc-chart[data-catdim=system] .lc-cat-seg[data-cat=system],.lc-chart[data-catdim=tools] .lc-cat-seg[data-cat=tools],.lc-chart[data-catdim=user] .lc-cat-seg[data-cat=user],.lc-chart[data-catdim=inject] .lc-cat-seg[data-cat=inject],.lc-chart[data-catdim=assistant] .lc-cat-seg[data-cat=assistant],.lc-chart[data-catdim=tool] .lc-cat-seg[data-cat=tool]{opacity:1;filter:brightness(1.18)}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex;overflow:hidden}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;cursor:pointer;height:14px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:3px;flex:none;font-size:10px;font-weight:600;line-height:14px}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{display:inline-block}";
|
|
8887
|
+
const css$8 = ".lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-q3{top:41px}.lc-axis-mid{top:69px}.lc-axis-q1{top:97px}.lc-axis-bot{top:125px}.lc-chart-wrap{flex:1;min-width:0;position:relative}.lc-chart-scroll{overscroll-behavior-x:contain;scrollbar-gutter:stable;padding-bottom:var(--dsh-scrollbar-width,8px);overflow:auto hidden}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l2);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-q3{top:46px}.lc-grid-mid{top:74px}.lc-grid-q1{top:102px}.lc-grid-zero{border-top-style:solid;border-top-color:#80808080;top:130px}.lc-bar{cursor:pointer;outline-offset:1px;width:14px;height:100%;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), outline-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;outline:1px dashed #0000;flex:none;align-items:flex-end;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;box-sizing:border-box;overflow-wrap:break-word;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:100%;color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;flex-direction:column;gap:2px;padding:3px 8px;font-size:12px;display:flex;position:absolute;bottom:calc(100% + 4px);left:0;box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline-color:var(--dsw-alias-brand-primary)}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-stack>div:first-child{border-radius:0 0 2px 2px}.lc-bar-stack>div:last-child{border-radius:2px 2px 0 0}.lc-bar-stack>div:only-child{border-radius:2px}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-up>div:last-child{border-radius:2px 2px 0 0}.lc-bar-down>div:last-child{border-radius:0 0 2px 2px}.lc-cat-seg{transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-chart[data-catdim] .lc-cat-seg{opacity:.35}.lc-chart[data-catdim=system] .lc-cat-seg[data-cat=system],.lc-chart[data-catdim=tools] .lc-cat-seg[data-cat=tools],.lc-chart[data-catdim=user] .lc-cat-seg[data-cat=user],.lc-chart[data-catdim=inject] .lc-cat-seg[data-cat=inject],.lc-chart[data-catdim=assistant] .lc-cat-seg[data-cat=assistant],.lc-chart[data-catdim=tool] .lc-cat-seg[data-cat=tool]{opacity:1;filter:brightness(1.18)}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex;overflow:hidden}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;cursor:pointer;height:14px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:3px;flex:none;font-size:10px;font-weight:600;line-height:14px}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{display:inline-block}";
|
|
8842
8888
|
const tagId$8 = "dsh-context/trendChart.css";
|
|
8843
8889
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$8) + "]") === null) {
|
|
8844
8890
|
const tag = document.createElement("style");
|
|
@@ -8893,7 +8939,7 @@ window.__ModuleLoader__.load({
|
|
|
8893
8939
|
}
|
|
8894
8940
|
//#endregion
|
|
8895
8941
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/browser.css.mjs
|
|
8896
|
-
const css$3 = ".lc-br-dna-ctl{flex:none;margin-left:0}.lc-br-bar-dna .lc-stacked-seg{
|
|
8942
|
+
const css$3 = ".lc-br-dna-ctl{flex:none;margin-left:0}.lc-br-bar-dna .lc-stacked-seg{background-image:linear-gradient(to right, transparent calc(100% - min(1px, 25%)), var(--dsw-alias-bg-layer-1) 0)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-retry{color:var(--dsw-alias-brand-primary);font:inherit;cursor:pointer;text-underline-offset:2px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;padding:0;font-size:12px;text-decoration:underline}.lc-br-retry:hover{filter:brightness(1.15)}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;display:flex}.lc-br-cat-row:hover,.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{width:8px;height:8px;transition:box-shadow var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;flex:none;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:flex}.lc-br-chev:before{content:\"\";width:5px;height:5px;transition:transform var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-bottom:1px solid;border-right:1px solid;transform:rotate(-45deg)}.lc-br-chev-on:before{transform:rotate(45deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;display:flex}.lc-br-elem-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-err-dot{background:var(--dsw-alias-state-error-primary);width:6px;height:6px;box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);border-radius:50%;flex:none}.lc-br-tag{min-width:0;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-tool-plugin{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);border-color:#0000;border-radius:6px;font-size:12px}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}";
|
|
8897
8943
|
const tagId$3 = "dsh-context/browser.css";
|
|
8898
8944
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
|
|
8899
8945
|
const tag = document.createElement("style");
|
package/lib/index.d.ts
CHANGED
|
@@ -56,6 +56,15 @@ interface HeadersState {
|
|
|
56
56
|
headers: StoredHeaderRecord[];
|
|
57
57
|
}
|
|
58
58
|
//#endregion
|
|
59
|
+
//#region src/host/logShapes.d.ts
|
|
60
|
+
/**
|
|
61
|
+
* The decode bucket a stream's `block-start.blockType` names: the model's
|
|
62
|
+
* thinking, the answer text, or the tool-call arguments. Undefined for an
|
|
63
|
+
* unknown/hostile marker, whose interval then stays unattributed rather than
|
|
64
|
+
* poisoning a bucket.
|
|
65
|
+
*/
|
|
66
|
+
type DecodeKind = 'reasoning' | 'text' | 'toolarg';
|
|
67
|
+
//#endregion
|
|
59
68
|
//#region src/host/fold.d.ts
|
|
60
69
|
/**
|
|
61
70
|
* History retention bounds (configurable since 0.11 — see config.ts; these
|
|
@@ -153,10 +162,21 @@ interface TimelineState {
|
|
|
153
162
|
* one those events close — a hostile interleaved log degrades to skipped
|
|
154
163
|
* durations, never to unbounded state. Same arm/remove lifecycle as
|
|
155
164
|
* `pendingShadowedSeqs`.
|
|
165
|
+
*
|
|
166
|
+
* `decode` and `block` carry the generation split (reasoning / answer text /
|
|
167
|
+
* tool arguments — see TimingTotals): a V0 log's `assistant/chunk`
|
|
168
|
+
* `block-start` markers open `block` and close the previous one into
|
|
169
|
+
* `decode`; a V2+ log carries no such events, so `decode` stays absent and
|
|
170
|
+
* `assistant/message` reads the spans off its embedded stream instead.
|
|
156
171
|
*/
|
|
157
172
|
stepStart?: {
|
|
158
173
|
time: number;
|
|
159
174
|
firstToken?: number;
|
|
175
|
+
decode?: Record<DecodeKind, number>;
|
|
176
|
+
block?: {
|
|
177
|
+
kind: DecodeKind;
|
|
178
|
+
since: number;
|
|
179
|
+
};
|
|
160
180
|
};
|
|
161
181
|
/**
|
|
162
182
|
* Tool callId → the call's name, start instant, and raw arguments, armed by
|
|
@@ -503,6 +523,18 @@ interface ToolTimingTotals {
|
|
|
503
523
|
* both only over calls whose stream carried a token delta, `toolsMs` the sum
|
|
504
524
|
* of per-call tool durations (parallel calls each count, so it can overlap).
|
|
505
525
|
* Absent until the first step lifecycle completes in the log.
|
|
526
|
+
*
|
|
527
|
+
* The generation window itself splits by WHAT was being decoded, off the
|
|
528
|
+
* stream's `block-start` framing (`blockType`): `reasoningMs` (the model's
|
|
529
|
+
* thinking), `textMs` (the answer text), and `toolArgMs` (the tool-call
|
|
530
|
+
* arguments). Each marker owns the interval up to the next one (the last one
|
|
531
|
+
* up to the assistant message), so the three tile the marker span and together
|
|
532
|
+
* account for essentially all of `genMs` — the span opens at the first marker,
|
|
533
|
+
* which can sit marginally before the first token, so it is not an exact
|
|
534
|
+
* partition. They are ADDITIVE-OPTIONAL: cached projection rows written before
|
|
535
|
+
* the split carry `genMs` without them, so the card falls back to the
|
|
536
|
+
* un-split shape instead of the cache row being discarded (the
|
|
537
|
+
* stateVersion-15 rationale in host/timeline.ts).
|
|
506
538
|
*/
|
|
507
539
|
interface TimingTotals {
|
|
508
540
|
/** Summed wall time of completed steps (the session's active time). */
|
|
@@ -511,6 +543,12 @@ interface TimingTotals {
|
|
|
511
543
|
ttftMs: number;
|
|
512
544
|
/** Summed first-token → assistant-message time (the generation). */
|
|
513
545
|
genMs: number;
|
|
546
|
+
/** Reasoning-decode slice of `genMs` (the model's thinking). */
|
|
547
|
+
reasoningMs?: number;
|
|
548
|
+
/** Answer-text decode slice of `genMs`. */
|
|
549
|
+
textMs?: number;
|
|
550
|
+
/** Tool-call-argument decode slice of `genMs`. */
|
|
551
|
+
toolArgMs?: number;
|
|
514
552
|
/** Completed model calls (assistant messages folded). */
|
|
515
553
|
calls: number;
|
|
516
554
|
/** Summed per-call durations of completed tool calls. */
|
|
@@ -530,7 +568,7 @@ interface CostFamilyUsage {
|
|
|
530
568
|
* token totals per DeepSeek V4 model family (matched on the model NAME,
|
|
531
569
|
* provider-agnostic) and pricing period. The Client prices these with its
|
|
532
570
|
* hardcoded list-price table in the locale's currency. Absent until a
|
|
533
|
-
* deepseek-v4-flash / deepseek-v4-pro request reports usage.
|
|
571
|
+
* deepseek-v4.1-flash / deepseek-v4-pro request reports usage.
|
|
534
572
|
*/
|
|
535
573
|
interface SessionCostUsage {
|
|
536
574
|
flash?: CostFamilyUsage;
|
package/lib/index.js
CHANGED
|
@@ -596,7 +596,7 @@ function isInjection(source) {
|
|
|
596
596
|
*
|
|
597
597
|
* - V0 (dsh 0.1.2-rc.1): `request/header.header.system`, `assistant/chunk`
|
|
598
598
|
* stream events, `SurfaceOp { start, end }`, `tool/code-dispatch`.
|
|
599
|
-
* - V3 (dsh 0.1.5-alpha.
|
|
599
|
+
* - V3 (dsh 0.1.5-alpha.x+): `system/message` surface nodes,
|
|
600
600
|
* `assistant/message.data.stream` / `assistant/attempt.data.stream`,
|
|
601
601
|
* `SurfaceOp { startSeq, endSeq }`, `tool/ptc-dispatch`.
|
|
602
602
|
*
|
|
@@ -627,6 +627,45 @@ function isTokenChunk(chunk) {
|
|
|
627
627
|
default: return false;
|
|
628
628
|
}
|
|
629
629
|
}
|
|
630
|
+
/** Map one `blockType` to its timing bucket (see {@link DecodeKind}). */
|
|
631
|
+
function decodeKindOfBlock(blockType) {
|
|
632
|
+
if (blockType === "reasoning") return "reasoning";
|
|
633
|
+
if (blockType === "text") return "text";
|
|
634
|
+
if (blockType === "tool-call") return "toolarg";
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Per-kind decode spans inside one embedded assistant stream, tiling
|
|
638
|
+
* [first block-start, endTime]: each `block-start` record owns the interval up
|
|
639
|
+
* to the next one, the last one up to `endTime`. This is the V2+ shape, whose
|
|
640
|
+
* timed stream rides the settlement (`assistant/message.data.stream`) instead
|
|
641
|
+
* of separate `assistant/chunk` events. Total over untrusted input — a
|
|
642
|
+
* malformed record is skipped, a non-finite boundary yields zero, and a stream
|
|
643
|
+
* with no marker (or not an array) yields all zeros.
|
|
644
|
+
*/
|
|
645
|
+
function decodeSpansOfStream(stream, endTime) {
|
|
646
|
+
const spans = {
|
|
647
|
+
reasoning: 0,
|
|
648
|
+
text: 0,
|
|
649
|
+
toolarg: 0
|
|
650
|
+
};
|
|
651
|
+
if (!Array.isArray(stream) || !Number.isFinite(endTime)) return spans;
|
|
652
|
+
let kind;
|
|
653
|
+
let since = 0;
|
|
654
|
+
for (const record of stream) {
|
|
655
|
+
if (record === null || typeof record !== "object") continue;
|
|
656
|
+
const r = record;
|
|
657
|
+
if (r.type !== "chunk" || r.chunk === null || typeof r.chunk !== "object") continue;
|
|
658
|
+
const chunk = r.chunk;
|
|
659
|
+
if (chunk.type !== "block-start") continue;
|
|
660
|
+
const time = r.time;
|
|
661
|
+
if (typeof time !== "number" || !Number.isFinite(time)) continue;
|
|
662
|
+
if (kind !== void 0) spans[kind] += Math.max(0, time - since);
|
|
663
|
+
kind = decodeKindOfBlock(chunk.blockType);
|
|
664
|
+
since = time;
|
|
665
|
+
}
|
|
666
|
+
if (kind !== void 0) spans[kind] += Math.max(0, endTime - since);
|
|
667
|
+
return spans;
|
|
668
|
+
}
|
|
630
669
|
/**
|
|
631
670
|
* The first token's instant inside one PACKED delta run (`text-chunks` /
|
|
632
671
|
* `reasoning-chunks` / `tool-call-chunks`): the run's base time plus the
|
|
@@ -1237,7 +1276,7 @@ function tokenCountOf(value) {
|
|
|
1237
1276
|
/**
|
|
1238
1277
|
* The DeepSeek V4 model family a model name prices as — matched on the NAME
|
|
1239
1278
|
* alone (provider-agnostic: official API, proxies, OpenRouter spellings like
|
|
1240
|
-
* `deepseek/deepseek-v4-flash` all land here). Null for any other model:
|
|
1279
|
+
* `deepseek/deepseek-v4.1-flash` all land here). Null for any other model:
|
|
1241
1280
|
* non-V4 usage is simply not priced.
|
|
1242
1281
|
*/
|
|
1243
1282
|
function costFamilyOf(model) {
|
|
@@ -1300,6 +1339,12 @@ function accumulateCost(st, time, usage) {
|
|
|
1300
1339
|
*/
|
|
1301
1340
|
/** The timing card's per-tool ranking cap: the busiest 16 names are kept. */
|
|
1302
1341
|
const TOOL_TIMING_CAP = 16;
|
|
1342
|
+
/** The decode buckets of the generation split, in card order (see TimingTotals). */
|
|
1343
|
+
const DECODE_KINDS = [
|
|
1344
|
+
"reasoning",
|
|
1345
|
+
"text",
|
|
1346
|
+
"toolarg"
|
|
1347
|
+
];
|
|
1303
1348
|
/** Non-negative, NaN-proof duration between two instants (hostile times degrade to 0). */
|
|
1304
1349
|
function durOf(from, to) {
|
|
1305
1350
|
if (!Number.isFinite(from) || !Number.isFinite(to)) return 0;
|
|
@@ -1323,6 +1368,18 @@ function ensureTiming(st) {
|
|
|
1323
1368
|
return st.timing;
|
|
1324
1369
|
}
|
|
1325
1370
|
/**
|
|
1371
|
+
* Fold one block's decode span into the totals' generation split (see
|
|
1372
|
+
* TimingTotals). A zero span stays ABSENT — the field then carries the
|
|
1373
|
+
* "no time was decoded in this bucket" fact without adding dead properties to
|
|
1374
|
+
* every pre-split-shaped state, and the card reads absence as 0.
|
|
1375
|
+
*/
|
|
1376
|
+
function addDecode(timing, kind, ms) {
|
|
1377
|
+
if (!(ms > 0)) return;
|
|
1378
|
+
if (kind === "reasoning") timing.reasoningMs = (timing.reasoningMs ?? 0) + ms;
|
|
1379
|
+
else if (kind === "text") timing.textMs = (timing.textMs ?? 0) + ms;
|
|
1380
|
+
else timing.toolArgMs = (timing.toolArgMs ?? 0) + ms;
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1326
1383
|
* Tally one completed tool call into the per-name ranking, bounded to
|
|
1327
1384
|
* TOOL_TIMING_CAP names: repeated names update in place, a new name beyond
|
|
1328
1385
|
* the cap evicts the smallest tally first (the ranking's tail), so state
|
|
@@ -1462,11 +1519,34 @@ function applyTimeline(state, event, bounds) {
|
|
|
1462
1519
|
}
|
|
1463
1520
|
case "assistant/chunk": {
|
|
1464
1521
|
const start = state.stepStart;
|
|
1465
|
-
if (start === void 0
|
|
1522
|
+
if (start === void 0) return state;
|
|
1523
|
+
const chunk = data?.chunk;
|
|
1524
|
+
if (chunk !== null && typeof chunk === "object" && chunk.type === "block-start") {
|
|
1525
|
+
const kind = decodeKindOfBlock(chunk.blockType);
|
|
1526
|
+
if (start.block === void 0 && kind === void 0) return state;
|
|
1527
|
+
const s = ensure();
|
|
1528
|
+
const decode = { ...start.decode ?? {
|
|
1529
|
+
reasoning: 0,
|
|
1530
|
+
text: 0,
|
|
1531
|
+
toolarg: 0
|
|
1532
|
+
} };
|
|
1533
|
+
if (start.block !== void 0) decode[start.block.kind] += durOf(start.block.since, event.time);
|
|
1534
|
+
s.stepStart = {
|
|
1535
|
+
time: start.time,
|
|
1536
|
+
...start.firstToken !== void 0 ? { firstToken: start.firstToken } : {},
|
|
1537
|
+
decode,
|
|
1538
|
+
...kind !== void 0 ? { block: {
|
|
1539
|
+
kind,
|
|
1540
|
+
since: event.time
|
|
1541
|
+
} } : {}
|
|
1542
|
+
};
|
|
1543
|
+
break;
|
|
1544
|
+
}
|
|
1545
|
+
if (start.firstToken !== void 0) return state;
|
|
1466
1546
|
if (!isTokenChunk(data?.chunk)) return state;
|
|
1467
1547
|
const s = ensure();
|
|
1468
1548
|
s.stepStart = {
|
|
1469
|
-
|
|
1549
|
+
...start,
|
|
1470
1550
|
firstToken: event.time
|
|
1471
1551
|
};
|
|
1472
1552
|
break;
|
|
@@ -1613,6 +1693,14 @@ function applyTimeline(state, event, bounds) {
|
|
|
1613
1693
|
if (firstToken !== void 0) {
|
|
1614
1694
|
timing.ttftMs += durOf(stepStart.time, firstToken);
|
|
1615
1695
|
timing.genMs += durOf(firstToken, event.time);
|
|
1696
|
+
if (stepStart.decode !== void 0) {
|
|
1697
|
+
const decode = { ...stepStart.decode };
|
|
1698
|
+
if (stepStart.block !== void 0) decode[stepStart.block.kind] += durOf(stepStart.block.since, event.time);
|
|
1699
|
+
for (const kind of DECODE_KINDS) addDecode(timing, kind, decode[kind]);
|
|
1700
|
+
} else {
|
|
1701
|
+
const spans = decodeSpansOfStream(data?.stream, event.time);
|
|
1702
|
+
for (const kind of DECODE_KINDS) addDecode(timing, kind, spans[kind]);
|
|
1703
|
+
}
|
|
1616
1704
|
}
|
|
1617
1705
|
}
|
|
1618
1706
|
const asstMsg = deriveEventMessage(event);
|
|
@@ -2254,6 +2342,9 @@ const timingTotalsSchema = z.object({
|
|
|
2254
2342
|
wallMs: z.number().nonnegative(),
|
|
2255
2343
|
ttftMs: z.number().nonnegative(),
|
|
2256
2344
|
genMs: z.number().nonnegative(),
|
|
2345
|
+
reasoningMs: z.number().nonnegative().optional(),
|
|
2346
|
+
textMs: z.number().nonnegative().optional(),
|
|
2347
|
+
toolArgMs: z.number().nonnegative().optional(),
|
|
2257
2348
|
calls: z.number().int().nonnegative(),
|
|
2258
2349
|
toolsMs: z.number().nonnegative(),
|
|
2259
2350
|
toolCalls: z.number().int().nonnegative(),
|
|
@@ -2347,7 +2438,20 @@ const timelineStateSchema = z.object({
|
|
|
2347
2438
|
timing: timingTotalsSchema.optional(),
|
|
2348
2439
|
stepStart: z.object({
|
|
2349
2440
|
time: z.number(),
|
|
2350
|
-
firstToken: z.number().optional()
|
|
2441
|
+
firstToken: z.number().optional(),
|
|
2442
|
+
decode: z.object({
|
|
2443
|
+
reasoning: z.number(),
|
|
2444
|
+
text: z.number(),
|
|
2445
|
+
toolarg: z.number()
|
|
2446
|
+
}).strict().optional(),
|
|
2447
|
+
block: z.object({
|
|
2448
|
+
kind: z.enum([
|
|
2449
|
+
"reasoning",
|
|
2450
|
+
"text",
|
|
2451
|
+
"toolarg"
|
|
2452
|
+
]),
|
|
2453
|
+
since: z.number()
|
|
2454
|
+
}).strict().optional()
|
|
2351
2455
|
}).strict().optional(),
|
|
2352
2456
|
callNames: z.record(z.string(), z.object({
|
|
2353
2457
|
name: z.string(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-context",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.0",
|
|
4
4
|
"description": "A DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.",
|
|
5
5
|
"author": "bowenliang123",
|
|
6
6
|
"repository": {
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"dshReleases": {
|
|
67
67
|
"0.1.2-rc.1": "compatible",
|
|
68
68
|
"0.1.3-alpha.2": "compatible",
|
|
69
|
-
"0.1.5-alpha.
|
|
69
|
+
"0.1.5-alpha.2": "compatible"
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
},
|