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.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +346 -0
  3. package/dist/__tests__/context.test.d.ts +2 -0
  4. package/dist/__tests__/context.test.d.ts.map +1 -0
  5. package/dist/__tests__/context.test.js +94 -0
  6. package/dist/__tests__/context.test.js.map +1 -0
  7. package/dist/__tests__/elevenlabs.test.d.ts +2 -0
  8. package/dist/__tests__/elevenlabs.test.d.ts.map +1 -0
  9. package/dist/__tests__/elevenlabs.test.js +108 -0
  10. package/dist/__tests__/elevenlabs.test.js.map +1 -0
  11. package/dist/__tests__/fal.test.d.ts +2 -0
  12. package/dist/__tests__/fal.test.d.ts.map +1 -0
  13. package/dist/__tests__/fal.test.js +153 -0
  14. package/dist/__tests__/fal.test.js.map +1 -0
  15. package/dist/__tests__/pricing.test.d.ts +2 -0
  16. package/dist/__tests__/pricing.test.d.ts.map +1 -0
  17. package/dist/__tests__/pricing.test.js +76 -0
  18. package/dist/__tests__/pricing.test.js.map +1 -0
  19. package/dist/__tests__/recorder.test.d.ts +2 -0
  20. package/dist/__tests__/recorder.test.d.ts.map +1 -0
  21. package/dist/__tests__/recorder.test.js +133 -0
  22. package/dist/__tests__/recorder.test.js.map +1 -0
  23. package/dist/__tests__/storage.test.d.ts +2 -0
  24. package/dist/__tests__/storage.test.d.ts.map +1 -0
  25. package/dist/__tests__/storage.test.js +106 -0
  26. package/dist/__tests__/storage.test.js.map +1 -0
  27. package/dist/client/index.d.ts +8 -0
  28. package/dist/client/index.d.ts.map +1 -0
  29. package/dist/client/index.js +7 -0
  30. package/dist/client/index.js.map +1 -0
  31. package/dist/config.d.ts +92 -0
  32. package/dist/config.d.ts.map +1 -0
  33. package/dist/config.js +166 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/context.d.ts +80 -0
  36. package/dist/context.d.ts.map +1 -0
  37. package/dist/context.js +131 -0
  38. package/dist/context.js.map +1 -0
  39. package/dist/exporter/PostgresExporter.d.ts +82 -0
  40. package/dist/exporter/PostgresExporter.d.ts.map +1 -0
  41. package/dist/exporter/PostgresExporter.js +237 -0
  42. package/dist/exporter/PostgresExporter.js.map +1 -0
  43. package/dist/exporter/index.d.ts +8 -0
  44. package/dist/exporter/index.d.ts.map +1 -0
  45. package/dist/exporter/index.js +7 -0
  46. package/dist/exporter/index.js.map +1 -0
  47. package/dist/index.d.ts +31 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +37 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/instrumentation/proxy.d.ts +26 -0
  52. package/dist/instrumentation/proxy.d.ts.map +1 -0
  53. package/dist/instrumentation/proxy.js +337 -0
  54. package/dist/instrumentation/proxy.js.map +1 -0
  55. package/dist/instrumentation/strategies/index.d.ts +55 -0
  56. package/dist/instrumentation/strategies/index.d.ts.map +1 -0
  57. package/dist/instrumentation/strategies/index.js +429 -0
  58. package/dist/instrumentation/strategies/index.js.map +1 -0
  59. package/dist/integrations/express/index.d.ts +137 -0
  60. package/dist/integrations/express/index.d.ts.map +1 -0
  61. package/dist/integrations/express/index.js +186 -0
  62. package/dist/integrations/express/index.js.map +1 -0
  63. package/dist/integrations/inngest/index.d.ts +222 -0
  64. package/dist/integrations/inngest/index.d.ts.map +1 -0
  65. package/dist/integrations/inngest/index.js +223 -0
  66. package/dist/integrations/inngest/index.js.map +1 -0
  67. package/dist/integrations/langfuse/index.d.ts +170 -0
  68. package/dist/integrations/langfuse/index.d.ts.map +1 -0
  69. package/dist/integrations/langfuse/index.js +225 -0
  70. package/dist/integrations/langfuse/index.js.map +1 -0
  71. package/dist/integrations/next/index.d.ts +138 -0
  72. package/dist/integrations/next/index.d.ts.map +1 -0
  73. package/dist/integrations/next/index.js +170 -0
  74. package/dist/integrations/next/index.js.map +1 -0
  75. package/dist/integrations/nextjs/index.d.ts +198 -0
  76. package/dist/integrations/nextjs/index.d.ts.map +1 -0
  77. package/dist/integrations/nextjs/index.js +181 -0
  78. package/dist/integrations/nextjs/index.js.map +1 -0
  79. package/dist/integrations/vercel-ai/index.d.ts +288 -0
  80. package/dist/integrations/vercel-ai/index.d.ts.map +1 -0
  81. package/dist/integrations/vercel-ai/index.js +260 -0
  82. package/dist/integrations/vercel-ai/index.js.map +1 -0
  83. package/dist/logger.d.ts +58 -0
  84. package/dist/logger.d.ts.map +1 -0
  85. package/dist/logger.js +89 -0
  86. package/dist/logger.js.map +1 -0
  87. package/dist/pricing/catalog.d.ts +10 -0
  88. package/dist/pricing/catalog.d.ts.map +1 -0
  89. package/dist/pricing/catalog.js +297 -0
  90. package/dist/pricing/catalog.js.map +1 -0
  91. package/dist/pricing/index.d.ts +77 -0
  92. package/dist/pricing/index.d.ts.map +1 -0
  93. package/dist/pricing/index.js +251 -0
  94. package/dist/pricing/index.js.map +1 -0
  95. package/dist/pricing/manifest.d.ts +156 -0
  96. package/dist/pricing/manifest.d.ts.map +1 -0
  97. package/dist/pricing/manifest.js +381 -0
  98. package/dist/pricing/manifest.js.map +1 -0
  99. package/dist/pricing/manifest.json +12786 -0
  100. package/dist/pricing/providers/anthropic.json +253 -0
  101. package/dist/pricing/providers/bedrock.json +341 -0
  102. package/dist/pricing/providers/bfl.json +220 -0
  103. package/dist/pricing/providers/elevenlabs.json +142 -0
  104. package/dist/pricing/providers/fal.json +15866 -0
  105. package/dist/pricing/providers/google.json +346 -0
  106. package/dist/pricing/providers/openai.json +1035 -0
  107. package/dist/pricing/schema.d.ts +102 -0
  108. package/dist/pricing/schema.d.ts.map +1 -0
  109. package/dist/pricing/schema.js +56 -0
  110. package/dist/pricing/schema.js.map +1 -0
  111. package/dist/processor/TokenMeterProcessor.d.ts +55 -0
  112. package/dist/processor/TokenMeterProcessor.d.ts.map +1 -0
  113. package/dist/processor/TokenMeterProcessor.js +132 -0
  114. package/dist/processor/TokenMeterProcessor.js.map +1 -0
  115. package/dist/query/client.d.ts +61 -0
  116. package/dist/query/client.d.ts.map +1 -0
  117. package/dist/query/client.js +206 -0
  118. package/dist/query/client.js.map +1 -0
  119. package/dist/query/index.d.ts +8 -0
  120. package/dist/query/index.d.ts.map +1 -0
  121. package/dist/query/index.js +7 -0
  122. package/dist/query/index.js.map +1 -0
  123. package/dist/recorder.d.ts +74 -0
  124. package/dist/recorder.d.ts.map +1 -0
  125. package/dist/recorder.js +227 -0
  126. package/dist/recorder.js.map +1 -0
  127. package/dist/sdks/anthropic.d.ts +21 -0
  128. package/dist/sdks/anthropic.d.ts.map +1 -0
  129. package/dist/sdks/anthropic.js +258 -0
  130. package/dist/sdks/anthropic.js.map +1 -0
  131. package/dist/sdks/elevenlabs.d.ts +59 -0
  132. package/dist/sdks/elevenlabs.d.ts.map +1 -0
  133. package/dist/sdks/elevenlabs.js +192 -0
  134. package/dist/sdks/elevenlabs.js.map +1 -0
  135. package/dist/sdks/fal.d.ts +102 -0
  136. package/dist/sdks/fal.d.ts.map +1 -0
  137. package/dist/sdks/fal.js +306 -0
  138. package/dist/sdks/fal.js.map +1 -0
  139. package/dist/sdks/openai.d.ts +17 -0
  140. package/dist/sdks/openai.d.ts.map +1 -0
  141. package/dist/sdks/openai.js +191 -0
  142. package/dist/sdks/openai.js.map +1 -0
  143. package/dist/storage/interface.d.ts +15 -0
  144. package/dist/storage/interface.d.ts.map +1 -0
  145. package/dist/storage/interface.js +53 -0
  146. package/dist/storage/interface.js.map +1 -0
  147. package/dist/storage/prisma.d.ts +15 -0
  148. package/dist/storage/prisma.d.ts.map +1 -0
  149. package/dist/storage/prisma.js +135 -0
  150. package/dist/storage/prisma.js.map +1 -0
  151. package/dist/types.d.ts +206 -0
  152. package/dist/types.d.ts.map +1 -0
  153. package/dist/types.js +45 -0
  154. package/dist/types.js.map +1 -0
  155. package/dist/vercel-ai/index.d.ts +89 -0
  156. package/dist/vercel-ai/index.d.ts.map +1 -0
  157. package/dist/vercel-ai/index.js +298 -0
  158. package/dist/vercel-ai/index.js.map +1 -0
  159. package/package.json +119 -0
