@ai-sdk/gateway 3.0.81 → 3.0.82
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/CHANGELOG.md +6 -0
- package/dist/index.d.mts +62 -1
- package/dist/index.d.ts +62 -1
- package/dist/index.js +246 -131
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +298 -176
- package/dist/index.mjs.map +1 -1
- package/docs/00-ai-gateway.mdx +71 -0
- package/package.json +1 -1
- package/src/gateway-provider.ts +29 -0
- package/src/gateway-spend-report.ts +191 -0
- package/src/index.ts +5 -0
package/docs/00-ai-gateway.mdx
CHANGED
|
@@ -559,6 +559,77 @@ This allows you to:
|
|
|
559
559
|
- Filter and analyze spending by feature or use case using tags
|
|
560
560
|
- Track which users or features are driving the most AI usage
|
|
561
561
|
|
|
562
|
+
#### Querying Spend Reports
|
|
563
|
+
|
|
564
|
+
Use the `getSpendReport()` method to query usage data programmatically. The reporting API is only available for Vercel Pro and Enterprise plans. For pricing, see the [Custom Reporting docs](https://vercel.com/docs/ai-gateway/capabilities/custom-reporting).
|
|
565
|
+
|
|
566
|
+
```ts
|
|
567
|
+
import { gateway } from 'ai';
|
|
568
|
+
|
|
569
|
+
const report = await gateway.getSpendReport({
|
|
570
|
+
startDate: '2026-03-01',
|
|
571
|
+
endDate: '2026-03-25',
|
|
572
|
+
groupBy: 'model',
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
for (const row of report.results) {
|
|
576
|
+
console.log(`${row.model}: $${row.totalCost.toFixed(4)}`);
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
The `getSpendReport()` method accepts the following parameters:
|
|
581
|
+
|
|
582
|
+
- **startDate** _string_ - Start date in `YYYY-MM-DD` format (inclusive, required)
|
|
583
|
+
- **endDate** _string_ - End date in `YYYY-MM-DD` format (inclusive, required)
|
|
584
|
+
- **groupBy** _string_ - Aggregation dimension: `'day'` (default), `'user'`, `'model'`, `'tag'`, `'provider'`, or `'credential_type'`
|
|
585
|
+
- **datePart** _string_ - Time granularity when `groupBy` is `'day'`: `'day'` or `'hour'`
|
|
586
|
+
- **userId** _string_ - Filter to a specific user
|
|
587
|
+
- **model** _string_ - Filter to a specific model (e.g. `'anthropic/claude-sonnet-4.5'`)
|
|
588
|
+
- **provider** _string_ - Filter to a specific provider (e.g. `'anthropic'`)
|
|
589
|
+
- **credentialType** _string_ - Filter by `'byok'` or `'system'` credentials
|
|
590
|
+
- **tags** _string[]_ - Filter to requests matching these tags
|
|
591
|
+
|
|
592
|
+
Each row in `results` contains a grouping field (matching your `groupBy` choice) and metrics:
|
|
593
|
+
|
|
594
|
+
- **totalCost** _number_ - Total cost in USD
|
|
595
|
+
- **marketCost** _number_ - Market cost in USD
|
|
596
|
+
- **inputTokens** _number_ - Number of input tokens
|
|
597
|
+
- **outputTokens** _number_ - Number of output tokens
|
|
598
|
+
- **cachedInputTokens** _number_ - Number of cached input tokens
|
|
599
|
+
- **cacheCreationInputTokens** _number_ - Number of cache creation input tokens
|
|
600
|
+
- **reasoningTokens** _number_ - Number of reasoning tokens
|
|
601
|
+
- **requestCount** _number_ - Number of requests
|
|
602
|
+
|
|
603
|
+
You can combine tracking and querying to analyze spend by tags you defined:
|
|
604
|
+
|
|
605
|
+
```ts
|
|
606
|
+
import type { GatewayProviderOptions } from '@ai-sdk/gateway';
|
|
607
|
+
import { gateway, streamText } from 'ai';
|
|
608
|
+
|
|
609
|
+
// 1. Make requests with tags
|
|
610
|
+
const result = streamText({
|
|
611
|
+
model: gateway('anthropic/claude-haiku-4.5'),
|
|
612
|
+
prompt: 'Summarize this quarter's results',
|
|
613
|
+
providerOptions: {
|
|
614
|
+
gateway: {
|
|
615
|
+
tags: ['team:finance', 'feature:summaries'],
|
|
616
|
+
} satisfies GatewayProviderOptions,
|
|
617
|
+
},
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
// 2. Later, query spend filtered by those tags
|
|
621
|
+
const report = await gateway.getSpendReport({
|
|
622
|
+
startDate: '2026-03-01',
|
|
623
|
+
endDate: '2026-03-31',
|
|
624
|
+
groupBy: 'tag',
|
|
625
|
+
tags: ['team:finance'],
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
for (const row of report.results) {
|
|
629
|
+
console.log(`${row.tag}: $${row.totalCost.toFixed(4)} (${row.requestCount} requests)`);
|
|
630
|
+
}
|
|
631
|
+
```
|
|
632
|
+
|
|
562
633
|
## Provider Options
|
|
563
634
|
|
|
564
635
|
The AI Gateway provider accepts provider options that control routing behavior and provider-specific configurations.
|
package/package.json
CHANGED
package/src/gateway-provider.ts
CHANGED
|
@@ -13,6 +13,11 @@ import {
|
|
|
13
13
|
type GatewayFetchMetadataResponse,
|
|
14
14
|
type GatewayCreditsResponse,
|
|
15
15
|
} from './gateway-fetch-metadata';
|
|
16
|
+
import {
|
|
17
|
+
GatewaySpendReport,
|
|
18
|
+
type GatewaySpendReportParams,
|
|
19
|
+
type GatewaySpendReportResponse,
|
|
20
|
+
} from './gateway-spend-report';
|
|
16
21
|
import { GatewayLanguageModel } from './gateway-language-model';
|
|
17
22
|
import { GatewayEmbeddingModel } from './gateway-embedding-model';
|
|
18
23
|
import { GatewayImageModel } from './gateway-image-model';
|
|
@@ -56,6 +61,14 @@ export interface GatewayProvider extends ProviderV3 {
|
|
|
56
61
|
*/
|
|
57
62
|
getCredits(): Promise<GatewayCreditsResponse>;
|
|
58
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Returns a spend report with cost, token, and request count data,
|
|
66
|
+
* aggregated by the specified dimension.
|
|
67
|
+
*/
|
|
68
|
+
getSpendReport(
|
|
69
|
+
params: GatewaySpendReportParams,
|
|
70
|
+
): Promise<GatewaySpendReportResponse>;
|
|
71
|
+
|
|
59
72
|
/**
|
|
60
73
|
* Creates a model for generating text embeddings.
|
|
61
74
|
*/
|
|
@@ -253,6 +266,21 @@ export function createGatewayProvider(
|
|
|
253
266
|
});
|
|
254
267
|
};
|
|
255
268
|
|
|
269
|
+
const getSpendReport = async (params: GatewaySpendReportParams) => {
|
|
270
|
+
return new GatewaySpendReport({
|
|
271
|
+
baseURL,
|
|
272
|
+
headers: getHeaders,
|
|
273
|
+
fetch: options.fetch,
|
|
274
|
+
})
|
|
275
|
+
.getSpendReport(params)
|
|
276
|
+
.catch(async (error: unknown) => {
|
|
277
|
+
throw await asGatewayError(
|
|
278
|
+
error,
|
|
279
|
+
await parseAuthMethod(await getHeaders()),
|
|
280
|
+
);
|
|
281
|
+
});
|
|
282
|
+
};
|
|
283
|
+
|
|
256
284
|
const provider = function (modelId: GatewayModelId) {
|
|
257
285
|
if (new.target) {
|
|
258
286
|
throw new Error(
|
|
@@ -266,6 +294,7 @@ export function createGatewayProvider(
|
|
|
266
294
|
provider.specificationVersion = 'v3' as const;
|
|
267
295
|
provider.getAvailableModels = getAvailableModels;
|
|
268
296
|
provider.getCredits = getCredits;
|
|
297
|
+
provider.getSpendReport = getSpendReport;
|
|
269
298
|
provider.imageModel = (modelId: GatewayImageModelId) => {
|
|
270
299
|
return new GatewayImageModel(modelId, {
|
|
271
300
|
provider: 'gateway',
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createJsonErrorResponseHandler,
|
|
3
|
+
createJsonResponseHandler,
|
|
4
|
+
getFromApi,
|
|
5
|
+
lazySchema,
|
|
6
|
+
resolve,
|
|
7
|
+
zodSchema,
|
|
8
|
+
} from '@ai-sdk/provider-utils';
|
|
9
|
+
import { z } from 'zod/v4';
|
|
10
|
+
import { asGatewayError } from './errors';
|
|
11
|
+
import type { GatewayConfig } from './gateway-config';
|
|
12
|
+
|
|
13
|
+
export interface GatewaySpendReportParams {
|
|
14
|
+
/** Start date in YYYY-MM-DD format (inclusive) */
|
|
15
|
+
startDate: string;
|
|
16
|
+
/** End date in YYYY-MM-DD format (inclusive) */
|
|
17
|
+
endDate: string;
|
|
18
|
+
/** Primary aggregation dimension. Defaults to 'day'. */
|
|
19
|
+
groupBy?: 'day' | 'user' | 'model' | 'tag' | 'provider' | 'credential_type';
|
|
20
|
+
/** Time granularity when groupBy is 'day'. */
|
|
21
|
+
datePart?: 'day' | 'hour';
|
|
22
|
+
/** Filter to a specific user's spend. */
|
|
23
|
+
userId?: string;
|
|
24
|
+
/** Filter to a specific model (e.g. 'anthropic/claude-sonnet-4.5'). */
|
|
25
|
+
model?: string;
|
|
26
|
+
/** Filter to a specific provider (e.g. 'anthropic'). */
|
|
27
|
+
provider?: string;
|
|
28
|
+
/** Filter to BYOK or system credentials. */
|
|
29
|
+
credentialType?: 'byok' | 'system';
|
|
30
|
+
/** Filter to requests with these tags. */
|
|
31
|
+
tags?: string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface GatewaySpendReportRow {
|
|
35
|
+
/** Date string (present when groupBy is 'day') */
|
|
36
|
+
day?: string;
|
|
37
|
+
/** Hour timestamp (present when groupBy is 'day' and datePart is 'hour') */
|
|
38
|
+
hour?: string;
|
|
39
|
+
/** User identifier (present when groupBy is 'user') */
|
|
40
|
+
user?: string;
|
|
41
|
+
/** Model identifier (present when groupBy is 'model') */
|
|
42
|
+
model?: string;
|
|
43
|
+
/** Tag value (present when groupBy is 'tag') */
|
|
44
|
+
tag?: string;
|
|
45
|
+
/** Provider name (present when groupBy is 'provider') */
|
|
46
|
+
provider?: string;
|
|
47
|
+
/** Credential type (present when groupBy is 'credential_type') */
|
|
48
|
+
credentialType?: 'byok' | 'system';
|
|
49
|
+
|
|
50
|
+
/** Total cost in USD */
|
|
51
|
+
totalCost: number;
|
|
52
|
+
/** Market cost in USD */
|
|
53
|
+
marketCost?: number;
|
|
54
|
+
/** Number of input tokens */
|
|
55
|
+
inputTokens?: number;
|
|
56
|
+
/** Number of output tokens */
|
|
57
|
+
outputTokens?: number;
|
|
58
|
+
/** Number of cached input tokens */
|
|
59
|
+
cachedInputTokens?: number;
|
|
60
|
+
/** Number of cache creation input tokens */
|
|
61
|
+
cacheCreationInputTokens?: number;
|
|
62
|
+
/** Number of reasoning tokens */
|
|
63
|
+
reasoningTokens?: number;
|
|
64
|
+
/** Number of requests */
|
|
65
|
+
requestCount?: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface GatewaySpendReportResponse {
|
|
69
|
+
results: GatewaySpendReportRow[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class GatewaySpendReport {
|
|
73
|
+
constructor(private readonly config: GatewayConfig) {}
|
|
74
|
+
|
|
75
|
+
async getSpendReport(
|
|
76
|
+
params: GatewaySpendReportParams,
|
|
77
|
+
): Promise<GatewaySpendReportResponse> {
|
|
78
|
+
try {
|
|
79
|
+
const baseUrl = new URL(this.config.baseURL);
|
|
80
|
+
|
|
81
|
+
const searchParams = new URLSearchParams();
|
|
82
|
+
searchParams.set('start_date', params.startDate);
|
|
83
|
+
searchParams.set('end_date', params.endDate);
|
|
84
|
+
|
|
85
|
+
if (params.groupBy) {
|
|
86
|
+
searchParams.set('group_by', params.groupBy);
|
|
87
|
+
}
|
|
88
|
+
if (params.datePart) {
|
|
89
|
+
searchParams.set('date_part', params.datePart);
|
|
90
|
+
}
|
|
91
|
+
if (params.userId) {
|
|
92
|
+
searchParams.set('user_id', params.userId);
|
|
93
|
+
}
|
|
94
|
+
if (params.model) {
|
|
95
|
+
searchParams.set('model', params.model);
|
|
96
|
+
}
|
|
97
|
+
if (params.provider) {
|
|
98
|
+
searchParams.set('provider', params.provider);
|
|
99
|
+
}
|
|
100
|
+
if (params.credentialType) {
|
|
101
|
+
searchParams.set('credential_type', params.credentialType);
|
|
102
|
+
}
|
|
103
|
+
if (params.tags && params.tags.length > 0) {
|
|
104
|
+
searchParams.set('tags', params.tags.join(','));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const { value } = await getFromApi({
|
|
108
|
+
url: `${baseUrl.origin}/v1/report?${searchParams.toString()}`,
|
|
109
|
+
headers: await resolve(this.config.headers()),
|
|
110
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
111
|
+
gatewaySpendReportResponseSchema,
|
|
112
|
+
),
|
|
113
|
+
failedResponseHandler: createJsonErrorResponseHandler({
|
|
114
|
+
errorSchema: z.any(),
|
|
115
|
+
errorToMessage: data => data,
|
|
116
|
+
}),
|
|
117
|
+
fetch: this.config.fetch,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return value;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
throw await asGatewayError(error);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const gatewaySpendReportResponseSchema = lazySchema(() =>
|
|
128
|
+
zodSchema(
|
|
129
|
+
z.object({
|
|
130
|
+
results: z.array(
|
|
131
|
+
z
|
|
132
|
+
.object({
|
|
133
|
+
day: z.string().optional(),
|
|
134
|
+
hour: z.string().optional(),
|
|
135
|
+
user: z.string().optional(),
|
|
136
|
+
model: z.string().optional(),
|
|
137
|
+
tag: z.string().optional(),
|
|
138
|
+
provider: z.string().optional(),
|
|
139
|
+
credential_type: z.enum(['byok', 'system']).optional(),
|
|
140
|
+
total_cost: z.number(),
|
|
141
|
+
market_cost: z.number().optional(),
|
|
142
|
+
input_tokens: z.number().optional(),
|
|
143
|
+
output_tokens: z.number().optional(),
|
|
144
|
+
cached_input_tokens: z.number().optional(),
|
|
145
|
+
cache_creation_input_tokens: z.number().optional(),
|
|
146
|
+
reasoning_tokens: z.number().optional(),
|
|
147
|
+
request_count: z.number().optional(),
|
|
148
|
+
})
|
|
149
|
+
.transform(
|
|
150
|
+
({
|
|
151
|
+
credential_type,
|
|
152
|
+
total_cost,
|
|
153
|
+
market_cost,
|
|
154
|
+
input_tokens,
|
|
155
|
+
output_tokens,
|
|
156
|
+
cached_input_tokens,
|
|
157
|
+
cache_creation_input_tokens,
|
|
158
|
+
reasoning_tokens,
|
|
159
|
+
request_count,
|
|
160
|
+
...rest
|
|
161
|
+
}) => ({
|
|
162
|
+
...rest,
|
|
163
|
+
...(credential_type !== undefined
|
|
164
|
+
? { credentialType: credential_type }
|
|
165
|
+
: {}),
|
|
166
|
+
totalCost: total_cost,
|
|
167
|
+
...(market_cost !== undefined ? { marketCost: market_cost } : {}),
|
|
168
|
+
...(input_tokens !== undefined
|
|
169
|
+
? { inputTokens: input_tokens }
|
|
170
|
+
: {}),
|
|
171
|
+
...(output_tokens !== undefined
|
|
172
|
+
? { outputTokens: output_tokens }
|
|
173
|
+
: {}),
|
|
174
|
+
...(cached_input_tokens !== undefined
|
|
175
|
+
? { cachedInputTokens: cached_input_tokens }
|
|
176
|
+
: {}),
|
|
177
|
+
...(cache_creation_input_tokens !== undefined
|
|
178
|
+
? { cacheCreationInputTokens: cache_creation_input_tokens }
|
|
179
|
+
: {}),
|
|
180
|
+
...(reasoning_tokens !== undefined
|
|
181
|
+
? { reasoningTokens: reasoning_tokens }
|
|
182
|
+
: {}),
|
|
183
|
+
...(request_count !== undefined
|
|
184
|
+
? { requestCount: request_count }
|
|
185
|
+
: {}),
|
|
186
|
+
}),
|
|
187
|
+
),
|
|
188
|
+
),
|
|
189
|
+
}),
|
|
190
|
+
),
|
|
191
|
+
);
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,11 @@ export type {
|
|
|
5
5
|
GatewayLanguageModelSpecification,
|
|
6
6
|
} from './gateway-model-entry';
|
|
7
7
|
export type { GatewayCreditsResponse } from './gateway-fetch-metadata';
|
|
8
|
+
export type {
|
|
9
|
+
GatewaySpendReportParams,
|
|
10
|
+
GatewaySpendReportRow,
|
|
11
|
+
GatewaySpendReportResponse,
|
|
12
|
+
} from './gateway-spend-report';
|
|
8
13
|
export type { GatewayLanguageModelEntry as GatewayModelEntry } from './gateway-model-entry';
|
|
9
14
|
export {
|
|
10
15
|
createGatewayProvider,
|