@useautumn/gateway 0.0.1 → 0.1.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 CHANGED
@@ -6,6 +6,8 @@ Autumn converts the token counts to a dollar cost server-side using live [models
6
6
 
7
7
  ## Install
8
8
 
9
+ Requires Node.js 22+ and ESM imports. The AI SDK adapter targets AI SDK 7 and V4 language models; AI SDK 6 applications should stay on the previous gateway release.
10
+
9
11
  ```bash
10
12
  npm install @useautumn/gateway
11
13
  # or
@@ -30,7 +32,9 @@ const { text } = await generateText({ model, prompt: "Hello!" });
30
32
  // usage tracked automatically — streaming too
31
33
  ```
32
34
 
33
- The wrapped model is a drop-in `LanguageModelV3`. Usage is read from the response (or the stream's finish chunk) and reported as `<provider>/<model>` (e.g. `anthropic/claude-sonnet-4-5`). If your provider's name doesn't match its models.dev key, override it with `providerId`.
35
+ The wrapped model is a drop-in `LanguageModelV4`. Usage is read per model call from the response (or the stream's finish chunk), including each step in a tool loop, and reported as `<provider>/<model>` (e.g. `anthropic/claude-sonnet-4-5`). Tracking does not use AI SDK 7's aggregated top-level `result.usage`.
36
+
37
+ OpenAI, Anthropic, and Google provider names are normalized automatically: plain names and dotted SDK namespaces such as `openai.responses`, `anthropic.messages`, and `google.generative-ai` use their models.dev provider keys. Vercel's `gateway` namespace maps to `vercel`. Model IDs retain all dots and slashes. Other provider names pass through unchanged; use `providerId` for a custom mapping. Explicit overrides are always used verbatim.
34
38
 
35
39
  ## OpenRouter
36
40
 
@@ -1,4 +1,4 @@
1
- import { LanguageModelV3 } from '@ai-sdk/provider';
1
+ import { LanguageModelV4 } from '@ai-sdk/provider';
2
2
  import { A as AutumnTrackingOptions } from '../track-C73uYSG_.js';
3
3
  export { a as AutumnClient, T as TokenPools } from '../track-C73uYSG_.js';
4
4
 
@@ -34,10 +34,10 @@ type UsageLike = {
34
34
 
35
35
  type WithAutumnOptions = AutumnTrackingOptions & {
36
36
  /** The AI SDK language model to wrap. */
37
- model: LanguageModelV3;
38
- /** Override the provider prefix used in the model name (e.g. "openrouter", "custom"). Falls back to `model.provider`. */
37
+ model: LanguageModelV4;
38
+ /** Explicit pricing provider, used verbatim instead of the normalized model provider. */
39
39
  providerId?: string;
40
40
  };
41
- declare const withAutumn: ({ model, providerId, ...tracking }: WithAutumnOptions) => LanguageModelV3;
41
+ declare const withAutumn: ({ model, providerId, ...tracking }: WithAutumnOptions) => LanguageModelV4;
42
42
 
43
43
  export { AutumnTrackingOptions, type UsageLike, type WithAutumnOptions, withAutumn };
@@ -149,14 +149,16 @@ var withAutumn = ({
149
149
  providerId,
150
150
  ...tracking
151
151
  }) => {
152
- const modelName = `${providerId ?? model.provider}/${model.modelId}`;
152
+ const providerRoot = model.provider.match(/^(openai|anthropic|google|gateway)(?:\.|$)/)?.[1] ?? model.provider;
153
+ const pricingProvider = providerId ?? (providerRoot === "gateway" ? "vercel" : providerRoot);
154
+ const modelName = `${pricingProvider}/${model.modelId}`;
153
155
  const track = createTracker(tracking);
154
156
  const trackUsage = (usage) => track(() => ({
155
157
  pools: normalizeUsage(usage, modelName),
156
158
  modelId: modelName
157
159
  }));
158
160
  const middleware = {
159
- specificationVersion: "v3",
161
+ specificationVersion: "v4",
160
162
  wrapGenerate: async ({ doGenerate }) => {
161
163
  const result = await doGenerate();
162
164
  await trackUsage(result.usage);
package/package.json CHANGED
@@ -1,64 +1,59 @@
1
1
  {
2
- "name": "@useautumn/gateway",
3
- "version": "0.0.1",
4
- "type": "module",
5
- "description": "Autumn usage tracking adapters for AI SDKs and gateways (Vercel AI SDK, OpenRouter)",
6
- "repository": "github:useautumn/autumn",
7
- "homepage": "https://useautumn.com",
8
- "license": "MIT",
9
- "keywords": [
10
- "autumn",
11
- "billing",
12
- "usage-based-billing",
13
- "ai-sdk",
14
- "openrouter",
15
- "llm",
16
- "token-tracking"
17
- ],
18
- "sideEffects": false,
19
- "files": [
20
- "dist",
21
- "README.md",
22
- "LICENSE.md"
23
- ],
24
- "exports": {
25
- "./ai-sdk": {
26
- "types": "./dist/ai-sdk/index.d.ts",
27
- "require": "./dist/ai-sdk/index.cjs",
28
- "import": "./dist/ai-sdk/index.js"
29
- },
30
- "./openrouter": {
31
- "types": "./dist/openrouter/index.d.ts",
32
- "require": "./dist/openrouter/index.cjs",
33
- "import": "./dist/openrouter/index.js"
34
- }
35
- },
36
- "scripts": {
37
- "ts": "bunx tsgo --noEmit --skipLibCheck",
38
- "test": "bun test tests/unit",
39
- "build": "rm -rf dist && tsup",
40
- "prepublishOnly": "bun run build"
41
- },
42
- "dependencies": {
43
- "@ai-sdk/provider": "^3.0.0"
44
- },
45
- "peerDependencies": {
46
- "@openrouter/sdk": "*",
47
- "ai": "^6.0.116",
48
- "autumn-js": "*"
49
- },
50
- "peerDependenciesMeta": {
51
- "@openrouter/sdk": {
52
- "optional": true
53
- },
54
- "ai": {
55
- "optional": true
56
- }
57
- },
58
- "devDependencies": {
59
- "@types/node": "^24.9.1",
60
- "@typescript/native-preview": "7.0.0-dev.20260220.1",
61
- "tsup": "^8.4.0",
62
- "typescript": "^5.8.3"
63
- }
2
+ "name": "@useautumn/gateway",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "engines": {
6
+ "node": ">=22"
7
+ },
8
+ "description": "Autumn usage tracking adapters for AI SDKs and gateways (Vercel AI SDK, OpenRouter)",
9
+ "repository": "github:useautumn/autumn",
10
+ "homepage": "https://useautumn.com",
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "autumn",
14
+ "billing",
15
+ "usage-based-billing",
16
+ "ai-sdk",
17
+ "openrouter",
18
+ "llm",
19
+ "token-tracking"
20
+ ],
21
+ "sideEffects": false,
22
+ "files": [
23
+ "dist",
24
+ "README.md",
25
+ "LICENSE.md"
26
+ ],
27
+ "exports": {
28
+ "./ai-sdk": {
29
+ "types": "./dist/ai-sdk/index.d.ts",
30
+ "import": "./dist/ai-sdk/index.js"
31
+ },
32
+ "./openrouter": {
33
+ "types": "./dist/openrouter/index.d.ts",
34
+ "import": "./dist/openrouter/index.js"
35
+ }
36
+ },
37
+ "scripts": {
38
+ "ts": "bunx tsgo --noEmit --skipLibCheck",
39
+ "test": "bun test tests/unit",
40
+ "build": "rm -rf dist && tsup"
41
+ },
42
+ "dependencies": {
43
+ "@ai-sdk/provider": "^4.0.7"
44
+ },
45
+ "peerDependencies": {
46
+ "@openrouter/sdk": "*",
47
+ "ai": "^7.0.58",
48
+ "autumn-js": "*"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "@openrouter/sdk": {
52
+ "optional": true
53
+ },
54
+ "ai": {
55
+ "optional": true
56
+ }
57
+ },
58
+ "gitHead": "eaad8f3f364512886a433c292ea59a70b9532941"
64
59
  }
@@ -1,214 +0,0 @@
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/ai-sdk/index.ts
21
- var ai_sdk_exports = {};
22
- __export(ai_sdk_exports, {
23
- withAutumn: () => withAutumn
24
- });
25
- module.exports = __toCommonJS(ai_sdk_exports);
26
- var import_ai = require("ai");
27
-
28
- // src/shared/track.ts
29
- var WIRE_KEYS = {
30
- customerId: "customer_id",
31
- entityId: "entity_id",
32
- featureId: "feature_id",
33
- modelId: "model_id",
34
- inputTokens: "input_tokens",
35
- outputTokens: "output_tokens",
36
- cacheReadTokens: "cache_read_tokens",
37
- cacheWriteTokens: "cache_write_tokens",
38
- audioInputTokens: "audio_input_tokens",
39
- audioOutputTokens: "audio_output_tokens",
40
- reasoningTokens: "reasoning_tokens"
41
- };
42
- var toWire = (params) => Object.fromEntries(
43
- Object.entries(params).filter(([, value]) => value !== void 0).map(([key, value]) => [WIRE_KEYS[key] ?? key, value])
44
- );
45
- var envClient = () => {
46
- const env = typeof process === "undefined" ? void 0 : process.env;
47
- const secretKey = env?.AUTUMN_API_KEY ?? env?.AUTUMN_SECRET_KEY;
48
- const baseUrl = env?.AUTUMN_BASE_URL ?? "https://api.useautumn.com";
49
- return {
50
- trackTokens: async (params) => {
51
- if (!secretKey) {
52
- throw new Error(
53
- "[Autumn] No autumn client was passed and AUTUMN_API_KEY is not set."
54
- );
55
- }
56
- const response = await fetch(`${baseUrl}/v1/balances.track_tokens`, {
57
- method: "POST",
58
- headers: {
59
- authorization: `Bearer ${secretKey}`,
60
- "content-type": "application/json"
61
- },
62
- body: JSON.stringify(toWire(params))
63
- });
64
- if (!response.ok) {
65
- throw new Error(
66
- `track_tokens failed (${response.status}): ${await response.text()}`
67
- );
68
- }
69
- return response.json();
70
- }
71
- };
72
- };
73
- var createTracker = ({
74
- autumn = envClient(),
75
- customerId,
76
- featureId,
77
- entityId,
78
- properties
79
- }) => (getEvent) => trackTokenUsage({
80
- autumn,
81
- getParams: () => {
82
- const event = getEvent();
83
- return {
84
- ...event.pools,
85
- customerId,
86
- modelId: event.modelId,
87
- featureId,
88
- entityId,
89
- properties: event.properties ?? properties
90
- };
91
- }
92
- });
93
- var trackTokenUsage = async ({
94
- autumn,
95
- getParams
96
- }) => {
97
- try {
98
- const balances = autumn.balances;
99
- const trackTokens = balances?.trackTokens?.bind(balances) ?? autumn.trackTokens?.bind(autumn);
100
- if (!trackTokens) {
101
- throw new Error(
102
- "Autumn client does not support trackTokens \u2014 upgrade @useautumn/sdk."
103
- );
104
- }
105
- await trackTokens(getParams());
106
- } catch (error) {
107
- console.error("[Autumn Tracking] Failed to track usage:", error);
108
- }
109
- };
110
-
111
- // src/shared/usage.ts
112
- var clamp = (value) => Math.max(0, value);
113
- var requiredCount = (value, label, modelName) => {
114
- if (value == null) {
115
- throw new Error(
116
- `[Autumn] ${label} token usage was not returned by the model provider (${modelName}). This provider may not support usage tracking.`
117
- );
118
- }
119
- return value;
120
- };
121
- var poolsFromParts = (parts, modelName) => {
122
- const cacheRead = parts.cacheRead ?? 0;
123
- const cacheWrite = parts.cacheWrite ?? 0;
124
- const reasoning = parts.reasoning ?? 0;
125
- const audioInput = parts.audioInput ?? 0;
126
- const textInput = parts.textInput ?? (parts.totalInput != null ? parts.totalInput - cacheRead - cacheWrite - audioInput : void 0);
127
- const textOutput = parts.textOutput ?? (parts.totalOutput != null ? parts.totalOutput - reasoning : void 0);
128
- return {
129
- inputTokens: clamp(requiredCount(textInput, "Input", modelName)),
130
- outputTokens: clamp(requiredCount(textOutput, "Output", modelName)),
131
- cacheReadTokens: clamp(cacheRead),
132
- cacheWriteTokens: clamp(cacheWrite),
133
- reasoningTokens: clamp(reasoning),
134
- ...parts.audioInput !== void 0 && {
135
- audioInputTokens: clamp(audioInput)
136
- }
137
- };
138
- };
139
-
140
- // src/ai-sdk/usage.ts
141
- var flatCount = (value) => typeof value === "number" ? value : value?.total ?? void 0;
142
- var isNested = (value) => value != null && typeof value === "object";
143
- var toParts = (usage) => {
144
- const input = usage.inputTokens;
145
- const output = usage.outputTokens;
146
- if (isNested(input)) {
147
- const out = isNested(output) ? output : void 0;
148
- return {
149
- cacheRead: input.cacheRead ?? 0,
150
- cacheWrite: input.cacheWrite ?? 0,
151
- reasoning: out?.reasoning ?? 0,
152
- textInput: input.noCache,
153
- totalInput: input.total,
154
- textOutput: out?.text,
155
- totalOutput: out?.total
156
- };
157
- }
158
- return {
159
- cacheRead: usage.inputTokenDetails?.cacheReadTokens ?? usage.cachedInputTokens ?? 0,
160
- cacheWrite: usage.inputTokenDetails?.cacheWriteTokens ?? 0,
161
- reasoning: usage.outputTokenDetails?.reasoningTokens ?? usage.reasoningTokens ?? 0,
162
- textInput: usage.inputTokenDetails?.noCacheTokens,
163
- totalInput: typeof input === "number" ? input : flatCount(usage.promptTokens),
164
- textOutput: usage.outputTokenDetails?.textTokens,
165
- totalOutput: typeof output === "number" ? output : flatCount(usage.completionTokens)
166
- };
167
- };
168
- var normalizeUsage = (usage, modelName) => poolsFromParts(toParts(usage), modelName);
169
-
170
- // src/ai-sdk/index.ts
171
- var withAutumn = ({
172
- model,
173
- providerId,
174
- ...tracking
175
- }) => {
176
- const modelName = `${providerId ?? model.provider}/${model.modelId}`;
177
- const track = createTracker(tracking);
178
- const trackUsage = (usage) => track(() => ({
179
- pools: normalizeUsage(usage, modelName),
180
- modelId: modelName
181
- }));
182
- const middleware = {
183
- specificationVersion: "v3",
184
- wrapGenerate: async ({ doGenerate }) => {
185
- const result = await doGenerate();
186
- await trackUsage(result.usage);
187
- return result;
188
- },
189
- wrapStream: async ({ doStream }) => {
190
- const { stream, ...rest } = await doStream();
191
- let trackingPromise;
192
- const transformStream = new TransformStream({
193
- transform(chunk, controller) {
194
- if (chunk.type === "finish" && chunk.usage) {
195
- trackingPromise = trackUsage(chunk.usage);
196
- }
197
- controller.enqueue(chunk);
198
- },
199
- async flush() {
200
- await trackingPromise;
201
- }
202
- });
203
- return {
204
- stream: stream.pipeThrough(transformStream),
205
- ...rest
206
- };
207
- }
208
- };
209
- return (0, import_ai.wrapLanguageModel)({ model, middleware });
210
- };
211
- // Annotate the CommonJS export names for ESM import in node:
212
- 0 && (module.exports = {
213
- withAutumn
214
- });
@@ -1,43 +0,0 @@
1
- import { LanguageModelV3 } from '@ai-sdk/provider';
2
- import { A as AutumnTrackingOptions } from '../track-C73uYSG_.cjs';
3
- export { a as AutumnClient, T as TokenPools } from '../track-C73uYSG_.cjs';
4
-
5
- type NestedTokens = {
6
- total?: number | null;
7
- noCache?: number | null;
8
- cacheRead?: number | null;
9
- cacheWrite?: number | null;
10
- text?: number | null;
11
- reasoning?: number | null;
12
- };
13
- type LegacyCount = number | {
14
- total?: number | null;
15
- } | null;
16
- /** Lenient view over AI SDK usage shapes: nested V3 counts, flat counts with token details, and legacy prompt/completion counts. */
17
- type UsageLike = {
18
- inputTokens?: number | NestedTokens | null;
19
- outputTokens?: number | NestedTokens | null;
20
- promptTokens?: LegacyCount;
21
- completionTokens?: LegacyCount;
22
- inputTokenDetails?: {
23
- noCacheTokens?: number | null;
24
- cacheReadTokens?: number | null;
25
- cacheWriteTokens?: number | null;
26
- } | null;
27
- outputTokenDetails?: {
28
- textTokens?: number | null;
29
- reasoningTokens?: number | null;
30
- } | null;
31
- cachedInputTokens?: number | null;
32
- reasoningTokens?: number | null;
33
- };
34
-
35
- type WithAutumnOptions = AutumnTrackingOptions & {
36
- /** The AI SDK language model to wrap. */
37
- model: LanguageModelV3;
38
- /** Override the provider prefix used in the model name (e.g. "openrouter", "custom"). Falls back to `model.provider`. */
39
- providerId?: string;
40
- };
41
- declare const withAutumn: ({ model, providerId, ...tracking }: WithAutumnOptions) => LanguageModelV3;
42
-
43
- export { AutumnTrackingOptions, type UsageLike, type WithAutumnOptions, withAutumn };
@@ -1,327 +0,0 @@
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
- });
@@ -1,69 +0,0 @@
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 };
@@ -1,47 +0,0 @@
1
- /** Mutually exclusive token pools — each pool is priced at its own rate server-side. */
2
- type TokenPools = {
3
- inputTokens: number;
4
- outputTokens: number;
5
- cacheReadTokens: number;
6
- cacheWriteTokens: number;
7
- reasoningTokens: number;
8
- audioInputTokens?: number;
9
- audioOutputTokens?: number;
10
- };
11
-
12
- type TrackTokensParams = TokenPools & {
13
- customerId: string;
14
- modelId: string;
15
- featureId?: string;
16
- entityId?: string;
17
- properties?: Record<string, unknown>;
18
- };
19
- /** Tracking options every adapter shares. */
20
- type AutumnTrackingOptions = {
21
- /**
22
- * Autumn SDK client instance. When omitted, a minimal fetch client is
23
- * created from AUTUMN_API_KEY (or AUTUMN_SECRET_KEY).
24
- */
25
- autumn?: AutumnClient;
26
- /** The Autumn customer ID to attribute usage to. */
27
- customerId: string;
28
- /** Target a specific AI credit system feature. Auto-detected if omitted. */
29
- featureId?: string;
30
- /** Entity ID for entity-scoped balance tracking. */
31
- entityId?: string;
32
- /** Additional properties to attach to each usage event. */
33
- properties?: Record<string, unknown>;
34
- };
35
- /**
36
- * Structural view of the Autumn SDK client. @useautumn/sdk exposes
37
- * trackTokens at the client root; balances.trackTokens is accepted for
38
- * clients that namespace it. `balances` stays `unknown` so SDK namespace
39
- * classes without trackTokens still satisfy the type. Older clients may
40
- * ship neither shape.
41
- */
42
- type AutumnClient = {
43
- trackTokens?: (params: TrackTokensParams) => Promise<unknown>;
44
- balances?: unknown;
45
- };
46
-
47
- export type { AutumnTrackingOptions as A, TokenPools as T, AutumnClient as a };