@diogonzafe/tokenwatch 0.1.17 → 0.2.1
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 +58 -6
- package/dist/adapters.cjs +56 -22
- package/dist/adapters.cjs.map +1 -1
- package/dist/adapters.d.cts +7 -3
- package/dist/adapters.d.ts +7 -3
- package/dist/adapters.js +56 -22
- package/dist/adapters.js.map +1 -1
- package/dist/cli.js +42 -13
- package/dist/cli.js.map +1 -1
- package/dist/{index-Cy_sl3FI.d.ts → index-B_EmA3K7.d.cts} +15 -1
- package/dist/{index-Cy_sl3FI.d.cts → index-B_EmA3K7.d.ts} +15 -1
- package/dist/index.cjs +117 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -8
- package/dist/index.d.ts +23 -8
- package/dist/index.js +117 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -2
- package/dist/cli.cjs +0 -1625
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.cts +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { T as TrackerConfig, a as Tracker, b as TrackingMeta } from './index-
|
|
2
|
-
export { I as IStorage, M as ModelPrice, c as ModelStats, P as PriceMap, d as PricesFile, R as Report, S as SessionStats, U as UsageEntry, e as UserStats } from './index-
|
|
1
|
+
import { T as TrackerConfig, a as Tracker, b as TrackingMeta } from './index-B_EmA3K7.js';
|
|
2
|
+
export { F as FeatureStats, I as IStorage, M as ModelPrice, c as ModelStats, P as PriceMap, d as PricesFile, R as Report, S as SessionStats, U as UsageEntry, e as UserStats } from './index-B_EmA3K7.js';
|
|
3
3
|
|
|
4
4
|
declare function createTracker(config?: TrackerConfig): Tracker;
|
|
5
5
|
|
|
@@ -9,23 +9,31 @@ interface CompletionsLike {
|
|
|
9
9
|
interface ChatLike {
|
|
10
10
|
completions: CompletionsLike;
|
|
11
11
|
}
|
|
12
|
+
interface EmbeddingsLike {
|
|
13
|
+
create(params: Record<string, unknown>): Promise<unknown>;
|
|
14
|
+
}
|
|
12
15
|
type OpenAILike = {
|
|
13
16
|
chat: ChatLike;
|
|
17
|
+
embeddings?: EmbeddingsLike;
|
|
14
18
|
} & Record<string, unknown>;
|
|
15
19
|
type AugmentedCreate$1<TCreate extends (...args: any[]) => any> = (params: Parameters<TCreate>[0] & TrackingMeta) => ReturnType<TCreate>;
|
|
16
|
-
type WrappedOpenAI<T extends OpenAILike> = Omit<T, 'chat'> & {
|
|
20
|
+
type WrappedOpenAI<T extends OpenAILike> = Omit<T, 'chat' | 'embeddings'> & {
|
|
17
21
|
chat: Omit<T['chat'], 'completions'> & {
|
|
18
22
|
completions: Omit<T['chat']['completions'], 'create'> & {
|
|
19
23
|
create: AugmentedCreate$1<T['chat']['completions']['create']>;
|
|
20
24
|
};
|
|
21
25
|
};
|
|
26
|
+
embeddings: T['embeddings'] extends EmbeddingsLike ? Omit<T['embeddings'], 'create'> & {
|
|
27
|
+
create: AugmentedCreate$1<T['embeddings']['create']>;
|
|
28
|
+
} : T['embeddings'];
|
|
22
29
|
};
|
|
23
30
|
/**
|
|
24
31
|
* Wraps an OpenAI client (or any OpenAI-compatible client) to transparently
|
|
25
|
-
* intercept chat.completions.create calls and report
|
|
32
|
+
* intercept chat.completions.create and embeddings.create calls and report
|
|
33
|
+
* token usage to the tracker.
|
|
26
34
|
*
|
|
27
|
-
* The returned client is typed to accept __sessionId
|
|
28
|
-
* normal params — no type cast required at the call site.
|
|
35
|
+
* The returned client is typed to accept __sessionId, __userId, and __feature
|
|
36
|
+
* alongside the normal params — no type cast required at the call site.
|
|
29
37
|
*/
|
|
30
38
|
declare function wrapOpenAI<T extends OpenAILike>(client: T, tracker: Tracker): WrappedOpenAI<T>;
|
|
31
39
|
|
|
@@ -45,8 +53,12 @@ type WrappedAnthropic<T extends AnthropicLike> = Omit<T, 'messages'> & {
|
|
|
45
53
|
* Wraps an Anthropic client to transparently intercept messages.create calls
|
|
46
54
|
* and report token usage to the tracker.
|
|
47
55
|
*
|
|
48
|
-
* The returned client is typed to accept __sessionId
|
|
49
|
-
* normal params — no type cast required at the call site.
|
|
56
|
+
* The returned client is typed to accept __sessionId, __userId, and __feature
|
|
57
|
+
* alongside the normal params — no type cast required at the call site.
|
|
58
|
+
*
|
|
59
|
+
* For extended thinking models, reasoningTokens is stored as an approximation
|
|
60
|
+
* (thinking block characters ÷ 4). It is informational only — thinking output
|
|
61
|
+
* is already included in outputTokens and is not double-counted in cost.
|
|
50
62
|
*/
|
|
51
63
|
declare function wrapAnthropic<T extends AnthropicLike>(client: T, tracker: Tracker): WrappedAnthropic<T>;
|
|
52
64
|
|
|
@@ -79,6 +91,9 @@ interface GenAILike {
|
|
|
79
91
|
* Wraps a GoogleGenerativeAI client to transparently intercept
|
|
80
92
|
* generateContent / generateContentStream calls and report token usage.
|
|
81
93
|
*
|
|
94
|
+
* Pass __feature in getGenerativeModel params to tag all calls from that model
|
|
95
|
+
* instance with a product feature name (appears in report.byFeature).
|
|
96
|
+
*
|
|
82
97
|
* Returns the same type T that was passed in.
|
|
83
98
|
*/
|
|
84
99
|
declare function wrapGemini<T extends GenAILike>(client: T, tracker: Tracker): T;
|
package/dist/index.js
CHANGED
|
@@ -74,29 +74,40 @@ var SqliteStorage = class {
|
|
|
74
74
|
migrate() {
|
|
75
75
|
this.db.exec(`
|
|
76
76
|
CREATE TABLE IF NOT EXISTS usage (
|
|
77
|
-
id
|
|
78
|
-
model
|
|
79
|
-
input_tokens
|
|
80
|
-
output_tokens
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
77
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
78
|
+
model TEXT NOT NULL,
|
|
79
|
+
input_tokens INTEGER NOT NULL,
|
|
80
|
+
output_tokens INTEGER NOT NULL,
|
|
81
|
+
reasoning_tokens INTEGER NOT NULL DEFAULT 0,
|
|
82
|
+
cost_usd REAL NOT NULL,
|
|
83
|
+
session_id TEXT,
|
|
84
|
+
user_id TEXT,
|
|
85
|
+
feature TEXT,
|
|
86
|
+
timestamp TEXT NOT NULL
|
|
85
87
|
)
|
|
86
88
|
`);
|
|
89
|
+
const cols = this.db.prepare(`PRAGMA table_info(usage)`).all().map((c) => c.name);
|
|
90
|
+
if (!cols.includes("reasoning_tokens")) {
|
|
91
|
+
this.db.exec(`ALTER TABLE usage ADD COLUMN reasoning_tokens INTEGER NOT NULL DEFAULT 0`);
|
|
92
|
+
}
|
|
93
|
+
if (!cols.includes("feature")) {
|
|
94
|
+
this.db.exec(`ALTER TABLE usage ADD COLUMN feature TEXT`);
|
|
95
|
+
}
|
|
87
96
|
}
|
|
88
97
|
record(entry) {
|
|
89
98
|
this.db.prepare(
|
|
90
99
|
`INSERT INTO usage
|
|
91
|
-
(model, input_tokens, output_tokens, cost_usd, session_id, user_id, timestamp)
|
|
92
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
100
|
+
(model, input_tokens, output_tokens, reasoning_tokens, cost_usd, session_id, user_id, feature, timestamp)
|
|
101
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
93
102
|
).run(
|
|
94
103
|
entry.model,
|
|
95
104
|
entry.inputTokens,
|
|
96
105
|
entry.outputTokens,
|
|
106
|
+
entry.reasoningTokens ?? 0,
|
|
97
107
|
entry.costUSD,
|
|
98
108
|
entry.sessionId ?? null,
|
|
99
109
|
entry.userId ?? null,
|
|
110
|
+
entry.feature ?? null,
|
|
100
111
|
entry.timestamp
|
|
101
112
|
);
|
|
102
113
|
}
|
|
@@ -106,9 +117,11 @@ var SqliteStorage = class {
|
|
|
106
117
|
model: r.model,
|
|
107
118
|
inputTokens: r.input_tokens,
|
|
108
119
|
outputTokens: r.output_tokens,
|
|
120
|
+
...r.reasoning_tokens > 0 && { reasoningTokens: r.reasoning_tokens },
|
|
109
121
|
costUSD: r.cost_usd,
|
|
110
122
|
...r.session_id != null && { sessionId: r.session_id },
|
|
111
123
|
...r.user_id != null && { userId: r.user_id },
|
|
124
|
+
...r.feature != null && { feature: r.feature },
|
|
112
125
|
timestamp: r.timestamp
|
|
113
126
|
}));
|
|
114
127
|
}
|
|
@@ -1415,6 +1428,7 @@ ${issues}`);
|
|
|
1415
1428
|
const byModel = {};
|
|
1416
1429
|
const bySession = {};
|
|
1417
1430
|
const byUser = {};
|
|
1431
|
+
const byFeature = {};
|
|
1418
1432
|
let totalInput = 0;
|
|
1419
1433
|
let totalOutput = 0;
|
|
1420
1434
|
let totalCost = 0;
|
|
@@ -1424,11 +1438,12 @@ ${issues}`);
|
|
|
1424
1438
|
totalOutput += e.outputTokens;
|
|
1425
1439
|
totalCost += e.costUSD;
|
|
1426
1440
|
if (e.timestamp > lastTimestamp) lastTimestamp = e.timestamp;
|
|
1427
|
-
const m = byModel[e.model] ??= { costUSD: 0, calls: 0, tokens: { input: 0, output: 0 } };
|
|
1441
|
+
const m = byModel[e.model] ??= { costUSD: 0, calls: 0, tokens: { input: 0, output: 0, reasoning: 0 } };
|
|
1428
1442
|
m.costUSD += e.costUSD;
|
|
1429
1443
|
m.calls += 1;
|
|
1430
1444
|
m.tokens.input += e.inputTokens;
|
|
1431
1445
|
m.tokens.output += e.outputTokens;
|
|
1446
|
+
m.tokens.reasoning += e.reasoningTokens ?? 0;
|
|
1432
1447
|
if (e.sessionId) {
|
|
1433
1448
|
const s = bySession[e.sessionId] ??= { costUSD: 0, calls: 0 };
|
|
1434
1449
|
s.costUSD += e.costUSD;
|
|
@@ -1439,6 +1454,11 @@ ${issues}`);
|
|
|
1439
1454
|
u.costUSD += e.costUSD;
|
|
1440
1455
|
u.calls += 1;
|
|
1441
1456
|
}
|
|
1457
|
+
if (e.feature) {
|
|
1458
|
+
const f = byFeature[e.feature] ??= { costUSD: 0, calls: 0 };
|
|
1459
|
+
f.costUSD += e.costUSD;
|
|
1460
|
+
f.calls += 1;
|
|
1461
|
+
}
|
|
1442
1462
|
}
|
|
1443
1463
|
return {
|
|
1444
1464
|
totalCostUSD: totalCost,
|
|
@@ -1446,6 +1466,7 @@ ${issues}`);
|
|
|
1446
1466
|
byModel,
|
|
1447
1467
|
bySession,
|
|
1448
1468
|
byUser,
|
|
1469
|
+
byFeature,
|
|
1449
1470
|
period: { from: startedAt, to: lastTimestamp }
|
|
1450
1471
|
};
|
|
1451
1472
|
}
|
|
@@ -1461,16 +1482,18 @@ ${issues}`);
|
|
|
1461
1482
|
}
|
|
1462
1483
|
async function exportCSV() {
|
|
1463
1484
|
const entries = await Promise.resolve(storage.getAll());
|
|
1464
|
-
const header = "timestamp,model,inputTokens,outputTokens,costUSD,sessionId,userId";
|
|
1485
|
+
const header = "timestamp,model,inputTokens,outputTokens,reasoningTokens,costUSD,sessionId,userId,feature";
|
|
1465
1486
|
const rows = entries.map(
|
|
1466
1487
|
(e) => [
|
|
1467
1488
|
csvEscape(e.timestamp),
|
|
1468
1489
|
csvEscape(e.model),
|
|
1469
1490
|
e.inputTokens,
|
|
1470
1491
|
e.outputTokens,
|
|
1492
|
+
e.reasoningTokens ?? 0,
|
|
1471
1493
|
e.costUSD.toFixed(8),
|
|
1472
1494
|
csvEscape(e.sessionId ?? ""),
|
|
1473
|
-
csvEscape(e.userId ?? "")
|
|
1495
|
+
csvEscape(e.userId ?? ""),
|
|
1496
|
+
csvEscape(e.feature ?? "")
|
|
1474
1497
|
].join(",")
|
|
1475
1498
|
);
|
|
1476
1499
|
return [header, ...rows].join("\n");
|
|
@@ -1496,42 +1519,46 @@ function csvEscape(value) {
|
|
|
1496
1519
|
|
|
1497
1520
|
// src/providers/openai.ts
|
|
1498
1521
|
function extractMeta(params) {
|
|
1499
|
-
const { __sessionId, __userId, ...cleaned } = params;
|
|
1522
|
+
const { __sessionId, __userId, __feature, ...cleaned } = params;
|
|
1500
1523
|
return {
|
|
1501
1524
|
cleaned,
|
|
1502
1525
|
sessionId: typeof __sessionId === "string" ? __sessionId : void 0,
|
|
1503
|
-
userId: typeof __userId === "string" ? __userId : void 0
|
|
1526
|
+
userId: typeof __userId === "string" ? __userId : void 0,
|
|
1527
|
+
feature: typeof __feature === "string" ? __feature : void 0
|
|
1504
1528
|
};
|
|
1505
1529
|
}
|
|
1506
1530
|
function extractUsage(usage) {
|
|
1507
|
-
if (!usage) return { inputTokens: 0, outputTokens: 0 };
|
|
1531
|
+
if (!usage) return { inputTokens: 0, outputTokens: 0, reasoningTokens: 0 };
|
|
1508
1532
|
return {
|
|
1509
1533
|
inputTokens: usage.prompt_tokens ?? usage.input_tokens ?? 0,
|
|
1510
|
-
outputTokens: usage.completion_tokens ?? usage.output_tokens ?? 0
|
|
1534
|
+
outputTokens: usage.completion_tokens ?? usage.output_tokens ?? 0,
|
|
1535
|
+
reasoningTokens: usage.completion_tokens_details?.reasoning_tokens ?? 0
|
|
1511
1536
|
};
|
|
1512
1537
|
}
|
|
1513
|
-
function trackWithMeta(tracker, model, inputTokens, outputTokens, sessionId, userId) {
|
|
1538
|
+
function trackWithMeta(tracker, model, inputTokens, outputTokens, reasoningTokens, sessionId, userId, feature) {
|
|
1514
1539
|
tracker.track({
|
|
1515
1540
|
model,
|
|
1516
1541
|
inputTokens,
|
|
1517
|
-
outputTokens,
|
|
1542
|
+
outputTokens: outputTokens + reasoningTokens,
|
|
1543
|
+
...reasoningTokens > 0 && { reasoningTokens },
|
|
1518
1544
|
...sessionId !== void 0 && { sessionId },
|
|
1519
|
-
...userId !== void 0 && { userId }
|
|
1545
|
+
...userId !== void 0 && { userId },
|
|
1546
|
+
...feature !== void 0 && { feature }
|
|
1520
1547
|
});
|
|
1521
1548
|
}
|
|
1522
|
-
async function* wrapStream(stream, model, sessionId, userId, tracker) {
|
|
1549
|
+
async function* wrapStream(stream, model, sessionId, userId, feature, tracker) {
|
|
1523
1550
|
let lastChunk;
|
|
1524
1551
|
for await (const chunk of stream) {
|
|
1525
1552
|
lastChunk = chunk;
|
|
1526
1553
|
yield chunk;
|
|
1527
1554
|
}
|
|
1528
|
-
const { inputTokens, outputTokens } = extractUsage(lastChunk?.usage);
|
|
1555
|
+
const { inputTokens, outputTokens, reasoningTokens } = extractUsage(lastChunk?.usage);
|
|
1529
1556
|
if (!lastChunk?.usage) {
|
|
1530
1557
|
console.warn(
|
|
1531
1558
|
`[tokenwatch] No usage data in stream for model "${model}". Cost recorded as $0. Pass stream_options: { include_usage: true } to get accurate costs.`
|
|
1532
1559
|
);
|
|
1533
1560
|
}
|
|
1534
|
-
trackWithMeta(tracker, model, inputTokens, outputTokens, sessionId, userId);
|
|
1561
|
+
trackWithMeta(tracker, model, inputTokens, outputTokens, reasoningTokens, sessionId, userId, feature);
|
|
1535
1562
|
}
|
|
1536
1563
|
function wrapOpenAI(client, tracker) {
|
|
1537
1564
|
const proxiedCompletions = new Proxy(client.chat.completions, {
|
|
@@ -1539,7 +1566,7 @@ function wrapOpenAI(client, tracker) {
|
|
|
1539
1566
|
if (prop !== "create")
|
|
1540
1567
|
return target[prop];
|
|
1541
1568
|
return async function(params) {
|
|
1542
|
-
const { cleaned, sessionId, userId } = extractMeta(params);
|
|
1569
|
+
const { cleaned, sessionId, userId, feature } = extractMeta(params);
|
|
1543
1570
|
const model = typeof cleaned["model"] === "string" ? cleaned["model"] : "unknown";
|
|
1544
1571
|
const result = await target.create(cleaned);
|
|
1545
1572
|
if (result && typeof result === "object" && Symbol.asyncIterator in result) {
|
|
@@ -1548,18 +1575,21 @@ function wrapOpenAI(client, tracker) {
|
|
|
1548
1575
|
model,
|
|
1549
1576
|
sessionId,
|
|
1550
1577
|
userId,
|
|
1578
|
+
feature,
|
|
1551
1579
|
tracker
|
|
1552
1580
|
);
|
|
1553
1581
|
}
|
|
1554
1582
|
const completion = result;
|
|
1555
|
-
const { inputTokens, outputTokens } = extractUsage(completion.usage);
|
|
1583
|
+
const { inputTokens, outputTokens, reasoningTokens } = extractUsage(completion.usage);
|
|
1556
1584
|
trackWithMeta(
|
|
1557
1585
|
tracker,
|
|
1558
1586
|
completion.model ?? model,
|
|
1559
1587
|
inputTokens,
|
|
1560
1588
|
outputTokens,
|
|
1589
|
+
reasoningTokens,
|
|
1561
1590
|
sessionId,
|
|
1562
|
-
userId
|
|
1591
|
+
userId,
|
|
1592
|
+
feature
|
|
1563
1593
|
);
|
|
1564
1594
|
return result;
|
|
1565
1595
|
};
|
|
@@ -1571,9 +1601,25 @@ function wrapOpenAI(client, tracker) {
|
|
|
1571
1601
|
return target[prop];
|
|
1572
1602
|
}
|
|
1573
1603
|
});
|
|
1604
|
+
const proxiedEmbeddings = client.embeddings ? new Proxy(client.embeddings, {
|
|
1605
|
+
get(target, prop) {
|
|
1606
|
+
if (prop !== "create")
|
|
1607
|
+
return target[prop];
|
|
1608
|
+
return async function(params) {
|
|
1609
|
+
const { cleaned, sessionId, userId, feature } = extractMeta(params);
|
|
1610
|
+
const model = typeof cleaned["model"] === "string" ? cleaned["model"] : "unknown";
|
|
1611
|
+
const result = await target.create(cleaned);
|
|
1612
|
+
const embedding = result;
|
|
1613
|
+
const inputTokens = embedding.usage?.total_tokens ?? 0;
|
|
1614
|
+
trackWithMeta(tracker, embedding.model ?? model, inputTokens, 0, 0, sessionId, userId, feature);
|
|
1615
|
+
return result;
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
}) : void 0;
|
|
1574
1619
|
return new Proxy(client, {
|
|
1575
1620
|
get(target, prop) {
|
|
1576
1621
|
if (prop === "chat") return proxiedChat;
|
|
1622
|
+
if (prop === "embeddings") return proxiedEmbeddings;
|
|
1577
1623
|
return target[prop];
|
|
1578
1624
|
}
|
|
1579
1625
|
});
|
|
@@ -1581,11 +1627,12 @@ function wrapOpenAI(client, tracker) {
|
|
|
1581
1627
|
|
|
1582
1628
|
// src/providers/anthropic.ts
|
|
1583
1629
|
function extractMeta2(params) {
|
|
1584
|
-
const { __sessionId, __userId, ...cleaned } = params;
|
|
1630
|
+
const { __sessionId, __userId, __feature, ...cleaned } = params;
|
|
1585
1631
|
return {
|
|
1586
1632
|
cleaned,
|
|
1587
1633
|
sessionId: typeof __sessionId === "string" ? __sessionId : void 0,
|
|
1588
|
-
userId: typeof __userId === "string" ? __userId : void 0
|
|
1634
|
+
userId: typeof __userId === "string" ? __userId : void 0,
|
|
1635
|
+
feature: typeof __feature === "string" ? __feature : void 0
|
|
1589
1636
|
};
|
|
1590
1637
|
}
|
|
1591
1638
|
function extractUsage2(usage) {
|
|
@@ -1595,18 +1642,27 @@ function extractUsage2(usage) {
|
|
|
1595
1642
|
outputTokens: usage.output_tokens ?? 0
|
|
1596
1643
|
};
|
|
1597
1644
|
}
|
|
1598
|
-
function
|
|
1645
|
+
function extractThinkingTokenApprox(content) {
|
|
1646
|
+
if (!content) return 0;
|
|
1647
|
+
const chars = content.filter((b) => b.type === "thinking").reduce((sum, b) => sum + (b.thinking?.length ?? 0), 0);
|
|
1648
|
+
return chars > 0 ? Math.round(chars / 4) : 0;
|
|
1649
|
+
}
|
|
1650
|
+
function trackWithMeta2(tracker, model, inputTokens, outputTokens, reasoningTokens, sessionId, userId, feature) {
|
|
1599
1651
|
tracker.track({
|
|
1600
1652
|
model,
|
|
1601
1653
|
inputTokens,
|
|
1602
1654
|
outputTokens,
|
|
1655
|
+
...reasoningTokens > 0 && { reasoningTokens },
|
|
1603
1656
|
...sessionId !== void 0 && { sessionId },
|
|
1604
|
-
...userId !== void 0 && { userId }
|
|
1657
|
+
...userId !== void 0 && { userId },
|
|
1658
|
+
...feature !== void 0 && { feature }
|
|
1605
1659
|
});
|
|
1606
1660
|
}
|
|
1607
|
-
async function* wrapStream2(stream, model, sessionId, userId, tracker) {
|
|
1661
|
+
async function* wrapStream2(stream, model, sessionId, userId, feature, tracker) {
|
|
1608
1662
|
let inputTokens = 0;
|
|
1609
1663
|
let outputTokens = 0;
|
|
1664
|
+
let currentBlockIsThinking = false;
|
|
1665
|
+
let thinkingCharCount = 0;
|
|
1610
1666
|
for await (const event of stream) {
|
|
1611
1667
|
yield event;
|
|
1612
1668
|
if (event.type === "message_start" && event.message?.usage) {
|
|
@@ -1615,8 +1671,18 @@ async function* wrapStream2(stream, model, sessionId, userId, tracker) {
|
|
|
1615
1671
|
if (event.type === "message_delta" && event.usage) {
|
|
1616
1672
|
outputTokens = event.usage.output_tokens ?? 0;
|
|
1617
1673
|
}
|
|
1674
|
+
if (event.type === "content_block_start") {
|
|
1675
|
+
currentBlockIsThinking = event.content_block?.type === "thinking";
|
|
1676
|
+
}
|
|
1677
|
+
if (event.type === "content_block_stop") {
|
|
1678
|
+
currentBlockIsThinking = false;
|
|
1679
|
+
}
|
|
1680
|
+
if (event.type === "content_block_delta" && currentBlockIsThinking && event.delta?.thinking) {
|
|
1681
|
+
thinkingCharCount += event.delta.thinking.length;
|
|
1682
|
+
}
|
|
1618
1683
|
}
|
|
1619
|
-
|
|
1684
|
+
const reasoningTokens = thinkingCharCount > 0 ? Math.round(thinkingCharCount / 4) : 0;
|
|
1685
|
+
trackWithMeta2(tracker, model, inputTokens, outputTokens, reasoningTokens, sessionId, userId, feature);
|
|
1620
1686
|
}
|
|
1621
1687
|
function wrapAnthropic(client, tracker) {
|
|
1622
1688
|
const proxiedMessages = new Proxy(client.messages, {
|
|
@@ -1624,7 +1690,7 @@ function wrapAnthropic(client, tracker) {
|
|
|
1624
1690
|
if (prop !== "create")
|
|
1625
1691
|
return target[prop];
|
|
1626
1692
|
return async function(params) {
|
|
1627
|
-
const { cleaned, sessionId, userId } = extractMeta2(params);
|
|
1693
|
+
const { cleaned, sessionId, userId, feature } = extractMeta2(params);
|
|
1628
1694
|
const model = typeof cleaned["model"] === "string" ? cleaned["model"] : "unknown";
|
|
1629
1695
|
const result = await target.create(cleaned);
|
|
1630
1696
|
if (result && typeof result === "object" && Symbol.asyncIterator in result) {
|
|
@@ -1633,18 +1699,22 @@ function wrapAnthropic(client, tracker) {
|
|
|
1633
1699
|
model,
|
|
1634
1700
|
sessionId,
|
|
1635
1701
|
userId,
|
|
1702
|
+
feature,
|
|
1636
1703
|
tracker
|
|
1637
1704
|
);
|
|
1638
1705
|
}
|
|
1639
1706
|
const message = result;
|
|
1640
1707
|
const { inputTokens, outputTokens } = extractUsage2(message.usage);
|
|
1708
|
+
const reasoningTokens = extractThinkingTokenApprox(message.content);
|
|
1641
1709
|
trackWithMeta2(
|
|
1642
1710
|
tracker,
|
|
1643
1711
|
message.model ?? model,
|
|
1644
1712
|
inputTokens,
|
|
1645
1713
|
outputTokens,
|
|
1714
|
+
reasoningTokens,
|
|
1646
1715
|
sessionId,
|
|
1647
|
-
userId
|
|
1716
|
+
userId,
|
|
1717
|
+
feature
|
|
1648
1718
|
);
|
|
1649
1719
|
return result;
|
|
1650
1720
|
};
|
|
@@ -1665,7 +1735,11 @@ function wrapGemini(client, tracker) {
|
|
|
1665
1735
|
if (prop !== "getGenerativeModel")
|
|
1666
1736
|
return target[prop];
|
|
1667
1737
|
return function(modelParams) {
|
|
1668
|
-
const
|
|
1738
|
+
const { __sessionId, __userId, __feature, ...cleanedParams } = modelParams;
|
|
1739
|
+
const feature = typeof __feature === "string" ? __feature : void 0;
|
|
1740
|
+
const sessionId = typeof __sessionId === "string" ? __sessionId : void 0;
|
|
1741
|
+
const userId = typeof __userId === "string" ? __userId : void 0;
|
|
1742
|
+
const modelInstance = target.getGenerativeModel(cleanedParams);
|
|
1669
1743
|
const modelId = modelParams.model;
|
|
1670
1744
|
return new Proxy(modelInstance, {
|
|
1671
1745
|
get(mTarget, mProp) {
|
|
@@ -1676,7 +1750,10 @@ function wrapGemini(client, tracker) {
|
|
|
1676
1750
|
tracker.track({
|
|
1677
1751
|
model: modelId,
|
|
1678
1752
|
inputTokens: meta?.promptTokenCount ?? 0,
|
|
1679
|
-
outputTokens: meta?.candidatesTokenCount ?? 0
|
|
1753
|
+
outputTokens: meta?.candidatesTokenCount ?? 0,
|
|
1754
|
+
...sessionId !== void 0 && { sessionId },
|
|
1755
|
+
...userId !== void 0 && { userId },
|
|
1756
|
+
...feature !== void 0 && { feature }
|
|
1680
1757
|
});
|
|
1681
1758
|
return result;
|
|
1682
1759
|
};
|
|
@@ -1689,7 +1766,10 @@ function wrapGemini(client, tracker) {
|
|
|
1689
1766
|
tracker.track({
|
|
1690
1767
|
model: modelId,
|
|
1691
1768
|
inputTokens: meta?.promptTokenCount ?? 0,
|
|
1692
|
-
outputTokens: meta?.candidatesTokenCount ?? 0
|
|
1769
|
+
outputTokens: meta?.candidatesTokenCount ?? 0,
|
|
1770
|
+
...sessionId !== void 0 && { sessionId },
|
|
1771
|
+
...userId !== void 0 && { userId },
|
|
1772
|
+
...feature !== void 0 && { feature }
|
|
1693
1773
|
});
|
|
1694
1774
|
}).catch(() => {
|
|
1695
1775
|
});
|