@@ -0,0 +1,186 @@
1
+ import { withCostTrace } from "../../context.js";
2
+ import { isInitialized } from "../../config.js";
3
+ /**
4
+ * Create an Express middleware that automatically sets up cost tracking context.
5
+ *
6
+ * This middleware wraps incoming requests with `withCostTrace()`, allowing
7
+ * any AI calls made during request handling to be automatically tracked.
8
+ *
9
+ * @param config - Configuration for extracting identifiers and metadata
10
+ * @returns An Express middleware function
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import express from 'express';
15
+ * import { init } from 'tokenmeter';
16
+ * import { tokenmeterMiddleware } from 'tokenmeter/express';
17
+ *
18
+ * init({ databaseUrl: process.env.DATABASE_URL });
19
+ *
20
+ * const app = express();
21
+ *
22
+ * app.use(tokenmeterMiddleware({
23
+ * getIdentifier: (req) => req.user?.id || req.headers['x-user-id'] as string,
24
+ * shouldTrack: (req) => req.path.startsWith('/api/'),
25
+ * }));
26
+ *
27
+ * app.post('/api/chat', async (req, res) => {
28
+ * // AI calls here are automatically tracked
29
+ * const result = await openai.chat.completions.create({ ... });
30
+ * res.json({ result });
31
+ * });
32
+ * ```
33
+ */
34
+ export function tokenmeterMiddleware(config) {
35
+ return (req, res, next) => {
36
+ // Check if tokenmeter is initialized
37
+ if (!isInitialized()) {
38
+ console.warn("[tokenmeter] Not initialized. Call init() before using middleware.");
39
+ next();
40
+ return;
41
+ }
42
+ // Check if we should track this request
43
+ if (config.shouldTrack && !config.shouldTrack(req)) {
44
+ next();
45
+ return;
46
+ }
47
+ // Get identifier
48
+ const identifier = config.getIdentifier(req);
49
+ if (!identifier) {
50
+ // No identifier - skip tracking but continue request
51
+ next();
52
+ return;
53
+ }
54
+ // Build trace options
55
+ const traceOptions = {
56
+ identifier,
57
+ secondaryIdentifier: config.getSecondaryIdentifier?.(req) ?? undefined,
58
+ workflowId: config.getWorkflowId?.(req) ??
59
+ req.headers["x-request-id"] ??
60
+ crypto.randomUUID(),
61
+ workflowType: config.getWorkflowType?.(req) ?? undefined,
62
+ requestId: req.headers["x-request-id"] ?? undefined,
63
+ metadata: {
64
+ ...config.getMetadata?.(req),
65
+ path: req.path,
66
+ method: req.method,
67
+ },
68
+ };
69
+ // Wrap the request handling in a cost trace context
70
+ // We need to handle this differently since Express uses callbacks
71
+ void withCostTrace(traceOptions, async () => {
72
+ return new Promise((resolve, reject) => {
73
+ // Override next to resolve our promise when called
74
+ const wrappedNext = (err) => {
75
+ if (err) {
76
+ reject(err);
77
+ }
78
+ else {
79
+ resolve();
80
+ }
81
+ };
82
+ // Call next to continue the middleware chain
83
+ // The actual next() should not be called until the response is sent
84
+ // So we need to hook into res.end or similar
85
+ // Store original end
86
+ const originalEnd = res.end.bind(res);
87
+ let ended = false;
88
+ // Override end to resolve our promise
89
+ res.end = function (...args) {
90
+ if (!ended) {
91
+ ended = true;
92
+ resolve();
93
+ }
94
+ return originalEnd(...args);
95
+ };
96
+ // Call next to continue
97
+ next();
98
+ });
99
+ }).catch((err) => {
100
+ // If there's an error, pass it to Express error handling
101
+ next(err);
102
+ });
103
+ };
104
+ }
105
+ /**
106
+ * Wrap an async Express route handler with cost tracking.
107
+ *
108
+ * Use this for individual route handlers when you need more control than middleware.
109
+ *
110
+ * @param handler - The async route handler function
111
+ * @param getOptions - Function to extract trace options from the request
112
+ * @returns A wrapped route handler
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * import { withTokenmeter } from 'tokenmeter/express';
117
+ *
118
+ * const chatHandler = withTokenmeter(
119
+ * async (req, res) => {
120
+ * const result = await openai.chat.completions.create({ ... });
121
+ * res.json({ result });
122
+ * },
123
+ * (req) => ({
124
+ * identifier: req.user?.id || 'anonymous',
125
+ * workflowType: 'chat',
126
+ * })
127
+ * );
128
+ *
129
+ * app.post('/api/chat', chatHandler);
130
+ * ```
131
+ */
132
+ export function withTokenmeter(handler, getOptions) {
133
+ return async (req, res, next) => {
134
+ if (!isInitialized()) {
135
+ console.warn("[tokenmeter] Not initialized. Call init() before using withTokenmeter.");
136
+ return handler(req, res, next);
137
+ }
138
+ try {
139
+ const options = await getOptions(req);
140
+ if (!options.identifier) {
141
+ return handler(req, res, next);
142
+ }
143
+ await withCostTrace(options, async () => {
144
+ await handler(req, res, next);
145
+ });
146
+ }
147
+ catch (err) {
148
+ next(err);
149
+ }
150
+ };
151
+ }
152
+ /**
153
+ * Helper to extract common identifiers from Express request headers.
154
+ */
155
+ export const headerExtractors = {
156
+ /**
157
+ * Get identifier from x-user-id header
158
+ */
159
+ userId: (req) => req.headers["x-user-id"],
160
+ /**
161
+ * Get identifier from x-org-id header
162
+ */
163
+ orgId: (req) => req.headers["x-org-id"],
164
+ /**
165
+ * Get identifier from x-api-key header
166
+ */
167
+ apiKey: (req) => req.headers["x-api-key"],
168
+ /**
169
+ * Get identifier from Authorization header (Bearer token)
170
+ */
171
+ bearerToken: (req) => {
172
+ const auth = req.headers.authorization;
173
+ if (auth?.startsWith("Bearer ")) {
174
+ return auth.slice(7);
175
+ }
176
+ return undefined;
177
+ },
178
+ /**
179
+ * Get request ID from common headers
180
+ */
181
+ requestId: (req) => req.headers["x-request-id"] ||
182
+ req.headers["x-correlation-id"] ||
183
+ req.headers["x-trace-id"],
184
+ };
185
+ export default tokenmeterMiddleware;
186
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/integrations/express/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Inngest Integration
3
+ *
4
+ * Provides helpers for propagating trace context to and from Inngest functions.
5
+ */
6
+ import type { TokenMeterAttributes } from "../../types.js";
7
+ /**
8
+ * Trace headers that can be passed in Inngest event metadata
9
+ */
10
+ export interface TraceHeaders {
11
+ traceparent?: string;
12
+ tracestate?: string;
13
+ baggage?: string;
14
+ }
15
+ /**
16
+ * Inngest event with optional trace metadata
17
+ */
18
+ export interface InngestEventWithTrace<T = unknown> {
19
+ name: string;
20
+ data: T;
21
+ /** Trace headers for context propagation */
22
+ trace?: TraceHeaders;
23
+ }
24
+ /**
25
+ * Options for creating traced Inngest events
26
+ */
27
+ export interface CreateTracedEventOptions {
28
+ /** Additional attributes to include in the trace */
29
+ attributes?: TokenMeterAttributes;
30
+ }
31
+ /**
32
+ * Extract trace headers to include when sending an Inngest event.
33
+ *
34
+ * Call this in your API route before sending an Inngest event to propagate
35
+ * the trace context to the Inngest function.
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * import { inngest } from './inngest';
40
+ * import { getInngestTraceHeaders } from 'tokenmeter/inngest';
41
+ *
42
+ * // In your API route
43
+ * export async function POST(req: Request) {
44
+ * const trace = getInngestTraceHeaders();
45
+ *
46
+ * await inngest.send({
47
+ * name: 'document/process',
48
+ * data: { documentId: '123' },
49
+ * trace, // Pass trace headers
50
+ * });
51
+ *
52
+ * return Response.json({ ok: true });
53
+ * }
54
+ * ```
55
+ */
56
+ export declare function getInngestTraceHeaders(): TraceHeaders;
57
+ /**
58
+ * Create a traced Inngest event with trace headers included.
59
+ *
60
+ * This is a convenience wrapper that combines your event data with trace headers.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * import { inngest } from './inngest';
65
+ * import { createTracedEvent } from 'tokenmeter/inngest';
66
+ *
67
+ * await inngest.send(createTracedEvent({
68
+ * name: 'document/process',
69
+ * data: { documentId: '123' },
70
+ * }));
71
+ * ```
72
+ */
73
+ export declare function createTracedEvent<T>(event: {
74
+ name: string;
75
+ data: T;
76
+ }, options?: CreateTracedEventOptions): InngestEventWithTrace<T>;
77
+ /**
78
+ * Run a function within the trace context from an Inngest event.
79
+ *
80
+ * Use this at the start of your Inngest function to resume the trace
81
+ * from the parent request.
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * import { inngest } from './inngest';
86
+ * import { withInngestTrace } from 'tokenmeter/inngest';
87
+ * import { monitor } from 'tokenmeter';
88
+ * import OpenAI from 'openai';
89
+ *
90
+ * const openai = monitor(new OpenAI());
91
+ *
92
+ * export const processDocument = inngest.createFunction(
93
+ * { id: 'process-document' },
94
+ * { event: 'document/process' },
95
+ * async ({ event }) => {
96
+ * return withInngestTrace(event, async () => {
97
+ * // All AI calls here are traced back to the original request
98
+ * const result = await openai.chat.completions.create({
99
+ * model: 'gpt-4o',
100
+ * messages: [{ role: 'user', content: 'Summarize this document' }],
101
+ * });
102
+ *
103
+ * return { summary: result.choices[0].message.content };
104
+ * });
105
+ * }
106
+ * );
107
+ * ```
108
+ */
109
+ export declare function withInngestTrace<T>(event: {
110
+ trace?: TraceHeaders;
111
+ }, fn: () => Promise<T>): Promise<T>;
112
+ /**
113
+ * Run a function within trace context and with additional attributes.
114
+ *
115
+ * Combines trace context restoration with attribute setting for Inngest functions.
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * export const processDocument = inngest.createFunction(
120
+ * { id: 'process-document' },
121
+ * { event: 'document/process' },
122
+ * async ({ event }) => {
123
+ * return withInngestTraceAndAttributes(
124
+ * event,
125
+ * { 'workflow.type': 'document-processing' },
126
+ * async () => {
127
+ * // AI calls have both parent trace and custom attributes
128
+ * const result = await openai.chat.completions.create({...});
129
+ * return { result };
130
+ * }
131
+ * );
132
+ * }
133
+ * );
134
+ * ```
135
+ */
136
+ export declare function withInngestTraceAndAttributes<T>(event: {
137
+ trace?: TraceHeaders;
138
+ }, attributes: TokenMeterAttributes, fn: () => Promise<T>): Promise<T>;
139
+ /**
140
+ * Inngest handler context type
141
+ */
142
+ export interface InngestHandlerContext<TEvent = unknown> {
143
+ event: TEvent & {
144
+ trace?: TraceHeaders;
145
+ };
146
+ step: unknown;
147
+ [key: string]: unknown;
148
+ }
149
+ /**
150
+ * Inngest handler function type
151
+ */
152
+ export type InngestHandler<TEvent, TResult> = (ctx: InngestHandlerContext<TEvent>) => Promise<TResult>;
153
+ /**
154
+ * Wrap an Inngest function handler to automatically restore trace context.
155
+ *
156
+ * This is the recommended way to integrate TokenMeter with Inngest. It extracts
157
+ * trace headers from the event and runs your handler within that context.
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * import { inngest } from './inngest';
162
+ * import { withInngest } from 'tokenmeter/inngest';
163
+ * import { monitor } from 'tokenmeter';
164
+ * import OpenAI from 'openai';
165
+ *
166
+ * const openai = monitor(new OpenAI());
167
+ *
168
+ * export const processDocument = inngest.createFunction(
169
+ * { id: 'process-document' },
170
+ * { event: 'document/process' },
171
+ * withInngest(async ({ event, step }) => {
172
+ * // All AI calls here are traced back to the original request
173
+ * const result = await openai.chat.completions.create({
174
+ * model: 'gpt-4o',
175
+ * messages: [{ role: 'user', content: 'Summarize this document' }],
176
+ * });
177
+ *
178
+ * return { summary: result.choices[0].message.content };
179
+ * })
180
+ * );
181
+ * ```
182
+ */
183
+ export declare function withInngest<TEvent, TResult>(handler: InngestHandler<TEvent, TResult>): InngestHandler<TEvent, TResult>;
184
+ /**
185
+ * Create Inngest middleware that automatically handles trace context.
186
+ *
187
+ * This middleware extracts trace headers from incoming events and sets up
188
+ * the context for all AI calls within the function.
189
+ *
190
+ * @example
191
+ * ```typescript
192
+ * import { Inngest } from 'inngest';
193
+ * import { createInngestMiddleware } from 'tokenmeter/inngest';
194
+ *
195
+ * export const inngest = new Inngest({
196
+ * id: 'my-app',
197
+ * middleware: [createInngestMiddleware()],
198
+ * });
199
+ * ```
200
+ */
201
+ export declare function createInngestMiddleware(): {
202
+ name: string;
203
+ init(): {
204
+ onFunctionRun({ fn, ctx, }: {
205
+ fn: unknown;
206
+ ctx: {
207
+ event: {
208
+ trace?: TraceHeaders;
209
+ };
210
+ };
211
+ }): {
212
+ transformInput({ ctx: inputCtx, steps, }: {
213
+ ctx: unknown;
214
+ steps: unknown;
215
+ }): {
216
+ ctx: unknown;
217
+ steps: unknown;
218
+ };
219
+ };
220
+ };
221
+ };
222
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/integrations/inngest/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB,CAAC,CAAC,GAAG,OAAO;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,CAAC,CAAC;IACR,4CAA4C;IAC5C,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,oDAAoD;IACpD,UAAU,CAAC,EAAE,oBAAoB,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,sBAAsB,IAAI,YAAY,CAErD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,EAChC,OAAO,CAAC,EAAE,wBAAwB,GACjC,qBAAqB,CAAC,CAAC,CAAC,CAK1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,KAAK,EAAE;IAAE,KAAK,CAAC,EAAE,YAAY,CAAA;CAAE,EAC/B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAcZ;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,6BAA6B,CAAC,CAAC,EACnD,KAAK,EAAE;IAAE,KAAK,CAAC,EAAE,YAAY,CAAA;CAAE,EAC/B,UAAU,EAAE,oBAAoB,EAChC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB,CAAC,MAAM,GAAG,OAAO;IACrD,KAAK,EAAE,MAAM,GAAG;QAAE,KAAK,CAAC,EAAE,YAAY,CAAA;KAAE,CAAC;IACzC,IAAI,EAAE,OAAO,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,CAAC,MAAM,EAAE,OAAO,IAAI,CAC5C,GAAG,EAAE,qBAAqB,CAAC,MAAM,CAAC,KAC/B,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,EACzC,OAAO,EAAE,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,GACvC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBjC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB;;;oCAQ5B;YACD,EAAE,EAAE,OAAO,CAAC;YACZ,GAAG,EAAE;gBAAE,KAAK,EAAE;oBAAE,KAAK,CAAC,EAAE,YAAY,CAAA;iBAAE,CAAA;aAAE,CAAC;SAC1C;sDAKM;gBACD,GAAG,EAAE,OAAO,CAAC;gBACb,KAAK,EAAE,OAAO,CAAC;aAChB;;;;;;EA2BZ"}
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Inngest Integration
3
+ *
4
+ * Provides helpers for propagating trace context to and from Inngest functions.
5
+ */
6
+ import { extractTraceHeaders, withExtractedContext, withAttributes, } from "../../context.js";
7
+ /**
8
+ * Extract trace headers to include when sending an Inngest event.
9
+ *
10
+ * Call this in your API route before sending an Inngest event to propagate
11
+ * the trace context to the Inngest function.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { inngest } from './inngest';
16
+ * import { getInngestTraceHeaders } from 'tokenmeter/inngest';
17
+ *
18
+ * // In your API route
19
+ * export async function POST(req: Request) {
20
+ * const trace = getInngestTraceHeaders();
21
+ *
22
+ * await inngest.send({
23
+ * name: 'document/process',
24
+ * data: { documentId: '123' },
25
+ * trace, // Pass trace headers
26
+ * });
27
+ *
28
+ * return Response.json({ ok: true });
29
+ * }
30
+ * ```
31
+ */
32
+ export function getInngestTraceHeaders() {
33
+ return extractTraceHeaders();
34
+ }
35
+ /**
36
+ * Create a traced Inngest event with trace headers included.
37
+ *
38
+ * This is a convenience wrapper that combines your event data with trace headers.
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * import { inngest } from './inngest';
43
+ * import { createTracedEvent } from 'tokenmeter/inngest';
44
+ *
45
+ * await inngest.send(createTracedEvent({
46
+ * name: 'document/process',
47
+ * data: { documentId: '123' },
48
+ * }));
49
+ * ```
50
+ */
51
+ export function createTracedEvent(event, options) {
52
+ return {
53
+ ...event,
54
+ trace: getInngestTraceHeaders(),
55
+ };
56
+ }
57
+ /**
58
+ * Run a function within the trace context from an Inngest event.
59
+ *
60
+ * Use this at the start of your Inngest function to resume the trace
61
+ * from the parent request.
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * import { inngest } from './inngest';
66
+ * import { withInngestTrace } from 'tokenmeter/inngest';
67
+ * import { monitor } from 'tokenmeter';
68
+ * import OpenAI from 'openai';
69
+ *
70
+ * const openai = monitor(new OpenAI());
71
+ *
72
+ * export const processDocument = inngest.createFunction(
73
+ * { id: 'process-document' },
74
+ * { event: 'document/process' },
75
+ * async ({ event }) => {
76
+ * return withInngestTrace(event, async () => {
77
+ * // All AI calls here are traced back to the original request
78
+ * const result = await openai.chat.completions.create({
79
+ * model: 'gpt-4o',
80
+ * messages: [{ role: 'user', content: 'Summarize this document' }],
81
+ * });
82
+ *
83
+ * return { summary: result.choices[0].message.content };
84
+ * });
85
+ * }
86
+ * );
87
+ * ```
88
+ */
89
+ export async function withInngestTrace(event, fn) {
90
+ const headers = {};
91
+ if (event.trace?.traceparent) {
92
+ headers.traceparent = event.trace.traceparent;
93
+ }
94
+ if (event.trace?.tracestate) {
95
+ headers.tracestate = event.trace.tracestate;
96
+ }
97
+ if (event.trace?.baggage) {
98
+ headers.baggage = event.trace.baggage;
99
+ }
100
+ return withExtractedContext(headers, fn);
101
+ }
102
+ /**
103
+ * Run a function within trace context and with additional attributes.
104
+ *
105
+ * Combines trace context restoration with attribute setting for Inngest functions.
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * export const processDocument = inngest.createFunction(
110
+ * { id: 'process-document' },
111
+ * { event: 'document/process' },
112
+ * async ({ event }) => {
113
+ * return withInngestTraceAndAttributes(
114
+ * event,
115
+ * { 'workflow.type': 'document-processing' },
116
+ * async () => {
117
+ * // AI calls have both parent trace and custom attributes
118
+ * const result = await openai.chat.completions.create({...});
119
+ * return { result };
120
+ * }
121
+ * );
122
+ * }
123
+ * );
124
+ * ```
125
+ */
126
+ export async function withInngestTraceAndAttributes(event, attributes, fn) {
127
+ return withInngestTrace(event, () => withAttributes(attributes, fn));
128
+ }
129
+ /**
130
+ * Wrap an Inngest function handler to automatically restore trace context.
131
+ *
132
+ * This is the recommended way to integrate TokenMeter with Inngest. It extracts
133
+ * trace headers from the event and runs your handler within that context.
134
+ *
135
+ * @example
136
+ * ```typescript
137
+ * import { inngest } from './inngest';
138
+ * import { withInngest } from 'tokenmeter/inngest';
139
+ * import { monitor } from 'tokenmeter';
140
+ * import OpenAI from 'openai';
141
+ *
142
+ * const openai = monitor(new OpenAI());
143
+ *
144
+ * export const processDocument = inngest.createFunction(
145
+ * { id: 'process-document' },
146
+ * { event: 'document/process' },
147
+ * withInngest(async ({ event, step }) => {
148
+ * // All AI calls here are traced back to the original request
149
+ * const result = await openai.chat.completions.create({
150
+ * model: 'gpt-4o',
151
+ * messages: [{ role: 'user', content: 'Summarize this document' }],
152
+ * });
153
+ *
154
+ * return { summary: result.choices[0].message.content };
155
+ * })
156
+ * );
157
+ * ```
158
+ */
159
+ export function withInngest(handler) {
160
+ return async (ctx) => {
161
+ const headers = {};
162
+ if (ctx.event?.trace?.traceparent) {
163
+ headers.traceparent = ctx.event.trace.traceparent;
164
+ }
165
+ if (ctx.event?.trace?.tracestate) {
166
+ headers.tracestate = ctx.event.trace.tracestate;
167
+ }
168
+ if (ctx.event?.trace?.baggage) {
169
+ headers.baggage = ctx.event.trace.baggage;
170
+ }
171
+ return withExtractedContext(headers, () => handler(ctx));
172
+ };
173
+ }
174
+ /**
175
+ * Create Inngest middleware that automatically handles trace context.
176
+ *
177
+ * This middleware extracts trace headers from incoming events and sets up
178
+ * the context for all AI calls within the function.
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * import { Inngest } from 'inngest';
183
+ * import { createInngestMiddleware } from 'tokenmeter/inngest';
184
+ *
185
+ * export const inngest = new Inngest({
186
+ * id: 'my-app',
187
+ * middleware: [createInngestMiddleware()],
188
+ * });
189
+ * ```
190
+ */
191
+ export function createInngestMiddleware() {
192
+ return {
193
+ name: "tokenmeter",
194
+ init() {
195
+ return {
196
+ onFunctionRun({ fn, ctx, }) {
197
+ return {
198
+ transformInput({ ctx: inputCtx, steps, }) {
199
+ // Extract trace headers from event
200
+ const event = inputCtx
201
+ ?.event;
202
+ const headers = {};
203
+ if (event?.trace?.traceparent) {
204
+ headers.traceparent = event.trace.traceparent;
205
+ }
206
+ if (event?.trace?.tracestate) {
207
+ headers.tracestate = event.trace.tracestate;
208
+ }
209
+ if (event?.trace?.baggage) {
210
+ headers.baggage = event.trace.baggage;
211
+ }
212
+ // Note: Middleware can't wrap async execution directly
213
+ // Users should still use withInngest() in their function
214
+ // This middleware is for future compatibility
215
+ return { ctx: inputCtx, steps };
216
+ },
217
+ };
218
+ },
219
+ };
220
+ },
221
+ };
222
+ }
223
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/integrations/inngest/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,GACf,MAAM,kBAAkB,CAAC;AA8B1B;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,mBAAmB,EAAkB,CAAC;AAC/C,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAgC,EAChC,OAAkC;IAElC,OAAO;QACL,GAAG,KAAK;QACR,KAAK,EAAE,sBAAsB,EAAE;KAChC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAA+B,EAC/B,EAAoB;IAEpB,MAAM,OAAO,GAA2B,EAAE,CAAC;IAE3C,IAAI,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC;QAC7B,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC;IAChD,CAAC;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,CAAC;QAC5B,OAAO,CAAC,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC;IAC9C,CAAC;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;QACzB,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;IACxC,CAAC;IAED,OAAO,oBAAoB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,KAA+B,EAC/B,UAAgC,EAChC,EAAoB;IAEpB,OAAO,gBAAgB,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC;AACvE,CAAC;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,WAAW,CACzB,OAAwC;IAExC,OAAO,KAAK,EAAE,GAAkC,EAAoB,EAAE;QACpE,MAAM,OAAO,GAA2B,EAAE,CAAC;QAE3C,IAAI,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;YAClC,OAAO,CAAC,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC;QACpD,CAAC;QACD,IAAI,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;YACjC,OAAO,CAAC,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC;QAClD,CAAC;QACD,IAAI,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;YAC9B,OAAO,CAAC,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;QAC5C,CAAC;QAED,OAAO,oBAAoB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3D,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,IAAI;YACF,OAAO;gBACL,aAAa,CAAC,EACZ,EAAE,EACF,GAAG,GAIJ;oBACC,OAAO;wBACL,cAAc,CAAC,EACb,GAAG,EAAE,QAAQ,EACb,KAAK,GAIN;4BACC,mCAAmC;4BACnC,MAAM,KAAK,GAAI,QAAiD;gCAC9D,EAAE,KAAK,CAAC;4BACV,MAAM,OAAO,GAA2B,EAAE,CAAC;4BAE3C,IAAI,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;gCAC9B,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC;4BAChD,CAAC;4BACD,IAAI,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;gCAC7B,OAAO,CAAC,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC;4BAC9C,CAAC;4BACD,IAAI,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;gCAC1B,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC;4BACxC,CAAC;4BAED,uDAAuD;4BACvD,yDAAyD;4BACzD,8CAA8C;4BAE9C,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;wBAClC,CAAC;qBACF,CAAC;gBACJ,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}