@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/README.md +137 -2
- package/dist/adapters.d.cts +1 -1
- package/dist/adapters.d.ts +1 -1
- package/dist/cli.js +467 -41
- package/dist/cli.js.map +1 -1
- package/dist/exporters.cjs +76 -0
- package/dist/exporters.cjs.map +1 -0
- package/dist/exporters.d.cts +60 -0
- package/dist/exporters.d.ts +60 -0
- package/dist/exporters.js +56 -0
- package/dist/exporters.js.map +1 -0
- package/dist/{index-CJKk1hHw.d.cts → index-D9xq0RNg.d.cts} +19 -1
- package/dist/{index-CJKk1hHw.d.ts → index-D9xq0RNg.d.ts} +19 -1
- package/dist/index.cjs +69 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +69 -3
- package/dist/index.js.map +1 -1
- package/dist/langchain.d.cts +1 -1
- package/dist/langchain.d.ts +1 -1
- package/package.json +13 -3
- package/prices.json +7 -1
- package/dist/cli.d.ts +0 -1
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-
|
|
2
|
-
export { B as BudgetConfig, C as CostForecast, F as FeatureStats, c as ForecastOptions, I as IStorage, M as ModelPrice,
|
|
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-
|
|
2
|
-
export { B as BudgetConfig, C as CostForecast, F as FeatureStats, c as ForecastOptions, I as IStorage, M as ModelPrice,
|
|
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-
|
|
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));
|