@diogonzafe/tokenwatch 0.5.0 → 0.7.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-CJKk1hHw.cjs';
2
- export { B as BudgetConfig, C as CostForecast, F as FeatureStats, c as ForecastOptions, I as IStorage, M as ModelPrice, d as ModelStats, P as PriceMap, e as PricesFile, R as Report, f as ReportOptions, S as SessionStats, U as UsageEntry, g as UserStats } from './index-CJKk1hHw.cjs';
1
+ import { T as TrackerConfig, a as Tracker, L as LazyTracker, b as TrackingMeta } from './index-D9xq0RNg.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-D9xq0RNg.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-CJKk1hHw.js';
2
- export { B as BudgetConfig, C as CostForecast, F as FeatureStats, c as ForecastOptions, I as IStorage, M as ModelPrice, d as ModelStats, P as PriceMap, e as PricesFile, R as Report, f as ReportOptions, S as SessionStats, U as UsageEntry, g as UserStats } from './index-CJKk1hHw.js';
1
+ import { T as TrackerConfig, a as Tracker, L as LazyTracker, b as TrackingMeta } from './index-D9xq0RNg.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-D9xq0RNg.js';
3
3
 
4
4
  declare function createTracker(config?: TrackerConfig): Tracker;
5
5
 
package/dist/index.js CHANGED
@@ -237,7 +237,7 @@ async function getRemotePrices() {
237
237
 
238
238
  // prices.json
239
239
  var prices_default = {
240
- updated_at: "2026-04-22",
240
+ updated_at: "2026-04-24",
241
241
  source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
242
242
  models: {
243
243
  "gpt-4o": {
@@ -1554,6 +1554,12 @@ var prices_default = {
1554
1554
  cachedInput: 0.3,
1555
1555
  cacheCreationInput: 3.75,
1556
1556
  maxInputTokens: 1e6
1557
+ },
1558
+ "gpt-5.5": {
1559
+ input: 5,
1560
+ output: 30,
1561
+ cachedInput: 0.5,
1562
+ maxInputTokens: 272e3
1557
1563
  }
1558
1564
  }
1559
1565
  };
@@ -1586,7 +1592,14 @@ var TrackerConfigSchema = z.object({
1586
1592
  perUser: BudgetConfigSchema.optional(),
1587
1593
  perSession: BudgetConfigSchema.optional()
1588
1594
  }).optional(),
1589
- suggestions: z.boolean().optional().default(false)
1595
+ suggestions: z.boolean().optional().default(false),
1596
+ anomalyDetection: z.object({
1597
+ multiplierThreshold: z.number().positive(),
1598
+ webhookUrl: z.string().url(),
1599
+ windowHours: z.number().positive().optional().default(24),
1600
+ mode: z.enum(["once", "always"]).optional().default("once")
1601
+ }).optional(),
1602
+ exporter: z.custom((v) => v !== null && typeof v === "object" && typeof v.export === "function").optional()
1590
1603
  });
1591
1604
  function createTracker(config = {}) {
1592
1605
  const parsed = TrackerConfigSchema.safeParse(config);
@@ -1603,7 +1616,9 @@ ${issues}`);
1603
1616
  customPrices,
1604
1617
  warnIfStaleAfterHours,
1605
1618
  budgets,
1606
- suggestions
1619
+ suggestions,
1620
+ anomalyDetection,
1621
+ exporter
1607
1622
  } = parsed.data;
1608
1623
  const storage = typeof storageOption === "object" ? storageOption : createStorage(storageOption);
1609
1624
  let remotePrices;
@@ -1636,6 +1651,7 @@ ${issues}`);
1636
1651
  let alertFired = false;
1637
1652
  const firedUserAlerts = /* @__PURE__ */ new Set();
1638
1653
  const firedSessionAlerts = /* @__PURE__ */ new Set();
1654
+ const firedAnomalyKeys = /* @__PURE__ */ new Set();
1639
1655
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
1640
1656
  function resolveModelPrice(model) {
1641
1657
  maybeWarnStaleness();
@@ -1660,7 +1676,12 @@ ${issues}`);
1660
1676
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1661
1677
  };
1662
1678
  storage.record(full);
1679
+ if (exporter) {
1680
+ Promise.resolve(exporter.export(full)).catch(() => {
1681
+ });
1682
+ }
1663
1683
  maybeFireAlerts(full);
1684
+ if (anomalyDetection) maybeDetectAnomaly(full);
1664
1685
  if (suggestions) {
1665
1686
  maybeSuggestCheaperModel(entry.model, costUSD, entry.inputTokens, entry.outputTokens, {
1666
1687
  bundledPrices,
@@ -1830,11 +1851,56 @@ ${issues}`);
1830
1851
  basedOnPeriod: { from: first, to: last }
1831
1852
  };
1832
1853
  }
1854
+ function maybeDetectAnomaly(entry) {
1855
+ if (entry.costUSD <= 0) return;
1856
+ const { multiplierThreshold, webhookUrl: aUrl, windowHours: wh, mode: modeRaw } = anomalyDetection;
1857
+ const wHours = wh ?? 24;
1858
+ const mode = modeRaw ?? "once";
1859
+ const windowStart = Date.now() - wHours * 60 * 60 * 1e3;
1860
+ const entryTs = new Date(entry.timestamp).getTime();
1861
+ function checkEntity(key, label, predicate) {
1862
+ if (mode !== "always" && firedAnomalyKeys.has(key)) return;
1863
+ if (mode !== "always") firedAnomalyKeys.add(key);
1864
+ Promise.resolve(storage.getAll()).then((all) => {
1865
+ const history = all.filter(
1866
+ (e) => predicate(e) && new Date(e.timestamp).getTime() >= windowStart && new Date(e.timestamp).getTime() !== entryTs
1867
+ );
1868
+ if (history.length === 0) {
1869
+ if (mode !== "always") firedAnomalyKeys.delete(key);
1870
+ return;
1871
+ }
1872
+ const avg = history.reduce((s, e) => s + e.costUSD, 0) / history.length;
1873
+ if (avg <= 0 || entry.costUSD <= avg * multiplierThreshold) {
1874
+ if (mode !== "always") firedAnomalyKeys.delete(key);
1875
+ return;
1876
+ }
1877
+ const multiple = (entry.costUSD / avg).toFixed(1);
1878
+ fireWebhook(aUrl, {
1879
+ text: `[tokenwatch] Anomaly: ${label} call cost $${entry.costUSD.toFixed(4)} is ${multiple}x above ${wHours}h average ($${avg.toFixed(4)})`
1880
+ });
1881
+ }).catch(() => {
1882
+ if (mode !== "always") firedAnomalyKeys.delete(key);
1883
+ });
1884
+ }
1885
+ if (entry.userId) {
1886
+ checkEntity(
1887
+ `user:${entry.userId}`,
1888
+ `user "${entry.userId}"`,
1889
+ (e) => e.userId === entry.userId
1890
+ );
1891
+ }
1892
+ checkEntity(
1893
+ `model:${entry.model}`,
1894
+ `model "${entry.model}"`,
1895
+ (e) => e.model === entry.model
1896
+ );
1897
+ }
1833
1898
  async function reset() {
1834
1899
  await Promise.resolve(storage.clearAll());
1835
1900
  alertFired = false;
1836
1901
  firedUserAlerts.clear();
1837
1902
  firedSessionAlerts.clear();
1903
+ firedAnomalyKeys.clear();
1838
1904
  }
1839
1905
  async function resetSession(sessionId) {
1840
1906
  await Promise.resolve(storage.clearSession(sessionId));