@rayadesu/dsh-llm-billing 0.3.7 → 0.3.9
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.i18n.yaml +2 -2
- package/README.md +15 -11
- package/README.zh.md +15 -11
- package/lib/index.js +924 -382
- package/lib/typert.host.js +49 -5
- package/lib/typert.remote-client.d.ts +5 -3
- package/lib/typert.remote-client.js +49 -5
- package/lib/types/balance.d.ts +33 -15
- package/lib/types/balance.js +28 -13
- package/lib/types/billing.d.ts +217 -21
- package/lib/types/billing.js +445 -71
- package/lib/types/index.d.ts +14 -4
- package/lib/types/index.js +213 -70
- package/lib/types/projection.d.ts +24 -25
- package/lib/types/projection.js +30 -31
- package/lib/types/today-spend.d.ts +138 -62
- package/lib/types/today-spend.js +209 -261
- package/lib/types/types.d.ts +30 -5
- package/lib/types/types.js +8 -0
- package/package.json +5 -1
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
* compute the aggregate behind a cache miss:
|
|
5
5
|
*
|
|
6
6
|
* - projection path (plan C): live sessions read their eagerly folded
|
|
7
|
-
* `billingTodaySpend` projection cell; cold sessions
|
|
8
|
-
* projection-cache
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* `billingTodaySpend` projection cell; cold sessions are answered from the
|
|
8
|
+
* zero-I/O projection-cache row whenever that row's own day is not the
|
|
9
|
+
* queried one, and otherwise resolved through one detached local fold over
|
|
10
|
+
* a full `inspect`. Persisted revisions gate every cold read, so a session
|
|
11
|
+
* whose log did not change since the last resolution costs nothing — and a
|
|
12
|
+
* failed resolution is remembered by revision instead of being retried on
|
|
13
|
+
* every scan.
|
|
12
14
|
* - events path (plans A2/A3): collect and price only today's events in one
|
|
13
15
|
* pass (per-event Beijing-day filter during collection) with a hard cap,
|
|
14
16
|
* skipping sessions whose persisted revision is unchanged since the last
|
|
@@ -20,14 +22,15 @@
|
|
|
20
22
|
* unchanged log provably cannot change the aggregate.
|
|
21
23
|
*
|
|
22
24
|
* Forked sessions never double-count: a fork child's log opens with a
|
|
23
|
-
* verbatim copy of its source session's events (its inherited boundary),
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* durable
|
|
30
|
-
*
|
|
25
|
+
* verbatim copy of its source session's events (its inherited boundary), so
|
|
26
|
+
* the scanner prices only the child's OWN events on every path. The
|
|
27
|
+
* `billingTodaySpend` unit is boundary-aware (its state carries the inherited
|
|
28
|
+
* cut, and `apply` skips events below it), so the eager cell is correct for a
|
|
29
|
+
* fork child; the cold path skips the projection cache for a seeded session
|
|
30
|
+
* (its cached row may predate the boundary) and folds its own events with the
|
|
31
|
+
* durable cut instead. The boundary is the durable session state, read across
|
|
32
|
+
* both DSH runtime families — a resumed fork child keeps its original
|
|
33
|
+
* boundary and an unseeded session stays at 0.
|
|
31
34
|
*
|
|
32
35
|
* The live `Session` log surface changed in 0.1.2-alpha.4: `Session.events`
|
|
33
36
|
* was removed and replaced by `Session.snapshotEvents()` / `ownEvents()`, and
|
|
@@ -41,7 +44,7 @@
|
|
|
41
44
|
import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session';
|
|
42
45
|
import type { SessionPersistenceRevision } from '@deepseek-ai/dsh-session-persistence';
|
|
43
46
|
import type { ResolvedBilling } from './billing.ts';
|
|
44
|
-
import type { DeepSeekTodaySessionsSpend, DeepSeekTodaySpend } from './types.ts';
|
|
47
|
+
import type { DeepSeekTodaySessionSpend, DeepSeekTodaySessionsSpend, DeepSeekTodaySpend } from './types.ts';
|
|
45
48
|
import { BILLING_UNIT_KEY, type BillingUnitFold, type BillingUnitState } from './projection.ts';
|
|
46
49
|
/**
|
|
47
50
|
* Fold one session's durable display title: the latest `session/title`
|
|
@@ -86,6 +89,12 @@ export interface ScannerPersistedHeader {
|
|
|
86
89
|
readonly seedLength?: number;
|
|
87
90
|
/** 0.1.2-alpha.4+: whether the session has a fork-inherited prefix (exact cut arrives with the inspect result). */
|
|
88
91
|
readonly isSeeded?: boolean;
|
|
92
|
+
/** Session format generation; part of the projection-cache record identity. */
|
|
93
|
+
readonly version?: number;
|
|
94
|
+
/** Session creation time; part of the projection-cache record identity. */
|
|
95
|
+
readonly createdAt?: number;
|
|
96
|
+
/** Working directory recorded on the header; part of the projection-cache record identity. */
|
|
97
|
+
readonly cwd?: string;
|
|
89
98
|
}
|
|
90
99
|
/** One stored-session read: the full event log plus the durable inherited boundary. */
|
|
91
100
|
export interface ScannerPersistedRead {
|
|
@@ -109,6 +118,21 @@ export interface ScannerPersistenceLegacy {
|
|
|
109
118
|
events: readonly SessionEvent[];
|
|
110
119
|
}>;
|
|
111
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* One handle read result across DSH generations. The handle seam first
|
|
123
|
+
* returned the bare event array; since `9b78f99dec` (2026-09-06, in the
|
|
124
|
+
* 0.1.5-alpha.1 checkout) it returns `{ eventState, events }`. Both shapes are
|
|
125
|
+
* accepted so the same build serves the npm alpha line and the checkout.
|
|
126
|
+
*/
|
|
127
|
+
export type ScannerHandleRead = readonly SessionEvent[] | {
|
|
128
|
+
readonly events: readonly SessionEvent[];
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Unwrap a handle read across both return shapes.
|
|
132
|
+
* @param read - the handle's read result.
|
|
133
|
+
* @returns the event array.
|
|
134
|
+
*/
|
|
135
|
+
export declare function handleReadEvents(read: ScannerHandleRead): readonly SessionEvent[];
|
|
112
136
|
/** 0.1.2-alpha.5+ handle-based persistence slice: service-level `list` / `open` + `SessionHandle`. */
|
|
113
137
|
export interface ScannerPersistenceHandle {
|
|
114
138
|
list(): Promise<readonly {
|
|
@@ -122,7 +146,7 @@ export interface ScannerPersistenceHandle {
|
|
|
122
146
|
};
|
|
123
147
|
/** 0.1.2-alpha.5+: the handle carries the exact inherited cut beside the header. */
|
|
124
148
|
readonly inheritedEventCount?: number;
|
|
125
|
-
read(): Promise<
|
|
149
|
+
read(): Promise<ScannerHandleRead>;
|
|
126
150
|
close(): Promise<void>;
|
|
127
151
|
}>;
|
|
128
152
|
}
|
|
@@ -160,11 +184,16 @@ export interface TodaySpendScannerDeps {
|
|
|
160
184
|
projections?: () => {
|
|
161
185
|
stateOf(session: ScannerSession, key: typeof BILLING_UNIT_KEY): BillingUnitState | undefined;
|
|
162
186
|
} | undefined;
|
|
163
|
-
/**
|
|
187
|
+
/**
|
|
188
|
+
* Resolves the projection cache at scan time (absent → detached fold for
|
|
189
|
+
* cold sessions). Only the zero-I/O `cachedSnapshot` reader is used: it
|
|
190
|
+
* serves already-checkpointed wire rows without touching a session log.
|
|
191
|
+
*/
|
|
164
192
|
projectionCache?: () => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
193
|
+
cachedSnapshot(header: ScannerPersistedHeader, inheritedEventCount: number, keys?: readonly string[]): {
|
|
194
|
+
readonly asOfSeq: number;
|
|
195
|
+
readonly values: Partial<Record<typeof BILLING_UNIT_KEY, BillingUnitState>>;
|
|
196
|
+
} | undefined;
|
|
168
197
|
} | undefined;
|
|
169
198
|
/**
|
|
170
199
|
* Registers the billing unit on the projection registry, called once before
|
|
@@ -219,6 +248,23 @@ export declare class TodaySpendCache<T = DeepSeekTodaySpend> {
|
|
|
219
248
|
*/
|
|
220
249
|
get(force?: boolean): Promise<T>;
|
|
221
250
|
}
|
|
251
|
+
/** Max session-ids kept in the scanner's cold-resolution cache before eviction. */
|
|
252
|
+
export declare const COLD_RESOLVE_CACHE_LIMIT = 1024;
|
|
253
|
+
/** Max session-ids kept in the scanner's cold-failure cache before eviction. */
|
|
254
|
+
export declare const COLD_FAILED_CACHE_LIMIT = 1024;
|
|
255
|
+
/** Bounded parallel fan-out for cold-session resolution. */
|
|
256
|
+
export declare const COLD_RESOLVE_CONCURRENCY = 8;
|
|
257
|
+
/**
|
|
258
|
+
* One day's aggregate plus its per-session ranking, computed in a single pass:
|
|
259
|
+
* the aggregate is the sum of the rows, so both reads share every session
|
|
260
|
+
* read, unit fold, and title fold.
|
|
261
|
+
*/
|
|
262
|
+
export interface TodaySpendDetail {
|
|
263
|
+
/** Today's spend across every session. */
|
|
264
|
+
aggregate: DeepSeekTodaySpend;
|
|
265
|
+
/** Today's per-session rows, sorted by cost descending. */
|
|
266
|
+
sessions: DeepSeekTodaySessionSpend[];
|
|
267
|
+
}
|
|
222
268
|
/**
|
|
223
269
|
* The aggregate computation behind a cache miss. Chooses the projection path
|
|
224
270
|
* when the projection registry is composed, the events path otherwise; both
|
|
@@ -229,10 +275,10 @@ export declare class TodaySpendScanner {
|
|
|
229
275
|
private readonly deps;
|
|
230
276
|
/** Cold sessions resolved on the projection path: id → revision + unit state + title. */
|
|
231
277
|
private readonly coldResolved;
|
|
278
|
+
/** Cold sessions whose resolution failed: id → revision (retried only when the log changes). */
|
|
279
|
+
private readonly coldFailed;
|
|
232
280
|
/** Cold sessions resolved on the events path: id → revision (events were collected). */
|
|
233
281
|
private lastEventsScan;
|
|
234
|
-
/** Live fork children priced on the projection path: id → own-events count + folded state. */
|
|
235
|
-
private readonly ownStates;
|
|
236
282
|
constructor(deps: TodaySpendScannerDeps);
|
|
237
283
|
/**
|
|
238
284
|
* Compute today's aggregate for one Beijing day.
|
|
@@ -249,62 +295,92 @@ export declare class TodaySpendScanner {
|
|
|
249
295
|
*/
|
|
250
296
|
scanSessions(dayKey: string): Promise<DeepSeekTodaySessionsSpend>;
|
|
251
297
|
/**
|
|
252
|
-
*
|
|
253
|
-
* the
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
298
|
+
* Compute the day's aggregate AND its per-session ranking in ONE pass: the
|
|
299
|
+
* aggregate is the sum of the rows, so the two reads share every session
|
|
300
|
+
* read, unit fold, and title fold instead of scanning twice. Chooses the
|
|
301
|
+
* projection path when the projection registry is composed, the events path
|
|
302
|
+
* otherwise.
|
|
303
|
+
* @param dayKey - the Beijing-time calendar-day key to aggregate.
|
|
304
|
+
* @returns the aggregate plus per-session rows sorted by cost descending.
|
|
305
|
+
*/
|
|
306
|
+
scanDetail(dayKey: string): Promise<TodaySpendDetail>;
|
|
307
|
+
/**
|
|
308
|
+
* Resolve one cold session's billing unit state and display title.
|
|
309
|
+
*
|
|
310
|
+
* The zero-I/O projection-cache row answers the query directly whenever its
|
|
311
|
+
* own latest priced day is NOT the queried day: the row then proves the
|
|
312
|
+
* session contributed nothing to the queried day, so the log is never read.
|
|
313
|
+
* When the row IS the queried day (or no usable row exists) the session is
|
|
314
|
+
* inspected and folded locally, because the row may trail the log (a crash
|
|
315
|
+
* between the last checkpoint and the session's last event).
|
|
316
|
+
*
|
|
317
|
+
* A cache-served value carries no title (the ladder only stores projection
|
|
318
|
+
* values), so such rows report `title: null`. A SEEDED session (fork child)
|
|
319
|
+
* skips the cache entirely: its cached row was folded over the inherited
|
|
258
320
|
* prefix too, so it always detaches through inspect with the durable
|
|
259
321
|
* boundary (the inspect result's inherited count or `meta.seedLength`,
|
|
260
322
|
* depending on the runtime family) applied to the local fold.
|
|
261
|
-
* @param
|
|
262
|
-
* @param seeded - whether the session carries a fork-inherited prefix
|
|
263
|
-
*
|
|
264
|
-
* at and before the 0.1.1-rc.2 baseline).
|
|
323
|
+
* @param header - the listed session header (the cache identity witness).
|
|
324
|
+
* @param seeded - whether the session carries a fork-inherited prefix.
|
|
325
|
+
* @param dayKey - the Beijing-time day being aggregated.
|
|
265
326
|
* @returns the resolved state and title, or `undefined` when unreadable.
|
|
266
327
|
*/
|
|
267
328
|
private resolveCold;
|
|
268
329
|
/**
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
330
|
+
* Live-session entries of one projection-path scan: each session with its
|
|
331
|
+
* eager `billingTodaySpend` cell. The cell is boundary-aware (the unit skips
|
|
332
|
+
* a fork child's inherited prefix), so a fork child reads the same own-event
|
|
333
|
+
* spend a non-fork session does.
|
|
334
|
+
*/
|
|
335
|
+
private liveBillingEntries;
|
|
336
|
+
/**
|
|
337
|
+
* Cold-ladder adopt: for every stored session not live, either the
|
|
338
|
+
* revision-gated resolution already in {@link coldResolved} is adopted
|
|
339
|
+
* (unchanged log costs nothing) or the session is queued behind a bounded
|
|
340
|
+
* parallel fan-out, resolved, remembered, and then adopted. A session whose
|
|
341
|
+
* resolution failed is remembered too (by revision), so an unreadable log
|
|
342
|
+
* is not re-read on every scan; a changed revision retries it. One
|
|
343
|
+
* unreadable session never blanks the whole-day aggregate.
|
|
344
|
+
* @param liveIds - ids of sessions already folded from the live store.
|
|
345
|
+
* @param snapshots - stored snapshot list (either runtime family).
|
|
346
|
+
* @param dayKey - the Beijing-time day being aggregated.
|
|
347
|
+
* @param adopt - fold one resolved cold session into the scan's result.
|
|
276
348
|
*/
|
|
277
|
-
private
|
|
278
|
-
/** Projection path: eager cells for live sessions, revision-gated cold ladder for the rest. */
|
|
279
|
-
private scanProjections;
|
|
349
|
+
private coldAdopt;
|
|
280
350
|
/**
|
|
281
|
-
* Events
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
351
|
+
* Events-path collection shared by both aggregate and per-session scans:
|
|
352
|
+
* fold each session's log with the shared pricing fold (attempt samples with
|
|
353
|
+
* same-step replacement) and announce the session's latest-day spend, gated
|
|
354
|
+
* by revisions — a persisted session whose log did not change since the last
|
|
355
|
+
* scan is skipped. A fork child's inherited prefix (`seq < seedLength`) is
|
|
356
|
+
* skipped, so each model output is priced only in its source session. The
|
|
357
|
+
* hard cap counts the queried day's events; the revision watermark only
|
|
358
|
+
* advances on a complete pass.
|
|
359
|
+
* @param dayKey - the Beijing-time calendar-day key to aggregate.
|
|
360
|
+
* @param onSession - fold one session's state plus its complete log.
|
|
361
|
+
* @returns whether the hard cap truncated the scan.
|
|
285
362
|
*/
|
|
286
|
-
private
|
|
363
|
+
private collectTodayEvents;
|
|
287
364
|
/**
|
|
288
|
-
* Projection
|
|
289
|
-
* folded from the live log, so a rename is reflected immediately),
|
|
290
|
-
* revision-gated cold ladder for the rest (title resolved on inspect,
|
|
291
|
-
*
|
|
292
|
-
*
|
|
365
|
+
* Projection path, one pass for both outputs: eager cells for live sessions
|
|
366
|
+
* (title folded from the live log, so a rename is reflected immediately),
|
|
367
|
+
* revision-gated cold ladder for the rest (title resolved on inspect, `null`
|
|
368
|
+
* when answered from the projection cache). A fork child's cell covers its
|
|
369
|
+
* inherited prefix, so its own-events fold supplies both outputs.
|
|
293
370
|
* @param dayKey - the Beijing-time calendar-day key to aggregate.
|
|
294
|
-
* @returns
|
|
371
|
+
* @returns the aggregate plus per-session rows, sorted by cost descending.
|
|
295
372
|
*/
|
|
296
|
-
private
|
|
373
|
+
private scanDetailProjections;
|
|
297
374
|
/**
|
|
298
|
-
* Events
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
* is re-read.
|
|
375
|
+
* Events path, one pass for both outputs: price today's events (per-event
|
|
376
|
+
* Beijing-day filter during collection, hard cap), gated by revisions. A
|
|
377
|
+
* fork child's inherited prefix (`seq < seedLength`) is skipped, so each
|
|
378
|
+
* model output is priced only in its source session. Titles fold from each
|
|
379
|
+
* session's complete log — a `session/title` event can predate today — so a
|
|
380
|
+
* rename is reflected as soon as the session's log is re-read.
|
|
305
381
|
* @param dayKey - the Beijing-time calendar-day key to aggregate.
|
|
306
|
-
* @returns
|
|
382
|
+
* @returns the aggregate plus per-session rows, sorted by cost descending.
|
|
307
383
|
*/
|
|
308
|
-
private
|
|
384
|
+
private scanDetailEvents;
|
|
309
385
|
}
|
|
310
386
|
//# sourceMappingURL=today-spend.d.ts.map
|