@diogonzafe/tokenwatch 0.1.8 → 0.1.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.md +107 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,11 @@ npm install openai # OpenAI / DeepSeek
|
|
|
17
17
|
npm install @anthropic-ai/sdk # Anthropic
|
|
18
18
|
npm install @google/generative-ai # Gemini
|
|
19
19
|
npm install better-sqlite3 # optional — only for storage: 'sqlite'
|
|
20
|
+
|
|
21
|
+
# Database adapters (optional — only if using @diogonzafe/tokenwatch/adapters)
|
|
22
|
+
npm install pg # PostgreSQL
|
|
23
|
+
npm install mysql2 # MySQL / MariaDB
|
|
24
|
+
npm install mongodb # MongoDB
|
|
20
25
|
```
|
|
21
26
|
|
|
22
27
|
---
|
|
@@ -28,7 +33,7 @@ import { createTracker } from '@diogonzafe/tokenwatch'
|
|
|
28
33
|
|
|
29
34
|
const tracker = createTracker({
|
|
30
35
|
// All fields are optional
|
|
31
|
-
storage: 'memory', // 'memory' (default) | 'sqlite'
|
|
36
|
+
storage: 'memory', // 'memory' (default) | 'sqlite' | IStorage instance
|
|
32
37
|
alertThreshold: 1.00, // USD — fires webhookUrl when exceeded
|
|
33
38
|
webhookUrl: 'https://...', // Discord / Slack webhook
|
|
34
39
|
syncPrices: true, // fetch fresh prices from GitHub (default: true)
|
|
@@ -135,8 +140,10 @@ const res = await deepseek.chat.completions.create({
|
|
|
135
140
|
|
|
136
141
|
## Reports
|
|
137
142
|
|
|
143
|
+
All report methods are async:
|
|
144
|
+
|
|
138
145
|
```ts
|
|
139
|
-
tracker.getReport()
|
|
146
|
+
const report = await tracker.getReport()
|
|
140
147
|
// {
|
|
141
148
|
// totalCostUSD: 0.087,
|
|
142
149
|
// totalTokens: { input: 24000, output: 6000 },
|
|
@@ -151,12 +158,12 @@ tracker.getReport()
|
|
|
151
158
|
|
|
152
159
|
tracker.getModelInfo('gpt-4o')
|
|
153
160
|
// { input: 2.5, output: 10, maxInputTokens: 128000 }
|
|
154
|
-
// Returns null if the model is unknown
|
|
161
|
+
// Returns null if the model is unknown (synchronous)
|
|
155
162
|
|
|
156
|
-
tracker.reset() // clear all data
|
|
157
|
-
tracker.resetSession('session_abc') // clear one session
|
|
158
|
-
tracker.exportJSON() // full report as JSON string
|
|
159
|
-
tracker.exportCSV() // all calls as CSV string
|
|
163
|
+
await tracker.reset() // clear all data
|
|
164
|
+
await tracker.resetSession('session_abc') // clear one session
|
|
165
|
+
await tracker.exportJSON() // full report as JSON string
|
|
166
|
+
await tracker.exportCSV() // all calls as CSV string
|
|
160
167
|
```
|
|
161
168
|
|
|
162
169
|
---
|
|
@@ -167,7 +174,7 @@ Prices are resolved in this priority order:
|
|
|
167
174
|
|
|
168
175
|
1. **`customPrices`** — your own overrides, highest priority
|
|
169
176
|
2. **Remote `prices.json`** — fetched from GitHub, cached for 24h in `~/.tokenwatch/prices.json`
|
|
170
|
-
3. **Bundled `prices.json`** — always-present fallback, updated
|
|
177
|
+
3. **Bundled `prices.json`** — always-present fallback, updated daily via GitHub Action
|
|
171
178
|
|
|
172
179
|
If a model is not found in any layer, cost is recorded as **$0** with a `console.warn`.
|
|
173
180
|
|
|
@@ -188,11 +195,20 @@ Prices are in **USD per 1 million tokens**.
|
|
|
188
195
|
}
|
|
189
196
|
```
|
|
190
197
|
|
|
191
|
-
Prices are updated every
|
|
198
|
+
Prices are updated every day via a GitHub Action that pulls from the [LiteLLM community model registry](https://github.com/BerriAI/litellm). New models are auto-discovered — no manual updates needed.
|
|
192
199
|
|
|
193
200
|
---
|
|
194
201
|
|
|
195
|
-
##
|
|
202
|
+
## Storage
|
|
203
|
+
|
|
204
|
+
### In-memory (default)
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
const tracker = createTracker({ storage: 'memory' })
|
|
208
|
+
// Resets on process restart. Good for short-lived processes and testing.
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### SQLite
|
|
196
212
|
|
|
197
213
|
For persistent tracking across restarts:
|
|
198
214
|
|
|
@@ -205,6 +221,76 @@ const tracker = createTracker({ storage: 'sqlite' })
|
|
|
205
221
|
// Data stored in ~/.tokenwatch/usage.db
|
|
206
222
|
```
|
|
207
223
|
|
|
224
|
+
### PostgreSQL
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
npm install pg
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
import { Pool } from 'pg'
|
|
232
|
+
import { createTracker } from '@diogonzafe/tokenwatch'
|
|
233
|
+
import { PostgresStorage } from '@diogonzafe/tokenwatch/adapters'
|
|
234
|
+
|
|
235
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
|
|
236
|
+
const storage = new PostgresStorage(pool)
|
|
237
|
+
await storage.migrate() // creates tokenwatch_usage table if it doesn't exist
|
|
238
|
+
|
|
239
|
+
const tracker = createTracker({ storage })
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### MySQL / MariaDB
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
npm install mysql2
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
import mysql from 'mysql2/promise'
|
|
250
|
+
import { MySQLStorage } from '@diogonzafe/tokenwatch/adapters'
|
|
251
|
+
|
|
252
|
+
const pool = mysql.createPool({ uri: process.env.MYSQL_URL })
|
|
253
|
+
const storage = new MySQLStorage(pool)
|
|
254
|
+
await storage.migrate()
|
|
255
|
+
|
|
256
|
+
const tracker = createTracker({ storage })
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### MongoDB
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
npm install mongodb
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
import { MongoClient } from 'mongodb'
|
|
267
|
+
import { MongoStorage } from '@diogonzafe/tokenwatch/adapters'
|
|
268
|
+
|
|
269
|
+
const client = new MongoClient(process.env.MONGO_URL!)
|
|
270
|
+
await client.connect()
|
|
271
|
+
const storage = new MongoStorage(client.db('myapp'))
|
|
272
|
+
await storage.createIndexes() // optional but recommended
|
|
273
|
+
|
|
274
|
+
const tracker = createTracker({ storage })
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Custom adapter
|
|
278
|
+
|
|
279
|
+
Any object that implements `IStorage` works:
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
import type { IStorage, UsageEntry } from '@diogonzafe/tokenwatch'
|
|
283
|
+
|
|
284
|
+
class RedisStorage implements IStorage {
|
|
285
|
+
record(entry: UsageEntry): void { /* ... */ }
|
|
286
|
+
async getAll(): Promise<UsageEntry[]> { /* ... */ }
|
|
287
|
+
async clearAll(): Promise<void> { /* ... */ }
|
|
288
|
+
async clearSession(sessionId: string): Promise<void> { /* ... */ }
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const tracker = createTracker({ storage: new RedisStorage() })
|
|
292
|
+
```
|
|
293
|
+
|
|
208
294
|
---
|
|
209
295
|
|
|
210
296
|
## Alerts & Webhooks
|
|
@@ -235,13 +321,23 @@ npx tokenwatch help # show help
|
|
|
235
321
|
|
|
236
322
|
---
|
|
237
323
|
|
|
324
|
+
## Privacy & Security
|
|
325
|
+
|
|
326
|
+
- Prompt and response **content is never read or stored** — only token counts and model names
|
|
327
|
+
- API keys are **never accessed** by tokenwatch — they remain solely in the provider client
|
|
328
|
+
- SQLite, Postgres, MySQL, and MongoDB data stays **entirely in your own infrastructure** — nothing is transmitted to external services
|
|
329
|
+
- The wrapper is a thin `Proxy` with **no outbound network calls** of its own (only the daily price sync script fetches external data)
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
238
333
|
## Behaviour Guarantees
|
|
239
334
|
|
|
240
335
|
- `__sessionId` and `__userId` are **stripped before** the request reaches the API
|
|
241
336
|
- The response object returned is **identical** to the original SDK response
|
|
242
|
-
-
|
|
337
|
+
- `track()` is **synchronous and non-blocking** — zero latency added to API calls
|
|
243
338
|
- If the API call **fails**, no cost is recorded and the original error is re-thrown unchanged
|
|
244
339
|
- Streaming is fully supported — usage is accumulated from the final stream event
|
|
340
|
+
- Database writes from `record()` are **fire-and-forget** — a storage failure never interrupts your API call
|
|
245
341
|
|
|
246
342
|
---
|
|
247
343
|
|