@diogonzafe/tokenwatch 0.8.0 → 0.10.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.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { T as TrackerConfig, a as Tracker, L as LazyTracker, b as TrackingMeta } from './index-CFBI-1ab.cjs';
2
- export { A as AnomalyDetectionConfig, B as BudgetConfig, C as CostForecast, F as FeatureStats, c as ForecastOptions, I as IExporter, d as IStorage, M as ModelPrice, e as ModelStats, P as PriceMap, f as PricesFile, R as Report, g as ReportOptions, S as SessionStats, U as UsageEntry, h as UserStats } from './index-CFBI-1ab.cjs';
1
+ import { T as TrackerConfig, a as Tracker, L as LazyTracker, b as TrackingMeta } from './index-B5OF0YCl.cjs';
2
+ export { A as AnomalyDetectionConfig, B as BudgetConfig, C as CostForecast, F as FeatureStats, c as ForecastOptions, I as IExporter, d as IStorage, M as ModelPrice, e as ModelStats, P as PriceMap, f as PricesFile, R as Report, g as ReportOptions, S as SessionStats, U as UsageEntry, h as UserStats } from './index-B5OF0YCl.cjs';
3
3
 
4
4
  declare function createTracker(config?: TrackerConfig): Tracker;
5
5
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { T as TrackerConfig, a as Tracker, L as LazyTracker, b as TrackingMeta } from './index-CFBI-1ab.js';
2
- export { A as AnomalyDetectionConfig, B as BudgetConfig, C as CostForecast, F as FeatureStats, c as ForecastOptions, I as IExporter, d as IStorage, M as ModelPrice, e as ModelStats, P as PriceMap, f as PricesFile, R as Report, g as ReportOptions, S as SessionStats, U as UsageEntry, h as UserStats } from './index-CFBI-1ab.js';
1
+ import { T as TrackerConfig, a as Tracker, L as LazyTracker, b as TrackingMeta } from './index-B5OF0YCl.js';
2
+ export { A as AnomalyDetectionConfig, B as BudgetConfig, C as CostForecast, F as FeatureStats, c as ForecastOptions, I as IExporter, d as IStorage, M as ModelPrice, e as ModelStats, P as PriceMap, f as PricesFile, R as Report, g as ReportOptions, S as SessionStats, U as UsageEntry, h as UserStats } from './index-B5OF0YCl.js';
3
3
 
4
4
  declare function createTracker(config?: TrackerConfig): Tracker;
5
5
 
package/dist/index.js CHANGED
@@ -33,6 +33,41 @@ function calculateCost(inputTokens, outputTokens, price, cachedTokens = 0, cache
33
33
  return regularInputCost + cachedReadCost + cacheCreationCost + outputCost;
34
34
  }
35
35
 
36
+ // src/exporters/cloud.ts
37
+ var DEFAULT_ENDPOINT = "https://api.tokenwatch.dev/v1/ingest";
38
+ var CloudExporter = class {
39
+ constructor(apiKey, endpoint) {
40
+ this.apiKey = apiKey;
41
+ this.endpoint = endpoint ?? DEFAULT_ENDPOINT;
42
+ }
43
+ apiKey;
44
+ endpoint;
45
+ export(entry) {
46
+ fetch(this.endpoint, {
47
+ method: "POST",
48
+ headers: {
49
+ "Content-Type": "application/json",
50
+ Authorization: `Bearer ${this.apiKey}`
51
+ },
52
+ body: JSON.stringify({
53
+ model: entry.model,
54
+ inputTokens: entry.inputTokens,
55
+ outputTokens: entry.outputTokens,
56
+ reasoningTokens: entry.reasoningTokens ?? 0,
57
+ cachedTokens: entry.cachedTokens ?? 0,
58
+ cacheCreationTokens: entry.cacheCreationTokens ?? 0,
59
+ costUSD: entry.costUSD,
60
+ sessionId: entry.sessionId,
61
+ userId: entry.userId,
62
+ feature: entry.feature,
63
+ metadata: entry.metadata,
64
+ timestamp: entry.timestamp
65
+ })
66
+ }).catch(() => {
67
+ });
68
+ }
69
+ };
70
+
36
71
  // src/core/suggestions.ts
