@diogonzafe/tokenwatch 0.6.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.
- package/README.md +36 -9
- package/dist/adapters.cjs +17 -6
- package/dist/adapters.cjs.map +1 -1
- package/dist/adapters.d.cts +2 -1
- package/dist/adapters.d.ts +2 -1
- package/dist/adapters.js +17 -6
- package/dist/adapters.js.map +1 -1
- package/dist/cli.js +1978 -550
- package/dist/cli.js.map +1 -1
- package/dist/exporters.d.cts +1 -1
- package/dist/exporters.d.ts +1 -1
- package/dist/{index-D9xq0RNg.d.cts → index-CFBI-1ab.d.cts} +12 -0
- package/dist/{index-D9xq0RNg.d.ts → index-CFBI-1ab.d.ts} +12 -0
- package/dist/index.cjs +128 -17
- 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 +128 -17
- package/dist/index.js.map +1 -1
- package/dist/langchain.d.cts +1 -1
- package/dist/langchain.d.ts +1 -1
- package/package.json +2 -2
- package/prices.json +102 -9
package/dist/exporters.d.cts
CHANGED
package/dist/exporters.d.ts
CHANGED
|
@@ -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-
|
|
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
|
-
|
|
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:
|
|
869
|
-
output:
|
|
870
|
-
maxInputTokens:
|
|
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:
|
|
1313
|
+
maxInputTokens: 32768
|
|
1311
1314
|
},
|
|
1312
1315
|
"deepseek-llama3.3-70b": {
|
|
1313
1316
|
input: 0.2,
|
|
@@ -1586,6 +1589,102 @@ var prices_default = {
|
|
|
1586
1589
|
cachedInput: 0.3,
|
|
1587
1590
|
cacheCreationInput: 3.75,
|
|
1588
1591
|
maxInputTokens: 1e6
|
|
1592
|
+
},
|
|
1593
|
+
"gpt-5.5": {
|
|
1594
|
+
input: 5,
|
|
1595
|
+
output: 30,
|
|
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,
|
|
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
|
|
1589
1688
|
}
|
|
1590
1689
|
}
|
|
1591
1690
|
};
|
|
@@ -1625,7 +1724,8 @@ var TrackerConfigSchema = import_zod.z.object({
|
|
|
1625
1724
|
windowHours: import_zod.z.number().positive().optional().default(24),
|
|
1626
1725
|
mode: import_zod.z.enum(["once", "always"]).optional().default("once")
|
|
1627
1726
|
}).optional(),
|
|
1628
|
-
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()
|
|
1629
1729
|
});
|
|
1630
1730
|
function createTracker(config = {}) {
|
|
1631
1731
|
const parsed = TrackerConfigSchema.safeParse(config);
|
|
@@ -1644,7 +1744,8 @@ ${issues}`);
|
|
|
1644
1744
|
budgets,
|
|
1645
1745
|
suggestions,
|
|
1646
1746
|
anomalyDetection,
|
|
1647
|
-
exporter
|
|
1747
|
+
exporter,
|
|
1748
|
+
appId
|
|
1648
1749
|
} = parsed.data;
|
|
1649
1750
|
const storage = typeof storageOption === "object" ? storageOption : createStorage(storageOption);
|
|
1650
1751
|
let remotePrices;
|
|
@@ -1699,7 +1800,8 @@ ${issues}`);
|
|
|
1699
1800
|
const full = {
|
|
1700
1801
|
...entry,
|
|
1701
1802
|
costUSD,
|
|
1702
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1803
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1804
|
+
...appId !== void 0 && entry.appId === void 0 && { appId }
|
|
1703
1805
|
};
|
|
1704
1806
|
storage.record(full);
|
|
1705
1807
|
if (exporter) {
|
|
@@ -1786,6 +1888,7 @@ ${issues}`);
|
|
|
1786
1888
|
const bySession = {};
|
|
1787
1889
|
const byUser = {};
|
|
1788
1890
|
const byFeature = {};
|
|
1891
|
+
const byApp = {};
|
|
1789
1892
|
let totalInput = 0;
|
|
1790
1893
|
let totalOutput = 0;
|
|
1791
1894
|
let totalCost = 0;
|
|
@@ -1822,6 +1925,11 @@ ${issues}`);
|
|
|
1822
1925
|
f.costUSD += e.costUSD;
|
|
1823
1926
|
f.calls += 1;
|
|
1824
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
|
+
}
|
|
1825
1933
|
}
|
|
1826
1934
|
if (options && entries.length > 0) {
|
|
1827
1935
|
periodFrom = entries[0]?.timestamp ?? periodFrom;
|
|
@@ -1833,6 +1941,7 @@ ${issues}`);
|
|
|
1833
1941
|
bySession,
|
|
1834
1942
|
byUser,
|
|
1835
1943
|
byFeature,
|
|
1944
|
+
byApp,
|
|
1836
1945
|
period: { from: periodFrom, to: lastTimestamp },
|
|
1837
1946
|
...pricesUpdatedAt ? { pricesUpdatedAt } : {}
|
|
1838
1947
|
};
|
|
@@ -1937,7 +2046,7 @@ ${issues}`);
|
|
|
1937
2046
|
}
|
|
1938
2047
|
async function exportCSV() {
|
|
1939
2048
|
const entries = await Promise.resolve(storage.getAll());
|
|
1940
|
-
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";
|
|
1941
2050
|
const rows = entries.map(
|
|
1942
2051
|
(e) => [
|
|
1943
2052
|
csvEscape(e.timestamp),
|
|
@@ -1950,7 +2059,8 @@ ${issues}`);
|
|
|
1950
2059
|
e.costUSD.toFixed(8),
|
|
1951
2060
|
csvEscape(e.sessionId ?? ""),
|
|
1952
2061
|
csvEscape(e.userId ?? ""),
|
|
1953
|
-
csvEscape(e.feature ?? "")
|
|
2062
|
+
csvEscape(e.feature ?? ""),
|
|
2063
|
+
csvEscape(e.appId ?? "")
|
|
1954
2064
|
].join(",")
|
|
1955
2065
|
);
|
|
1956
2066
|
return [header, ...rows].join("\n");
|
|
@@ -2011,7 +2121,7 @@ function csvEscape(value) {
|
|
|
2011
2121
|
}
|
|
2012
2122
|
|
|
2013
2123
|
// src/core/lazy-tracker.ts
|
|
2014
|
-
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";
|
|
2015
2125
|
function emptyReport() {
|
|
2016
2126
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2017
2127
|
return {
|
|
@@ -2021,6 +2131,7 @@ function emptyReport() {
|
|
|
2021
2131
|
bySession: {},
|
|
2022
2132
|
byUser: {},
|
|
2023
2133
|
byFeature: {},
|
|
2134
|
+
byApp: {},
|
|
2024
2135
|
period: { from: now, to: now }
|
|
2025
2136
|
};
|
|
2026
2137
|
}
|