@ai-sdk/gateway 3.0.80 → 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.
@@ -538,7 +538,7 @@ for await (const part of result.fullStream) {
538
538
  Track usage per end-user and categorize requests with tags:
539
539
 
540
540
  ```ts
541
- import type { GatewayLanguageModelOptions } from '@ai-sdk/gateway';
541
+ import type { GatewayProviderOptions } from '@ai-sdk/gateway';
542
542
  import { generateText } from 'ai';
543
543
 
544
544
  const { text } = await generateText({
@@ -548,7 +548,7 @@ const { text } = await generateText({
548
548
  gateway: {
549
549
  user: 'user-abc-123', // Track usage for this specific end-user
550
550
  tags: ['document-summary', 'premium-feature'], // Categorize for reporting
551
- } satisfies GatewayLanguageModelOptions,
551
+ } satisfies GatewayProviderOptions,
552
552
  },
553
553
  });
554
554
  ```
@@ -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.
@@ -568,7 +639,7 @@ The AI Gateway provider accepts provider options that control routing behavior a
568
639
  You can use the `gateway` key in `providerOptions` to control how AI Gateway routes requests:
569
640
 
570
641
  ```ts
571
- import type { GatewayLanguageModelOptions } from '@ai-sdk/gateway';
642
+ import type { GatewayProviderOptions } from '@ai-sdk/gateway';
572
643
  import { generateText } from 'ai';
573
644
 
574
645
  const { text } = await generateText({
@@ -578,7 +649,7 @@ const { text } = await generateText({
578
649
  gateway: {
579
650
  order: ['vertex', 'anthropic'], // Try Vertex AI first, then Anthropic
580
651
  only: ['vertex', 'anthropic'], // Only use these providers
581
- } satisfies GatewayLanguageModelOptions,
652
+ } satisfies GatewayProviderOptions,
582
653
  },
583
654
  });
584
655
  ```
@@ -642,7 +713,7 @@ The following gateway provider options are available:
642
713
  You can combine these options to have fine-grained control over routing and tracking:
643
714
 
644
715
  ```ts
645
- import type { GatewayLanguageModelOptions } from '@ai-sdk/gateway';
716
+ import type { GatewayProviderOptions } from '@ai-sdk/gateway';
646
717
  import { generateText } from 'ai';
647
718
 
648
719
  const { text } = await generateText({
@@ -652,7 +723,7 @@ const { text } = await generateText({
652
723
  gateway: {
653
724
  order: ['vertex'], // Prefer Vertex AI
654
725
  only: ['anthropic', 'vertex'], // Only allow these providers
655
- } satisfies GatewayLanguageModelOptions,
726
+ } satisfies GatewayProviderOptions,
656
727
  },
657
728
  });
658
729
  ```
@@ -662,7 +733,7 @@ const { text } = await generateText({
662
733
  The `models` option enables automatic fallback to alternative models when the primary model fails:
663
734
 
664
735
  ```ts
665
- import type { GatewayLanguageModelOptions } from '@ai-sdk/gateway';
736
+ import type { GatewayProviderOptions } from '@ai-sdk/gateway';
666
737
  import { generateText } from 'ai';
667
738
 
668
739
  const { text } = await generateText({
@@ -671,7 +742,7 @@ const { text } = await generateText({
671
742
  providerOptions: {
672
743
  gateway: {
673
744
  models: ['openai/gpt-5-nano', 'gemini-2.0-flash'], // Fallback models
674
- } satisfies GatewayLanguageModelOptions,
745
+ } satisfies GatewayProviderOptions,
675
746
  },
676
747
  });
677
748
 
@@ -689,7 +760,7 @@ that have zero data retention policies. When `zeroDataRetention` is `false` or n
689
760
  specified, there is no enforcement of restricting routing.
690
761
 
691
762
  ```ts
692
- import type { GatewayLanguageModelOptions } from '@ai-sdk/gateway';
763
+ import type { GatewayProviderOptions } from '@ai-sdk/gateway';
693
764
  import { generateText } from 'ai';
694
765
 
695
766
  const { text } = await generateText({
@@ -698,7 +769,7 @@ const { text } = await generateText({
698
769
  providerOptions: {
699
770
  gateway: {
700
771
  zeroDataRetention: true,
701
- } satisfies GatewayLanguageModelOptions,
772
+ } satisfies GatewayProviderOptions,
702
773
  },
703
774
  });
704
775
  ```
@@ -709,7 +780,7 @@ When using provider-specific options through AI Gateway, use the actual provider
709
780
 
710
781
  ```ts
711
782
  import type { AnthropicLanguageModelOptions } from '@ai-sdk/anthropic';
712
- import type { GatewayLanguageModelOptions } from '@ai-sdk/gateway';
783
+ import type { GatewayProviderOptions } from '@ai-sdk/gateway';
713
784
  import { generateText } from 'ai';
714
785
 
715
786
  const { text } = await generateText({
@@ -718,7 +789,7 @@ const { text } = await generateText({
718
789
  providerOptions: {
719
790
  gateway: {
720
791
  order: ['vertex', 'anthropic'],
721
- } satisfies GatewayLanguageModelOptions,
792
+ } satisfies GatewayProviderOptions,
722
793
  anthropic: {
723
794
  thinking: { type: 'enabled', budgetTokens: 12000 },
724
795
  } satisfies AnthropicLanguageModelOptions,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ai-sdk/gateway",
3
3
  "private": false,
4
- "version": "3.0.80",
4
+ "version": "3.0.82",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
7
7
  "main": "./dist/index.js",
@@ -2,7 +2,7 @@ import { InferSchema, lazySchema, zodSchema } from '@ai-sdk/provider-utils';
2
2
  import { z } from 'zod/v4';
3
3
 
4
4
  // https://vercel.com/docs/ai-gateway/provider-options
5
- const gatewayLanguageModelOptions = lazySchema(() =>
5
+ const gatewayProviderOptions = lazySchema(() =>
6
6
  zodSchema(
7
7
  z.object({
8
8
  /**
@@ -75,6 +75,4 @@ const gatewayLanguageModelOptions = lazySchema(() =>
75
75
  ),
76
76
  );
77
77
 
78
- export type GatewayLanguageModelOptions = InferSchema<
79
- typeof gatewayLanguageModelOptions
80
- >;
78
+ export type GatewayProviderOptions = InferSchema<typeof gatewayProviderOptions>;
@@ -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,
@@ -16,9 +21,9 @@ export type {
16
21
  GatewayProviderSettings,
17
22
  } from './gateway-provider';
18
23
  export type {
19
- GatewayLanguageModelOptions,
20
- /** @deprecated Use `GatewayLanguageModelOptions` instead. */
21
- GatewayLanguageModelOptions as GatewayProviderOptions,
24
+ GatewayProviderOptions,
25
+ /** @deprecated Use `GatewayProviderOptions` instead. */
26
+ GatewayProviderOptions as GatewayLanguageModelOptions,
22
27
  } from './gateway-provider-options';
23
28
  export {
24
29
  GatewayError,