@goodandready/dsh-cost-meter 0.8.27 → 0.8.28
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/lib/client.js +2379 -2329
- package/lib/commands.js +4 -0
- package/lib/export.js +12 -1
- package/lib/index.js +6 -0
- package/lib/projection.js +12 -0
- package/lib/routes.js +3 -0
- package/package.json +1 -1
package/lib/commands.js
CHANGED
|
@@ -60,6 +60,10 @@ export function formatCostReceipt(data) {
|
|
|
60
60
|
`| **Output Tokens** | ${formatNumber(output)} |`,
|
|
61
61
|
]
|
|
62
62
|
|
|
63
|
+
if (tokens.reasoningTokens > 0) {
|
|
64
|
+
lines.push(`| **Thinking / Reasoning** | 🧠 ${formatNumber(tokens.reasoningTokens)} |`)
|
|
65
|
+
}
|
|
66
|
+
|
|
63
67
|
if (cacheWrite > 0) {
|
|
64
68
|
lines.push(`| **Cache Write** | ${formatNumber(cacheWrite)} |`)
|
|
65
69
|
}
|
package/lib/export.js
CHANGED
|
@@ -40,6 +40,7 @@ export function generateSessionCsv(data) {
|
|
|
40
40
|
'Cache_Read_Tokens',
|
|
41
41
|
'Cache_Hit_Rate_%',
|
|
42
42
|
'Output_Tokens',
|
|
43
|
+
'Reasoning_Tokens',
|
|
43
44
|
'Total_Tokens',
|
|
44
45
|
'Cost_USD',
|
|
45
46
|
'Cost_Local',
|
|
@@ -51,6 +52,7 @@ export function generateSessionCsv(data) {
|
|
|
51
52
|
let sumUncached = 0
|
|
52
53
|
let sumHit = 0
|
|
53
54
|
let sumOut = 0
|
|
55
|
+
let sumReasoning = 0
|
|
54
56
|
let sumTotalTok = 0
|
|
55
57
|
let sumCostUsd = 0
|
|
56
58
|
|
|
@@ -60,6 +62,7 @@ export function generateSessionCsv(data) {
|
|
|
60
62
|
const uncached = Number(t.uncachedInputTokens) || 0
|
|
61
63
|
const hit = Number(t.cacheReadTokens) || 0
|
|
62
64
|
const out = Number(t.outputTokens) || 0
|
|
65
|
+
const reasoning = Number(t.reasoningTokens) || 0
|
|
63
66
|
const totalTok = uncached + hit + out
|
|
64
67
|
const hitRate = t.cacheHitRate !== undefined
|
|
65
68
|
? t.cacheHitRate
|
|
@@ -70,6 +73,7 @@ export function generateSessionCsv(data) {
|
|
|
70
73
|
sumUncached += uncached
|
|
71
74
|
sumHit += hit
|
|
72
75
|
sumOut += out
|
|
76
|
+
sumReasoning += reasoning
|
|
73
77
|
sumTotalTok += totalTok
|
|
74
78
|
sumCostUsd += costUsd
|
|
75
79
|
|
|
@@ -83,6 +87,7 @@ export function generateSessionCsv(data) {
|
|
|
83
87
|
hit,
|
|
84
88
|
hitRate,
|
|
85
89
|
out,
|
|
90
|
+
reasoning,
|
|
86
91
|
totalTok,
|
|
87
92
|
costUsd.toFixed(6),
|
|
88
93
|
costLocal.toFixed(4),
|
|
@@ -103,6 +108,7 @@ export function generateSessionCsv(data) {
|
|
|
103
108
|
sumHit,
|
|
104
109
|
overallHitRate,
|
|
105
110
|
sumOut,
|
|
111
|
+
sumReasoning,
|
|
106
112
|
sumTotalTok,
|
|
107
113
|
(totalCostUsd || sumCostUsd).toFixed(6),
|
|
108
114
|
((totalCostUsd || sumCostUsd) * fx).toFixed(4),
|
|
@@ -137,14 +143,17 @@ export function generateSessionJson(data) {
|
|
|
137
143
|
let sumUncached = 0
|
|
138
144
|
let sumHit = 0
|
|
139
145
|
let sumOut = 0
|
|
146
|
+
let sumReasoning = 0
|
|
140
147
|
|
|
141
148
|
const turnsFormatted = turns.map((t) => {
|
|
142
149
|
const uncached = Number(t.uncachedInputTokens) || 0
|
|
143
150
|
const hit = Number(t.cacheReadTokens) || 0
|
|
144
151
|
const out = Number(t.outputTokens) || 0
|
|
152
|
+
const reasoning = Number(t.reasoningTokens) || 0
|
|
145
153
|
sumUncached += uncached
|
|
146
154
|
sumHit += hit
|
|
147
155
|
sumOut += out
|
|
156
|
+
sumReasoning += reasoning
|
|
148
157
|
return {
|
|
149
158
|
turn: t.turn,
|
|
150
159
|
step: t.step ?? 0,
|
|
@@ -153,6 +162,7 @@ export function generateSessionJson(data) {
|
|
|
153
162
|
uncachedInput: uncached,
|
|
154
163
|
cacheRead: hit,
|
|
155
164
|
output: out,
|
|
165
|
+
reasoningTokens: reasoning,
|
|
156
166
|
total: uncached + hit + out,
|
|
157
167
|
cacheHitRate: t.cacheHitRate !== undefined ? t.cacheHitRate : (uncached + hit > 0 ? Math.round((hit / (uncached + hit)) * 100) : 0),
|
|
158
168
|
},
|
|
@@ -180,6 +190,7 @@ export function generateSessionJson(data) {
|
|
|
180
190
|
uncachedInput: sumUncached,
|
|
181
191
|
cacheRead: sumHit,
|
|
182
192
|
output: sumOut,
|
|
193
|
+
reasoningTokens: sumReasoning,
|
|
183
194
|
total: sumUncached + sumHit + sumOut,
|
|
184
195
|
cacheHitRate: overallHitRate,
|
|
185
196
|
},
|
|
@@ -230,7 +241,7 @@ export function renderSummaryCardToCanvas(canvas, data) {
|
|
|
230
241
|
} = data || {}
|
|
231
242
|
|
|
232
243
|
const totalCost = costUsd * fx
|
|
233
|
-
const costFormatted = totalCost >= 1 ? `${currency}${totalCost.toFixed(2)}` : (totalCost > 0 ? `${currency}${totalCost.toFixed(4)}` : `${currency}0.00`)
|
|
244
|
+
const costFormatted = data?.privacyMode ? `${currency}****` : (totalCost >= 1 ? `${currency}${totalCost.toFixed(2)}` : (totalCost > 0 ? `${currency}${totalCost.toFixed(4)}` : `${currency}0.00`))
|
|
234
245
|
const uncached = Number(tokens.uncachedInputTokens || tokens.inputTokens) || 0
|
|
235
246
|
const hit = Number(tokens.cacheReadTokens) || 0
|
|
236
247
|
const out = Number(tokens.outputTokens) || 0
|
package/lib/index.js
CHANGED
|
@@ -131,6 +131,9 @@ export function normalizeConfig(raw) {
|
|
|
131
131
|
alertBeforeSwitch: u.alertBeforeSwitch !== false,
|
|
132
132
|
alertLeadMinutes: Math.max(1, Math.min(30, Number(u.alertLeadMinutes) || 5)),
|
|
133
133
|
browserNotifications: Boolean(u.browserNotifications),
|
|
134
|
+
privacyMode: Boolean(u.privacyMode),
|
|
135
|
+
showHeaderChip: u.showHeaderChip !== false,
|
|
136
|
+
showMessageAction: u.showMessageAction !== false,
|
|
134
137
|
useOpenRouter: u.useOpenRouter !== false,
|
|
135
138
|
refreshHours: Number(u.refreshHours) || 24,
|
|
136
139
|
deepseekPeakPrices: u.deepseekPeakPrices || {},
|
|
@@ -162,6 +165,9 @@ export const Config = z.object({
|
|
|
162
165
|
alertBeforeSwitch: z.boolean().description('Pre-switch notification and visual alert before tariff change.').default(true).volatile(),
|
|
163
166
|
alertLeadMinutes: z.number().description('Minutes before tariff change to trigger pre-switch alert.').default(5).volatile(),
|
|
164
167
|
browserNotifications: z.boolean().description('Enable desktop browser notifications for tariff switches.').default(false).volatile(),
|
|
168
|
+
privacyMode: z.boolean().description('Streamer/Privacy mode: mask financial spend with asterisks while retaining token stats.').default(false).volatile(),
|
|
169
|
+
showHeaderChip: z.boolean().description('Show the cost meter chip in the conversation header.').default(true).volatile(),
|
|
170
|
+
showMessageAction: z.boolean().description('Show the token inspector action button on assistant messages.').default(true).volatile(),
|
|
165
171
|
deepseekPeakPrices: z.dict(PriceRow).description('Override the built-in DeepSeek peak rates, by model id.').default({}).volatile(),
|
|
166
172
|
useOpenRouter: z.boolean().description('Pull rates and discount windows from the public OpenRouter catalog.').default(true).volatile(),
|
|
167
173
|
refreshHours: z.number().description('How often to refetch the catalog.').default(24).volatile(),
|
package/lib/projection.js
CHANGED
|
@@ -20,6 +20,7 @@ export const bucketSchema = zod.object({
|
|
|
20
20
|
outputTokens: zod.number().nonnegative(),
|
|
21
21
|
cacheReadTokens: zod.number().nonnegative(),
|
|
22
22
|
cacheWriteTokens: zod.number().nonnegative(),
|
|
23
|
+
reasoningTokens: zod.number().nonnegative().optional(),
|
|
23
24
|
}).strict()
|
|
24
25
|
|
|
25
26
|
export const turnRecordSchema = zod.object({
|
|
@@ -30,6 +31,7 @@ export const turnRecordSchema = zod.object({
|
|
|
30
31
|
outputTokens: zod.number().nonnegative().optional(),
|
|
31
32
|
cacheReadTokens: zod.number().nonnegative().optional(),
|
|
32
33
|
cacheWriteTokens: zod.number().nonnegative().optional(),
|
|
34
|
+
reasoningTokens: zod.number().nonnegative().optional(),
|
|
33
35
|
costDeltaUsd: zod.number().nonnegative().optional(),
|
|
34
36
|
timestamp: zod.number().nonnegative(),
|
|
35
37
|
durationMs: zod.number().nonnegative().optional(),
|
|
@@ -38,11 +40,19 @@ export const turnRecordSchema = zod.object({
|
|
|
38
40
|
|
|
39
41
|
/** Pull the four buckets out of one provider usage record. */
|
|
40
42
|
export function bucketsFromUsage(usage) {
|
|
43
|
+
const reasoningTokens = Number(
|
|
44
|
+
usage?.completion_tokens_details?.reasoning_tokens ||
|
|
45
|
+
usage?.reasoningTokens ||
|
|
46
|
+
usage?.reasoning_tokens ||
|
|
47
|
+
usage?.details?.reasoning_tokens ||
|
|
48
|
+
0
|
|
49
|
+
) || 0
|
|
41
50
|
return {
|
|
42
51
|
uncachedInputTokens: Number(usage.inputTokens) || 0,
|
|
43
52
|
outputTokens: Number(usage.outputTokens) || 0,
|
|
44
53
|
cacheReadTokens: Number(usage.cacheReadTokens) || 0,
|
|
45
54
|
cacheWriteTokens: Number(usage.cacheWriteTokens) || 0,
|
|
55
|
+
reasoningTokens,
|
|
46
56
|
}
|
|
47
57
|
}
|
|
48
58
|
|
|
@@ -180,6 +190,7 @@ export const usageByModelProjection = {
|
|
|
180
190
|
outputTokens: buckets.outputTokens,
|
|
181
191
|
cacheReadTokens: buckets.cacheReadTokens,
|
|
182
192
|
cacheWriteTokens: buckets.cacheWriteTokens,
|
|
193
|
+
reasoningTokens: buckets.reasoningTokens || 0,
|
|
183
194
|
costDeltaUsd: nextTurns[existingIndex].costDeltaUsd || 0,
|
|
184
195
|
timestamp,
|
|
185
196
|
durationMs: nextTurns[existingIndex].durationMs || durationMs,
|
|
@@ -194,6 +205,7 @@ export const usageByModelProjection = {
|
|
|
194
205
|
outputTokens: buckets.outputTokens,
|
|
195
206
|
cacheReadTokens: buckets.cacheReadTokens,
|
|
196
207
|
cacheWriteTokens: buckets.cacheWriteTokens,
|
|
208
|
+
reasoningTokens: buckets.reasoningTokens || 0,
|
|
197
209
|
costDeltaUsd: 0,
|
|
198
210
|
timestamp,
|
|
199
211
|
durationMs,
|
package/lib/routes.js
CHANGED
|
@@ -111,6 +111,9 @@ export function registerRoutes(ctx, options) {
|
|
|
111
111
|
fxRates: { USD: 1, ...(getCachedFxState().rates || {}) },
|
|
112
112
|
displayTimeZone: typeof cfg?.displayTimeZone === 'string' && cfg.displayTimeZone ? cfg.displayTimeZone : 'Europe/Moscow',
|
|
113
113
|
budgetThreshold: Number(cfg?.budgetThreshold) || 0,
|
|
114
|
+
privacyMode: Boolean(cfg?.privacyMode),
|
|
115
|
+
showHeaderChip: cfg?.showHeaderChip !== false,
|
|
116
|
+
showMessageAction: cfg?.showMessageAction !== false,
|
|
114
117
|
displayMode: cfg?.displayMode || 'auto',
|
|
115
118
|
alertBeforeSwitch: cfg?.alertBeforeSwitch !== false,
|
|
116
119
|
alertLeadMinutes: Number(cfg?.alertLeadMinutes) || 5,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-cost-meter",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.28",
|
|
4
4
|
"description": "DeepSeek Harness plugin: a session cost chip in the conversation header - live token usage by bucket, peak/off-peak tariff, and a countdown to the next tariff switch.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|