@rayadesu/dsh-llm-billing 0.2.3 → 0.2.4
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 +1 -1
- package/README.zh.md +1 -1
- package/lib/index.js +295 -20
- package/lib/typert.host.js +77 -3
- package/lib/typert.remote-client.d.ts +5 -1
- package/lib/typert.remote-client.js +77 -3
- package/lib/types/balance.d.ts +31 -1
- package/lib/types/balance.js +29 -0
- package/lib/types/billing.d.ts +19 -1
- package/lib/types/billing.js +44 -0
- package/lib/types/index.d.ts +2 -2
- package/lib/types/index.js +10 -4
- package/lib/types/today-spend.d.ts +54 -6
- package/lib/types/today-spend.js +214 -25
- package/lib/types/types.d.ts +23 -0
- package/package.json +1 -1
|
@@ -27,6 +27,14 @@ const _rayadesu_dsh_llm_billing_billing_getSessionSpend_result$schema = z.object
|
|
|
27
27
|
'outputCost': z.number(),
|
|
28
28
|
})),
|
|
29
29
|
})
|
|
30
|
+
const _rayadesu_dsh_llm_billing_billing_getTodaySessionsSpend_parameter_0$schema = z.union([z.undefined(), z.literal(false), z.literal(true)])
|
|
31
|
+
const _rayadesu_dsh_llm_billing_billing_getTodaySessionsSpend_result$schema = z.object({
|
|
32
|
+
'sessions': z.array(z.object({
|
|
33
|
+
'sessionId': z.intersection(z.string(), z.unknown()),
|
|
34
|
+
'title': z.union([z.literal(null), z.string()]),
|
|
35
|
+
'total': z.number(),
|
|
36
|
+
})),
|
|
37
|
+
})
|
|
30
38
|
const _rayadesu_dsh_llm_billing_billing_getTodaySpend_parameter_0$schema = z.union([z.undefined(), z.literal(false), z.literal(true)])
|
|
31
39
|
const _rayadesu_dsh_llm_billing_billing_getTodaySpend_result$schema = z.object({
|
|
32
40
|
'total': z.number(),
|
|
@@ -44,6 +52,11 @@ const _rayadesu_dsh_llm_billing_billing_getTodaySpend_result$schema = z.object({
|
|
|
44
52
|
'outputCost': z.number(),
|
|
45
53
|
})),
|
|
46
54
|
})
|
|
55
|
+
const _rayadesu_dsh_llm_billing_billing_getTurnSpend_parameter_0$schema = z.intersection(z.string(), z.unknown())
|
|
56
|
+
const _rayadesu_dsh_llm_billing_billing_getTurnSpend_parameter_1$schema = z.string()
|
|
57
|
+
const _rayadesu_dsh_llm_billing_billing_getTurnSpend_result$schema = z.object({
|
|
58
|
+
'total': z.number(),
|
|
59
|
+
})
|
|
47
60
|
|
|
48
61
|
export const TYPERT_REMOTE = {
|
|
49
62
|
package: '@rayadesu/dsh-llm-billing',
|
|
@@ -61,7 +74,7 @@ export const TYPERT_REMOTE = {
|
|
|
61
74
|
typeSymbol: '@rayadesu/dsh-llm-billing/types#DeepSeekBalance',
|
|
62
75
|
schema: _rayadesu_dsh_llm_billing_billing_getBalance_result$schema,
|
|
63
76
|
},
|
|
64
|
-
sourceLocation: {"file":"packages/llm-billing/src/balance.ts","line":
|
|
77
|
+
sourceLocation: {"file":"packages/llm-billing/src/balance.ts","line":154,"column":3},
|
|
65
78
|
},
|
|
66
79
|
{
|
|
67
80
|
id: '@rayadesu/dsh-llm-billing#billing/getSessionSpend',
|
|
@@ -86,7 +99,33 @@ export const TYPERT_REMOTE = {
|
|
|
86
99
|
typeSymbol: '@rayadesu/dsh-llm-billing/types#DeepSeekSessionSpend',
|
|
87
100
|
schema: _rayadesu_dsh_llm_billing_billing_getSessionSpend_result$schema,
|
|
88
101
|
},
|
|
89
|
-
sourceLocation: {"file":"packages/llm-billing/src/balance.ts","line":
|
|
102
|
+
sourceLocation: {"file":"packages/llm-billing/src/balance.ts","line":165,"column":3},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: '@rayadesu/dsh-llm-billing#billing/getTodaySessionsSpend',
|
|
106
|
+
service: 'billing',
|
|
107
|
+
namespace: 'billing',
|
|
108
|
+
method: 'getTodaySessionsSpend',
|
|
109
|
+
invocation: { kind: 'direct' },
|
|
110
|
+
parameters: [
|
|
111
|
+
{
|
|
112
|
+
name: 'force',
|
|
113
|
+
wire: 'force',
|
|
114
|
+
source: 'json',
|
|
115
|
+
acceptsUndefined: true,
|
|
116
|
+
codec: {
|
|
117
|
+
mode: 'strict',
|
|
118
|
+
typeSymbol: '@rayadesu/dsh-llm-billing#billing/getTodaySessionsSpend:force',
|
|
119
|
+
schema: _rayadesu_dsh_llm_billing_billing_getTodaySessionsSpend_parameter_0$schema,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
result: {
|
|
124
|
+
mode: 'strict',
|
|
125
|
+
typeSymbol: '@rayadesu/dsh-llm-billing/types#DeepSeekTodaySessionsSpend',
|
|
126
|
+
schema: _rayadesu_dsh_llm_billing_billing_getTodaySessionsSpend_result$schema,
|
|
127
|
+
},
|
|
128
|
+
sourceLocation: {"file":"packages/llm-billing/src/balance.ts","line":192,"column":3},
|
|
90
129
|
},
|
|
91
130
|
{
|
|
92
131
|
id: '@rayadesu/dsh-llm-billing#billing/getTodaySpend',
|
|
@@ -112,7 +151,42 @@ export const TYPERT_REMOTE = {
|
|
|
112
151
|
typeSymbol: '@rayadesu/dsh-llm-billing/types#DeepSeekTodaySpend',
|
|
113
152
|
schema: _rayadesu_dsh_llm_billing_billing_getTodaySpend_result$schema,
|
|
114
153
|
},
|
|
115
|
-
sourceLocation: {"file":"packages/llm-billing/src/balance.ts","line":
|
|
154
|
+
sourceLocation: {"file":"packages/llm-billing/src/balance.ts","line":178,"column":3},
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: '@rayadesu/dsh-llm-billing#billing/getTurnSpend',
|
|
158
|
+
service: 'billing',
|
|
159
|
+
namespace: 'billing',
|
|
160
|
+
method: 'getTurnSpend',
|
|
161
|
+
invocation: { kind: 'direct' },
|
|
162
|
+
parameters: [
|
|
163
|
+
{
|
|
164
|
+
name: 'sessionId',
|
|
165
|
+
wire: 'sessionId',
|
|
166
|
+
source: 'json',
|
|
167
|
+
codec: {
|
|
168
|
+
mode: 'strict',
|
|
169
|
+
typeSymbol: '@rayadesu/dsh-llm-billing#billing/getTurnSpend:sessionId',
|
|
170
|
+
schema: _rayadesu_dsh_llm_billing_billing_getTurnSpend_parameter_0$schema,
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: 'messageId',
|
|
175
|
+
wire: 'messageId',
|
|
176
|
+
source: 'json',
|
|
177
|
+
codec: {
|
|
178
|
+
mode: 'strict',
|
|
179
|
+
typeSymbol: '@rayadesu/dsh-llm-billing#billing/getTurnSpend:messageId',
|
|
180
|
+
schema: _rayadesu_dsh_llm_billing_billing_getTurnSpend_parameter_1$schema,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
result: {
|
|
185
|
+
mode: 'strict',
|
|
186
|
+
typeSymbol: '@rayadesu/dsh-llm-billing/types#DeepSeekTurnSpend',
|
|
187
|
+
schema: _rayadesu_dsh_llm_billing_billing_getTurnSpend_result$schema,
|
|
188
|
+
},
|
|
189
|
+
sourceLocation: {"file":"packages/llm-billing/src/balance.ts","line":205,"column":3},
|
|
116
190
|
},
|
|
117
191
|
],
|
|
118
192
|
}
|
package/lib/types/balance.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { Context } from '@deepseek-ai/cordis';
|
|
10
10
|
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
11
11
|
import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
12
|
-
import type { DeepSeekBalance, DeepSeekSessionSpend, DeepSeekTodaySpend } from './types.ts';
|
|
12
|
+
import type { DeepSeekBalance, DeepSeekSessionSpend, DeepSeekTodaySessionsSpend, DeepSeekTodaySpend, DeepSeekTurnSpend } from './types.ts';
|
|
13
13
|
/**
|
|
14
14
|
* Parse and validate the DeepSeek balance response at the wire boundary.
|
|
15
15
|
* @param body - decoded JSON response body.
|
|
@@ -38,6 +38,17 @@ export interface DeepSeekBalanceGatewayOptions {
|
|
|
38
38
|
* refresh path); a `force` miss still reuses revision-gated increments.
|
|
39
39
|
*/
|
|
40
40
|
fetchTodaySpend: (force?: boolean) => Promise<DeepSeekTodaySpend>;
|
|
41
|
+
/**
|
|
42
|
+
* Compute today's billed spend per session through the plugin's resolved
|
|
43
|
+
* facts, sorted by cost descending. `force` bypasses the host-side time
|
|
44
|
+
* window like {@link fetchTodaySpend}.
|
|
45
|
+
*/
|
|
46
|
+
fetchTodaySessionsSpend: (force?: boolean) => Promise<DeepSeekTodaySessionsSpend>;
|
|
47
|
+
/**
|
|
48
|
+
* Compute one completed Turn's billed spend through the plugin's resolved
|
|
49
|
+
* facts, identified by its closing assistant message id.
|
|
50
|
+
*/
|
|
51
|
+
fetchTurnSpend: (sessionId: SessionId, messageId: string) => Promise<DeepSeekTurnSpend>;
|
|
41
52
|
}
|
|
42
53
|
/**
|
|
43
54
|
* Remote-only service exposing the DeepSeek account balance and session spend.
|
|
@@ -74,6 +85,25 @@ export declare class DeepSeekBalanceGateway extends TypertRemoteService {
|
|
|
74
85
|
* @returns today's total cost plus one row per priced model.
|
|
75
86
|
*/
|
|
76
87
|
getTodaySpend(force?: boolean): Promise<DeepSeekTodaySpend>;
|
|
88
|
+
/**
|
|
89
|
+
* Read today's billed spend per session, priced per event by its
|
|
90
|
+
* Beijing-time calendar day, hour, and weekday (weekends are always
|
|
91
|
+
* off-peak). Rows carry the session's durable title and sort by cost
|
|
92
|
+
* descending; sessions with no priced usage on the day are omitted.
|
|
93
|
+
* @param force - bypass the host-side 60s cache (manual refresh); omitted
|
|
94
|
+
* means a cached read.
|
|
95
|
+
* @returns today's per-session rows, highest first.
|
|
96
|
+
*/
|
|
97
|
+
getTodaySessionsSpend(force?: boolean): Promise<DeepSeekTodaySessionsSpend>;
|
|
98
|
+
/**
|
|
99
|
+
* Read one completed Turn's billed spend, priced per event by its
|
|
100
|
+
* Beijing-time hour and weekday (weekends are always off-peak).
|
|
101
|
+
* @param sessionId - the session owning the Turn.
|
|
102
|
+
* @param messageId - the closing assistant message's durable id, which
|
|
103
|
+
* locates the Turn in the session log.
|
|
104
|
+
* @returns the Turn's total cost in CNY.
|
|
105
|
+
*/
|
|
106
|
+
getTurnSpend(sessionId: SessionId, messageId: string): Promise<DeepSeekTurnSpend>;
|
|
77
107
|
}
|
|
78
108
|
export default DeepSeekBalanceGateway;
|
|
79
109
|
//# sourceMappingURL=balance.d.ts.map
|
package/lib/types/balance.js
CHANGED
|
@@ -136,15 +136,21 @@ let DeepSeekBalanceGateway = (() => {
|
|
|
136
136
|
let _getBalance_decorators;
|
|
137
137
|
let _getSessionSpend_decorators;
|
|
138
138
|
let _getTodaySpend_decorators;
|
|
139
|
+
let _getTodaySessionsSpend_decorators;
|
|
140
|
+
let _getTurnSpend_decorators;
|
|
139
141
|
return class DeepSeekBalanceGateway extends _classSuper {
|
|
140
142
|
static {
|
|
141
143
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
142
144
|
_getBalance_decorators = [Remote('getBalance')];
|
|
143
145
|
_getSessionSpend_decorators = [Remote('getSessionSpend')];
|
|
144
146
|
_getTodaySpend_decorators = [Remote('getTodaySpend')];
|
|
147
|
+
_getTodaySessionsSpend_decorators = [Remote('getTodaySessionsSpend')];
|
|
148
|
+
_getTurnSpend_decorators = [Remote('getTurnSpend')];
|
|
145
149
|
__esDecorate(this, null, _getBalance_decorators, { kind: "method", name: "getBalance", static: false, private: false, access: { has: obj => "getBalance" in obj, get: obj => obj.getBalance }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
146
150
|
__esDecorate(this, null, _getSessionSpend_decorators, { kind: "method", name: "getSessionSpend", static: false, private: false, access: { has: obj => "getSessionSpend" in obj, get: obj => obj.getSessionSpend }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
147
151
|
__esDecorate(this, null, _getTodaySpend_decorators, { kind: "method", name: "getTodaySpend", static: false, private: false, access: { has: obj => "getTodaySpend" in obj, get: obj => obj.getTodaySpend }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
152
|
+
__esDecorate(this, null, _getTodaySessionsSpend_decorators, { kind: "method", name: "getTodaySessionsSpend", static: false, private: false, access: { has: obj => "getTodaySessionsSpend" in obj, get: obj => obj.getTodaySessionsSpend }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
153
|
+
__esDecorate(this, null, _getTurnSpend_decorators, { kind: "method", name: "getTurnSpend", static: false, private: false, access: { has: obj => "getTurnSpend" in obj, get: obj => obj.getTurnSpend }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
148
154
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
149
155
|
}
|
|
150
156
|
options = __runInitializers(this, _instanceExtraInitializers);
|
|
@@ -184,6 +190,29 @@ let DeepSeekBalanceGateway = (() => {
|
|
|
184
190
|
getTodaySpend(force) {
|
|
185
191
|
return this.options.fetchTodaySpend(force ?? false);
|
|
186
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Read today's billed spend per session, priced per event by its
|
|
195
|
+
* Beijing-time calendar day, hour, and weekday (weekends are always
|
|
196
|
+
* off-peak). Rows carry the session's durable title and sort by cost
|
|
197
|
+
* descending; sessions with no priced usage on the day are omitted.
|
|
198
|
+
* @param force - bypass the host-side 60s cache (manual refresh); omitted
|
|
199
|
+
* means a cached read.
|
|
200
|
+
* @returns today's per-session rows, highest first.
|
|
201
|
+
*/
|
|
202
|
+
getTodaySessionsSpend(force) {
|
|
203
|
+
return this.options.fetchTodaySessionsSpend(force ?? false);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Read one completed Turn's billed spend, priced per event by its
|
|
207
|
+
* Beijing-time hour and weekday (weekends are always off-peak).
|
|
208
|
+
* @param sessionId - the session owning the Turn.
|
|
209
|
+
* @param messageId - the closing assistant message's durable id, which
|
|
210
|
+
* locates the Turn in the session log.
|
|
211
|
+
* @returns the Turn's total cost in CNY.
|
|
212
|
+
*/
|
|
213
|
+
getTurnSpend(sessionId, messageId) {
|
|
214
|
+
return this.options.fetchTurnSpend(sessionId, messageId);
|
|
215
|
+
}
|
|
187
216
|
};
|
|
188
217
|
})();
|
|
189
218
|
export { DeepSeekBalanceGateway };
|
package/lib/types/billing.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* @module @rayadesu/dsh-llm-billing/billing
|
|
14
14
|
*/
|
|
15
15
|
import type { SessionEvent } from '@deepseek-ai/dsh-session';
|
|
16
|
-
import type { DeepSeekSessionSpend, DeepSeekTodaySpend } from './types.ts';
|
|
16
|
+
import type { DeepSeekSessionSpend, DeepSeekTodaySpend, DeepSeekTurnSpend } from './types.ts';
|
|
17
17
|
/** One token price point, in CNY per 1M tokens. */
|
|
18
18
|
export interface DeepSeekTokenPrice {
|
|
19
19
|
/** 1M input cache-hit tokens. */
|
|
@@ -187,6 +187,24 @@ export declare function computeSessionSpend(events: readonly SessionEvent[], bil
|
|
|
187
187
|
id: string;
|
|
188
188
|
name: string;
|
|
189
189
|
}[]): DeepSeekSessionSpend;
|
|
190
|
+
/**
|
|
191
|
+
* Price one completed Turn's billed usage at the official per-model rates,
|
|
192
|
+
* identified by its closing assistant message id. The turn's events are those
|
|
193
|
+
* between its `turn/start` and `turn/end` (both matched by the message's own
|
|
194
|
+
* turn coordinate); each priced event applies the peak/off-peak table by its
|
|
195
|
+
* Beijing-time hour and weekday. A message that cannot be located, a turn
|
|
196
|
+
* without bracketing `turn/start` / `turn/end` events (for example after
|
|
197
|
+
* compaction), or a session with no priced usage prices to zero.
|
|
198
|
+
* @param events - one session's complete event log.
|
|
199
|
+
* @param billing - resolved pricing with peak-hour windows.
|
|
200
|
+
* @param catalog - model display rows, in presentation order.
|
|
201
|
+
* @param messageId - the closing assistant message's durable id.
|
|
202
|
+
* @returns the turn's total cost in CNY.
|
|
203
|
+
*/
|
|
204
|
+
export declare function computeTurnSpend(events: readonly SessionEvent[], billing: ResolvedBilling, catalog: readonly {
|
|
205
|
+
id: string;
|
|
206
|
+
name: string;
|
|
207
|
+
}[], messageId: string): DeepSeekTurnSpend;
|
|
190
208
|
/**
|
|
191
209
|
* Price every event whose Beijing-time calendar day is the day of `now`,
|
|
192
210
|
* aggregating across every session's event log. Events from other Beijing
|
package/lib/types/billing.js
CHANGED
|
@@ -272,6 +272,50 @@ export function computeSessionSpend(events, billing, catalog) {
|
|
|
272
272
|
const names = new Map(catalog.map(model => [model.id, model.name]));
|
|
273
273
|
return priceEvents(events, billing, names);
|
|
274
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Price one completed Turn's billed usage at the official per-model rates,
|
|
277
|
+
* identified by its closing assistant message id. The turn's events are those
|
|
278
|
+
* between its `turn/start` and `turn/end` (both matched by the message's own
|
|
279
|
+
* turn coordinate); each priced event applies the peak/off-peak table by its
|
|
280
|
+
* Beijing-time hour and weekday. A message that cannot be located, a turn
|
|
281
|
+
* without bracketing `turn/start` / `turn/end` events (for example after
|
|
282
|
+
* compaction), or a session with no priced usage prices to zero.
|
|
283
|
+
* @param events - one session's complete event log.
|
|
284
|
+
* @param billing - resolved pricing with peak-hour windows.
|
|
285
|
+
* @param catalog - model display rows, in presentation order.
|
|
286
|
+
* @param messageId - the closing assistant message's durable id.
|
|
287
|
+
* @returns the turn's total cost in CNY.
|
|
288
|
+
*/
|
|
289
|
+
export function computeTurnSpend(events, billing, catalog, messageId) {
|
|
290
|
+
const names = new Map(catalog.map(model => [model.id, model.name]));
|
|
291
|
+
let turn;
|
|
292
|
+
for (const event of events) {
|
|
293
|
+
if (event.type !== 'assistant/message')
|
|
294
|
+
continue;
|
|
295
|
+
if (event.data.message.id !== messageId)
|
|
296
|
+
continue;
|
|
297
|
+
turn = event.data.turn;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
if (turn === undefined)
|
|
301
|
+
return { total: 0 };
|
|
302
|
+
const accumulator = new SpendAccumulator();
|
|
303
|
+
let active = false;
|
|
304
|
+
for (const event of events) {
|
|
305
|
+
if (event.type === 'turn/start' && event.data.turn === turn) {
|
|
306
|
+
active = true;
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
if (event.type === 'turn/end' && event.data.turn === turn)
|
|
310
|
+
break;
|
|
311
|
+
if (!active)
|
|
312
|
+
continue;
|
|
313
|
+
const priced = priceEvent(event, billing, names);
|
|
314
|
+
if (priced !== undefined)
|
|
315
|
+
accumulator.add(priced);
|
|
316
|
+
}
|
|
317
|
+
return { total: accumulator.finish().total };
|
|
318
|
+
}
|
|
275
319
|
/**
|
|
276
320
|
* Price every event whose Beijing-time calendar day is the day of `now`,
|
|
277
321
|
* aggregating across every session's event log. Events from other Beijing
|
package/lib/types/index.d.ts
CHANGED
|
@@ -24,12 +24,12 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
24
24
|
import z from '@deepseek-ai/schemastery';
|
|
25
25
|
import type { BillingConfig } from './billing.ts';
|
|
26
26
|
export { DeepSeekBalanceGateway, fetchDeepSeekBalance, parseDeepSeekBalance } from './balance.ts';
|
|
27
|
-
export { addEventContribution, beijingDayKey, computeSessionSpend, computeTodaySpend, DEFAULT_MODEL_PRICING, DEFAULT_PEAK_HOURS, emptyTodaySpend, isPeak, mergeTodaySpend, priceEvent, resolveBilling, SpendAccumulator, } from './billing.ts';
|
|
27
|
+
export { addEventContribution, beijingDayKey, computeSessionSpend, computeTodaySpend, computeTurnSpend, DEFAULT_MODEL_PRICING, DEFAULT_PEAK_HOURS, emptyTodaySpend, isPeak, mergeTodaySpend, priceEvent, resolveBilling, SpendAccumulator, } from './billing.ts';
|
|
28
28
|
export type { BillingConfig, BillingConfigModel, BillingEventContribution, DeepSeekModelPricing, DeepSeekTokenPrice, PeakHourWindow, ResolvedBilling, } from './billing.ts';
|
|
29
29
|
export type * from './types.ts';
|
|
30
30
|
export { BILLING_UNIT_KEY, billingTodaySpendDefinition, foldBillingUnit } from './projection.ts';
|
|
31
31
|
export type { BillingUnitState } from './projection.ts';
|
|
32
|
-
export { TodaySpendCache, TodaySpendScanner } from './today-spend.ts';
|
|
32
|
+
export { foldSessionTitle, TodaySpendCache, TodaySpendScanner } from './today-spend.ts';
|
|
33
33
|
export type { ScannerPersistedHeader, ScannerSession, TodaySpendScannerDeps } from './today-spend.ts';
|
|
34
34
|
export declare const name = "llm-billing";
|
|
35
35
|
/** Public API default; deployments may point elsewhere via $DEEPSEEK_BASE_URL. */
|
package/lib/types/index.js
CHANGED
|
@@ -25,13 +25,13 @@ import { assertUsableApiKey, LlmError } from '@deepseek-ai/dsh-llm';
|
|
|
25
25
|
import { credentialRef } from '@deepseek-ai/dsh-credentials';
|
|
26
26
|
import { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment';
|
|
27
27
|
import { DeepSeekBalanceGateway, fetchDeepSeekBalance } from "./balance.js";
|
|
28
|
-
import { computeSessionSpend, DEFAULT_MODEL_PRICING, DEFAULT_PEAK_HOURS, mergeTodaySpend, resolveBilling, } from "./billing.js";
|
|
28
|
+
import { computeSessionSpend, computeTurnSpend, DEFAULT_MODEL_PRICING, DEFAULT_PEAK_HOURS, mergeTodaySpend, resolveBilling, } from "./billing.js";
|
|
29
29
|
import { billingTodaySpendDefinition } from "./projection.js";
|
|
30
30
|
import { TodaySpendCache, TodaySpendScanner } from "./today-spend.js";
|
|
31
31
|
export { DeepSeekBalanceGateway, fetchDeepSeekBalance, parseDeepSeekBalance } from "./balance.js";
|
|
32
|
-
export { addEventContribution, beijingDayKey, computeSessionSpend, computeTodaySpend, DEFAULT_MODEL_PRICING, DEFAULT_PEAK_HOURS, emptyTodaySpend, isPeak, mergeTodaySpend, priceEvent, resolveBilling, SpendAccumulator, } from "./billing.js";
|
|
32
|
+
export { addEventContribution, beijingDayKey, computeSessionSpend, computeTodaySpend, computeTurnSpend, DEFAULT_MODEL_PRICING, DEFAULT_PEAK_HOURS, emptyTodaySpend, isPeak, mergeTodaySpend, priceEvent, resolveBilling, SpendAccumulator, } from "./billing.js";
|
|
33
33
|
export { BILLING_UNIT_KEY, billingTodaySpendDefinition, foldBillingUnit } from "./projection.js";
|
|
34
|
-
export { TodaySpendCache, TodaySpendScanner } from "./today-spend.js";
|
|
34
|
+
export { foldSessionTitle, TodaySpendCache, TodaySpendScanner } from "./today-spend.js";
|
|
35
35
|
export const name = 'llm-billing';
|
|
36
36
|
const DEFAULT_API_KEY_ENV = 'DEEPSEEK_API_KEY';
|
|
37
37
|
const BASE_URL_ENV = 'DEEPSEEK_BASE_URL';
|
|
@@ -186,7 +186,13 @@ export function apply(ctx, config) {
|
|
|
186
186
|
catalog,
|
|
187
187
|
});
|
|
188
188
|
const todayCache = new TodaySpendCache(dayKey => scanner.scan(dayKey), TODAY_SPEND_CACHE_MS);
|
|
189
|
+
const todaySessionsCache = new TodaySpendCache(dayKey => scanner.scanSessions(dayKey), TODAY_SPEND_CACHE_MS);
|
|
189
190
|
const fetchTodaySpend = async (force = false) => todayCache.get(force);
|
|
190
|
-
|
|
191
|
+
const fetchTodaySessionsSpend = async (force = false) => todaySessionsCache.get(force);
|
|
192
|
+
const fetchTurnSpend = async (sessionId, messageId) => {
|
|
193
|
+
const events = await sessionEvents(ctx, sessionId);
|
|
194
|
+
return computeTurnSpend(events, billing, catalog, messageId);
|
|
195
|
+
};
|
|
196
|
+
new DeepSeekBalanceGateway(ctx, { fetchBalance, fetchSessionSpend, fetchTodaySpend, fetchTodaySessionsSpend, fetchTurnSpend });
|
|
191
197
|
}
|
|
192
198
|
//# sourceMappingURL=index.js.map
|
|
@@ -23,9 +23,18 @@
|
|
|
23
23
|
import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session';
|
|
24
24
|
import type { SessionPersistenceRevision } from '@deepseek-ai/dsh-session-persistence';
|
|
25
25
|
import type { ResolvedBilling } from './billing.ts';
|
|
26
|
-
import type { DeepSeekTodaySpend } from './types.ts';
|
|
26
|
+
import type { DeepSeekTodaySessionsSpend, DeepSeekTodaySpend } from './types.ts';
|
|
27
27
|
import { BILLING_UNIT_KEY, type BillingUnitState } from './projection.ts';
|
|
28
28
|
import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection';
|
|
29
|
+
/**
|
|
30
|
+
* Fold one session's durable display title: the latest `session/title`
|
|
31
|
+
* event's text (last-wins, matching the `title` projection), or `null` before
|
|
32
|
+
* the first title lands. The fold runs over the complete log, so an explicit
|
|
33
|
+
* user rename is picked up as soon as its event commits.
|
|
34
|
+
* @param events - one session's complete event log.
|
|
35
|
+
* @returns the session's current title, or `null` when untitled.
|
|
36
|
+
*/
|
|
37
|
+
export declare function foldSessionTitle(events: readonly SessionEvent[]): string | null;
|
|
29
38
|
/** Structural slice of a live session the scanner reads. */
|
|
30
39
|
export interface ScannerSession {
|
|
31
40
|
readonly id: SessionId;
|
|
@@ -89,8 +98,10 @@ export interface TodaySpendScannerDeps {
|
|
|
89
98
|
* coalesces concurrent misses, and a `force` bypass for the manual refresh
|
|
90
99
|
* path. Cross-day invalidation is automatic (the day key changes); a failed
|
|
91
100
|
* scan leaves the previous value in place and retries on the next call.
|
|
101
|
+
* @typeParam T - the cached aggregate's value shape (the spend or its
|
|
102
|
+
* per-session breakdown).
|
|
92
103
|
*/
|
|
93
|
-
export declare class TodaySpendCache {
|
|
104
|
+
export declare class TodaySpendCache<T = DeepSeekTodaySpend> {
|
|
94
105
|
private readonly scan;
|
|
95
106
|
private readonly ttlMs;
|
|
96
107
|
private readonly now;
|
|
@@ -99,18 +110,18 @@ export declare class TodaySpendCache {
|
|
|
99
110
|
private cachedAt;
|
|
100
111
|
private inFlight;
|
|
101
112
|
/**
|
|
113
|
+
* @param scan - the aggregate computation behind a miss.
|
|
102
114
|
* @param ttlMs - time window in milliseconds (default 60 000).
|
|
103
115
|
* @param now - clock source (injectable for tests).
|
|
104
|
-
* @param scan - the aggregate computation behind a miss.
|
|
105
116
|
*/
|
|
106
|
-
constructor(scan: (dayKey: string) => Promise<
|
|
117
|
+
constructor(scan: (dayKey: string) => Promise<T>, ttlMs?: number, now?: () => Date);
|
|
107
118
|
/**
|
|
108
119
|
* Read today's spend, cached per Beijing day within the TTL window.
|
|
109
120
|
* @param force - bypass the time window (manual refresh); the day-key gate
|
|
110
121
|
* and the in-flight coalescing still apply to non-force callers.
|
|
111
122
|
* @returns today's spend.
|
|
112
123
|
*/
|
|
113
|
-
get(force?: boolean): Promise<
|
|
124
|
+
get(force?: boolean): Promise<T>;
|
|
114
125
|
}
|
|
115
126
|
/**
|
|
116
127
|
* The aggregate computation behind a cache miss. Chooses the projection path
|
|
@@ -120,7 +131,7 @@ export declare class TodaySpendCache {
|
|
|
120
131
|
*/
|
|
121
132
|
export declare class TodaySpendScanner {
|
|
122
133
|
private readonly deps;
|
|
123
|
-
/** Cold sessions resolved on the projection path: id → revision + unit state. */
|
|
134
|
+
/** Cold sessions resolved on the projection path: id → revision + unit state + title. */
|
|
124
135
|
private readonly coldResolved;
|
|
125
136
|
/** Cold sessions resolved on the events path: id → revision (events were collected). */
|
|
126
137
|
private lastEventsScan;
|
|
@@ -131,6 +142,24 @@ export declare class TodaySpendScanner {
|
|
|
131
142
|
* @returns today's spend across every session.
|
|
132
143
|
*/
|
|
133
144
|
scan(dayKey: string): Promise<DeepSeekTodaySpend>;
|
|
145
|
+
/**
|
|
146
|
+
* Compute today's per-session spend for one Beijing day, sorted by cost
|
|
147
|
+
* descending. Sessions with no priced usage on the day are omitted; each
|
|
148
|
+
* row carries the session's durable title folded from its log.
|
|
149
|
+
* @param dayKey - the Beijing-time calendar-day key to aggregate.
|
|
150
|
+
* @returns today's per-session rows, highest first.
|
|
151
|
+
*/
|
|
152
|
+
scanSessions(dayKey: string): Promise<DeepSeekTodaySessionsSpend>;
|
|
153
|
+
/**
|
|
154
|
+
* Resolve one cold session's billing unit state and display title through
|
|
155
|
+
* the projection-cache ladder (cached row first, then a detached local
|
|
156
|
+
* fold over a full inspect). A cache-served value carries no title (the
|
|
157
|
+
* ladder only stores projection values), so such rows report `title: null`
|
|
158
|
+
* until the session is inspected again.
|
|
159
|
+
* @param id - the cold session's id.
|
|
160
|
+
* @returns the resolved state and title, or `undefined` when unreadable.
|
|
161
|
+
*/
|
|
162
|
+
private resolveCold;
|
|
134
163
|
/** Projection path: eager cells for live sessions, revision-gated cold ladder for the rest. */
|
|
135
164
|
private scanProjections;
|
|
136
165
|
/**
|
|
@@ -138,5 +167,24 @@ export declare class TodaySpendScanner {
|
|
|
138
167
|
* filter during collection, hard cap), gated by revisions.
|
|
139
168
|
*/
|
|
140
169
|
private scanEvents;
|
|
170
|
+
/**
|
|
171
|
+
* Projection-path per-session scan: eager cells for live sessions (title
|
|
172
|
+
* folded from the live log, so a rename is reflected immediately),
|
|
173
|
+
* revision-gated cold ladder for the rest (title resolved on inspect,
|
|
174
|
+
* `null` when served from the projection cache).
|
|
175
|
+
* @param dayKey - the Beijing-time calendar-day key to aggregate.
|
|
176
|
+
* @returns unsorted per-session rows for the day.
|
|
177
|
+
*/
|
|
178
|
+
private scanSessionsProjections;
|
|
179
|
+
/**
|
|
180
|
+
* Events-path per-session scan: price today's events in a single pass,
|
|
181
|
+
* accumulating per session (per-event Beijing-day filter during collection,
|
|
182
|
+
* hard cap), gated by revisions. Titles fold from each session's complete
|
|
183
|
+
* log — a `session/title` event can predate today — so a rename is reflected
|
|
184
|
+
* as soon as the session's log is re-read.
|
|
185
|
+
* @param dayKey - the Beijing-time calendar-day key to aggregate.
|
|
186
|
+
* @returns unsorted per-session rows for the day.
|
|
187
|
+
*/
|
|
188
|
+
private scanSessionsEvents;
|
|
141
189
|
}
|
|
142
190
|
//# sourceMappingURL=today-spend.d.ts.map
|