dsh-key-usage 0.1.1 → 0.1.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/README.md +34 -12
- package/client/client.js +905 -260
- package/client/client.js.map +1 -1
- package/lib/index.js +4 -1
- package/lib/live-quota.js +96 -30
- package/lib/quota-snapshots.js +68 -0
- package/lib/routes.js +70 -11
- package/lib/session-meta.js +101 -0
- package/lib/session-usage.js +33 -1
- package/lib/sessions.js +58 -4
- package/package.json +9 -3
package/lib/sessions.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* 服务缺失或读取失败时降级为空结果 + coverage 说明,绝不影响台账相关的其它端点。
|
|
7
7
|
*/
|
|
8
8
|
import { attributeModelKey, foldSessionEvents, summarizeSession, } from './session-usage.js';
|
|
9
|
+
import { normalizeSessionId } from './session-meta.js';
|
|
9
10
|
/**
|
|
10
11
|
* 一次最多折叠的会话数:按更新时间倒序取,避免首次请求扫全量历史。
|
|
11
12
|
* 本机会话约 120 个 / 143 MB,取 200 覆盖全量;折叠结果按 revision 缓存,
|
|
@@ -192,11 +193,15 @@ function attributedModels(models, knownProviders) {
|
|
|
192
193
|
};
|
|
193
194
|
});
|
|
194
195
|
}
|
|
195
|
-
export function sessionRowsOf(records, knownProviders) {
|
|
196
|
+
export function sessionRowsOf(records, knownProviders, meta = {}) {
|
|
197
|
+
const titles = meta.titles ?? new Map();
|
|
198
|
+
const archived = meta.archived ?? new Set();
|
|
196
199
|
return records
|
|
197
200
|
.map((record) => ({
|
|
198
201
|
id: record.id,
|
|
199
|
-
|
|
202
|
+
// 标题优先级:宿主投影缓存的标题 → 会话头自带字段 → 首条用户提问 → 界面回落成短 id
|
|
203
|
+
title: titles.get(normalizeSessionId(record.id)) ?? record.title ?? record.summary.firstPrompt ?? null,
|
|
204
|
+
archived: archived.has(normalizeSessionId(record.id)),
|
|
200
205
|
workspace: record.workspace,
|
|
201
206
|
provider: record.summary.primaryModelKey === null ? null : attributeModelKey(record.summary.primaryModelKey, knownProviders).provider,
|
|
202
207
|
firstTime: record.summary.firstTime,
|
|
@@ -209,7 +214,53 @@ export function sessionRowsOf(records, knownProviders) {
|
|
|
209
214
|
}))
|
|
210
215
|
.sort((first, second) => (second.lastTime ?? 0) - (first.lastTime ?? 0));
|
|
211
216
|
}
|
|
212
|
-
export function
|
|
217
|
+
export function modelRowsOf(records, knownProviders) {
|
|
218
|
+
const byModel = new Map();
|
|
219
|
+
for (const record of records) {
|
|
220
|
+
const sessionLastTime = record.summary.lastTime;
|
|
221
|
+
for (const model of attributedModels(record.summary.models, knownProviders)) {
|
|
222
|
+
const key = model.provider + '|' + model.model;
|
|
223
|
+
let accumulator = byModel.get(key);
|
|
224
|
+
if (accumulator === undefined) {
|
|
225
|
+
accumulator = {
|
|
226
|
+
provider: model.provider, model: model.model, calls: 0,
|
|
227
|
+
inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, sessionCount: 0, lastTime: null,
|
|
228
|
+
};
|
|
229
|
+
byModel.set(key, accumulator);
|
|
230
|
+
}
|
|
231
|
+
accumulator.calls += model.calls;
|
|
232
|
+
accumulator.inputTokens += model.buckets.inputTokens;
|
|
233
|
+
accumulator.outputTokens += model.buckets.outputTokens;
|
|
234
|
+
accumulator.cacheReadTokens += model.buckets.cacheReadTokens;
|
|
235
|
+
accumulator.sessionCount += 1;
|
|
236
|
+
// 会话事件只有会话级时间戳,「最近活跃」只能取用到该模型的最晚会话时间,是近似值
|
|
237
|
+
if (sessionLastTime !== null && (accumulator.lastTime === null || sessionLastTime > accumulator.lastTime)) {
|
|
238
|
+
accumulator.lastTime = sessionLastTime;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return [...byModel.values()]
|
|
243
|
+
.map((accumulator) => {
|
|
244
|
+
const displayTokens = accumulator.inputTokens + accumulator.outputTokens + accumulator.cacheReadTokens;
|
|
245
|
+
return {
|
|
246
|
+
provider: accumulator.provider,
|
|
247
|
+
model: accumulator.model,
|
|
248
|
+
calls: accumulator.calls,
|
|
249
|
+
tokens: displayTokens,
|
|
250
|
+
cacheHitRate: displayTokens > 0 ? Math.round(accumulator.cacheReadTokens / displayTokens * 1000) / 10 : null,
|
|
251
|
+
sessionCount: accumulator.sessionCount,
|
|
252
|
+
lastTime: accumulator.lastTime,
|
|
253
|
+
buckets: {
|
|
254
|
+
inputTokens: accumulator.inputTokens,
|
|
255
|
+
outputTokens: accumulator.outputTokens,
|
|
256
|
+
cacheReadTokens: accumulator.cacheReadTokens,
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
})
|
|
260
|
+
.sort((first, second) => second.tokens - first.tokens);
|
|
261
|
+
}
|
|
262
|
+
export function projectRowsOf(records, knownProviders, meta = {}) {
|
|
263
|
+
const archived = meta.archived ?? new Set();
|
|
213
264
|
const byWorkspace = new Map();
|
|
214
265
|
for (const record of records) {
|
|
215
266
|
const workspace = record.workspace ?? '未归属工作区';
|
|
@@ -217,7 +268,7 @@ export function projectRowsOf(records, knownProviders) {
|
|
|
217
268
|
if (accumulator === undefined) {
|
|
218
269
|
accumulator = {
|
|
219
270
|
workspace, calls: 0, tokens: 0, cacheReadTokens: 0, inputTokens: 0, outputTokens: 0, cacheWriteTokens: 0,
|
|
220
|
-
sessionCount: 0, lastTime: null, providers: new Map(), models: new Map(),
|
|
271
|
+
sessionCount: 0, archivedSessionCount: 0, lastTime: null, providers: new Map(), models: new Map(),
|
|
221
272
|
};
|
|
222
273
|
byWorkspace.set(workspace, accumulator);
|
|
223
274
|
}
|
|
@@ -228,6 +279,8 @@ export function projectRowsOf(records, knownProviders) {
|
|
|
228
279
|
accumulator.cacheReadTokens += record.summary.buckets.cacheReadTokens;
|
|
229
280
|
accumulator.cacheWriteTokens += record.summary.buckets.cacheWriteTokens;
|
|
230
281
|
accumulator.sessionCount += 1;
|
|
282
|
+
if (archived.has(normalizeSessionId(record.id)))
|
|
283
|
+
accumulator.archivedSessionCount += 1;
|
|
231
284
|
const lastTime = record.summary.lastTime;
|
|
232
285
|
if (lastTime !== null && (accumulator.lastTime === null || lastTime > accumulator.lastTime))
|
|
233
286
|
accumulator.lastTime = lastTime;
|
|
@@ -259,6 +312,7 @@ export function projectRowsOf(records, knownProviders) {
|
|
|
259
312
|
tokens: accumulator.tokens,
|
|
260
313
|
cacheHitRate: displayTokens > 0 ? Math.round(accumulator.cacheReadTokens / displayTokens * 1000) / 10 : null,
|
|
261
314
|
sessionCount: accumulator.sessionCount,
|
|
315
|
+
archivedSessionCount: accumulator.archivedSessionCount,
|
|
262
316
|
lastTime: accumulator.lastTime,
|
|
263
317
|
providers: Object.fromEntries(accumulator.providers),
|
|
264
318
|
models: [...accumulator.models.values()].sort((first, second) => second.tokens - first.tokens),
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-key-usage",
|
|
3
3
|
"description": "DeepSeek Harness 侧边栏「使用情况」:按通道展示用量明细、活跃热力图、项目/会话用量与官方余额/配额(实时优先,失败回落快照)",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"packageManager": "pnpm@10.32.1",
|
|
6
7
|
"type": "module",
|
|
7
8
|
"keywords": [
|
|
8
9
|
"deepseek",
|
|
@@ -45,9 +46,14 @@
|
|
|
45
46
|
"registry": "https://registry.npmjs.org/"
|
|
46
47
|
},
|
|
47
48
|
"scripts": {
|
|
49
|
+
"build": "tsc -p tsconfig.server.json && tsdown && node scripts/normalize-client-banner.mjs",
|
|
48
50
|
"build:client": "tsdown && node scripts/normalize-client-banner.mjs",
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
+
"build:server": "tsc -p tsconfig.server.json",
|
|
52
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
53
|
+
"check": "node --check lib/index.js && node --check lib/ledger.js && node --check lib/collect.js && node --check lib/routes.js && node --check lib/sessions.js && node --check lib/session-usage.js && node --check lib/session-meta.js && node --check lib/quota-snapshots.js && node --check lib/merge.js && node --check lib/live-quota.js",
|
|
54
|
+
"test": "node test/ledger.test.mjs && node test/collect.test.mjs && node test/channels.test.mjs && node test/session-usage.test.mjs && node test/session-meta.test.mjs && node test/sessions.test.mjs && node test/merge.test.mjs && node test/live-quota.test.mjs && node test/routes.test.mjs",
|
|
55
|
+
"verify": "npm run typecheck && npm run build && npm run check && npm run test",
|
|
56
|
+
"pack:dry": "npm pack --dry-run"
|
|
51
57
|
},
|
|
52
58
|
"peerDependencies": {
|
|
53
59
|
"@deepseek-ai/cordis": "^4.0.1"
|