tokenmeter 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/LICENSE +21 -0
- package/README.md +346 -0
- package/dist/__tests__/context.test.d.ts +2 -0
- package/dist/__tests__/context.test.d.ts.map +1 -0
- package/dist/__tests__/context.test.js +94 -0
- package/dist/__tests__/context.test.js.map +1 -0
- package/dist/__tests__/elevenlabs.test.d.ts +2 -0
- package/dist/__tests__/elevenlabs.test.d.ts.map +1 -0
- package/dist/__tests__/elevenlabs.test.js +108 -0
- package/dist/__tests__/elevenlabs.test.js.map +1 -0
- package/dist/__tests__/fal.test.d.ts +2 -0
- package/dist/__tests__/fal.test.d.ts.map +1 -0
- package/dist/__tests__/fal.test.js +153 -0
- package/dist/__tests__/fal.test.js.map +1 -0
- package/dist/__tests__/pricing.test.d.ts +2 -0
- package/dist/__tests__/pricing.test.d.ts.map +1 -0
- package/dist/__tests__/pricing.test.js +76 -0
- package/dist/__tests__/pricing.test.js.map +1 -0
- package/dist/__tests__/recorder.test.d.ts +2 -0
- package/dist/__tests__/recorder.test.d.ts.map +1 -0
- package/dist/__tests__/recorder.test.js +133 -0
- package/dist/__tests__/recorder.test.js.map +1 -0
- package/dist/__tests__/storage.test.d.ts +2 -0
- package/dist/__tests__/storage.test.d.ts.map +1 -0
- package/dist/__tests__/storage.test.js +106 -0
- package/dist/__tests__/storage.test.js.map +1 -0
- package/dist/client/index.d.ts +8 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +7 -0
- package/dist/client/index.js.map +1 -0
- package/dist/config.d.ts +92 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +166 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +80 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +131 -0
- package/dist/context.js.map +1 -0
- package/dist/exporter/PostgresExporter.d.ts +82 -0
- package/dist/exporter/PostgresExporter.d.ts.map +1 -0
- package/dist/exporter/PostgresExporter.js +237 -0
- package/dist/exporter/PostgresExporter.js.map +1 -0
- package/dist/exporter/index.d.ts +8 -0
- package/dist/exporter/index.d.ts.map +1 -0
- package/dist/exporter/index.js +7 -0
- package/dist/exporter/index.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumentation/proxy.d.ts +26 -0
- package/dist/instrumentation/proxy.d.ts.map +1 -0
- package/dist/instrumentation/proxy.js +337 -0
- package/dist/instrumentation/proxy.js.map +1 -0
- package/dist/instrumentation/strategies/index.d.ts +55 -0
- package/dist/instrumentation/strategies/index.d.ts.map +1 -0
- package/dist/instrumentation/strategies/index.js +429 -0
- package/dist/instrumentation/strategies/index.js.map +1 -0
- package/dist/integrations/express/index.d.ts +137 -0
- package/dist/integrations/express/index.d.ts.map +1 -0
- package/dist/integrations/express/index.js +186 -0
- package/dist/integrations/express/index.js.map +1 -0
- package/dist/integrations/inngest/index.d.ts +222 -0
- package/dist/integrations/inngest/index.d.ts.map +1 -0
- package/dist/integrations/inngest/index.js +223 -0
- package/dist/integrations/inngest/index.js.map +1 -0
- package/dist/integrations/langfuse/index.d.ts +170 -0
- package/dist/integrations/langfuse/index.d.ts.map +1 -0
- package/dist/integrations/langfuse/index.js +225 -0
- package/dist/integrations/langfuse/index.js.map +1 -0
- package/dist/integrations/next/index.d.ts +138 -0
- package/dist/integrations/next/index.d.ts.map +1 -0
- package/dist/integrations/next/index.js +170 -0
- package/dist/integrations/next/index.js.map +1 -0
- package/dist/integrations/nextjs/index.d.ts +198 -0
- package/dist/integrations/nextjs/index.d.ts.map +1 -0
- package/dist/integrations/nextjs/index.js +181 -0
- package/dist/integrations/nextjs/index.js.map +1 -0
- package/dist/integrations/vercel-ai/index.d.ts +288 -0
- package/dist/integrations/vercel-ai/index.d.ts.map +1 -0
- package/dist/integrations/vercel-ai/index.js +260 -0
- package/dist/integrations/vercel-ai/index.js.map +1 -0
- package/dist/logger.d.ts +58 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +89 -0
- package/dist/logger.js.map +1 -0
- package/dist/pricing/catalog.d.ts +10 -0
- package/dist/pricing/catalog.d.ts.map +1 -0
- package/dist/pricing/catalog.js +297 -0
- package/dist/pricing/catalog.js.map +1 -0
- package/dist/pricing/index.d.ts +77 -0
- package/dist/pricing/index.d.ts.map +1 -0
- package/dist/pricing/index.js +251 -0
- package/dist/pricing/index.js.map +1 -0
- package/dist/pricing/manifest.d.ts +156 -0
- package/dist/pricing/manifest.d.ts.map +1 -0
- package/dist/pricing/manifest.js +381 -0
- package/dist/pricing/manifest.js.map +1 -0
- package/dist/pricing/manifest.json +12786 -0
- package/dist/pricing/providers/anthropic.json +253 -0
- package/dist/pricing/providers/bedrock.json +341 -0
- package/dist/pricing/providers/bfl.json +220 -0
- package/dist/pricing/providers/elevenlabs.json +142 -0
- package/dist/pricing/providers/fal.json +15866 -0
- package/dist/pricing/providers/google.json +346 -0
- package/dist/pricing/providers/openai.json +1035 -0
- package/dist/pricing/schema.d.ts +102 -0
- package/dist/pricing/schema.d.ts.map +1 -0
- package/dist/pricing/schema.js +56 -0
- package/dist/pricing/schema.js.map +1 -0
- package/dist/processor/TokenMeterProcessor.d.ts +55 -0
- package/dist/processor/TokenMeterProcessor.d.ts.map +1 -0
- package/dist/processor/TokenMeterProcessor.js +132 -0
- package/dist/processor/TokenMeterProcessor.js.map +1 -0
- package/dist/query/client.d.ts +61 -0
- package/dist/query/client.d.ts.map +1 -0
- package/dist/query/client.js +206 -0
- package/dist/query/client.js.map +1 -0
- package/dist/query/index.d.ts +8 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/index.js +7 -0
- package/dist/query/index.js.map +1 -0
- package/dist/recorder.d.ts +74 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/recorder.js +227 -0
- package/dist/recorder.js.map +1 -0
- package/dist/sdks/anthropic.d.ts +21 -0
- package/dist/sdks/anthropic.d.ts.map +1 -0
- package/dist/sdks/anthropic.js +258 -0
- package/dist/sdks/anthropic.js.map +1 -0
- package/dist/sdks/elevenlabs.d.ts +59 -0
- package/dist/sdks/elevenlabs.d.ts.map +1 -0
- package/dist/sdks/elevenlabs.js +192 -0
- package/dist/sdks/elevenlabs.js.map +1 -0
- package/dist/sdks/fal.d.ts +102 -0
- package/dist/sdks/fal.d.ts.map +1 -0
- package/dist/sdks/fal.js +306 -0
- package/dist/sdks/fal.js.map +1 -0
- package/dist/sdks/openai.d.ts +17 -0
- package/dist/sdks/openai.d.ts.map +1 -0
- package/dist/sdks/openai.js +191 -0
- package/dist/sdks/openai.js.map +1 -0
- package/dist/storage/interface.d.ts +15 -0
- package/dist/storage/interface.d.ts.map +1 -0
- package/dist/storage/interface.js +53 -0
- package/dist/storage/interface.js.map +1 -0
- package/dist/storage/prisma.d.ts +15 -0
- package/dist/storage/prisma.d.ts.map +1 -0
- package/dist/storage/prisma.js +135 -0
- package/dist/storage/prisma.js.map +1 -0
- package/dist/types.d.ts +206 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +45 -0
- package/dist/types.js.map +1 -0
- package/dist/vercel-ai/index.d.ts +89 -0
- package/dist/vercel-ai/index.d.ts.map +1 -0
- package/dist/vercel-ai/index.js +298 -0
- package/dist/vercel-ai/index.js.map +1 -0
- package/package.json +119 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { RecordEventInput, TrackCostInput } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Record a cost event (called by SDK wrappers)
|
|
4
|
+
*/
|
|
5
|
+
export declare function recordEvent(input: RecordEventInput): void;
|
|
6
|
+
/**
|
|
7
|
+
* Manually track a cost event for custom or unsupported providers.
|
|
8
|
+
*
|
|
9
|
+
* Use this when you're calling an AI provider that doesn't have a tokenmeter
|
|
10
|
+
* SDK wrapper, or when you need to track custom costs.
|
|
11
|
+
*
|
|
12
|
+
* @param input - The cost event details
|
|
13
|
+
* @param input.provider - The provider name (e.g., "custom-provider")
|
|
14
|
+
* @param input.model - The model identifier
|
|
15
|
+
* @param input.costUsd - The cost in USD
|
|
16
|
+
* @param input.operation - Optional operation type (e.g., "chat", "embed")
|
|
17
|
+
* @param input.inputTokens - Optional input token count
|
|
18
|
+
* @param input.outputTokens - Optional output token count
|
|
19
|
+
* @param input.status - Optional status ("success", "failed", "partial")
|
|
20
|
+
* @param input.billable - Whether this cost is billable (default: true)
|
|
21
|
+
* @param input.errorMessage - Error message if status is "failed"
|
|
22
|
+
* @param input.metadata - Optional custom metadata
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* await withCostTrace({ identifier: "user_123" }, async () => {
|
|
27
|
+
* const response = await customProvider.generate({ ... });
|
|
28
|
+
*
|
|
29
|
+
* trackCost({
|
|
30
|
+
* provider: "custom-provider",
|
|
31
|
+
* model: "custom-model",
|
|
32
|
+
* costUsd: response.usage.totalCost,
|
|
33
|
+
* inputTokens: response.usage.input,
|
|
34
|
+
* outputTokens: response.usage.output,
|
|
35
|
+
* });
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare function trackCost(input: TrackCostInput): void;
|
|
40
|
+
/**
|
|
41
|
+
* Flush all pending events to storage immediately.
|
|
42
|
+
*
|
|
43
|
+
* By default, tokenmeter batches events and flushes them periodically.
|
|
44
|
+
* Call this function to force an immediate flush, useful for:
|
|
45
|
+
* - Before process exit
|
|
46
|
+
* - After completing a batch of operations
|
|
47
|
+
* - When you need to ensure events are persisted
|
|
48
|
+
*
|
|
49
|
+
* @returns A promise that resolves when all pending events are written
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* await withCostTrace({ identifier: "user_123" }, async () => {
|
|
54
|
+
* await openai.chat.completions.create({ ... });
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* // Ensure all events are written before continuing
|
|
58
|
+
* await flush();
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare function flush(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Shutdown handler - flushes events and cleans up
|
|
64
|
+
*/
|
|
65
|
+
export declare function shutdownRecorder(): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Initialize the recorder (called from init())
|
|
68
|
+
*/
|
|
69
|
+
export declare function initRecorder(): void;
|
|
70
|
+
/**
|
|
71
|
+
* Register process exit handlers for graceful shutdown
|
|
72
|
+
*/
|
|
73
|
+
export declare function registerShutdownHooks(): void;
|
|
74
|
+
//# sourceMappingURL=recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA6E9E;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAuDzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAarD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAG3C;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAItD;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAEnC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAsB5C"}
|
package/dist/recorder.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { getStorage, getBatching, isInitialized } from "./config.js";
|
|
2
|
+
import { getCurrentContext, addCostToContext, addEventToContext, } from "./context.js";
|
|
3
|
+
/**
|
|
4
|
+
* Event queue for batching
|
|
5
|
+
*/
|
|
6
|
+
let eventQueue = [];
|
|
7
|
+
let flushTimer = null;
|
|
8
|
+
let isShuttingDown = false;
|
|
9
|
+
let isFlushing = false;
|
|
10
|
+
let flushPromise = null;
|
|
11
|
+
let shutdownHooksRegistered = false;
|
|
12
|
+
/**
|
|
13
|
+
* Start the flush timer if batching is enabled
|
|
14
|
+
*/
|
|
15
|
+
function startFlushTimer() {
|
|
16
|
+
const batching = getBatching();
|
|
17
|
+
if (batching.enabled && !flushTimer && !isShuttingDown) {
|
|
18
|
+
flushTimer = setInterval(() => {
|
|
19
|
+
void flushInternal();
|
|
20
|
+
}, batching.flushInterval);
|
|
21
|
+
flushTimer.unref(); // Don't keep process alive just for flushing
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Stop the flush timer
|
|
26
|
+
*/
|
|
27
|
+
function stopFlushTimer() {
|
|
28
|
+
if (flushTimer) {
|
|
29
|
+
clearInterval(flushTimer);
|
|
30
|
+
flushTimer = null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Internal flush function with mutex to prevent race conditions
|
|
35
|
+
*/
|
|
36
|
+
async function flushInternal() {
|
|
37
|
+
// If already flushing, wait for the current flush to complete
|
|
38
|
+
if (isFlushing && flushPromise) {
|
|
39
|
+
await flushPromise;
|
|
40
|
+
// After waiting, check if there are still events to flush
|
|
41
|
+
if (eventQueue.length === 0)
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (eventQueue.length === 0)
|
|
45
|
+
return;
|
|
46
|
+
isFlushing = true;
|
|
47
|
+
// Create the flush promise
|
|
48
|
+
flushPromise = (async () => {
|
|
49
|
+
const events = eventQueue;
|
|
50
|
+
eventQueue = [];
|
|
51
|
+
try {
|
|
52
|
+
const storage = getStorage();
|
|
53
|
+
await storage.writeBatch(events);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
// Put events back in queue on failure (at the front)
|
|
57
|
+
eventQueue = [...events, ...eventQueue];
|
|
58
|
+
console.error("[tokenmeter] Failed to flush events:", error);
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
isFlushing = false;
|
|
62
|
+
flushPromise = null;
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
65
|
+
await flushPromise;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Record a cost event (called by SDK wrappers)
|
|
69
|
+
*/
|
|
70
|
+
export function recordEvent(input) {
|
|
71
|
+
if (!isInitialized()) {
|
|
72
|
+
console.warn("[tokenmeter] Not initialized. Call init() before recording events.");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const context = getCurrentContext();
|
|
76
|
+
if (!context) {
|
|
77
|
+
console.warn("[tokenmeter] No trace context. Wrap your code in withCostTrace().");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const event = {
|
|
81
|
+
identifier: context.identifier,
|
|
82
|
+
secondaryIdentifier: context.secondaryIdentifier,
|
|
83
|
+
requestId: context.requestId,
|
|
84
|
+
workflowId: context.workflowId,
|
|
85
|
+
workflowType: context.workflowType,
|
|
86
|
+
provider: input.provider,
|
|
87
|
+
model: input.model,
|
|
88
|
+
operation: input.operation,
|
|
89
|
+
inputTokens: input.inputTokens,
|
|
90
|
+
outputTokens: input.outputTokens,
|
|
91
|
+
costUsd: input.costUsd ?? 0,
|
|
92
|
+
status: input.status ?? "success",
|
|
93
|
+
billable: input.billable ?? true,
|
|
94
|
+
errorMessage: input.errorMessage,
|
|
95
|
+
metadata: { ...context.metadata, ...input.metadata },
|
|
96
|
+
createdAt: new Date(),
|
|
97
|
+
};
|
|
98
|
+
// Update context with cost
|
|
99
|
+
addCostToContext(event.costUsd);
|
|
100
|
+
addEventToContext(event);
|
|
101
|
+
// Add to queue
|
|
102
|
+
const batching = getBatching();
|
|
103
|
+
if (batching.enabled) {
|
|
104
|
+
eventQueue.push(event);
|
|
105
|
+
// Start timer if not running
|
|
106
|
+
startFlushTimer();
|
|
107
|
+
// Flush if batch size reached
|
|
108
|
+
if (eventQueue.length >= batching.maxBatchSize) {
|
|
109
|
+
void flushInternal();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Write immediately
|
|
114
|
+
void getStorage().writeEvent(event);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Manually track a cost event for custom or unsupported providers.
|
|
119
|
+
*
|
|
120
|
+
* Use this when you're calling an AI provider that doesn't have a tokenmeter
|
|
121
|
+
* SDK wrapper, or when you need to track custom costs.
|
|
122
|
+
*
|
|
123
|
+
* @param input - The cost event details
|
|
124
|
+
* @param input.provider - The provider name (e.g., "custom-provider")
|
|
125
|
+
* @param input.model - The model identifier
|
|
126
|
+
* @param input.costUsd - The cost in USD
|
|
127
|
+
* @param input.operation - Optional operation type (e.g., "chat", "embed")
|
|
128
|
+
* @param input.inputTokens - Optional input token count
|
|
129
|
+
* @param input.outputTokens - Optional output token count
|
|
130
|
+
* @param input.status - Optional status ("success", "failed", "partial")
|
|
131
|
+
* @param input.billable - Whether this cost is billable (default: true)
|
|
132
|
+
* @param input.errorMessage - Error message if status is "failed"
|
|
133
|
+
* @param input.metadata - Optional custom metadata
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* await withCostTrace({ identifier: "user_123" }, async () => {
|
|
138
|
+
* const response = await customProvider.generate({ ... });
|
|
139
|
+
*
|
|
140
|
+
* trackCost({
|
|
141
|
+
* provider: "custom-provider",
|
|
142
|
+
* model: "custom-model",
|
|
143
|
+
* costUsd: response.usage.totalCost,
|
|
144
|
+
* inputTokens: response.usage.input,
|
|
145
|
+
* outputTokens: response.usage.output,
|
|
146
|
+
* });
|
|
147
|
+
* });
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export function trackCost(input) {
|
|
151
|
+
recordEvent({
|
|
152
|
+
provider: input.provider,
|
|
153
|
+
model: input.model,
|
|
154
|
+
operation: input.operation,
|
|
155
|
+
inputTokens: input.inputTokens,
|
|
156
|
+
outputTokens: input.outputTokens,
|
|
157
|
+
costUsd: input.costUsd,
|
|
158
|
+
status: input.status,
|
|
159
|
+
billable: input.billable,
|
|
160
|
+
errorMessage: input.errorMessage,
|
|
161
|
+
metadata: input.metadata,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Flush all pending events to storage immediately.
|
|
166
|
+
*
|
|
167
|
+
* By default, tokenmeter batches events and flushes them periodically.
|
|
168
|
+
* Call this function to force an immediate flush, useful for:
|
|
169
|
+
* - Before process exit
|
|
170
|
+
* - After completing a batch of operations
|
|
171
|
+
* - When you need to ensure events are persisted
|
|
172
|
+
*
|
|
173
|
+
* @returns A promise that resolves when all pending events are written
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* await withCostTrace({ identifier: "user_123" }, async () => {
|
|
178
|
+
* await openai.chat.completions.create({ ... });
|
|
179
|
+
* });
|
|
180
|
+
*
|
|
181
|
+
* // Ensure all events are written before continuing
|
|
182
|
+
* await flush();
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
export async function flush() {
|
|
186
|
+
stopFlushTimer();
|
|
187
|
+
await flushInternal();
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Shutdown handler - flushes events and cleans up
|
|
191
|
+
*/
|
|
192
|
+
export async function shutdownRecorder() {
|
|
193
|
+
isShuttingDown = true;
|
|
194
|
+
stopFlushTimer();
|
|
195
|
+
await flushInternal();
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Initialize the recorder (called from init())
|
|
199
|
+
*/
|
|
200
|
+
export function initRecorder() {
|
|
201
|
+
registerShutdownHooks();
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Register process exit handlers for graceful shutdown
|
|
205
|
+
*/
|
|
206
|
+
export function registerShutdownHooks() {
|
|
207
|
+
// Only register once to avoid memory leak warnings
|
|
208
|
+
if (shutdownHooksRegistered)
|
|
209
|
+
return;
|
|
210
|
+
const batching = getBatching();
|
|
211
|
+
if (!batching.flushOnExit)
|
|
212
|
+
return;
|
|
213
|
+
shutdownHooksRegistered = true;
|
|
214
|
+
const handleShutdown = async (signal) => {
|
|
215
|
+
console.log(`[tokenmeter] Received ${signal}, flushing events...`);
|
|
216
|
+
await shutdownRecorder();
|
|
217
|
+
process.exit(0);
|
|
218
|
+
};
|
|
219
|
+
process.on("SIGTERM", () => void handleShutdown("SIGTERM"));
|
|
220
|
+
process.on("SIGINT", () => void handleShutdown("SIGINT"));
|
|
221
|
+
// Handle beforeExit for graceful shutdown
|
|
222
|
+
process.on("beforeExit", () => {
|
|
223
|
+
void flushInternal();
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
// Shutdown hooks are now registered via initRecorder() called from init()
|
|
227
|
+
//# sourceMappingURL=recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import type { ClientOptions } from "@anthropic-ai/sdk";
|
|
3
|
+
/**
|
|
4
|
+
* Wrapped Anthropic client that automatically tracks costs
|
|
5
|
+
*/
|
|
6
|
+
export declare class TrackedAnthropic extends Anthropic {
|
|
7
|
+
constructor(opts?: ClientOptions);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Create a tracked Anthropic client
|
|
11
|
+
*/
|
|
12
|
+
export declare function createAnthropic(opts?: ClientOptions): TrackedAnthropic;
|
|
13
|
+
/**
|
|
14
|
+
* Wrap an existing Anthropic client to track costs
|
|
15
|
+
*/
|
|
16
|
+
export declare function trackAnthropic<T extends Anthropic>(client: T): T;
|
|
17
|
+
/**
|
|
18
|
+
* Default export - a function to create tracked clients
|
|
19
|
+
*/
|
|
20
|
+
export default createAnthropic;
|
|
21
|
+
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/sdks/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAmUvD;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,SAAS;gBACjC,IAAI,CAAC,EAAE,aAAa;CAQjC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,aAAa,GAAG,gBAAgB,CAEtE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,SAAS,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,CAIhE;AAED;;GAEG;AACH,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import { recordEvent } from "../recorder.js";
|
|
3
|
+
import { getPricing, resolveModel } from "../config.js";
|
|
4
|
+
import { calculateCost } from "../pricing/index.js";
|
|
5
|
+
/**
|
|
6
|
+
* Normalize model name to match pricing catalog format
|
|
7
|
+
*/
|
|
8
|
+
function normalizeModel(model) {
|
|
9
|
+
// Add anthropic/ prefix if not present
|
|
10
|
+
if (!model.startsWith("anthropic/")) {
|
|
11
|
+
return `anthropic/${model}`;
|
|
12
|
+
}
|
|
13
|
+
return model;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a tracked messages API
|
|
17
|
+
*/
|
|
18
|
+
function createTrackedMessages(messages) {
|
|
19
|
+
const originalCreate = messages.create.bind(messages);
|
|
20
|
+
const originalStream = messages.stream.bind(messages);
|
|
21
|
+
async function trackedCreate(body, options) {
|
|
22
|
+
const startTime = Date.now();
|
|
23
|
+
const model = resolveModel(body.model);
|
|
24
|
+
const normalizedModel = normalizeModel(model);
|
|
25
|
+
if (body.stream) {
|
|
26
|
+
// Streaming request via create()
|
|
27
|
+
try {
|
|
28
|
+
const stream = (await originalCreate(body, options));
|
|
29
|
+
return wrapRawStream(stream, normalizedModel, startTime);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
recordEvent({
|
|
33
|
+
provider: "anthropic",
|
|
34
|
+
model: normalizedModel,
|
|
35
|
+
operation: "messages",
|
|
36
|
+
status: "failed",
|
|
37
|
+
billable: false,
|
|
38
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
39
|
+
costUsd: 0,
|
|
40
|
+
metadata: {
|
|
41
|
+
durationMs: Date.now() - startTime,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Non-streaming request
|
|
49
|
+
try {
|
|
50
|
+
const response = (await originalCreate(body, options));
|
|
51
|
+
const inputTokens = response.usage.input_tokens;
|
|
52
|
+
const outputTokens = response.usage.output_tokens;
|
|
53
|
+
const cacheCreationTokens = response.usage
|
|
54
|
+
.cache_creation_input_tokens || 0;
|
|
55
|
+
const cacheReadTokens = response.usage
|
|
56
|
+
.cache_read_input_tokens || 0;
|
|
57
|
+
const pricing = getPricing();
|
|
58
|
+
const cost = calculateCost(normalizedModel, inputTokens, outputTokens, pricing, cacheReadTokens);
|
|
59
|
+
recordEvent({
|
|
60
|
+
provider: "anthropic",
|
|
61
|
+
model: normalizedModel,
|
|
62
|
+
operation: "messages",
|
|
63
|
+
inputTokens,
|
|
64
|
+
outputTokens,
|
|
65
|
+
costUsd: cost,
|
|
66
|
+
status: "success",
|
|
67
|
+
metadata: {
|
|
68
|
+
durationMs: Date.now() - startTime,
|
|
69
|
+
stopReason: response.stop_reason,
|
|
70
|
+
cacheCreationTokens: cacheCreationTokens > 0 ? cacheCreationTokens : undefined,
|
|
71
|
+
cacheReadTokens: cacheReadTokens > 0 ? cacheReadTokens : undefined,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
return response;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
recordEvent({
|
|
78
|
+
provider: "anthropic",
|
|
79
|
+
model: normalizedModel,
|
|
80
|
+
operation: "messages",
|
|
81
|
+
status: "failed",
|
|
82
|
+
billable: false,
|
|
83
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
84
|
+
costUsd: 0,
|
|
85
|
+
metadata: {
|
|
86
|
+
durationMs: Date.now() - startTime,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Wrap the stream method (returns MessageStream helper)
|
|
94
|
+
function trackedStream(body, options) {
|
|
95
|
+
const startTime = Date.now();
|
|
96
|
+
const model = resolveModel(body.model);
|
|
97
|
+
const normalizedModel = normalizeModel(model);
|
|
98
|
+
const messageStream = originalStream(body, options);
|
|
99
|
+
// Track when the stream completes
|
|
100
|
+
messageStream
|
|
101
|
+
.finalMessage()
|
|
102
|
+
.then((message) => {
|
|
103
|
+
const inputTokens = message.usage.input_tokens;
|
|
104
|
+
const outputTokens = message.usage.output_tokens;
|
|
105
|
+
const cacheCreationTokens = message.usage
|
|
106
|
+
.cache_creation_input_tokens || 0;
|
|
107
|
+
const cacheReadTokens = message.usage
|
|
108
|
+
.cache_read_input_tokens || 0;
|
|
109
|
+
const pricing = getPricing();
|
|
110
|
+
const cost = calculateCost(normalizedModel, inputTokens, outputTokens, pricing, cacheReadTokens);
|
|
111
|
+
recordEvent({
|
|
112
|
+
provider: "anthropic",
|
|
113
|
+
model: normalizedModel,
|
|
114
|
+
operation: "messages",
|
|
115
|
+
inputTokens,
|
|
116
|
+
outputTokens,
|
|
117
|
+
costUsd: cost,
|
|
118
|
+
status: "success",
|
|
119
|
+
metadata: {
|
|
120
|
+
durationMs: Date.now() - startTime,
|
|
121
|
+
stopReason: message.stop_reason,
|
|
122
|
+
streamed: true,
|
|
123
|
+
cacheCreationTokens: cacheCreationTokens > 0 ? cacheCreationTokens : undefined,
|
|
124
|
+
cacheReadTokens: cacheReadTokens > 0 ? cacheReadTokens : undefined,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
})
|
|
128
|
+
.catch((error) => {
|
|
129
|
+
recordEvent({
|
|
130
|
+
provider: "anthropic",
|
|
131
|
+
model: normalizedModel,
|
|
132
|
+
operation: "messages",
|
|
133
|
+
status: "failed",
|
|
134
|
+
billable: false,
|
|
135
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
136
|
+
costUsd: 0,
|
|
137
|
+
metadata: {
|
|
138
|
+
durationMs: Date.now() - startTime,
|
|
139
|
+
streamed: true,
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
return messageStream;
|
|
144
|
+
}
|
|
145
|
+
// Create result with wrapped methods
|
|
146
|
+
const result = Object.create(messages);
|
|
147
|
+
result.create = trackedCreate;
|
|
148
|
+
result.stream = trackedStream;
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Wrap a raw stream (from create with stream: true) to track usage
|
|
153
|
+
*/
|
|
154
|
+
function wrapRawStream(stream, model, startTime) {
|
|
155
|
+
let inputTokens = 0;
|
|
156
|
+
let outputTokens = 0;
|
|
157
|
+
let stopReason = null;
|
|
158
|
+
let cacheCreationTokens = 0;
|
|
159
|
+
let cacheReadTokens = 0;
|
|
160
|
+
const originalIterator = stream[Symbol.asyncIterator].bind(stream);
|
|
161
|
+
const wrappedIterator = async function* () {
|
|
162
|
+
try {
|
|
163
|
+
for await (const event of { [Symbol.asyncIterator]: originalIterator }) {
|
|
164
|
+
// Track usage from events
|
|
165
|
+
if (event.type === "message_start" && event.message?.usage) {
|
|
166
|
+
inputTokens = event.message.usage.input_tokens;
|
|
167
|
+
const usage = event.message.usage;
|
|
168
|
+
cacheCreationTokens = usage.cache_creation_input_tokens || 0;
|
|
169
|
+
cacheReadTokens = usage.cache_read_input_tokens || 0;
|
|
170
|
+
}
|
|
171
|
+
if (event.type === "message_delta") {
|
|
172
|
+
if (event.usage) {
|
|
173
|
+
outputTokens = event.usage.output_tokens;
|
|
174
|
+
}
|
|
175
|
+
if (event.delta?.stop_reason) {
|
|
176
|
+
stopReason = event.delta.stop_reason;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
yield event;
|
|
180
|
+
}
|
|
181
|
+
// Stream completed successfully
|
|
182
|
+
const pricing = getPricing();
|
|
183
|
+
const cost = calculateCost(model, inputTokens, outputTokens, pricing, cacheReadTokens);
|
|
184
|
+
recordEvent({
|
|
185
|
+
provider: "anthropic",
|
|
186
|
+
model,
|
|
187
|
+
operation: "messages",
|
|
188
|
+
inputTokens,
|
|
189
|
+
outputTokens,
|
|
190
|
+
costUsd: cost,
|
|
191
|
+
status: "success",
|
|
192
|
+
metadata: {
|
|
193
|
+
durationMs: Date.now() - startTime,
|
|
194
|
+
stopReason,
|
|
195
|
+
streamed: true,
|
|
196
|
+
cacheCreationTokens: cacheCreationTokens > 0 ? cacheCreationTokens : undefined,
|
|
197
|
+
cacheReadTokens: cacheReadTokens > 0 ? cacheReadTokens : undefined,
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
// Stream failed
|
|
203
|
+
const pricing = getPricing();
|
|
204
|
+
const cost = inputTokens > 0 || outputTokens > 0
|
|
205
|
+
? calculateCost(model, inputTokens, outputTokens, pricing)
|
|
206
|
+
: 0;
|
|
207
|
+
recordEvent({
|
|
208
|
+
provider: "anthropic",
|
|
209
|
+
model,
|
|
210
|
+
operation: "messages",
|
|
211
|
+
inputTokens,
|
|
212
|
+
outputTokens,
|
|
213
|
+
status: "partial",
|
|
214
|
+
billable: inputTokens > 0 || outputTokens > 0,
|
|
215
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
216
|
+
costUsd: cost,
|
|
217
|
+
metadata: {
|
|
218
|
+
durationMs: Date.now() - startTime,
|
|
219
|
+
streamed: true,
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
// Return a new stream with the wrapped iterator
|
|
226
|
+
const wrappedStream = Object.create(stream);
|
|
227
|
+
wrappedStream[Symbol.asyncIterator] = wrappedIterator;
|
|
228
|
+
return wrappedStream;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Wrapped Anthropic client that automatically tracks costs
|
|
232
|
+
*/
|
|
233
|
+
export class TrackedAnthropic extends Anthropic {
|
|
234
|
+
constructor(opts) {
|
|
235
|
+
super(opts);
|
|
236
|
+
// Replace messages with tracked version
|
|
237
|
+
this.messages = createTrackedMessages(this.messages);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Create a tracked Anthropic client
|
|
242
|
+
*/
|
|
243
|
+
export function createAnthropic(opts) {
|
|
244
|
+
return new TrackedAnthropic(opts);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Wrap an existing Anthropic client to track costs
|
|
248
|
+
*/
|
|
249
|
+
export function trackAnthropic(client) {
|
|
250
|
+
const trackedMessages = createTrackedMessages(client.messages);
|
|
251
|
+
client.messages = trackedMessages;
|
|
252
|
+
return client;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Default export - a function to create tracked clients
|
|
256
|
+
*/
|
|
257
|
+
export default createAnthropic;
|
|
258
|
+
//# sourceMappingURL=anthropic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/sdks/anthropic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ElevenLabs SDK Wrapper for cost tracking
|
|
3
|
+
*
|
|
4
|
+
* ElevenLabs pricing is based on characters, not tokens.
|
|
5
|
+
* Different voice models have different per-character costs.
|
|
6
|
+
*
|
|
7
|
+
* Official SDK: https://github.com/elevenlabs/elevenlabs-js
|
|
8
|
+
*
|
|
9
|
+
* Models:
|
|
10
|
+
* - eleven_multilingual_v2: High quality, 29 languages
|
|
11
|
+
* - eleven_flash_v2_5: Ultra-low latency, 50% cheaper, 32 languages
|
|
12
|
+
* - eleven_turbo_v2_5: Balanced quality/latency, 32 languages
|
|
13
|
+
*/
|
|
14
|
+
interface ElevenLabsClient {
|
|
15
|
+
textToSpeech: {
|
|
16
|
+
convert: (voiceId: string, options: TextToSpeechOptions) => Promise<Buffer | ArrayBuffer>;
|
|
17
|
+
stream: (voiceId: string, options: TextToSpeechOptions) => Promise<ReadableStream>;
|
|
18
|
+
};
|
|
19
|
+
generate?: (options: GenerateOptions) => Promise<Buffer | ArrayBuffer>;
|
|
20
|
+
}
|
|
21
|
+
interface TextToSpeechOptions {
|
|
22
|
+
text: string;
|
|
23
|
+
modelId?: string;
|
|
24
|
+
voiceSettings?: {
|
|
25
|
+
stability?: number;
|
|
26
|
+
similarityBoost?: number;
|
|
27
|
+
style?: number;
|
|
28
|
+
useSpeakerBoost?: boolean;
|
|
29
|
+
};
|
|
30
|
+
outputFormat?: string;
|
|
31
|
+
[key: string]: unknown;
|
|
32
|
+
}
|
|
33
|
+
interface GenerateOptions {
|
|
34
|
+
text: string;
|
|
35
|
+
voice?: string;
|
|
36
|
+
modelId?: string;
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Wrap an ElevenLabs client to track costs
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* import { ElevenLabsClient } from "elevenlabs";
|
|
45
|
+
* import { trackElevenLabs } from "tokenmeter/elevenlabs";
|
|
46
|
+
*
|
|
47
|
+
* const client = new ElevenLabsClient({ apiKey: "..." });
|
|
48
|
+
* const trackedClient = trackElevenLabs(client);
|
|
49
|
+
*
|
|
50
|
+
* // All calls are now tracked
|
|
51
|
+
* await trackedClient.textToSpeech.convert("voice_id", {
|
|
52
|
+
* text: "Hello world",
|
|
53
|
+
* modelId: "eleven_multilingual_v2"
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function trackElevenLabs<T extends Partial<ElevenLabsClient>>(client: T): T;
|
|
58
|
+
export default trackElevenLabs;
|
|
59
|
+
//# sourceMappingURL=elevenlabs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elevenlabs.d.ts","sourceRoot":"","sources":["../../src/sdks/elevenlabs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,UAAU,gBAAgB;IACxB,YAAY,EAAE;QACZ,OAAO,EAAE,CACP,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,mBAAmB,KACzB,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC;QACnC,MAAM,EAAE,CACN,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,mBAAmB,KACzB,OAAO,CAAC,cAAc,CAAC,CAAC;KAC9B,CAAC;IACF,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC;CACxE;AAED,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,eAAe,CAAC,EAAE,OAAO,CAAC;KAC3B,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AA+BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,OAAO,CAAC,gBAAgB,CAAC,EACjE,MAAM,EAAE,CAAC,GACR,CAAC,CA6JH;AAED,eAAe,eAAe,CAAC"}
|