@diogonzafe/tokenwatch 0.7.0 → 0.8.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.
@@ -1,4 +1,4 @@
1
- import { I as IExporter, U as UsageEntry } from './index-D9xq0RNg.cjs';
1
+ import { I as IExporter, U as UsageEntry } from './index-CFBI-1ab.cjs';
2
2
 
3
3
  interface OTelAttributes {
4
4
  [key: string]: string | number | boolean | undefined;
@@ -1,4 +1,4 @@
1
- import { I as IExporter, U as UsageEntry } from './index-D9xq0RNg.js';
1
+ import { I as IExporter, U as UsageEntry } from './index-CFBI-1ab.js';
2
2
 
3
3
  interface OTelAttributes {
4
4
  [key: string]: string | number | boolean | undefined;
@@ -41,6 +41,9 @@ interface IExporter {
41
41
  interface TrackerConfig {
42
42
  /** 'memory' (default), 'sqlite', or a custom IStorage instance (e.g. PostgresStorage, MySQLStorage, MongoStorage) */
43
43
  storage?: 'memory' | 'sqlite' | IStorage;
44
+ /** Tag all entries recorded by this tracker with an application identifier.
45
+ * Useful when multiple apps share a single database — each tracker stamps its own appId. */
46
+ appId?: string;
44
47
  /** USD threshold — fires webhookUrl when totalCostUSD exceeds this */
45
48
  alertThreshold?: number;
46
49
  /** Discord / Slack / generic webhook URL */
@@ -81,6 +84,8 @@ interface UsageEntry {
81
84
  userId?: string;
82
85
  /** Product feature that triggered this call (set via __feature in provider params) */
83
86
  feature?: string;
87
+ /** Application identifier — set once in TrackerConfig.appId and stamped on every entry */
88
+ appId?: string;
84
89
  timestamp: string;
85
90
  }
86
91
  interface ModelStats {
@@ -105,6 +110,10 @@ interface FeatureStats {
105
110
  costUSD: number;
106
111
  calls: number;
107
112
  }
113
+ interface AppStats {
114
+ costUSD: number;
115
+ calls: number;
116
+ }
108
117
  interface ReportOptions {
109
118
  /** ISO string or Date — only include entries at or after this time */
110
119
  since?: string | Date;
@@ -123,6 +132,7 @@ interface Report {
123
132
  bySession: Record<string, SessionStats>;
124
133
  byUser: Record<string, UserStats>;
125
134
  byFeature: Record<string, FeatureStats>;
135
+ byApp: Record<string, AppStats>;
126
136
  period: {
127
137
  from: string;
128
138
  to: string;
@@ -176,6 +186,8 @@ interface TrackingMeta {
176
186
  __userId?: string;
177
187
  /** Tag this call with a product feature name — appears in report.byFeature */
178
188
  __feature?: string;
189
+ /** Override the tracker-level appId for this specific call */
190
+ __appId?: string;
179
191
  }
180
192
 
181
193
  export type { AnomalyDetectionConfig as A, BudgetConfig as B, CostForecast as C, FeatureStats as F, IExporter as I, LazyTracker as L, ModelPrice as M, PriceMap as P, Report as R, SessionStats as S, TrackerConfig as T, UsageEntry as U, Tracker as a, TrackingMeta as b, ForecastOptions as c, IStorage as d, ModelStats as e, PricesFile as f, ReportOptions as g, UserStats as h };
@@ -41,6 +41,9 @@ interface IExporter {
41
41
  interface TrackerConfig {
42
42
  /** 'memory' (default), 'sqlite', or a custom IStorage instance (e.g. PostgresStorage, MySQLStorage, MongoStorage) */
43
43
  storage?: 'memory' | 'sqlite' | IStorage;
44
+ /** Tag all entries recorded by this tracker with an application identifier.
45
+ * Useful when multiple apps share a single database — each tracker stamps its own appId. */
46
+ appId?: string;
44
47
  /** USD threshold — fires webhookUrl when totalCostUSD exceeds this */
45
48
  alertThreshold?: number;
46
49
  /** Discord / Slack / generic webhook URL */
@@ -81,6 +84,8 @@ interface UsageEntry {
81
84
  userId?: string;
82
85
  /** Product feature that triggered this call (set via __feature in provider params) */
83
86
  feature?: string;
87
+ /** Application identifier — set once in TrackerConfig.appId and stamped on every entry */
88
+ appId?: string;
84
89
  timestamp: string;
85
90
  }
86
91
  interface ModelStats {
@@ -105,6 +110,10 @@ interface FeatureStats {
105
110
  costUSD: number;
106
111
  calls: number;
107
112
  }
113
+ interface AppStats {
114
+ costUSD: number;
115
+ calls: number;
116
+ }
108
117
  interface ReportOptions {
109
118
  /** ISO string or Date — only include entries at or after this time */
110
119
  since?: string | Date;
@@ -123,6 +132,7 @@ interface Report {
123
132
  bySession: Record<string, SessionStats>;
124
133
  byUser: Record<string, UserStats>;
125
134
  byFeature: Record<string, FeatureStats>;
135
+ byApp: Record<string, AppStats>;
126
136
  period: {
127
137
  from: string;
128
138
  to: string;
@@ -176,6 +186,8 @@ interface TrackingMeta {
176
186
  __userId?: string;
177
187
  /** Tag this call with a product feature name — appears in report.byFeature */
178
188
  __feature?: string;
189
+ /** Override the tracker-level appId for this specific call */
190
+ __appId?: string;
179
191
  }
180
192
 
181
193
  export type { AnomalyDetectionConfig as A, BudgetConfig as B, CostForecast as C, FeatureStats as F, IExporter as I, LazyTracker as L, ModelPrice as M, PriceMap as P, Report as R, SessionStats as S, TrackerConfig as T, UsageEntry as U, Tracker as a, TrackingMeta as b, ForecastOptions as c, IStorage as d, ModelStats as e, PricesFile as f, ReportOptions as g, UserStats as h };
package/dist/index.cjs CHANGED
@@ -154,6 +154,7 @@ var SqliteStorage = class {
154
154
  session_id TEXT,
155
155
  user_id TEXT,
156
156
  feature TEXT,
157
+ app_id TEXT,
157
158
  timestamp TEXT NOT NULL
158
159
  )
159
160
  `);
@@ -170,13 +171,16 @@ var SqliteStorage = class {
170
171
  if (!cols.includes("cache_creation_tokens")) {
171
172
  this.db.exec(`ALTER TABLE usage ADD COLUMN cache_creation_tokens INTEGER NOT NULL DEFAULT 0`);
172
173
  }
174
+ if (!cols.includes("app_id")) {
175
+ this.db.exec(`ALTER TABLE usage ADD COLUMN app_id TEXT`);
176
+ }
173
177
  }
174
178
  record(entry) {
175
179
  this.db.prepare(
176
180
  `INSERT INTO usage
177
181
  (model, input_tokens, output_tokens, reasoning_tokens, cached_tokens, cache_creation_tokens,
178
- cost_usd, session_id, user_id, feature, timestamp)
179
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
182
+ cost_usd, session_id, user_id, feature, app_id, timestamp)
183
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
180
184
  ).run(
181
185
  entry.model,
182
186
  entry.inputTokens,
@@ -188,6 +192,7 @@ var SqliteStorage = class {
188
192
  entry.sessionId ?? null,
189
193
  entry.userId ?? null,
190
194
  entry.feature ?? null,
195
+ entry.appId ?? null,
191
196
  entry.timestamp
192
197
  );
193
198
  }
@@ -204,6 +209,7 @@ var SqliteStorage = class {
204
209
  ...r.session_id != null && { sessionId: r.session_id },
205
210
  ...r.user_id != null && { userId: r.user_id },
206
211
  ...r.feature != null && { feature: r.feature },
212
+ ...r.app_id != null && { appId: r.app_id },
207
213
  timestamp: r.timestamp
208
214
  }));
209
215
  }
@@ -269,7 +275,7 @@ async function getRemotePrices() {
269
275
 
270
276
  // prices.json
271
277
  var prices_default = {
272
- updated_at: "2026-04-24",
278
+ updated_at: "2026-06-09",
273
279
  source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
274
280
  models: {
275
281
  "gpt-4o": {
@@ -313,14 +319,12 @@ var prices_default = {
313
319
  input: 3,
314
320
  output: 15,
315
321
  cachedInput: 0.3,
316
- cacheCreationInput: 3.75,
317
- maxInputTokens: 1e6
322
+ maxInputTokens: 2e5
318
323
  },
319
324
  "claude-haiku-4-5": {
320
325
  input: 1,
321
326
  output: 5,
322
327
  cachedInput: 0.1,
323
- cacheCreationInput: 1.25,
324
328
  maxInputTokens: 2e5
325
329
  },
326
330
  "gemini-2.5-pro": {
@@ -372,7 +376,6 @@ var prices_default = {
372
376
  input: 3,
373
377
  output: 15,
374
378
  cachedInput: 0.3,
375
- cacheCreationInput: 3.75,
376
379
  maxInputTokens: 2e5
377
380
  },
378
381
  "gpt-oss-120b": {
@@ -865,9 +868,9 @@ var prices_default = {
865
868
  maxInputTokens: 163840
866
869
  },
867
870
  "deepseek-r1": {
868
- input: 0.55,
869
- output: 2.19,
870
- maxInputTokens: 65536
871
+ input: 1.35,
872
+ output: 5.4,
873
+ maxInputTokens: 128e3
871
874
  },
872
875
  "deepseek-v3": {
873
876
  input: 0.27,
@@ -1307,7 +1310,7 @@ var prices_default = {
1307
1310
  "deepseek-r1-distill-llama-70b": {
1308
1311
  input: 0.99,
1309
1312
  output: 0.99,
1310
- maxInputTokens: 8e3
1313
+ maxInputTokens: 32768
1311
1314
  },
1312
1315
  "deepseek-llama3.3-70b": {
1313
1316
  input: 0.2,
@@ -1591,7 +1594,97 @@ var prices_default = {
1591
1594
  input: 5,
1592
1595
  output: 30,
1593
1596
  cachedInput: 0.5,
1597
+ maxInputTokens: 105e4
1598
+ },
1599
+ "gpt-5.5-2026-04-23": {
1600
+ input: 5,
1601
+ output: 30,
1602
+ cachedInput: 0.5,
1603
+ maxInputTokens: 105e4
1604
+ },
1605
+ "gpt-5.5-pro": {
1606
+ input: 30,
1607
+ output: 180,
1608
+ cachedInput: 3,
1609
+ maxInputTokens: 105e4
1610
+ },
1611
+ "gpt-5.5-pro-2026-04-23": {
1612
+ input: 30,
1613
+ output: 180,
1614
+ cachedInput: 3,
1615
+ maxInputTokens: 105e4
1616
+ },
1617
+ "gpt-5.4-mini-2026-03-17": {
1618
+ input: 0.75,
1619
+ output: 4.5,
1620
+ cachedInput: 0.075,
1594
1621
  maxInputTokens: 272e3
1622
+ },
1623
+ "gpt-5.4-nano-2026-03-17": {
1624
+ input: 0.2,
1625
+ output: 1.25,
1626
+ cachedInput: 0.02,
1627
+ maxInputTokens: 272e3
1628
+ },
1629
+ "gpt-image-2": {
1630
+ input: 5,
1631
+ output: 10,
1632
+ cachedInput: 1.25
1633
+ },
1634
+ "gpt-image-2-2026-04-21": {
1635
+ input: 5,
1636
+ output: 10,
1637
+ cachedInput: 1.25
1638
+ },
1639
+ "gpt-realtime-2": {
1640
+ input: 4,
1641
+ output: 16,
1642
+ cachedInput: 0.4,
1643
+ maxInputTokens: 32e3
1644
+ },
1645
+ "gemini-3.5-flash": {
1646
+ input: 1.5,
1647
+ output: 9,
1648
+ cachedInput: 0.15,
1649
+ maxInputTokens: 1048576
1650
+ },
1651
+ "gemini-3.1-flash-lite": {
1652
+ input: 0.25,
1653
+ output: 1.5,
1654
+ cachedInput: 0.025,
1655
+ maxInputTokens: 1048576
1656
+ },
1657
+ "claude-opus-4-8": {
1658
+ input: 5,
1659
+ output: 25,
1660
+ cachedInput: 0.5,
1661
+ cacheCreationInput: 6.25,
1662
+ maxInputTokens: 1e6
1663
+ },
1664
+ "claude-opus-4-8@default": {
1665
+ input: 5,
1666
+ output: 25,
1667
+ cachedInput: 0.5,
1668
+ cacheCreationInput: 6.25,
1669
+ maxInputTokens: 1e6
1670
+ },
1671
+ "claude-4-sonnet": {
1672
+ input: 3,
1673
+ output: 15,
1674
+ cachedInput: 0.3,
1675
+ maxInputTokens: 2e5
1676
+ },
1677
+ "claude-4-opus": {
1678
+ input: 5,
1679
+ output: 25,
1680
+ cachedInput: 0.5,
1681
+ maxInputTokens: 2e5
1682
+ },
1683
+ "claude-3-7-sonnet": {
1684
+ input: 3,
1685
+ output: 15,
1686
+ cachedInput: 0.3,
1687
+ maxInputTokens: 2e5
1595
1688
  }
1596
1689
  }
1597
1690
  };
@@ -1631,7 +1724,8 @@ var TrackerConfigSchema = import_zod.z.object({
1631
1724
  windowHours: import_zod.z.number().positive().optional().default(24),
1632
1725
  mode: import_zod.z.enum(["once", "always"]).optional().default("once")
1633
1726
  }).optional(),
1634
- exporter: import_zod.z.custom((v) => v !== null && typeof v === "object" && typeof v.export === "function").optional()
1727
+ exporter: import_zod.z.custom((v) => v !== null && typeof v === "object" && typeof v.export === "function").optional(),
1728
+ appId: import_zod.z.string().optional()
1635
1729
  });
1636
1730
  function createTracker(config = {}) {
1637
1731
  const parsed = TrackerConfigSchema.safeParse(config);
@@ -1650,7 +1744,8 @@ ${issues}`);
1650
1744
  budgets,
1651
1745
  suggestions,
1652
1746
  anomalyDetection,
1653
- exporter
1747
+ exporter,
1748
+ appId
1654
1749
  } = parsed.data;
1655
1750
  const storage = typeof storageOption === "object" ? storageOption : createStorage(storageOption);
1656
1751
  let remotePrices;
@@ -1705,7 +1800,8 @@ ${issues}`);
1705
1800
  const full = {
1706
1801
  ...entry,
1707
1802
  costUSD,
1708
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1803
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1804
+ ...appId !== void 0 && entry.appId === void 0 && { appId }
1709
1805
  };
1710
1806
  storage.record(full);
1711
1807
  if (exporter) {
@@ -1792,6 +1888,7 @@ ${issues}`);
1792
1888
  const bySession = {};
1793
1889
  const byUser = {};
1794
1890
  const byFeature = {};
1891
+ const byApp = {};
1795
1892
  let totalInput = 0;
1796
1893
  let totalOutput = 0;
1797
1894
  let totalCost = 0;
@@ -1828,6 +1925,11 @@ ${issues}`);
1828
1925
  f.costUSD += e.costUSD;
1829
1926
  f.calls += 1;
1830
1927
  }
1928
+ if (e.appId) {
1929
+ const a = byApp[e.appId] ??= { costUSD: 0, calls: 0 };
1930
+ a.costUSD += e.costUSD;
1931
+ a.calls += 1;
1932
+ }
1831
1933
  }
1832
1934
  if (options && entries.length > 0) {
1833
1935
  periodFrom = entries[0]?.timestamp ?? periodFrom;
@@ -1839,6 +1941,7 @@ ${issues}`);
1839
1941
  bySession,
1840
1942
  byUser,
1841
1943
  byFeature,
1944
+ byApp,
1842
1945
  period: { from: periodFrom, to: lastTimestamp },
1843
1946
  ...pricesUpdatedAt ? { pricesUpdatedAt } : {}
1844
1947
  };
@@ -1943,7 +2046,7 @@ ${issues}`);
1943
2046
  }
1944
2047
  async function exportCSV() {
1945
2048
  const entries = await Promise.resolve(storage.getAll());
1946
- const header = "timestamp,model,inputTokens,outputTokens,reasoningTokens,cachedTokens,cacheCreationTokens,costUSD,sessionId,userId,feature";
2049
+ const header = "timestamp,model,inputTokens,outputTokens,reasoningTokens,cachedTokens,cacheCreationTokens,costUSD,sessionId,userId,feature,appId";
1947
2050
  const rows = entries.map(
1948
2051
  (e) => [
1949
2052
  csvEscape(e.timestamp),
@@ -1956,7 +2059,8 @@ ${issues}`);
1956
2059
  e.costUSD.toFixed(8),
1957
2060
  csvEscape(e.sessionId ?? ""),
1958
2061
  csvEscape(e.userId ?? ""),
1959
- csvEscape(e.feature ?? "")
2062
+ csvEscape(e.feature ?? ""),
2063
+ csvEscape(e.appId ?? "")
1960
2064
  ].join(",")
1961
2065
  );
1962
2066
  return [header, ...rows].join("\n");
@@ -2017,7 +2121,7 @@ function csvEscape(value) {
2017
2121
  }
2018
2122
 
2019
2123
  // src/core/lazy-tracker.ts
2020
- var CSV_HEADER = "timestamp,model,inputTokens,outputTokens,reasoningTokens,cachedTokens,cacheCreationTokens,costUSD,sessionId,userId,feature";
2124
+ var CSV_HEADER = "timestamp,model,inputTokens,outputTokens,reasoningTokens,cachedTokens,cacheCreationTokens,costUSD,sessionId,userId,feature,appId";
2021
2125
  function emptyReport() {
2022
2126
  const now = (/* @__PURE__ */ new Date()).toISOString();
2023
2127
  return {
@@ -2027,6 +2131,7 @@ function emptyReport() {
2027
2131
  bySession: {},
2028
2132
  byUser: {},
2029
2133
  byFeature: {},
2134
+ byApp: {},
2030
2135
  period: { from: now, to: now }
2031
2136
  };
2032
2137
  }