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.
Files changed (69) hide show
  1. package/CHANGELOG.md +453 -386
  2. package/LICENSE +21 -21
  3. package/README.en.md +270 -0
  4. package/README.md +145 -273
  5. package/assets/model-icons/ai2-color.svg +1 -0
  6. package/assets/model-icons/ai21-brand-color.svg +1 -0
  7. package/assets/model-icons/aionlabs.svg +1 -0
  8. package/assets/model-icons/alephalpha.svg +1 -0
  9. package/assets/model-icons/baai.svg +1 -0
  10. package/assets/model-icons/baichuan-color.svg +1 -0
  11. package/assets/model-icons/baidu-color.svg +1 -0
  12. package/assets/model-icons/bytedance-color.svg +1 -0
  13. package/assets/model-icons/cohere-color.svg +1 -0
  14. package/assets/model-icons/hunyuan-color.svg +1 -0
  15. package/assets/model-icons/ibm.svg +1 -0
  16. package/assets/model-icons/iflytekcloud-color.svg +1 -0
  17. package/assets/model-icons/internlm-color.svg +1 -0
  18. package/assets/model-icons/kling-color.svg +1 -0
  19. package/assets/model-icons/liquid.svg +1 -0
  20. package/assets/model-icons/longcat-color.svg +1 -0
  21. package/assets/model-icons/manifest.json +739 -213
  22. package/assets/model-icons/microsoft-color.svg +1 -0
  23. package/assets/model-icons/mistral-color.svg +1 -0
  24. package/assets/model-icons/nousresearch.svg +1 -0
  25. package/assets/model-icons/nova-color.svg +1 -0
  26. package/assets/model-icons/nvidia-color.svg +1 -0
  27. package/assets/model-icons/perplexity-color.svg +1 -0
  28. package/assets/model-icons/sensenova-color.svg +1 -0
  29. package/assets/model-icons/skywork-color.svg +1 -0
  30. package/assets/model-icons/snowflake-color.svg +1 -0
  31. package/assets/model-icons/stepfun-color.svg +1 -0
  32. package/assets/model-icons/tencent-color.svg +1 -0
  33. package/assets/model-icons/tii-color.svg +1 -0
  34. package/assets/model-icons/upstage-color.svg +1 -0
  35. package/assets/model-icons/voyage-color.svg +1 -0
  36. package/assets/model-icons/xiaomimimo.svg +1 -0
  37. package/assets/model-icons/yandex.svg +1 -0
  38. package/assets/readme-architecture-en.svg +95 -0
  39. package/assets/readme-architecture-zh.svg +95 -0
  40. package/assets/readme-sync-en.svg +81 -0
  41. package/assets/readme-sync-zh.svg +81 -0
  42. package/assets/screenshots/en/01-overview.png +0 -0
  43. package/assets/screenshots/en/02-heatmap.png +0 -0
  44. package/assets/screenshots/en/03-model-usage.png +0 -0
  45. package/assets/screenshots/en/04-request-logs.png +0 -0
  46. package/assets/screenshots/en/05-cost-settings.png +0 -0
  47. package/assets/screenshots/zh/01-overview.png +0 -0
  48. package/assets/screenshots/zh/02-heatmap.png +0 -0
  49. package/assets/screenshots/zh/03-model-usage.png +0 -0
  50. package/assets/screenshots/zh/04-request-logs.png +0 -0
  51. package/assets/screenshots/zh/05-cost-settings.png +0 -0
  52. package/cordis.patch.yml +5 -5
  53. package/lib/aggregation.js +1842 -1112
  54. package/lib/client.js +1 -1
  55. package/lib/http.js +417 -370
  56. package/lib/icons.js +40 -0
  57. package/lib/ledger.js +1000 -531
  58. package/lib/plugin.js +347 -323
  59. package/lib/pricing-runtime.js +615 -551
  60. package/lib/pricing.js +1463 -1412
  61. package/lib/records-cache.js +113 -0
  62. package/lib/session-sync.js +976 -841
  63. package/lib/usage-core.js +33 -10
  64. package/lib/version-check.js +133 -0
  65. package/package.json +8 -3
  66. package/screenshots.json +10 -3
  67. package/assets/screenshot-1.png +0 -0
  68. package/assets/screenshot-2.png +0 -0
  69. 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
+ }