@diogonzafe/tokenwatch 0.4.0 → 0.6.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 +132 -6
- package/dist/adapters.d.cts +1 -1
- package/dist/adapters.d.ts +1 -1
- package/dist/cli.js +848 -8
- 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 +63 -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 +63 -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 +1 -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-23",
|
|
241
241
|
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
242
242
|
models: {
|
|
243
243
|
"gpt-4o": {
|
|
@@ -1586,7 +1586,14 @@ var TrackerConfigSchema = z.object({
|
|
|
1586
1586
|
perUser: BudgetConfigSchema.optional(),
|
|
1587
1587
|
perSession: BudgetConfigSchema.optional()
|
|
1588
1588
|
}).optional(),
|
|
1589
|
-
suggestions: z.boolean().optional().default(false)
|
|
1589
|
+
suggestions: z.boolean().optional().default(false),
|
|
1590
|
+
anomalyDetection: z.object({
|
|
1591
|
+
multiplierThreshold: z.number().positive(),
|
|
1592
|
+
webhookUrl: z.string().url(),
|
|
1593
|
+
windowHours: z.number().positive().optional().default(24),
|
|
1594
|
+
mode: z.enum(["once", "always"]).optional().default("once")
|
|
1595
|
+
}).optional(),
|
|
1596
|
+
exporter: z.custom((v) => v !== null && typeof v === "object" && typeof v.export === "function").optional()
|
|
1590
1597
|
});
|
|
1591
1598
|
function createTracker(config = {}) {
|
|
1592
1599
|
const parsed = TrackerConfigSchema.safeParse(config);
|
|
@@ -1603,7 +1610,9 @@ ${issues}`);
|
|
|
1603
1610
|
customPrices,
|
|
1604
1611
|
warnIfStaleAfterHours,
|
|
1605
1612
|
budgets,
|
|
1606
|
-
suggestions
|
|
1613
|
+
suggestions,
|
|
1614
|
+
anomalyDetection,
|
|
1615
|
+
exporter
|
|
1607
1616
|
} = parsed.data;
|
|
1608
1617
|
const storage = typeof storageOption === "object" ? storageOption : createStorage(storageOption);
|
|
1609
1618
|
let remotePrices;
|
|
@@ -1636,6 +1645,7 @@ ${issues}`);
|
|
|
1636
1645
|
let alertFired = false;
|
|
1637
1646
|
const firedUserAlerts = /* @__PURE__ */ new Set();
|
|
1638
1647
|
const firedSessionAlerts = /* @__PURE__ */ new Set();
|
|
1648
|
+
const firedAnomalyKeys = /* @__PURE__ */ new Set();
|
|
1639
1649
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1640
1650
|
function resolveModelPrice(model) {
|
|
1641
1651
|
maybeWarnStaleness();
|
|
@@ -1660,7 +1670,12 @@ ${issues}`);
|
|
|
1660
1670
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1661
1671
|
};
|
|
1662
1672
|
storage.record(full);
|
|
1673
|
+
if (exporter) {
|
|
1674
|
+
Promise.resolve(exporter.export(full)).catch(() => {
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1663
1677
|
maybeFireAlerts(full);
|
|
1678
|
+
if (anomalyDetection) maybeDetectAnomaly(full);
|
|
1664
1679
|
if (suggestions) {
|
|
1665
1680
|
maybeSuggestCheaperModel(entry.model, costUSD, entry.inputTokens, entry.outputTokens, {
|
|
1666
1681
|
bundledPrices,
|
|
@@ -1830,11 +1845,56 @@ ${issues}`);
|
|
|
1830
1845
|
basedOnPeriod: { from: first, to: last }
|
|
1831
1846
|
};
|
|
1832
1847
|
}
|
|
1848
|
+
function maybeDetectAnomaly(entry) {
|
|
1849
|
+
if (entry.costUSD <= 0) return;
|
|
1850
|
+
const { multiplierThreshold, webhookUrl: aUrl, windowHours: wh, mode: modeRaw } = anomalyDetection;
|
|
1851
|
+
const wHours = wh ?? 24;
|
|
1852
|
+
const mode = modeRaw ?? "once";
|
|
1853
|
+
const windowStart = Date.now() - wHours * 60 * 60 * 1e3;
|
|
1854
|
+
const entryTs = new Date(entry.timestamp).getTime();
|
|
1855
|
+
function checkEntity(key, label, predicate) {
|
|
1856
|
+
if (mode !== "always" && firedAnomalyKeys.has(key)) return;
|
|
1857
|
+
if (mode !== "always") firedAnomalyKeys.add(key);
|
|
1858
|
+
Promise.resolve(storage.getAll()).then((all) => {
|
|
1859
|
+
const history = all.filter(
|
|
1860
|
+
(e) => predicate(e) && new Date(e.timestamp).getTime() >= windowStart && new Date(e.timestamp).getTime() !== entryTs
|
|
1861
|
+
);
|
|
1862
|
+
if (history.length === 0) {
|
|
1863
|
+
if (mode !== "always") firedAnomalyKeys.delete(key);
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
const avg = history.reduce((s, e) => s + e.costUSD, 0) / history.length;
|
|
1867
|
+
if (avg <= 0 || entry.costUSD <= avg * multiplierThreshold) {
|
|
1868
|
+
if (mode !== "always") firedAnomalyKeys.delete(key);
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
const multiple = (entry.costUSD / avg).toFixed(1);
|
|
1872
|
+
fireWebhook(aUrl, {
|
|
1873
|
+
text: `[tokenwatch] Anomaly: ${label} call cost $${entry.costUSD.toFixed(4)} is ${multiple}x above ${wHours}h average ($${avg.toFixed(4)})`
|
|
1874
|
+
});
|
|
1875
|
+
}).catch(() => {
|
|
1876
|
+
if (mode !== "always") firedAnomalyKeys.delete(key);
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
if (entry.userId) {
|
|
1880
|
+
checkEntity(
|
|
1881
|
+
`user:${entry.userId}`,
|
|
1882
|
+
`user "${entry.userId}"`,
|
|
1883
|
+
(e) => e.userId === entry.userId
|
|
1884
|
+
);
|
|
1885
|
+
}
|
|
1886
|
+
checkEntity(
|
|
1887
|
+
`model:${entry.model}`,
|
|
1888
|
+
`model "${entry.model}"`,
|
|
1889
|
+
(e) => e.model === entry.model
|
|
1890
|
+
);
|
|
1891
|
+
}
|
|
1833
1892
|
async function reset() {
|
|
1834
1893
|
await Promise.resolve(storage.clearAll());
|
|
1835
1894
|
alertFired = false;
|
|
1836
1895
|
firedUserAlerts.clear();
|
|
1837
1896
|
firedSessionAlerts.clear();
|
|
1897
|
+
firedAnomalyKeys.clear();
|
|
1838
1898
|
}
|
|
1839
1899
|
async function resetSession(sessionId) {
|
|
1840
1900
|
await Promise.resolve(storage.clearSession(sessionId));
|