37
72
  var PROVIDER_PREFIXES = ["gpt-", "claude-", "gemini-", "deepseek-"];
38
73
  function getProviderPrefix(model) {
@@ -123,6 +158,7 @@ var SqliteStorage = class {
123
158
  user_id TEXT,
124
159
  feature TEXT,
125
160
  app_id TEXT,
161
+ metadata TEXT,
126
162
  timestamp TEXT NOT NULL
127
163
  )
128
164
  `);
@@ -142,13 +178,16 @@ var SqliteStorage = class {
142
178
  if (!cols.includes("app_id")) {
143
179
  this.db.exec(`ALTER TABLE usage ADD COLUMN app_id TEXT`);
144
180
  }
181
+ if (!cols.includes("metadata")) {
182
+ this.db.exec(`ALTER TABLE usage ADD COLUMN metadata TEXT`);
183
+ }
145
184
  }
146
185
  record(entry) {
147
186
  this.db.prepare(
148
187
  `INSERT INTO usage
149
188
  (model, input_tokens, output_tokens, reasoning_tokens, cached_tokens, cache_creation_tokens,
150
- cost_usd, session_id, user_id, feature, app_id, timestamp)
151
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
189
+ cost_usd, session_id, user_id, feature, app_id, metadata, timestamp)
190
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
152
191
  ).run(
153
192
  entry.model,
154
193
  entry.inputTokens,
@@ -161,6 +200,7 @@ var SqliteStorage = class {
161
200
  entry.userId ?? null,
162
201
  entry.feature ?? null,
163
202
  entry.appId ?? null,
203
+ entry.metadata != null ? JSON.stringify(entry.metadata) : null,
164
204
  entry.timestamp
165
205
  );
166
206
  }
@@ -178,6 +218,7 @@ var SqliteStorage = class {
178
218
  ...r.user_id != null && { userId: r.user_id },
179
219
  ...r.feature != null && { feature: r.feature },
180
220
  ...r.app_id != null && { appId: r.app_id },
221
+ ...r.metadata != null && { metadata: JSON.parse(r.metadata) },
181
222
  timestamp: r.timestamp
182
223
  }));
183
224
  }
@@ -1693,7 +1734,9 @@ var TrackerConfigSchema = z.object({
1693
1734
  mode: z.enum(["once", "always"]).optional().default("once")
1694
1735
  }).optional(),
1695
1736
  exporter: z.custom((v) => v !== null && typeof v === "object" && typeof v.export === "function").optional(),
1696
- appId: z.string().optional()
1737
+ appId: z.string().optional(),
1738
+ cloudApiKey: z.string().optional(),
1739
+ cloudEndpoint: z.string().url().optional()
1697
1740
  });
1698
1741
  function createTracker(config = {}) {
1699
1742
  const parsed = TrackerConfigSchema.safeParse(config);
@@ -1713,9 +1756,12 @@ ${issues}`);
1713
1756
  suggestions,
1714
1757
  anomalyDetection,
1715
1758
  exporter,
1716
- appId
1759
+ appId,
1760
+ cloudApiKey,
1761
+ cloudEndpoint
1717
1762
  } = parsed.data;
1718
1763
  const storage = typeof storageOption === "object" ? storageOption : createStorage(storageOption);
1764
+ const cloudExporter = cloudApiKey ? new CloudExporter(cloudApiKey, cloudEndpoint) : null;
1719
1765
  let remotePrices;
1720
1766
  let pricesUpdatedAt = bundledUpdatedAt;
1721
1767
  if (syncPrices) {
@@ -1776,6 +1822,9 @@ ${issues}`);
1776
1822
  Promise.resolve(exporter.export(full)).catch(() => {
1777
1823
  });
