@useautumn/gateway 0.0.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.
@@ -0,0 +1,327 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/openrouter/index.ts
21
+ var openrouter_exports = {};
22
+ __export(openrouter_exports, {
23
+ trackOpenRouterUsage: () => trackOpenRouterUsage,
24
+ trackingSettled: () => trackingSettled,
25
+ withAutumn: () => withAutumn
26
+ });
27
+ module.exports = __toCommonJS(openrouter_exports);
28
+
29
+ // src/shared/track.ts
30
+ var WIRE_KEYS = {
31
+ customerId: "customer_id",
32
+ entityId: "entity_id",
33
+ featureId: "feature_id",
34
+ modelId: "model_id",
35
+ inputTokens: "input_tokens",
36
+ outputTokens: "output_tokens",
37
+ cacheReadTokens: "cache_read_tokens",
38
+ cacheWriteTokens: "cache_write_tokens",
39
+ audioInputTokens: "audio_input_tokens",
40
+ audioOutputTokens: "audio_output_tokens",
41
+ reasoningTokens: "reasoning_tokens"
42
+ };
43
+ var toWire = (params) => Object.fromEntries(
44
+ Object.entries(params).filter(([, value]) => value !== void 0).map(([key, value]) => [WIRE_KEYS[key] ?? key, value])
45
+ );
46
+ var envClient = () => {
47
+ const env = typeof process === "undefined" ? void 0 : process.env;
48
+ const secretKey = env?.AUTUMN_API_KEY ?? env?.AUTUMN_SECRET_KEY;
49
+ const baseUrl = env?.AUTUMN_BASE_URL ?? "https://api.useautumn.com";
50
+ return {
51
+ trackTokens: async (params) => {
52
+ if (!secretKey) {
53
+ throw new Error(
54
+ "[Autumn] No autumn client was passed and AUTUMN_API_KEY is not set."
55
+ );
56
+ }
57
+ const response = await fetch(`${baseUrl}/v1/balances.track_tokens`, {
58
+ method: "POST",
59
+ headers: {
60
+ authorization: `Bearer ${secretKey}`,
61
+ "content-type": "application/json"
62
+ },
63
+ body: JSON.stringify(toWire(params))
64
+ });
65
+ if (!response.ok) {
66
+ throw new Error(
67
+ `track_tokens failed (${response.status}): ${await response.text()}`
68
+ );
69
+ }
70
+ return response.json();
71
+ }
72
+ };
73
+ };
74
+ var createTracker = ({
75
+ autumn = envClient(),
76
+ customerId,
77
+ featureId,
78
+ entityId,
79
+ properties
80
+ }) => (getEvent) => trackTokenUsage({
81
+ autumn,
82
+ getParams: () => {
83
+ const event = getEvent();
84
+ return {
85
+ ...event.pools,
86
+ customerId,
87
+ modelId: event.modelId,
88
+ featureId,
89
+ entityId,
90
+ properties: event.properties ?? properties
91
+ };
92
+ }
93
+ });
94
+ var trackTokenUsage = async ({
95
+ autumn,
96
+ getParams
97
+ }) => {
98
+ try {
99
+ const balances = autumn.balances;
100
+ const trackTokens = balances?.trackTokens?.bind(balances) ?? autumn.trackTokens?.bind(autumn);
101
+ if (!trackTokens) {
102
+ throw new Error(
103
+ "Autumn client does not support trackTokens \u2014 upgrade @useautumn/sdk."
104
+ );
105
+ }
106
+ await trackTokens(getParams());
107
+ } catch (error) {
108
+ console.error("[Autumn Tracking] Failed to track usage:", error);
109
+ }
110
+ };
111
+
112
+ // src/shared/usage.ts
113
+ var clamp = (value) => Math.max(0, value);
114
+ var requiredCount = (value, label, modelName) => {
115
+ if (value == null) {
116
+ throw new Error(
117
+ `[Autumn] ${label} token usage was not returned by the model provider (${modelName}). This provider may not support usage tracking.`
118
+ );
119
+ }
120
+ return value;
121
+ };
122
+ var poolsFromParts = (parts, modelName) => {
123
+ const cacheRead = parts.cacheRead ?? 0;
124
+ const cacheWrite = parts.cacheWrite ?? 0;
125
+ const reasoning = parts.reasoning ?? 0;
126
+ const audioInput = parts.audioInput ?? 0;
127
+ const textInput = parts.textInput ?? (parts.totalInput != null ? parts.totalInput - cacheRead - cacheWrite - audioInput : void 0);
128
+ const textOutput = parts.textOutput ?? (parts.totalOutput != null ? parts.totalOutput - reasoning : void 0);
129
+ return {
130
+ inputTokens: clamp(requiredCount(textInput, "Input", modelName)),
131
+ outputTokens: clamp(requiredCount(textOutput, "Output", modelName)),
132
+ cacheReadTokens: clamp(cacheRead),
133
+ cacheWriteTokens: clamp(cacheWrite),
134
+ reasoningTokens: clamp(reasoning),
135
+ ...parts.audioInput !== void 0 && {
136
+ audioInputTokens: clamp(audioInput)
137
+ }
138
+ };
139
+ };
140
+
141
+ // src/openrouter/usage.ts
142
+ var normalizeOpenRouterUsage = (usage, modelName) => {
143
+ const promptDetails = usage.promptTokensDetails ?? usage.prompt_tokens_details ?? usage.inputTokensDetails ?? usage.input_tokens_details;
144
+ const completionDetails = usage.completionTokensDetails ?? usage.completion_tokens_details ?? usage.outputTokensDetails ?? usage.output_tokens_details;
145
+ const promptTotal = usage.promptTokens ?? usage.prompt_tokens ?? usage.inputTokens ?? usage.input_tokens;
146
+ const completionTotal = usage.completionTokens ?? usage.completion_tokens ?? usage.outputTokens ?? usage.output_tokens;
147
+ return poolsFromParts(
148
+ {
149
+ totalInput: promptTotal,
150
+ totalOutput: completionTotal,
151
+ cacheRead: promptDetails?.cachedTokens ?? promptDetails?.cached_tokens ?? 0,
152
+ cacheWrite: promptDetails?.cacheWriteTokens ?? promptDetails?.cache_write_tokens ?? 0,
153
+ reasoning: completionDetails?.reasoningTokens ?? completionDetails?.reasoning_tokens ?? 0,
154
+ audioInput: promptDetails?.audioTokens ?? promptDetails?.audio_tokens ?? 0
155
+ },
156
+ modelName
157
+ );
158
+ };
159
+
160
+ // src/openrouter/index.ts
161
+ var withCost = (properties, cost) => cost == null ? properties : { ...properties, openrouter_cost: cost };
162
+ var HOOKED_OPERATIONS = /* @__PURE__ */ new Set(["createResponses"]);
163
+ var pendingTracking = /* @__PURE__ */ new WeakMap();
164
+ var trackingSettled = async (openRouter) => {
165
+ const pending = pendingTracking.get(openRouter);
166
+ while (pending && pending.size > 0) {
167
+ await Promise.allSettled([...pending]);
168
+ }
169
+ };
170
+ var findCarrier = (value) => {
171
+ if (value == null || typeof value !== "object") {
172
+ return;
173
+ }
174
+ const record = value;
175
+ if (record.usage && record.model) {
176
+ return record;
177
+ }
178
+ return findCarrier(record.response);
179
+ };
180
+ var lastCarrierFromSse = (body) => {
181
+ let carrier;
182
+ for (const line of body.split("\n")) {
183
+ if (!line.startsWith("data:")) {
184
+ continue;
185
+ }
186
+ try {
187
+ carrier = findCarrier(JSON.parse(line.slice(5).trim())) ?? carrier;
188
+ } catch {
189
+ }
190
+ }
191
+ return carrier;
192
+ };
193
+ var toModelId = (slug) => slug.startsWith("openrouter/") ? slug : `openrouter/${slug}`;
194
+ var trackOpenRouterUsage = ({
195
+ usage,
196
+ model,
197
+ ...tracking
198
+ }) => {
199
+ const modelId = toModelId(model);
200
+ return createTracker(tracking)(() => ({
201
+ pools: normalizeOpenRouterUsage(usage, modelId),
202
+ modelId,
203
+ properties: withCost(tracking.properties, usage.cost)
204
+ }));
205
+ };
206
+ var isAsyncIterable = (value) => value != null && typeof value === "object" && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
207
+ var hasUsage = (value) => value != null && typeof value === "object" && "usage" in value && value.usage != null;
208
+ var withAutumn = (options) => {
209
+ const { openRouter } = options;
210
+ const track = createTracker(options);
211
+ const trackCarrier = (carrier, requestModel) => track(() => {
212
+ const requested = requestModel?.endsWith("/auto") ? void 0 : requestModel;
213
+ const slug = requested ?? carrier.model ?? requestModel;
214
+ if (!slug) {
215
+ throw new Error(
216
+ "[Autumn] OpenRouter response did not include a model slug."
217
+ );
218
+ }
219
+ const modelId = toModelId(slug);
220
+ const usage = carrier.usage ?? {};
221
+ return {
222
+ pools: normalizeOpenRouterUsage(usage, modelId),
223
+ modelId,
224
+ properties: withCost(options.properties, usage.cost)
225
+ };
226
+ });
227
+ const wrapStream = (stream, requestModel) => {
228
+ let tracked = false;
229
+ async function* iterate() {
230
+ let finalCarrier;
231
+ try {
232
+ for await (const chunk of stream) {
233
+ if (hasUsage(chunk)) {
234
+ finalCarrier = chunk;
235
+ }
236
+ yield chunk;
237
+ }
238
+ } finally {
239
+ if (finalCarrier && !tracked) {
240
+ tracked = true;
241
+ await trackCarrier(finalCarrier, requestModel);
242
+ }
243
+ }
244
+ }
245
+ return new Proxy(stream, {
246
+ get(target, prop, receiver) {
247
+ if (prop === Symbol.asyncIterator) {
248
+ return () => iterate();
249
+ }
250
+ const value = Reflect.get(target, prop, receiver);
251
+ return typeof value === "function" ? value.bind(target) : value;
252
+ }
253
+ });
254
+ };
255
+ const send = async (request, ...rest) => {
256
+ const body = request.chatRequest ?? request;
257
+ const bodyWithUsage = {
258
+ ...body,
259
+ usage: { ...body.usage, include: true }
260
+ };
261
+ const requestWithUsage = request.chatRequest ? { ...request, chatRequest: bodyWithUsage } : bodyWithUsage;
262
+ const result = await openRouter.chat.send(requestWithUsage, ...rest);
263
+ if (isAsyncIterable(result)) {
264
+ return wrapStream(result, body.model);
265
+ }
266
+ await trackCarrier(result, body.model);
267
+ return result;
268
+ };
269
+ const captureHookedResponse = async (response, contentType) => {
270
+ let carrier;
271
+ if (contentType.includes("text/event-stream")) {
272
+ carrier = lastCarrierFromSse(await response.text());
273
+ } else if (contentType.includes("json")) {
274
+ carrier = findCarrier(await response.json());
275
+ }
276
+ if (carrier) {
277
+ await trackCarrier(carrier);
278
+ }
279
+ };
280
+ const pending = /* @__PURE__ */ new Set();
281
+ pendingTracking.set(openRouter, pending);
282
+ openRouter._options?.hooks?.registerAfterSuccessHook?.(
283
+ {
284
+ afterSuccess: (ctx, response) => {
285
+ if (!HOOKED_OPERATIONS.has(ctx.operationID)) {
286
+ return response;
287
+ }
288
+ const contentType = response.headers.get("content-type") ?? "";
289
+ const capture = captureHookedResponse(
290
+ response.clone(),
291
+ contentType
292
+ ).catch((error) => {
293
+ console.error("[Autumn Tracking] Failed to track usage:", error);
294
+ });
295
+ pending.add(capture);
296
+ capture.finally(() => pending.delete(capture));
297
+ return contentType.includes("text/event-stream") ? response : capture.then(() => response);
298
+ }
299
+ }
300
+ );
301
+ const wrappedChat = new Proxy(openRouter.chat, {
302
+ get(target, prop, receiver) {
303
+ if (prop === "send") {
304
+ return send;
305
+ }
306
+ const value = Reflect.get(target, prop, receiver);
307
+ return typeof value === "function" ? value.bind(target) : value;
308
+ }
309
+ });
310
+ const wrapped = new Proxy(openRouter, {
311
+ get(target, prop, receiver) {
312
+ if (prop === "chat") {
313
+ return wrappedChat;
314
+ }
315
+ const value = Reflect.get(target, prop, receiver);
316
+ return typeof value === "function" ? value.bind(target) : value;
317
+ }
318
+ });
319
+ pendingTracking.set(wrapped, pending);
320
+ return wrapped;
321
+ };
322
+ // Annotate the CommonJS export names for ESM import in node:
323
+ 0 && (module.exports = {
324
+ trackOpenRouterUsage,
325
+ trackingSettled,
326
+ withAutumn
327
+ });
@@ -0,0 +1,69 @@
1
+ import { A as AutumnTrackingOptions } from '../track-C73uYSG_.cjs';
2
+ export { a as AutumnClient, T as TokenPools } from '../track-C73uYSG_.cjs';
3
+
4
+ type PromptDetails = {
5
+ cachedTokens?: number | null;
6
+ cached_tokens?: number | null;
7
+ cacheWriteTokens?: number | null;
8
+ cache_write_tokens?: number | null;
9
+ audioTokens?: number | null;
10
+ audio_tokens?: number | null;
11
+ };
12
+ type CompletionDetails = {
13
+ reasoningTokens?: number | null;
14
+ reasoning_tokens?: number | null;
15
+ };
16
+ /**
17
+ * Lenient view over OpenRouter usage across all its surfaces: chat
18
+ * completions (prompt/completion naming) and the responses API
19
+ * (input/output naming), in SDK camelCase or raw snake_case.
20
+ */
21
+ type OpenRouterUsageLike = {
22
+ promptTokens?: number | null;
23
+ prompt_tokens?: number | null;
24
+ inputTokens?: number | null;
25
+ input_tokens?: number | null;
26
+ completionTokens?: number | null;
27
+ completion_tokens?: number | null;
28
+ outputTokens?: number | null;
29
+ output_tokens?: number | null;
30
+ promptTokensDetails?: PromptDetails | null;
31
+ prompt_tokens_details?: PromptDetails | null;
32
+ inputTokensDetails?: PromptDetails | null;
33
+ input_tokens_details?: PromptDetails | null;
34
+ completionTokensDetails?: CompletionDetails | null;
35
+ completion_tokens_details?: CompletionDetails | null;
36
+ outputTokensDetails?: CompletionDetails | null;
37
+ output_tokens_details?: CompletionDetails | null;
38
+ /** OpenRouter's own charge in USD credits, present when usage accounting is enabled. */
39
+ cost?: number | null;
40
+ };
41
+
42
+ /**
43
+ * Resolves once all in-flight Autumn usage tracking for a wrapped client has
44
+ * settled. Streaming responses (including @openrouter/agent's callModel,
45
+ * which always streams internally) are tracked in the background — await
46
+ * this before reading balances that must reflect the calls just made.
47
+ */
48
+ declare const trackingSettled: (openRouter: object) => Promise<void>;
49
+ /** Structural view of the @openrouter/sdk client — only chat.send is intercepted. */
50
+ type OpenRouterLike = {
51
+ chat: {
52
+ send: (request: any, ...rest: any[]) => Promise<unknown>;
53
+ };
54
+ };
55
+ type WithAutumnOptions<T extends OpenRouterLike> = AutumnTrackingOptions & {
56
+ /** The @openrouter/sdk client to wrap. */
57
+ openRouter: T;
58
+ };
59
+ type TrackOpenRouterUsageOptions = AutumnTrackingOptions & {
60
+ /** Usage object from an OpenRouter response (SDK model or raw API shape). */
61
+ usage: OpenRouterUsageLike;
62
+ /** OpenRouter model slug, e.g. "openai/gpt-4o". */
63
+ model: string;
64
+ };
65
+ /** Manual escape hatch for consumption patterns the wrapped client doesn't cover (e.g. callModel). */
66
+ declare const trackOpenRouterUsage: ({ usage, model, ...tracking }: TrackOpenRouterUsageOptions) => Promise<void>;
67
+ declare const withAutumn: <T extends OpenRouterLike>(options: WithAutumnOptions<T>) => T;
68
+
69
+ export { AutumnTrackingOptions, type OpenRouterLike, type OpenRouterUsageLike, type TrackOpenRouterUsageOptions, type WithAutumnOptions, trackOpenRouterUsage, trackingSettled, withAutumn };
@@ -0,0 +1,69 @@
1
+ import { A as AutumnTrackingOptions } from '../track-C73uYSG_.js';
2
+ export { a as AutumnClient, T as TokenPools } from '../track-C73uYSG_.js';
3
+
4
+ type PromptDetails = {
5
+ cachedTokens?: number | null;
6
+ cached_tokens?: number | null;
7
+ cacheWriteTokens?: number | null;
8
+ cache_write_tokens?: number | null;
9
+ audioTokens?: number | null;
10
+ audio_tokens?: number | null;
11
+ };
12
+ type CompletionDetails = {
13
+ reasoningTokens?: number | null;
14
+ reasoning_tokens?: number | null;
15
+ };
16
+ /**
17
+ * Lenient view over OpenRouter usage across all its surfaces: chat
18
+ * completions (prompt/completion naming) and the responses API
19
+ * (input/output naming), in SDK camelCase or raw snake_case.
20
+ */
21
+ type OpenRouterUsageLike = {
22
+ promptTokens?: number | null;
23
+ prompt_tokens?: number | null;
24
+ inputTokens?: number | null;
25
+ input_tokens?: number | null;
26
+ completionTokens?: number | null;
27
+ completion_tokens?: number | null;
28
+ outputTokens?: number | null;
29
+ output_tokens?: number | null;
30
+ promptTokensDetails?: PromptDetails | null;
31
+ prompt_tokens_details?: PromptDetails | null;
32
+ inputTokensDetails?: PromptDetails | null;
33
+ input_tokens_details?: PromptDetails | null;
34
+ completionTokensDetails?: CompletionDetails | null;
35
+ completion_tokens_details?: CompletionDetails | null;
36
+ outputTokensDetails?: CompletionDetails | null;
37
+ output_tokens_details?: CompletionDetails | null;
38
+ /** OpenRouter's own charge in USD credits, present when usage accounting is enabled. */
39
+ cost?: number | null;
40
+ };
41
+
42
+ /**
43
+ * Resolves once all in-flight Autumn usage tracking for a wrapped client has
44
+ * settled. Streaming responses (including @openrouter/agent's callModel,
45
+ * which always streams internally) are tracked in the background — await
46
+ * this before reading balances that must reflect the calls just made.
47
+ */
48
+ declare const trackingSettled: (openRouter: object) => Promise<void>;
49
+ /** Structural view of the @openrouter/sdk client — only chat.send is intercepted. */
50
+ type OpenRouterLike = {
51
+ chat: {
52
+ send: (request: any, ...rest: any[]) => Promise<unknown>;
53
+ };
54
+ };
55
+ type WithAutumnOptions<T extends OpenRouterLike> = AutumnTrackingOptions & {
56
+ /** The @openrouter/sdk client to wrap. */
57
+ openRouter: T;
58
+ };
59
+ type TrackOpenRouterUsageOptions = AutumnTrackingOptions & {
60
+ /** Usage object from an OpenRouter response (SDK model or raw API shape). */
61
+ usage: OpenRouterUsageLike;
62
+ /** OpenRouter model slug, e.g. "openai/gpt-4o". */
63
+ model: string;
64
+ };
65
+ /** Manual escape hatch for consumption patterns the wrapped client doesn't cover (e.g. callModel). */
66
+ declare const trackOpenRouterUsage: ({ usage, model, ...tracking }: TrackOpenRouterUsageOptions) => Promise<void>;
67
+ declare const withAutumn: <T extends OpenRouterLike>(options: WithAutumnOptions<T>) => T;
68
+
69
+ export { AutumnTrackingOptions, type OpenRouterLike, type OpenRouterUsageLike, type TrackOpenRouterUsageOptions, type WithAutumnOptions, trackOpenRouterUsage, trackingSettled, withAutumn };