@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.
- package/README.md +13 -10
- 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 +1580 -518
- 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 +122 -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 +122 -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 +96 -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,
|
|
@@ -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
|
}
|