dsh-token-total 1.1.4 → 1.1.5
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/index.js +23 -7
- package/package.json +1 -1
- package/src/session-detail.ts +28 -10
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ DSH(DeepSeek Harness)Web 插件:**Token 用量统计看板** + **单会话
|
|
|
60
60
|
- 两张卡都由会话明细轮询驱动(每 1.5 秒一次),不额外起计时器;旧宿主只提供其中一份数据时,另一张卡显示空态而非整组消失,两份都缺时整组不渲染。窄视口下自动上下堆叠。
|
|
61
61
|
|
|
62
62
|
- 浅色页面两行统计卡:第一行请求数量(附失败数)、总 tokens(附精确值)、缓存命中率(附命中 token)、token 速度(附平均首 token);第二行输入、输出、缓存读取、缓存写入四个 token 桶。不显示费用。
|
|
63
|
-
-
|
|
63
|
+
- 子代理明细与请求记录使用每个子代理的**真实名称**:优先子代理自身标签,再取最新非空会话标题;旧 CLI 记录缺少标题时,读取本次会话树中实际父会话的 `subagent/catalog` 任务标签,最后才回退到日志头名称、工作目录或会话 id。只使用各日志的自有事件,不把继承的父标题/目录当作自身名称,不改写历史日志,也不改变 Token 统计。若直接打开旧子会话、实际父会话不在本次读取树中,则保留原有回退名称。
|
|
64
64
|
- 中部为等宽、等高的 **子代理明细** 与 **按模型明细**;窄视口自动堆叠。使用固定列布局而非横向滚动,长名称省略并可悬浮查看全名,token 数同样可悬浮查看精确值。按模型明细的名称使用模型选择器的**显示名称**(路由 id 保留在悬浮提示中,无法解析时回退为 id);当多行显示名称相同(忽略大小写与分隔符)时,在名称下方用灰色小字标明各自供应商以便区分。
|
|
65
65
|
- 请求记录按**轮**折叠(压缩调用归入独立分组),**默认全部折叠**;展开某一轮可查看该轮每一次请求的时间、模型、tokens、命中率、状态与入 / 出 / 读 / 写明细,支持单组展开和全部展开 / 收起。失败尝试、实际重试、压缩调用纳入统计;仅继承的父会话历史不重复计算。
|
|
66
66
|
- 页面每 1.5 秒串行读取当前会话明细,切换或离开 tab 会取消请求和计时器。统计含本会话及所有后代的自有历史日志,缺失的首 token / 速度显示 `—`,读取错误保留上次成功结果。
|
package/lib/index.js
CHANGED
|
@@ -2217,8 +2217,11 @@ function nameOf(h, id) {
|
|
|
2217
2217
|
if (typeof x?.cwd === "string" && x.cwd) return x.cwd.split(/[\\/]/).filter(Boolean).pop() || id;
|
|
2218
2218
|
return id;
|
|
2219
2219
|
}
|
|
2220
|
-
function
|
|
2221
|
-
|
|
2220
|
+
function ownEventsOf(log) {
|
|
2221
|
+
return log.events.filter((e) => !(log.inheritedCut > 0 && typeof e.seq === "number" && e.seq < log.inheritedCut));
|
|
2222
|
+
}
|
|
2223
|
+
function one(log, parentCatalogLabel) {
|
|
2224
|
+
const { id, header, live } = log;
|
|
2222
2225
|
const fold = new UsageFold();
|
|
2223
2226
|
const totals = zero$1();
|
|
2224
2227
|
const records = [];
|
|
@@ -2229,15 +2232,14 @@ function one(log) {
|
|
|
2229
2232
|
let effort = "";
|
|
2230
2233
|
let pendingTopic = "";
|
|
2231
2234
|
const turnTopics = /* @__PURE__ */ new Map();
|
|
2232
|
-
const own =
|
|
2235
|
+
const own = ownEventsOf(log);
|
|
2233
2236
|
let label = "", title = "";
|
|
2234
2237
|
for (const ev of own) {
|
|
2235
2238
|
const d = ev.data ?? {};
|
|
2236
2239
|
if (!label && ev.type === "subagent/descriptor" && typeof d.label === "string" && d.label.trim()) label = d.label.trim();
|
|
2237
|
-
if (
|
|
2238
|
-
if (label && title) break;
|
|
2240
|
+
if (ev.type === "session/title" && typeof d.title === "string" && d.title.trim()) title = d.title.trim();
|
|
2239
2241
|
}
|
|
2240
|
-
const name = label || title || nameOf(header, id);
|
|
2242
|
+
const name = label || title || parentCatalogLabel || nameOf(header, id);
|
|
2241
2243
|
const stepTokens = /* @__PURE__ */ new Map();
|
|
2242
2244
|
const closedSteps = /* @__PURE__ */ new Set();
|
|
2243
2245
|
for (const ev of own) {
|
|
@@ -2418,7 +2420,21 @@ function one(log) {
|
|
|
2418
2420
|
}
|
|
2419
2421
|
async function buildSessionUsageDetail(persistence, sessions, rootId, ctx) {
|
|
2420
2422
|
const { root, descendants, coverage } = await readUsageTree(persistence, sessions, rootId);
|
|
2421
|
-
const
|
|
2423
|
+
const logs = [root, ...descendants];
|
|
2424
|
+
const catalogLabels = /* @__PURE__ */ new Map();
|
|
2425
|
+
for (const log of logs) {
|
|
2426
|
+
const labels = /* @__PURE__ */ new Map();
|
|
2427
|
+
for (const event of ownEventsOf(log)) {
|
|
2428
|
+
if (event.type !== "subagent/catalog") continue;
|
|
2429
|
+
const data = event.data;
|
|
2430
|
+
if (typeof data?.childId === "string" && data.childId && typeof data.label === "string" && data.label.trim()) labels.set(data.childId, data.label.trim());
|
|
2431
|
+
}
|
|
2432
|
+
catalogLabels.set(log.id, labels);
|
|
2433
|
+
}
|
|
2434
|
+
const parts = logs.map((log) => {
|
|
2435
|
+
const parentId = log.header?.parentSession ?? log.live?.header?.parentSession ?? log.live?.parentSession;
|
|
2436
|
+
return one(log, parentId === void 0 ? void 0 : catalogLabels.get(parentId)?.get(log.id));
|
|
2437
|
+
});
|
|
2422
2438
|
const totals = zero$1();
|
|
2423
2439
|
const models = /* @__PURE__ */ new Map();
|
|
2424
2440
|
const records = parts.flatMap((x) => x.records);
|
package/package.json
CHANGED
package/src/session-detail.ts
CHANGED
|
@@ -86,8 +86,12 @@ function nameOf(h: UsageSessionHeader | undefined, id: string): string {
|
|
|
86
86
|
return id
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
function
|
|
90
|
-
|
|
89
|
+
function ownEventsOf(log: UsageSessionLog) {
|
|
90
|
+
return log.events.filter(e => !(log.inheritedCut > 0 && typeof e.seq === 'number' && e.seq < log.inheritedCut))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function one(log: UsageSessionLog, parentCatalogLabel?: string) {
|
|
94
|
+
const { id, header, live } = log
|
|
91
95
|
const fold = new UsageFold(); const totals = zero(); const records: MutableRecord[] = []
|
|
92
96
|
const byKey = new Map<string, MutableRecord>(); let route = live?.requestContext?.() ?? {}
|
|
93
97
|
let ordinal = 0
|
|
@@ -96,18 +100,16 @@ function one(log: UsageSessionLog) {
|
|
|
96
100
|
// the prompt usually lands before `turn/start`, so buffer it until the turn opens.
|
|
97
101
|
let currentTurn = 0; let effort = ''; let pendingTopic = ''
|
|
98
102
|
const turnTopics = new Map<number, string>()
|
|
99
|
-
const own =
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
// generated `session/title`; only fall back to the header/cwd/id guesses.
|
|
103
|
+
const own = ownEventsOf(log)
|
|
104
|
+
// Prefer the child's own delegation label or latest title. Older CLI records
|
|
105
|
+
// carry their task name only in the actual parent's subagent/catalog.
|
|
103
106
|
let label = '', title = ''
|
|
104
107
|
for (const ev of own) {
|
|
105
108
|
const d: any = ev.data ?? {}
|
|
106
109
|
if (!label && ev.type === 'subagent/descriptor' && typeof d.label === 'string' && d.label.trim()) label = d.label.trim()
|
|
107
|
-
if (
|
|
108
|
-
if (label && title) break
|
|
110
|
+
if (ev.type === 'session/title' && typeof d.title === 'string' && d.title.trim()) title = d.title.trim()
|
|
109
111
|
}
|
|
110
|
-
const name = label || title || nameOf(header, id)
|
|
112
|
+
const name = label || title || parentCatalogLabel || nameOf(header, id)
|
|
111
113
|
// Decode accounting mirrors the official `sessionStats` projection: a step
|
|
112
114
|
// records its first token once (the earliest attempt wins, surviving an
|
|
113
115
|
// in-step retry), and only the assembled `assistant/message` closes the
|
|
@@ -192,7 +194,23 @@ function one(log: UsageSessionLog) {
|
|
|
192
194
|
|
|
193
195
|
export async function buildSessionUsageDetail(persistence: UsageSessionPersistence, sessions: UsageSessionStore | undefined, rootId: string, ctx?: Context): Promise<SessionUsageDetail> {
|
|
194
196
|
const { root, descendants, coverage } = await readUsageTree(persistence, sessions, rootId)
|
|
195
|
-
const
|
|
197
|
+
const logs = [root, ...descendants]
|
|
198
|
+
const catalogLabels = new Map<string, Map<string, string>>()
|
|
199
|
+
for (const log of logs) {
|
|
200
|
+
const labels = new Map<string, string>()
|
|
201
|
+
for (const event of ownEventsOf(log)) {
|
|
202
|
+
if (event.type !== 'subagent/catalog') continue
|
|
203
|
+
const data = event.data as { childId?: unknown; label?: unknown } | null
|
|
204
|
+
if (typeof data?.childId === 'string' && data.childId && typeof data.label === 'string' && data.label.trim()) {
|
|
205
|
+
labels.set(data.childId, data.label.trim())
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catalogLabels.set(log.id, labels)
|
|
209
|
+
}
|
|
210
|
+
const parts = logs.map(log => {
|
|
211
|
+
const parentId = log.header?.parentSession ?? log.live?.header?.parentSession ?? log.live?.parentSession
|
|
212
|
+
return one(log, parentId === undefined ? undefined : catalogLabels.get(parentId)?.get(log.id))
|
|
213
|
+
})
|
|
196
214
|
const totals = zero(); const models = new Map<string, { totals: SessionUsageTotals; requests: number; providers: Set<string> }>(); const records = parts.flatMap(x => x.records); for (const p of parts) add(totals, p.totals)
|
|
197
215
|
for (const r of records) { const key = r.model || '(unknown)'; const m = models.get(key) ?? { totals: zero(), requests: 0, providers: new Set<string>() }; add(m.totals, r.totals); m.requests++; if (r.provider) m.providers.add(r.provider); models.set(key, m) }
|
|
198
216
|
const subagents = parts.slice(1).map(p => ({ sessionId: p.id, name: p.name, totals: p.totals, requests: p.requests, averageFirstTokenMs: p.averageFirstTokenMs, tokensPerSecond: p.tokensPerSecond }))
|