dsh-all-usage 1.1.17 → 1.2.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/CHANGELOG.md +453 -386
- package/LICENSE +21 -21
- package/README.en.md +270 -0
- package/README.md +145 -273
- package/assets/model-icons/ai2-color.svg +1 -0
- package/assets/model-icons/ai21-brand-color.svg +1 -0
- package/assets/model-icons/aionlabs.svg +1 -0
- package/assets/model-icons/alephalpha.svg +1 -0
- package/assets/model-icons/baai.svg +1 -0
- package/assets/model-icons/baichuan-color.svg +1 -0
- package/assets/model-icons/baidu-color.svg +1 -0
- package/assets/model-icons/bytedance-color.svg +1 -0
- package/assets/model-icons/cohere-color.svg +1 -0
- package/assets/model-icons/hunyuan-color.svg +1 -0
- package/assets/model-icons/ibm.svg +1 -0
- package/assets/model-icons/iflytekcloud-color.svg +1 -0
- package/assets/model-icons/internlm-color.svg +1 -0
- package/assets/model-icons/kling-color.svg +1 -0
- package/assets/model-icons/liquid.svg +1 -0
- package/assets/model-icons/longcat-color.svg +1 -0
- package/assets/model-icons/manifest.json +739 -213
- package/assets/model-icons/microsoft-color.svg +1 -0
- package/assets/model-icons/mistral-color.svg +1 -0
- package/assets/model-icons/nousresearch.svg +1 -0
- package/assets/model-icons/nova-color.svg +1 -0
- package/assets/model-icons/nvidia-color.svg +1 -0
- package/assets/model-icons/perplexity-color.svg +1 -0
- package/assets/model-icons/sensenova-color.svg +1 -0
- package/assets/model-icons/skywork-color.svg +1 -0
- package/assets/model-icons/snowflake-color.svg +1 -0
- package/assets/model-icons/stepfun-color.svg +1 -0
- package/assets/model-icons/tencent-color.svg +1 -0
- package/assets/model-icons/tii-color.svg +1 -0
- package/assets/model-icons/upstage-color.svg +1 -0
- package/assets/model-icons/voyage-color.svg +1 -0
- package/assets/model-icons/xiaomimimo.svg +1 -0
- package/assets/model-icons/yandex.svg +1 -0
- package/assets/readme-architecture-en.svg +95 -0
- package/assets/readme-architecture-zh.svg +95 -0
- package/assets/readme-sync-en.svg +81 -0
- package/assets/readme-sync-zh.svg +81 -0
- package/assets/screenshots/en/01-overview.png +0 -0
- package/assets/screenshots/en/02-heatmap.png +0 -0
- package/assets/screenshots/en/03-model-usage.png +0 -0
- package/assets/screenshots/en/04-request-logs.png +0 -0
- package/assets/screenshots/en/05-cost-settings.png +0 -0
- package/assets/screenshots/zh/01-overview.png +0 -0
- package/assets/screenshots/zh/02-heatmap.png +0 -0
- package/assets/screenshots/zh/03-model-usage.png +0 -0
- package/assets/screenshots/zh/04-request-logs.png +0 -0
- package/assets/screenshots/zh/05-cost-settings.png +0 -0
- package/cordis.patch.yml +5 -5
- package/lib/aggregation.js +1842 -1112
- package/lib/client.js +1 -1
- package/lib/http.js +417 -370
- package/lib/icons.js +40 -0
- package/lib/ledger.js +1000 -531
- package/lib/plugin.js +347 -323
- package/lib/pricing-runtime.js +615 -551
- package/lib/pricing.js +1463 -1412
- package/lib/records-cache.js +113 -0
- package/lib/session-sync.js +976 -841
- package/lib/usage-core.js +33 -10
- package/lib/version-check.js +133 -0
- package/package.json +8 -3
- package/screenshots.json +10 -3
- package/assets/screenshot-1.png +0 -0
- package/assets/screenshot-2.png +0 -0
- package/assets/screenshot-3.png +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
// Preserve every JavaScript key code unit, including legacy lone surrogates.
|
|
4
|
+
export const recordKeyHash = key => createHash('sha256').update(key, 'utf16le').digest('base64url')
|
|
5
|
+
|
|
6
|
+
// Complete rows also retain costs and their parsed decimals. Compact scopes
|
|
7
|
+
// share only a key directory and sorted numeric ids after this total is reached.
|
|
8
|
+
export function recordKeyLocator(sid, key) {
|
|
9
|
+
const canonical = key.length <= 256 && key.startsWith(sid + ':') && /^(?:step:\d+:\d+|event:\d+)$/.test(key.slice(sid.length + 1))
|
|
10
|
+
return canonical ? 'raw:' + key : 'hash:' + recordKeyHash(key)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const RECORDS_CACHE_ROW_LIMIT = 24000
|
|
14
|
+
export const RECORDS_CACHE_ENTRY_LIMIT = 20
|
|
15
|
+
|
|
16
|
+
export function getRecordsResult(cache, key) {
|
|
17
|
+
const entry = cache.get(key)
|
|
18
|
+
if (entry !== undefined) { cache.delete(key); cache.set(key, entry) }
|
|
19
|
+
return entry
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function deleteRecordsResult(cache, key) {
|
|
23
|
+
const entry = cache.get(key)
|
|
24
|
+
if (entry === undefined) return
|
|
25
|
+
cache.delete(key)
|
|
26
|
+
if (entry.kind !== 'index') return
|
|
27
|
+
const { catalog } = entry
|
|
28
|
+
for (const id of entry.ids) {
|
|
29
|
+
const reference = catalog.references.get(id)
|
|
30
|
+
reference.uses -= 1
|
|
31
|
+
if (reference.uses !== 0) continue
|
|
32
|
+
const keys = catalog.bySession.get(reference.sid)
|
|
33
|
+
keys.delete(reference.key === undefined ? 'hash:' + reference.keyHash : 'raw:' + reference.key)
|
|
34
|
+
if (keys.size === 0) catalog.bySession.delete(reference.sid)
|
|
35
|
+
catalog.references.delete(id)
|
|
36
|
+
catalog.freeIds.push(id)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function compactResult(entry, catalog) {
|
|
41
|
+
const ids = new Uint32Array(entry.rows.length)
|
|
42
|
+
for (let index = 0; index < entry.rows.length; index += 1) {
|
|
43
|
+
const { item } = entry.rows[index]
|
|
44
|
+
// Legacy event keys may contain serialized event data. Only retain bounded
|
|
45
|
+
// canonical step/event keys; all other keys use a fixed-size digest.
|
|
46
|
+
const lookup = recordKeyLocator(item.sid, item.key)
|
|
47
|
+
const locator = lookup.startsWith('raw:') ? { key: item.key } : { keyHash: lookup.slice(5) }
|
|
48
|
+
let keys = catalog.bySession.get(item.sid)
|
|
49
|
+
if (keys === undefined) { keys = new Map(); catalog.bySession.set(item.sid, keys) }
|
|
50
|
+
let id = keys.get(lookup)
|
|
51
|
+
if (id === undefined) {
|
|
52
|
+
id = catalog.freeIds.pop() ?? catalog.nextId++
|
|
53
|
+
keys.set(lookup, id)
|
|
54
|
+
catalog.references.set(id, { ...locator, sid: item.sid, uses: 0 })
|
|
55
|
+
}
|
|
56
|
+
catalog.references.get(id).uses += 1
|
|
57
|
+
ids[index] = id
|
|
58
|
+
}
|
|
59
|
+
entry.kind = 'index'
|
|
60
|
+
entry.ids = ids
|
|
61
|
+
entry.catalog = catalog
|
|
62
|
+
delete entry.rows
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
export function cacheRecordsResult(cache, key, rows, forceIndex = false) {
|
|
67
|
+
deleteRecordsResult(cache, key)
|
|
68
|
+
const entry = { kind: 'rows', rows }
|
|
69
|
+
cache.set(key, entry)
|
|
70
|
+
while (cache.size > RECORDS_CACHE_ENTRY_LIMIT) deleteRecordsResult(cache, cache.keys().next().value)
|
|
71
|
+
let fullRows = 0
|
|
72
|
+
let catalog
|
|
73
|
+
for (const result of cache.values()) {
|
|
74
|
+
if (result.kind === 'rows') fullRows += result.rows.length
|
|
75
|
+
else catalog = result.catalog
|
|
76
|
+
}
|
|
77
|
+
const compact = result => {
|
|
78
|
+
// The directory is owned only by compact entries; clearing the cache drops
|
|
79
|
+
// it too. Eviction releases that entry's keys even while other scopes live.
|
|
80
|
+
catalog ||= { bySession: new Map(), references: new Map(), nextId: 0, freeIds: [] }
|
|
81
|
+
fullRows -= result.rows.length
|
|
82
|
+
compactResult(result, catalog)
|
|
83
|
+
}
|
|
84
|
+
// An oversized new range must not push useful small ranges out of the budget.
|
|
85
|
+
// A forced index describes metadata that was never fully materialized (the
|
|
86
|
+
// digest path), so it must never be retained as if it were a real row scope.
|
|
87
|
+
if (forceIndex || entry.rows.length > RECORDS_CACHE_ROW_LIMIT) compact(entry)
|
|
88
|
+
for (const result of cache.values()) {
|
|
89
|
+
if (fullRows <= RECORDS_CACHE_ROW_LIMIT) break
|
|
90
|
+
if (result.kind === 'rows') compact(result)
|
|
91
|
+
}
|
|
92
|
+
return entry
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function recordPageReferences(entry, offset, limit) {
|
|
96
|
+
// Copy keys before the caller awaits I/O: eviction can recycle numeric ids
|
|
97
|
+
// during that await, while this page must still refer to its selected keys.
|
|
98
|
+
return Array.from(entry.ids.slice(offset, offset + limit), id => {
|
|
99
|
+
const { sid, key, keyHash } = entry.catalog.references.get(id)
|
|
100
|
+
return key === undefined ? { sid, keyHash } : { sid, key }
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function recordsCacheStats(cache) {
|
|
105
|
+
let rows = 0, indices = 0, indexedRows = 0
|
|
106
|
+
const catalogs = new Set()
|
|
107
|
+
for (const entry of cache.values()) {
|
|
108
|
+
if (entry.kind === 'rows') rows += entry.rows.length
|
|
109
|
+
else { indices += 1; indexedRows += entry.ids.length; catalogs.add(entry.catalog) }
|
|
110
|
+
}
|
|
111
|
+
return { entries: cache.size, rows, rowLimit: RECORDS_CACHE_ROW_LIMIT, indices, indexedRows,
|
|
112
|
+
indexKeys: Array.from(catalogs).reduce((sum, catalog) => sum + catalog.references.size, 0) }
|
|
113
|
+
}
|