1778
1824
  }
1825
+ if (cloudExporter) {
1826
+ cloudExporter.export(full);
1827
+ }
1779
1828
  maybeFireAlerts(full);
1780
1829
  if (anomalyDetection) maybeDetectAnomaly(full);
1781
1830
  if (suggestions) {
@@ -1857,6 +1906,7 @@ ${issues}`);
1857
1906
  const byUser = {};
1858
1907
  const byFeature = {};
1859
1908
  const byApp = {};
1909
+ const byMetadata = {};
1860
1910
  let totalInput = 0;
1861
1911
  let totalOutput = 0;
1862
1912
  let totalCost = 0;
@@ -1898,6 +1948,14 @@ ${issues}`);
1898
1948
  a.costUSD += e.costUSD;
1899
1949
  a.calls += 1;
1900
1950
  }
1951
+ if (e.metadata) {
1952
+ for (const [key, val] of Object.entries(e.metadata)) {
1953
+ const group = byMetadata[key] ??= {};
1954
+ const slot = group[val] ??= { costUSD: 0, calls: 0 };
1955
+ slot.costUSD += e.costUSD;
1956
+ slot.calls += 1;
1957
+ }
1958
+ }
1901
1959
  }
1902
1960
  if (options && entries.length > 0) {
1903
1961
  periodFrom = entries[0]?.timestamp ?? periodFrom;
@@ -1910,6 +1968,7 @@ ${issues}`);
1910
1968
  byUser,
1911
1969
  byFeature,
1912
1970
  byApp,
1971
+ byMetadata,
1913
1972
  period: { from: periodFrom, to: lastTimestamp },
1914
1973
  ...pricesUpdatedAt ? { pricesUpdatedAt } : {}
1915
1974
  };
@@ -2014,7 +2073,7 @@ ${issues}`);
2014
2073
  }
