@diogonzafe/tokenwatch 0.7.0 → 0.9.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 +1622 -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-fD5QLTWg.d.cts} +16 -0
- package/dist/{index-D9xq0RNg.d.ts → index-fD5QLTWg.d.ts} +16 -0
- package/dist/index.cjs +164 -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 +164 -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 */
|
|
@@ -62,6 +65,10 @@ interface TrackerConfig {
|
|
|
62
65
|
anomalyDetection?: AnomalyDetectionConfig;
|
|
63
66
|
/** Custom exporter called after every tracked call (e.g. OTelExporter) */
|
|
64
67
|
exporter?: IExporter;
|
|
68
|
+
/** TokenWatch Cloud API key — mirrors every tracked call to the cloud dashboard (fire-and-forget) */
|
|
69
|
+
cloudApiKey?: string;
|
|
70
|
+
/** Override the cloud ingest endpoint (default: https://api.tokenwatch.dev/v1/ingest) */
|
|
71
|
+
cloudEndpoint?: string;
|
|
65
72
|
}
|
|
66
73
|
interface UsageEntry {
|
|
67
74
|
model: string;
|
|
@@ -81,6 +88,8 @@ interface UsageEntry {
|
|
|
81
88
|
userId?: string;
|
|
82
89
|
/** Product feature that triggered this call (set via __feature in provider params) */
|
|
83
90
|
feature?: string;
|
|
91
|
+
/** Application identifier — set once in TrackerConfig.appId and stamped on every entry */
|
|
92
|
+
appId?: string;
|
|
84
93
|
timestamp: string;
|
|
85
94
|
}
|
|
86
95
|
interface ModelStats {
|
|
@@ -105,6 +114,10 @@ interface FeatureStats {
|
|
|
105
114
|
costUSD: number;
|
|
106
115
|
calls: number;
|
|
107
116
|
}
|
|
117
|
+
interface AppStats {
|
|
118
|
+
costUSD: number;
|
|
119
|
+
calls: number;
|
|
120
|
+
}
|
|
108
121
|
interface ReportOptions {
|
|
109
122
|
/** ISO string or Date — only include entries at or after this time */
|
|
110
123
|
since?: string | Date;
|
|
@@ -123,6 +136,7 @@ interface Report {
|
|
|
123
136
|
bySession: Record<string, SessionStats>;
|
|
124
137
|
byUser: Record<string, UserStats>;
|
|
125
138
|
byFeature: Record<string, FeatureStats>;
|
|
139
|
+
byApp: Record<string, AppStats>;
|
|
126
140
|
period: {
|
|
127
141
|
from: string;
|
|
128
142
|
to: string;
|
|
@@ -176,6 +190,8 @@ interface TrackingMeta {
|
|
|
176
190
|
__userId?: string;
|
|
177
191
|
/** Tag this call with a product feature name — appears in report.byFeature */
|
|
178
192
|
__feature?: string;
|
|
193
|
+
/** Override the tracker-level appId for this specific call */
|
|
194
|
+
__appId?: string;
|
|
179
195
|
}
|
|
180
196
|
|
|
181
197
|
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 */
|
|
@@ -62,6 +65,10 @@ interface TrackerConfig {
|
|
|
62
65
|
anomalyDetection?: AnomalyDetectionConfig;
|
|
63
66
|
/** Custom exporter called after every tracked call (e.g. OTelExporter) */
|
|
64
67
|
exporter?: IExporter;
|
|
68
|
+
/** TokenWatch Cloud API key — mirrors every tracked call to the cloud dashboard (fire-and-forget) */
|
|
69
|
+
cloudApiKey?: string;
|
|
70
|
+
/** Override the cloud ingest endpoint (default: https://api.tokenwatch.dev/v1/ingest) */
|
|
71
|
+
cloudEndpoint?: string;
|
|
65
72
|
}
|
|
66
73
|
interface UsageEntry {
|
|
67
74
|
model: string;
|
|
@@ -81,6 +88,8 @@ interface UsageEntry {
|
|
|
81
88
|
userId?: string;
|
|
82
89
|
/** Product feature that triggered this call (set via __feature in provider params) */
|
|
83
90
|
feature?: string;
|
|
91
|
+
/** Application identifier — set once in TrackerConfig.appId and stamped on every entry */
|
|
92
|
+
appId?: string;
|
|
84
93
|
timestamp: string;
|
|
85
94
|
}
|
|
86
95
|
interface ModelStats {
|
|
@@ -105,6 +114,10 @@ interface FeatureStats {
|
|
|
105
114
|
costUSD: number;
|
|
106
115
|
calls: number;
|
|
107
116
|
}
|
|
117
|
+
interface AppStats {
|
|
118
|
+
costUSD: number;
|
|
119
|
+
calls: number;
|
|
120
|
+
}
|
|
108
121
|
interface ReportOptions {
|
|
109
122
|
/** ISO string or Date — only include entries at or after this time */
|
|
110
123
|
since?: string | Date;
|
|
@@ -123,6 +136,7 @@ interface Report {
|
|
|
123
136
|
bySession: Record<string, SessionStats>;
|
|
124
137
|
byUser: Record<string, UserStats>;
|
|
125
138
|
byFeature: Record<string, FeatureStats>;
|
|
139
|
+
byApp: Record<string, AppStats>;
|
|
126
140
|
period: {
|
|
127
141
|
from: string;
|
|
128
142
|
to: string;
|
|
@@ -176,6 +190,8 @@ interface TrackingMeta {
|
|
|
176
190
|
__userId?: string;
|
|
177
191
|
/** Tag this call with a product feature name — appears in report.byFeature */
|
|
178
192
|
__feature?: string;
|
|
193
|
+
/** Override the tracker-level appId for this specific call */
|
|
194
|
+
__appId?: string;
|
|
179
195
|
}
|
|
180
196
|
|
|
181
197
|
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
|
@@ -64,6 +64,40 @@ function calculateCost(inputTokens, outputTokens, price, cachedTokens = 0, cache
|
|
|
64
64
|
return regularInputCost + cachedReadCost + cacheCreationCost + outputCost;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
// src/exporters/cloud.ts
|
|
68
|
+
var DEFAULT_ENDPOINT = "https://api.tokenwatch.dev/v1/ingest";
|
|
69
|
+
var CloudExporter = class {
|
|
70
|
+
constructor(apiKey, endpoint) {
|
|
71
|
+
this.apiKey = apiKey;
|
|
72
|
+
this.endpoint = endpoint ?? DEFAULT_ENDPOINT;
|
|
73
|
+
}
|
|
74
|
+
apiKey;
|
|
75
|
+
endpoint;
|
|
76
|
+
export(entry) {
|
|
77
|
+
fetch(this.endpoint, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: {
|
|
80
|
+
"Content-Type": "application/json",
|
|
81
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify({
|
|
84
|
+
model: entry.model,
|
|
85
|
+
inputTokens: entry.inputTokens,
|
|
86
|
+
outputTokens: entry.outputTokens,
|
|
87
|
+
reasoningTokens: entry.reasoningTokens ?? 0,
|
|
88
|
+
cachedTokens: entry.cachedTokens ?? 0,
|
|
89
|
+
cacheCreationTokens: entry.cacheCreationTokens ?? 0,
|
|
90
|
+
costUSD: entry.costUSD,
|
|
91
|
+
sessionId: entry.sessionId,
|
|
92
|
+
userId: entry.userId,
|
|
93
|
+
feature: entry.feature,
|
|
94
|
+
timestamp: entry.timestamp
|
|
95
|
+
})
|
|
96
|
+
}).catch(() => {
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
67
101
|
// src/core/suggestions.ts
|
|
68
102
|
var PROVIDER_PREFIXES = ["gpt-", "claude-", "gemini-", "deepseek-"];
|
|
69
103
|
function getProviderPrefix(model) {
|
|
@@ -154,6 +188,7 @@ var SqliteStorage = class {
|
|
|
154
188
|
session_id TEXT,
|
|
155
189
|
user_id TEXT,
|
|
156
190
|
feature TEXT,
|
|
191
|
+
app_id TEXT,
|
|
157
192
|
timestamp TEXT NOT NULL
|
|
158
193
|
)
|
|
159
194
|
`);
|
|
@@ -170,13 +205,16 @@ var SqliteStorage = class {
|
|
|
170
205
|
if (!cols.includes("cache_creation_tokens")) {
|
|
171
206
|
this.db.exec(`ALTER TABLE usage ADD COLUMN cache_creation_tokens INTEGER NOT NULL DEFAULT 0`);
|
|
172
207
|
}
|
|
208
|
+
if (!cols.includes("app_id")) {
|
|
209
|
+
this.db.exec(`ALTER TABLE usage ADD COLUMN app_id TEXT`);
|
|
210
|
+
}
|
|
173
211
|
}
|
|
174
212
|
record(entry) {
|
|
175
213
|
this.db.prepare(
|
|
176
214
|
`INSERT INTO usage
|
|
177
215
|
(model, input_tokens, output_tokens, reasoning_tokens, cached_tokens, cache_creation_tokens,
|
|
178
|
-
cost_usd, session_id, user_id, feature, timestamp)
|
|
179
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
216
|
+
cost_usd, session_id, user_id, feature, app_id, timestamp)
|
|
217
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
180
218
|
).run(
|
|
181
219
|
entry.model,
|
|
182
220
|
entry.inputTokens,
|
|
@@ -188,6 +226,7 @@ var SqliteStorage = class {
|
|
|
188
226
|
entry.sessionId ?? null,
|
|
189
227
|
entry.userId ?? null,
|
|
190
228
|
entry.feature ?? null,
|
|
229
|
+
entry.appId ?? null,
|
|
191
230
|
entry.timestamp
|
|
192
231
|
);
|
|
193
232
|
}
|
|
@@ -204,6 +243,7 @@ var SqliteStorage = class {
|
|
|
204
243
|
...r.session_id != null && { sessionId: r.session_id },
|
|
205
244
|
...r.user_id != null && { userId: r.user_id },
|
|
206
245
|
...r.feature != null && { feature: r.feature },
|
|
246
|
+
...r.app_id != null && { appId: r.app_id },
|
|
207
247
|
timestamp: r.timestamp
|
|
208
248
|
}));
|
|
209
249
|
}
|
|
@@ -269,7 +309,7 @@ async function getRemotePrices() {
|
|
|
269
309
|
|
|
270
310
|
// prices.json
|
|
271
311
|
var prices_default = {
|
|
272
|
-
updated_at: "2026-
|
|
312
|
+
updated_at: "2026-06-09",
|
|
273
313
|
source: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
|
|
274
314
|
models: {
|
|
275
315
|
"gpt-4o": {
|
|
@@ -313,14 +353,12 @@ var prices_default = {
|
|
|
313
353
|
input: 3,
|
|
314
354
|
output: 15,
|
|
315
355
|
cachedInput: 0.3,
|
|
316
|
-
|
|
317
|
-
maxInputTokens: 1e6
|
|
356
|
+
maxInputTokens: 2e5
|
|
318
357
|
},
|
|
319
358
|
"claude-haiku-4-5": {
|
|
320
359
|
input: 1,
|
|
321
360
|
output: 5,
|
|
322
361
|
cachedInput: 0.1,
|
|
323
|
-
cacheCreationInput: 1.25,
|
|
324
362
|
maxInputTokens: 2e5
|
|
325
363
|
},
|
|
326
364
|
"gemini-2.5-pro": {
|
|
@@ -372,7 +410,6 @@ var prices_default = {
|
|
|
372
410
|
input: 3,
|
|
373
411
|
output: 15,
|
|
374
412
|
cachedInput: 0.3,
|
|
375
|
-
cacheCreationInput: 3.75,
|
|
376
413
|
maxInputTokens: 2e5
|
|
377
414
|
},
|
|
378
415
|
"gpt-oss-120b": {
|
|
@@ -865,9 +902,9 @@ var prices_default = {
|
|
|
865
902
|
maxInputTokens: 163840
|
|
866
903
|
},
|
|
867
904
|
"deepseek-r1": {
|
|
868
|
-
input:
|
|
869
|
-
output:
|
|
870
|
-
maxInputTokens:
|
|
905
|
+
input: 1.35,
|
|
906
|
+
output: 5.4,
|
|
907
|
+
maxInputTokens: 128e3
|
|
871
908
|
},
|
|
872
909
|
"deepseek-v3": {
|
|
873
910
|
input: 0.27,
|
|
@@ -1307,7 +1344,7 @@ var prices_default = {
|
|
|
1307
1344
|
"deepseek-r1-distill-llama-70b": {
|
|
1308
1345
|
input: 0.99,
|
|
1309
1346
|
output: 0.99,
|
|
1310
|
-
maxInputTokens:
|
|
1347
|
+
maxInputTokens: 32768
|
|
1311
1348
|
},
|
|
1312
1349
|
"deepseek-llama3.3-70b": {
|
|
1313
1350
|
input: 0.2,
|
|
@@ -1591,7 +1628,97 @@ var prices_default = {
|
|
|
1591
1628
|
input: 5,
|
|
1592
1629
|
output: 30,
|
|
1593
1630
|
cachedInput: 0.5,
|
|
1631
|
+
maxInputTokens: 105e4
|
|
1632
|
+
},
|
|
1633
|
+
"gpt-5.5-2026-04-23": {
|
|
1634
|
+
input: 5,
|
|
1635
|
+
output: 30,
|
|
1636
|
+
cachedInput: 0.5,
|
|
1637
|
+
maxInputTokens: 105e4
|
|
1638
|
+
},
|
|
1639
|
+
"gpt-5.5-pro": {
|
|
1640
|
+
input: 30,
|
|
1641
|
+
output: 180,
|
|
1642
|
+
cachedInput: 3,
|
|
1643
|
+
maxInputTokens: 105e4
|
|
1644
|
+
},
|
|
1645
|
+
"gpt-5.5-pro-2026-04-23": {
|
|
1646
|
+
input: 30,
|
|
1647
|
+
output: 180,
|
|
1648
|
+
cachedInput: 3,
|
|
1649
|
+
maxInputTokens: 105e4
|
|
1650
|
+
},
|
|
1651
|
+
"gpt-5.4-mini-2026-03-17": {
|
|
1652
|
+
input: 0.75,
|
|
1653
|
+
output: 4.5,
|
|
1654
|
+
cachedInput: 0.075,
|
|
1655
|
+
maxInputTokens: 272e3
|
|
1656
|
+
},
|
|
1657
|
+
"gpt-5.4-nano-2026-03-17": {
|
|
1658
|
+
input: 0.2,
|
|
1659
|
+
output: 1.25,
|
|
1660
|
+
cachedInput: 0.02,
|
|
1594
1661
|
maxInputTokens: 272e3
|
|
1662
|
+
},
|
|
1663
|
+
"gpt-image-2": {
|
|
1664
|
+
input: 5,
|
|
1665
|
+
output: 10,
|
|
1666
|
+
cachedInput: 1.25
|
|
1667
|
+
},
|
|
1668
|
+
"gpt-image-2-2026-04-21": {
|
|
1669
|
+
input: 5,
|
|
1670
|
+
output: 10,
|
|
1671
|
+
cachedInput: 1.25
|
|
1672
|
+
},
|
|
1673
|
+
"gpt-realtime-2": {
|
|
1674
|
+
input: 4,
|
|
1675
|
+
output: 16,
|
|
1676
|
+
cachedInput: 0.4,
|
|
1677
|
+
maxInputTokens: 32e3
|
|
1678
|
+
},
|
|
1679
|
+
"gemini-3.5-flash": {
|
|
1680
|
+
input: 1.5,
|
|
1681
|
+
output: 9,
|
|
1682
|
+
cachedInput: 0.15,
|
|
1683
|
+
maxInputTokens: 1048576
|
|
1684
|
+
},
|
|
1685
|
+
"gemini-3.1-flash-lite": {
|
|
1686
|
+
input: 0.25,
|
|
1687
|
+
output: 1.5,
|
|
1688
|
+
cachedInput: 0.025,
|
|
1689
|
+
maxInputTokens: 1048576
|
|
1690
|
+
},
|
|
1691
|
+
"claude-opus-4-8": {
|
|
1692
|
+
input: 5,
|
|
1693
|
+
output: 25,
|
|
1694
|
+
cachedInput: 0.5,
|
|
1695
|
+
cacheCreationInput: 6.25,
|
|
1696
|
+
maxInputTokens: 1e6
|
|
1697
|
+
},
|
|
1698
|
+
"claude-opus-4-8@default": {
|
|
1699
|
+
input: 5,
|
|
1700
|
+
output: 25,
|
|
1701
|
+
cachedInput: 0.5,
|
|
1702
|
+
cacheCreationInput: 6.25,
|
|
1703
|
+
maxInputTokens: 1e6
|
|
1704
|
+
},
|
|
1705
|
+
"claude-4-sonnet": {
|
|
1706
|
+
input: 3,
|
|
1707
|
+
output: 15,
|
|
1708
|
+
cachedInput: 0.3,
|
|
1709
|
+
maxInputTokens: 2e5
|
|
1710
|
+
},
|
|
1711
|
+
"claude-4-opus": {
|
|
1712
|
+
input: 5,
|
|
1713
|
+
output: 25,
|
|
1714
|
+
cachedInput: 0.5,
|
|
1715
|
+
maxInputTokens: 2e5
|
|
1716
|
+
},
|
|
1717
|
+
"claude-3-7-sonnet": {
|
|
1718
|
+
input: 3,
|
|
1719
|
+
output: 15,
|
|
1720
|
+
cachedInput: 0.3,
|
|
1721
|
+
maxInputTokens: 2e5
|
|
1595
1722
|
}
|
|
1596
1723
|
}
|
|
1597
1724
|
};
|
|
@@ -1631,7 +1758,10 @@ var TrackerConfigSchema = import_zod.z.object({
|
|
|
1631
1758
|
windowHours: import_zod.z.number().positive().optional().default(24),
|
|
1632
1759
|
mode: import_zod.z.enum(["once", "always"]).optional().default("once")
|
|
1633
1760
|
}).optional(),
|
|
1634
|
-
exporter: import_zod.z.custom((v) => v !== null && typeof v === "object" && typeof v.export === "function").optional()
|
|
1761
|
+
exporter: import_zod.z.custom((v) => v !== null && typeof v === "object" && typeof v.export === "function").optional(),
|
|
1762
|
+
appId: import_zod.z.string().optional(),
|
|
1763
|
+
cloudApiKey: import_zod.z.string().optional(),
|
|
1764
|
+
cloudEndpoint: import_zod.z.string().url().optional()
|
|
1635
1765
|
});
|
|
1636
1766
|
function createTracker(config = {}) {
|
|
1637
1767
|
const parsed = TrackerConfigSchema.safeParse(config);
|
|
@@ -1650,9 +1780,13 @@ ${issues}`);
|
|
|
1650
1780
|
budgets,
|
|
1651
1781
|
suggestions,
|
|
1652
1782
|
anomalyDetection,
|
|
1653
|
-
exporter
|
|
1783
|
+
exporter,
|
|
1784
|
+
appId,
|
|
1785
|
+
cloudApiKey,
|
|
1786
|
+
cloudEndpoint
|
|
1654
1787
|
} = parsed.data;
|
|
1655
1788
|
const storage = typeof storageOption === "object" ? storageOption : createStorage(storageOption);
|
|
1789
|
+
const cloudExporter = cloudApiKey ? new CloudExporter(cloudApiKey, cloudEndpoint) : null;
|
|
1656
1790
|
let remotePrices;
|
|
1657
1791
|
let pricesUpdatedAt = bundledUpdatedAt;
|
|
1658
1792
|
if (syncPrices) {
|
|
@@ -1705,13 +1839,17 @@ ${issues}`);
|
|
|
1705
1839
|
const full = {
|
|
1706
1840
|
...entry,
|
|
1707
1841
|
costUSD,
|
|
1708
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1842
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1843
|
+
...appId !== void 0 && entry.appId === void 0 && { appId }
|
|
1709
1844
|
};
|
|
1710
1845
|
storage.record(full);
|
|
1711
1846
|
if (exporter) {
|
|
1712
1847
|
Promise.resolve(exporter.export(full)).catch(() => {
|
|
1713
1848
|
});
|
|
1714
1849
|
}
|
|
1850
|
+
if (cloudExporter) {
|
|
1851
|
+
cloudExporter.export(full);
|
|
1852
|
+
}
|
|
1715
1853
|
maybeFireAlerts(full);
|
|
1716
1854
|
if (anomalyDetection) maybeDetectAnomaly(full);
|
|
1717
1855
|
if (suggestions) {
|
|
@@ -1792,6 +1930,7 @@ ${issues}`);
|
|
|
1792
1930
|
const bySession = {};
|
|
1793
1931
|
const byUser = {};
|
|
1794
1932
|
const byFeature = {};
|
|
1933
|
+
const byApp = {};
|
|
1795
1934
|
let totalInput = 0;
|
|
1796
1935
|
let totalOutput = 0;
|
|
1797
1936
|
let totalCost = 0;
|
|
@@ -1828,6 +1967,11 @@ ${issues}`);
|
|
|
1828
1967
|
f.costUSD += e.costUSD;
|
|
1829
1968
|
f.calls += 1;
|
|
1830
1969
|
}
|
|
1970
|
+
if (e.appId) {
|
|
1971
|
+
const a = byApp[e.appId] ??= { costUSD: 0, calls: 0 };
|
|
1972
|
+
a.costUSD += e.costUSD;
|
|
1973
|
+
a.calls += 1;
|
|
1974
|
+
}
|
|
1831
1975
|
}
|
|
1832
1976
|
if (options && entries.length > 0) {
|
|
1833
1977
|
periodFrom = entries[0]?.timestamp ?? periodFrom;
|
|
@@ -1839,6 +1983,7 @@ ${issues}`);
|
|
|
1839
1983
|
bySession,
|
|
1840
1984
|
byUser,
|
|
1841
1985
|
byFeature,
|
|
1986
|
+
byApp,
|
|
1842
1987
|
period: { from: periodFrom, to: lastTimestamp },
|
|
1843
1988
|
...pricesUpdatedAt ? { pricesUpdatedAt } : {}
|
|
1844
1989
|
};
|
|
@@ -1943,7 +2088,7 @@ ${issues}`);
|
|
|
1943
2088
|
}
|
|
1944
2089
|
async function exportCSV() {
|
|
1945
2090
|
const entries = await Promise.resolve(storage.getAll());
|
|
1946
|
-
const header = "timestamp,model,inputTokens,outputTokens,reasoningTokens,cachedTokens,cacheCreationTokens,costUSD,sessionId,userId,feature";
|
|
2091
|
+
const header = "timestamp,model,inputTokens,outputTokens,reasoningTokens,cachedTokens,cacheCreationTokens,costUSD,sessionId,userId,feature,appId";
|
|
1947
2092
|
const rows = entries.map(
|
|
1948
2093
|
(e) => [
|
|
1949
2094
|
csvEscape(e.timestamp),
|
|
@@ -1956,7 +2101,8 @@ ${issues}`);
|
|
|
1956
2101
|
e.costUSD.toFixed(8),
|
|
1957
2102
|
csvEscape(e.sessionId ?? ""),
|
|
1958
2103
|
csvEscape(e.userId ?? ""),
|
|
1959
|
-
csvEscape(e.feature ?? "")
|
|
2104
|
+
csvEscape(e.feature ?? ""),
|
|
2105
|
+
csvEscape(e.appId ?? "")
|
|
1960
2106
|
].join(",")
|
|
1961
2107
|
);
|
|
1962
2108
|
return [header, ...rows].join("\n");
|
|
@@ -2017,7 +2163,7 @@ function csvEscape(value) {
|
|
|
2017
2163
|
}
|
|
2018
2164
|
|
|
2019
2165
|
// src/core/lazy-tracker.ts
|
|
2020
|
-
var CSV_HEADER = "timestamp,model,inputTokens,outputTokens,reasoningTokens,cachedTokens,cacheCreationTokens,costUSD,sessionId,userId,feature";
|
|
2166
|
+
var CSV_HEADER = "timestamp,model,inputTokens,outputTokens,reasoningTokens,cachedTokens,cacheCreationTokens,costUSD,sessionId,userId,feature,appId";
|
|
2021
2167
|
function emptyReport() {
|
|
2022
2168
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2023
2169
|
return {
|
|
@@ -2027,6 +2173,7 @@ function emptyReport() {
|
|
|
2027
2173
|
bySession: {},
|
|
2028
2174
|
byUser: {},
|
|
2029
2175
|
byFeature: {},
|
|
2176
|
+
byApp: {},
|
|
2030
2177
|
period: { from: now, to: now }
|
|
2031
2178
|
};
|
|
2032
2179
|
}
|