2015
2074
  async function exportCSV() {
2016
2075
  const entries = await Promise.resolve(storage.getAll());
2017
- const header = "timestamp,model,inputTokens,outputTokens,reasoningTokens,cachedTokens,cacheCreationTokens,costUSD,sessionId,userId,feature,appId";
2076
+ const header = "timestamp,model,inputTokens,outputTokens,reasoningTokens,cachedTokens,cacheCreationTokens,costUSD,sessionId,userId,feature,appId,metadata";
2018
2077
  const rows = entries.map(
2019
2078
  (e) => [
2020
2079
  csvEscape(e.timestamp),
@@ -2028,7 +2087,8 @@ ${issues}`);
2028
2087
  csvEscape(e.sessionId ?? ""),
2029
2088
  csvEscape(e.userId ?? ""),
2030
2089
  csvEscape(e.feature ?? ""),
2031
- csvEscape(e.appId ?? "")
2090
+ csvEscape(e.appId ?? ""),
2091
+ csvEscape(e.metadata ? JSON.stringify(e.metadata) : "")
2032
2092
  ].join(",")
2033
2093
  );
2034
2094
  return [header, ...rows].join("\n");
@@ -2100,6 +2160,7 @@ function emptyReport() {
2100
2160
  byUser: {},
2101
2161
  byFeature: {},
2102
2162
  byApp: {},
2163
+ byMetadata: {},
2103
2164
  period: { from: now, to: now }
2104
2165
  };
2105
2166
  }
@@ -2156,12 +2217,13 @@ function createLazyTracker() {
2156
2217
 
2157
2218
  // src/providers/openai.ts
2158
2219
  function extractMeta(params) {
2159
- const { __sessionId, __userId, __feature, ...cleaned } = params;
2220
+ const { __sessionId, __userId, __feature, __metadata, ...cleaned } = params;
2160
2221
  return {
2161
2222
  cleaned,
2162
2223
  sessionId: typeof __sessionId === "string" ? __sessionId : void 0,
2163
2224
  userId: typeof __userId === "string" ? __userId : void 0,
2164
- feature: typeof __feature === "string" ? __feature : void 0
2225
+ feature: typeof __feature === "string" ? __feature : void 0,
2226
+ metadata: __metadata != null && typeof __metadata === "object" ? __metadata : void 0
2165
2227
  };
2166
2228
  }
2167
2229
  function extractUsage(usage) {
@@ -2176,7 +2238,7 @@ function extractUsage(usage) {
2176
2238
  cachedTokens
2177
2239
  };
2178
2240
  }
2179
- function trackWithMeta(tracker, model, inputTokens, outputTokens, reasoningTokens, sessionId, userId, feature, cachedTokens = 0) {
2241
+ function trackWithMeta(tracker, model, inputTokens, outputTokens, reasoningTokens, sessionId, userId, feature, cachedTokens = 0, metadata) {
2180
2242
  tracker.track({
2181
2243
  model,
2182
2244
  inputTokens,
@@ -2185,10 +2247,11 @@ function trackWithMeta(tracker, model, inputTokens, outputTokens, reasoningToken
2185
2247
  ...cachedTokens > 0 && { cachedTokens },
2186
2248
  ...sessionId !== void 0 && { sessionId },
2187
2249
  ...userId !== void 0 && { userId },
2188
- ...feature !== void 0 && { feature }
2250
+ ...feature !== void 0 && { feature },
2251
+ ...metadata !== void 0 && { metadata }
2189
2252
  });
2190
2253
  }
2191
- async function* wrapStream(stream, model, sessionId, userId, feature, tracker) {
2254
+ async function* wrapStream(stream, model, sessionId, userId, feature, tracker, metadata) {
2192
2255
  let lastChunk;
2193
2256
  for await (const chunk of stream) {
2194
2257
  lastChunk = chunk;
@@ -2200,7 +2263,7 @@ async function* wrapStream(stream, model, sessionId, userId, feature, tracker) {
2200
2263
  `[tokenwatch] No usage data in stream for model "${model}". Cost recorded as $0. Pass stream_options: { include_usage: true } to get accurate costs.`
2201
2264
  );
2202
2265
  }
2203
- trackWithMeta(tracker, model, inputTokens, outputTokens, reasoningTokens, sessionId, userId, feature, cachedTokens);
2266
+ trackWithMeta(tracker, model, inputTokens, outputTokens, reasoningTokens, sessionId, userId, feature, cachedTokens, metadata);
2204
2267
  }
2205
2268
  function wrapOpenAI(client, tracker) {
2206
2269
  const proxiedCompletions = new Proxy(client.chat.completions, {
@@ -2208,7 +2271,7 @@ function wrapOpenAI(client, tracker) {
2208
2271
  if (prop !== "create")
2209
2272
  return target[prop];
2210
2273
  return async function(params) {
2211
- const { cleaned, sessionId, userId, feature } = extractMeta(params);
2274
+ const { cleaned, sessionId, userId, feature, metadata } = extractMeta(params);
2212
2275
  const model = typeof cleaned["model"] === "string" ? cleaned["model"] : "unknown";
2213
2276
  const result = await target.create(cleaned);
2214
2277
  if (result && typeof result === "object" && Symbol.asyncIterator in result) {
@@ -2218,7 +2281,8 @@ function wrapOpenAI(client, tracker) {
2218
2281
  sessionId,
2219
2282
  userId,
2220
2283
  feature,
2221
- tracker
2284
+ tracker,
2285
+ metadata
2222
2286
  );
2223
2287
  }
2224
2288
  const completion = result;
@@ -2232,7 +2296,8 @@ function wrapOpenAI(client, tracker) {
2232
2296
  sessionId,
2233
2297
  userId,
2234
2298
  feature,
2235
- cachedTokens
2299
+ cachedTokens,
2300
+ metadata
2236
2301
  );
2237
2302
  return result;
2238
2303
  };
@@ -2249,12 +2314,12 @@ function wrapOpenAI(client, tracker) {
2249
2314
  if (prop !== "create")
2250
2315
  return target[prop];
2251
2316
  return async function(params) {
2252
- const { cleaned, sessionId, userId, feature } = extractMeta(params);
2317
+ const { cleaned, sessionId, userId, feature, metadata } = extractMeta(params);
2253
2318
  const model = typeof cleaned["model"] === "string" ? cleaned["model"] : "unknown";
2254
2319
  const result = await target.create(cleaned);
2255
2320
  const embedding = result;
2256
2321
  const inputTokens = embedding.usage?.total_tokens ?? 0;
2257
- trackWithMeta(tracker, embedding.model ?? model, inputTokens, 0, 0, sessionId, userId, feature);
2322
+ trackWithMeta(tracker, embedding.model ?? model, inputTokens, 0, 0, sessionId, userId, feature, 0, metadata);
2258
2323
  return result;
2259
2324
  };
2260
2325
  }
@@ -2270,12 +2335,13 @@ function wrapOpenAI(client, tracker) {
2270
2335
 
2271
2336
  // src/providers/anthropic.ts
2272
2337
  function extractMeta2(params) {
2273
- const { __sessionId, __userId, __feature, ...cleaned } = params;
2338
+ const { __sessionId, __userId, __feature, __metadata, ...cleaned } = params;
2274
2339
  return {
2275
2340
  cleaned,
2276
2341
  sessionId: typeof __sessionId === "string" ? __sessionId : void 0,
2277
2342
  userId: typeof __userId === "string" ? __userId : void 0,
2278
- feature: typeof __feature === "string" ? __feature : void 0
2343
+ feature: typeof __feature === "string" ? __feature : void 0,
2344
+ metadata: __metadata != null && typeof __metadata === "object" ? __metadata : void 0
2279
2345
  };
2280
2346
  }
2281
2347
  function extractUsage2(usage) {
@@ -2292,7 +2358,7 @@ function extractThinkingTokenApprox(content) {
2292
2358
  const chars = content.filter((b) => b.type === "thinking").reduce((sum, b) => sum + (b.thinking?.length ?? 0), 0);
2293
2359
  return chars > 0 ? Math.round(chars / 4) : 0;
2294
2360
  }
2295
- function trackWithMeta2(tracker, model, inputTokens, outputTokens, reasoningTokens, sessionId, userId, feature, cachedTokens = 0, cacheCreationTokens = 0) {
2361
+ function trackWithMeta2(tracker, model, inputTokens, outputTokens, reasoningTokens, sessionId, userId, feature, cachedTokens = 0, cacheCreationTokens = 0, metadata) {
2296
2362
  tracker.track({
2297
2363
  model,
2298
2364
  inputTokens,
@@ -2302,10 +2368,11 @@ function trackWithMeta2(tracker, model, inputTokens, outputTokens, reasoningToke
2302
2368
  ...cacheCreationTokens > 0 && { cacheCreationTokens },
2303
2369
  ...sessionId !== void 0 && { sessionId },
2304
2370
  ...userId !== void 0 && { userId },
2305
- ...feature !== void 0 && { feature }
2371
+ ...feature !== void 0 && { feature },
2372
+ ...metadata !== void 0 && { metadata }
2306
2373
  });
2307
2374
  }
2308
- async function* wrapStream2(stream, model, sessionId, userId, feature, tracker) {
2375
+ async function* wrapStream2(stream, model, sessionId, userId, feature, tracker, metadata) {
2309
2376
  let inputTokens = 0;
2310
2377
  let outputTokens = 0;
2311
2378
  let cachedTokens = 0;
@@ -2333,7 +2400,7 @@ async function* wrapStream2(stream, model, sessionId, userId, feature, tracker)
2333
2400
  }
2334
2401
  }
2335
2402
  const reasoningTokens = thinkingCharCount > 0 ? Math.round(thinkingCharCount / 4) : 0;
2336
- trackWithMeta2(tracker, model, inputTokens, outputTokens, reasoningTokens, sessionId, userId, feature, cachedTokens, cacheCreationTokens);
2403
+ trackWithMeta2(tracker, model, inputTokens, outputTokens, reasoningTokens, sessionId, userId, feature, cachedTokens, cacheCreationTokens, metadata);
2337
2404
  }
2338
2405
  function wrapAnthropic(client, tracker) {
2339
2406
  const proxiedMessages = new Proxy(client.messages, {
@@ -2341,7 +2408,7 @@ function wrapAnthropic(client, tracker) {
2341
2408
  if (prop !== "create")
2342
2409
  return target[prop];
2343
2410
  return async function(params) {
2344
- const { cleaned, sessionId, userId, feature } = extractMeta2(params);
2411
+ const { cleaned, sessionId, userId, feature, metadata } = extractMeta2(params);
2345
2412
  const model = typeof cleaned["model"] === "string" ? cleaned["model"] : "unknown";
2346
2413
  const result = await target.create(cleaned);
2347
2414
  if (result && typeof result === "object" && Symbol.asyncIterator in result) {
@@ -2351,7 +2418,8 @@ function wrapAnthropic(client, tracker) {
2351
2418
  sessionId,
2352
2419
  userId,
2353
2420
  feature,
2354
- tracker
2421
+ tracker,
2422
+ metadata
2355
2423
  );
2356
2424
  }
2357
2425
  const message = result;
@@ -2367,7 +2435,8 @@ function wrapAnthropic(client, tracker) {
2367
2435
  userId,
2368
2436
  feature,
2369
2437
  cachedTokens,
2370
- cacheCreationTokens
2438
+ cacheCreationTokens,
2439
+ metadata
2371
2440
  );
2372
2441
  return result;
2373
2442
  };
@@ -2388,10 +2457,11 @@ function wrapGemini(client, tracker) {
2388
2457
  if (prop !== "getGenerativeModel")
2389
2458
  return target[prop];
2390
2459
  return function(modelParams) {
2391
- const { __sessionId, __userId, __feature, ...cleanedParams } = modelParams;
2460
+ const { __sessionId, __userId, __feature, __metadata, ...cleanedParams } = modelParams;
2392
2461
  const feature = typeof __feature === "string" ? __feature : void 0;
2393
2462
  const sessionId = typeof __sessionId === "string" ? __sessionId : void 0;
2394
2463
  const userId = typeof __userId === "string" ? __userId : void 0;
2464
+ const metadata = __metadata != null && typeof __metadata === "object" ? __metadata : void 0;
2395
2465
  const modelInstance = target.getGenerativeModel(cleanedParams);
2396
2466
  const modelId = modelParams.model;
2397
2467
  return new Proxy(modelInstance, {
@@ -2406,7 +2476,8 @@ function wrapGemini(client, tracker) {
2406
2476
  outputTokens: meta?.candidatesTokenCount ?? 0,
2407
2477
  ...sessionId !== void 0 && { sessionId },
2408
2478
  ...userId !== void 0 && { userId },
2409
- ...feature !== void 0 && { feature }
2479
+ ...feature !== void 0 && { feature },
2480
+ ...metadata !== void 0 && { metadata }
2410
2481
  });
2411
2482
  return result;
2412
2483
  };
@@ -2422,7 +2493,8 @@ function wrapGemini(client, tracker) {
2422
2493
  outputTokens: meta?.candidatesTokenCount ?? 0,
2423
2494
  ...sessionId !== void 0 && { sessionId },
2424
2495
  ...userId !== void 0 && { userId },
2425
- ...feature !== void 0 && { feature }
2496
+ ...feature !== void 0 && { feature },
2497
+ ...metadata !== void 0 && { metadata }
2426
2498
  });
2427
2499
  }).catch(() => {
2428
2500
  });