@ai-sdk/gateway 4.0.0-beta.110 → 4.0.0-beta.112

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.
@@ -633,6 +633,93 @@ for await (const part of result.stream) {
633
633
  }
634
634
  ```
635
635
 
636
+ #### Exa Search
637
+
638
+ The Exa Search tool enables models to search the web using [Exa's Search API](https://exa.ai/docs/reference/search-api-guide-for-coding-agents). This tool is executed by the AI Gateway and returns token-efficient web excerpts for agent workflows.
639
+
640
+ ```ts
641
+ import { gateway, generateText } from 'ai';
642
+
643
+ const result = await generateText({
644
+ model: 'openai/gpt-5.4-nano',
645
+ prompt:
646
+ 'Find the latest AI regulation updates and summarize the key changes.',
647
+ tools: {
648
+ exa_search: gateway.tools.exaSearch(),
649
+ },
650
+ });
651
+
652
+ console.log(result.text);
653
+ console.log('Tool calls:', JSON.stringify(result.toolCalls, null, 2));
654
+ console.log('Tool results:', JSON.stringify(result.toolResults, null, 2));
655
+ ```
656
+
657
+ You can also configure the search with optional parameters:
658
+
659
+ ```ts
660
+ import { gateway, generateText } from 'ai';
661
+
662
+ const result = await generateText({
663
+ model: 'openai/gpt-5.4-nano',
664
+ prompt: 'Find recent AI regulation news from trusted sources.',
665
+ tools: {
666
+ exa_search: gateway.tools.exaSearch({
667
+ type: 'fast',
668
+ numResults: 5,
669
+ category: 'news',
670
+ includeDomains: ['reuters.com', 'bbc.com', 'nytimes.com'],
671
+ contents: {
672
+ highlights: true,
673
+ maxAgeHours: 24,
674
+ },
675
+ }),
676
+ },
677
+ });
678
+
679
+ console.log(result.text);
680
+ console.log('Tool calls:', JSON.stringify(result.toolCalls, null, 2));
681
+ console.log('Tool results:', JSON.stringify(result.toolResults, null, 2));
682
+ ```
683
+
684
+ The Exa Search tool supports the following optional configuration options:
685
+
686
+ - **type** _'auto' | 'fast' | 'instant'_
687
+
688
+ Search mode. `'auto'` is the default balance of speed and quality.
689
+
690
+ - **numResults** _number_
691
+
692
+ Maximum number of results to return (1-100, default: 10).
693
+
694
+ - **category** _'company' | 'people' | 'research paper' | 'news' | 'personal site' | 'financial report'_
695
+
696
+ Focus results on a specific content type.
697
+
698
+ - **includeDomains** / **excludeDomains** _string[]_
699
+
700
+ Restrict or exclude search results by domain.
701
+
702
+ - **startPublishedDate** / **endPublishedDate** _string_
703
+
704
+ Filter results by ISO 8601 publication date.
705
+
706
+ - **contents** _object_
707
+
708
+ Control result extraction and freshness:
709
+ - `text` - Return full page text, optionally capped with `maxCharacters`
710
+ - `highlights` - Return token-efficient excerpts
711
+ - `maxAgeHours` - Control cached content freshness
712
+ - `livecrawlTimeout` - Livecrawl timeout in milliseconds
713
+ - `subpages` / `subpageTarget` - Crawl related subpages
714
+ - `extras.links` / `extras.imageLinks` - Extract links from pages
715
+
716
+ The tool works with both `generateText` and `streamText`.
717
+
718
+ This initial Gateway integration supports Exa's plain Search modes and
719
+ token-efficient content controls. Deep synthesis modes and generated summaries
720
+ are intentionally not exposed yet because they have separate pricing from
721
+ standard Search.
722
+
636
723
  #### Parallel Search
637
724
 
638
725
  The Parallel Search tool enables models to search the web using [Parallel AI's Search API](https://docs.parallel.ai/api-reference/search-beta/search). This tool is optimized for LLM consumption, returning relevant excerpts from web pages that can replace multiple keyword searches with a single call.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ai-sdk/gateway",
3
3
  "private": false,
4
- "version": "4.0.0-beta.110",
4
+ "version": "4.0.0-beta.112",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "sideEffects": false,
@@ -31,8 +31,8 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@vercel/oidc": "3.2.0",
34
- "@ai-sdk/provider": "4.0.0-beta.19",
35
- "@ai-sdk/provider-utils": "5.0.0-beta.49"
34
+ "@ai-sdk/provider-utils": "5.0.0-beta.50",
35
+ "@ai-sdk/provider": "4.0.0-beta.20"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "22.19.19",
@@ -165,6 +165,7 @@ export type GatewayModelId =
165
165
  | 'perplexity/sonar'
166
166
  | 'perplexity/sonar-pro'
167
167
  | 'perplexity/sonar-reasoning-pro'
168
+ | 'sakana/fugu-ultra'
168
169
  | 'stepfun/step-3.5-flash'
169
170
  | 'stepfun/step-3.7-flash'
170
171
  | 'xai/grok-4.1-fast-non-reasoning'
@@ -1,3 +1,4 @@
1
+ import { exaSearch } from './tool/exa-search';
1
2
  import { parallelSearch } from './tool/parallel-search';
2
3
  import { perplexitySearch } from './tool/perplexity-search';
3
4
 
@@ -5,6 +6,15 @@ import { perplexitySearch } from './tool/perplexity-search';
5
6
  * Gateway-specific provider-defined tools.
6
7
  */
7
8
  export const gatewayTools = {
9
+ /**
10
+ * Search the web using Exa for current information and token-efficient
11
+ * excerpts optimized for agent workflows.
12
+ *
13
+ * Supports search type, category, domain, date, location, and content
14
+ * extraction controls.
15
+ */
16
+ exaSearch,
17
+
8
18
  /**
9
19
  * Search the web using Parallel AI's Search API for LLM-optimized excerpts.
10
20
  *
@@ -7,8 +7,6 @@ export type GatewayVideoModelId =
7
7
  | 'alibaba/wan-v2.6-t2v'
8
8
  | 'bytedance/seedance-2.0'
9
9
  | 'bytedance/seedance-2.0-fast'
10
- | 'bytedance/seedance-v1.0-lite-i2v'
11
- | 'bytedance/seedance-v1.0-lite-t2v'
12
10
  | 'bytedance/seedance-v1.0-pro'
13
11
  | 'bytedance/seedance-v1.0-pro-fast'
14
12
  | 'bytedance/seedance-v1.5-pro'
@@ -46,6 +46,7 @@ export class GatewayVideoModel implements Experimental_VideoModelV4 {
46
46
  duration,
47
47
  fps,
48
48
  seed,
49
+ generateAudio,
49
50
  image,
50
51
  providerOptions,
51
52
  headers,
@@ -81,6 +82,7 @@ export class GatewayVideoModel implements Experimental_VideoModelV4 {
81
82
  ...(duration && { duration }),
82
83
  ...(fps && { fps }),
83
84
  ...(seed && { seed }),
85
+ ...(generateAudio !== undefined && { generateAudio }),
84
86
  ...(providerOptions && { providerOptions }),
85
87
  ...(image && { image: maybeEncodeVideoFile(image) }),
86
88
  },
@@ -0,0 +1,352 @@
1
+ import {
2
+ createProviderExecutedToolFactory,
3
+ lazySchema,
4
+ zodSchema,
5
+ } from '@ai-sdk/provider-utils';
6
+ import { z } from 'zod';
7
+
8
+ export type ExaSearchType = 'auto' | 'fast' | 'instant';
9
+
10
+ export type ExaSearchCategory =
11
+ | 'company'
12
+ | 'people'
13
+ | 'research paper'
14
+ | 'news'
15
+ | 'personal site'
16
+ | 'financial report';
17
+
18
+ export type ExaTextSection =
19
+ | 'header'
20
+ | 'navigation'
21
+ | 'banner'
22
+ | 'body'
23
+ | 'sidebar'
24
+ | 'footer'
25
+ | 'metadata';
26
+
27
+ export interface ExaSearchTextConfig {
28
+ maxCharacters?: number;
29
+ includeHtmlTags?: boolean;
30
+ verbosity?: 'compact' | 'standard' | 'full';
31
+ includeSections?: ExaTextSection[];
32
+ excludeSections?: ExaTextSection[];
33
+ }
34
+
35
+ export interface ExaSearchHighlightsConfig {
36
+ query?: string;
37
+ maxCharacters?: number;
38
+ }
39
+
40
+ export interface ExaSearchExtrasConfig {
41
+ links?: number;
42
+ imageLinks?: number;
43
+ }
44
+
45
+ export interface ExaSearchContentsConfig {
46
+ text?: boolean | ExaSearchTextConfig;
47
+ highlights?: boolean | ExaSearchHighlightsConfig;
48
+ maxAgeHours?: number;
49
+ livecrawlTimeout?: number;
50
+ subpages?: number;
51
+ subpageTarget?: string | string[];
52
+ extras?: ExaSearchExtrasConfig;
53
+ }
54
+
55
+ export interface ExaSearchConfig {
56
+ /**
57
+ * Default search method. Exa defaults to auto when omitted.
58
+ */
59
+ type?: ExaSearchType;
60
+
61
+ /**
62
+ * Default maximum number of results to return (1-100, default: 10).
63
+ */
64
+ numResults?: number;
65
+
66
+ /**
67
+ * Default category filter for result types.
68
+ */
69
+ category?: ExaSearchCategory;
70
+
71
+ /**
72
+ * Default two-letter ISO country code for location-aware search.
73
+ */
74
+ userLocation?: string;
75
+
76
+ /**
77
+ * Default domains to include or exclude.
78
+ */
79
+ includeDomains?: string[];
80
+ excludeDomains?: string[];
81
+
82
+ /**
83
+ * Default published date filters in ISO 8601 format.
84
+ */
85
+ startPublishedDate?: string;
86
+ endPublishedDate?: string;
87
+
88
+ /**
89
+ * Default content extraction controls.
90
+ */
91
+ contents?: ExaSearchContentsConfig;
92
+ }
93
+
94
+ export interface ExaSearchResult {
95
+ title: string;
96
+ url: string;
97
+ id: string;
98
+ publishedDate?: string | null;
99
+ author?: string | null;
100
+ image?: string | null;
101
+ favicon?: string | null;
102
+ text?: string;
103
+ highlights?: string[];
104
+ highlightScores?: number[];
105
+ summary?: string;
106
+ subpages?: ExaSearchResult[];
107
+ extras?: {
108
+ links?: string[];
109
+ imageLinks?: string[];
110
+ };
111
+ }
112
+
113
+ export interface ExaSearchResponse {
114
+ requestId: string;
115
+ searchType?: string;
116
+ resolvedSearchType?: string;
117
+ results: ExaSearchResult[];
118
+ costDollars?: {
119
+ total?: number;
120
+ search?: Record<string, number>;
121
+ };
122
+ }
123
+
124
+ export interface ExaSearchError {
125
+ error:
126
+ | 'api_error'
127
+ | 'rate_limit'
128
+ | 'timeout'
129
+ | 'invalid_input'
130
+ | 'configuration_error'
131
+ | 'execution_error'
132
+ | 'unknown';
133
+ statusCode?: number;
134
+ message: string;
135
+ }
136
+
137
+ export interface ExaSearchInput {
138
+ query: string;
139
+ type?: ExaSearchType;
140
+ num_results?: number;
141
+ category?: ExaSearchCategory;
142
+ user_location?: string;
143
+ include_domains?: string[];
144
+ exclude_domains?: string[];
145
+ start_published_date?: string;
146
+ end_published_date?: string;
147
+ contents?: {
148
+ text?:
149
+ | boolean
150
+ | {
151
+ max_characters?: number;
152
+ include_html_tags?: boolean;
153
+ verbosity?: 'compact' | 'standard' | 'full';
154
+ include_sections?: ExaTextSection[];
155
+ exclude_sections?: ExaTextSection[];
156
+ };
157
+ highlights?:
158
+ | boolean
159
+ | {
160
+ query?: string;
161
+ max_characters?: number;
162
+ };
163
+ max_age_hours?: number;
164
+ livecrawl_timeout?: number;
165
+ subpages?: number;
166
+ subpage_target?: string | string[];
167
+ extras?: {
168
+ links?: number;
169
+ image_links?: number;
170
+ };
171
+ };
172
+ }
173
+
174
+ export type ExaSearchOutput = ExaSearchResponse | ExaSearchError;
175
+
176
+ const exaSearchInputSchema = lazySchema(() =>
177
+ zodSchema(
178
+ z.object({
179
+ query: z
180
+ .string()
181
+ .describe('Natural-language web search query. This is required.'),
182
+ type: z
183
+ .enum(['auto', 'fast', 'instant'])
184
+ .optional()
185
+ .describe(
186
+ 'Search method. Use auto for the default balance of speed and quality.',
187
+ ),
188
+ num_results: z
189
+ .number()
190
+ .optional()
191
+ .describe('Maximum number of results to return (1-100, default: 10).'),
192
+ category: z
193
+ .enum([
194
+ 'company',
195
+ 'people',
196
+ 'research paper',
197
+ 'news',
198
+ 'personal site',
199
+ 'financial report',
200
+ ])
201
+ .optional()
202
+ .describe('Optional content category to focus results.'),
203
+ user_location: z
204
+ .string()
205
+ .optional()
206
+ .describe("Two-letter ISO country code such as 'US'."),
207
+ include_domains: z
208
+ .array(z.string())
209
+ .optional()
210
+ .describe('Only return results from these domains.'),
211
+ exclude_domains: z
212
+ .array(z.string())
213
+ .optional()
214
+ .describe('Exclude results from these domains.'),
215
+ start_published_date: z
216
+ .string()
217
+ .optional()
218
+ .describe('Only return links published after this ISO 8601 date.'),
219
+ end_published_date: z
220
+ .string()
221
+ .optional()
222
+ .describe('Only return links published before this ISO 8601 date.'),
223
+ contents: z
224
+ .object({
225
+ text: z
226
+ .union([
227
+ z.boolean(),
228
+ z.object({
229
+ max_characters: z.number().optional(),
230
+ include_html_tags: z.boolean().optional(),
231
+ verbosity: z.enum(['compact', 'standard', 'full']).optional(),
232
+ include_sections: z
233
+ .array(
234
+ z.enum([
235
+ 'header',
236
+ 'navigation',
237
+ 'banner',
238
+ 'body',
239
+ 'sidebar',
240
+ 'footer',
241
+ 'metadata',
242
+ ]),
243
+ )
244
+ .optional(),
245
+ exclude_sections: z
246
+ .array(
247
+ z.enum([
248
+ 'header',
249
+ 'navigation',
250
+ 'banner',
251
+ 'body',
252
+ 'sidebar',
253
+ 'footer',
254
+ 'metadata',
255
+ ]),
256
+ )
257
+ .optional(),
258
+ }),
259
+ ])
260
+ .optional(),
261
+ highlights: z
262
+ .union([
263
+ z.boolean(),
264
+ z.object({
265
+ query: z.string().optional(),
266
+ max_characters: z.number().optional(),
267
+ }),
268
+ ])
269
+ .optional(),
270
+ max_age_hours: z.number().optional(),
271
+ livecrawl_timeout: z.number().optional(),
272
+ subpages: z.number().optional(),
273
+ subpage_target: z.union([z.string(), z.array(z.string())]).optional(),
274
+ extras: z
275
+ .object({
276
+ links: z.number().optional(),
277
+ image_links: z.number().optional(),
278
+ })
279
+ .optional(),
280
+ })
281
+ .optional()
282
+ .describe('Controls extracted page content and freshness.'),
283
+ }),
284
+ ),
285
+ );
286
+
287
+ const exaSearchOutputSchema = lazySchema(() =>
288
+ zodSchema(
289
+ z.union([
290
+ z.object({
291
+ requestId: z.string(),
292
+ searchType: z.string().optional(),
293
+ resolvedSearchType: z.string().optional(),
294
+ results: z.array(
295
+ z.object({
296
+ title: z.string(),
297
+ url: z.string(),
298
+ id: z.string(),
299
+ publishedDate: z.string().nullable().optional(),
300
+ author: z.string().nullable().optional(),
301
+ image: z.string().nullable().optional(),
302
+ favicon: z.string().nullable().optional(),
303
+ text: z.string().optional(),
304
+ highlights: z.array(z.string()).optional(),
305
+ highlightScores: z.array(z.number()).optional(),
306
+ summary: z.string().optional(),
307
+ subpages: z.array(z.any()).optional(),
308
+ extras: z
309
+ .object({
310
+ links: z.array(z.string()).optional(),
311
+ imageLinks: z.array(z.string()).optional(),
312
+ })
313
+ .optional(),
314
+ }),
315
+ ),
316
+ costDollars: z
317
+ .object({
318
+ total: z.number().optional(),
319
+ search: z.record(z.number()).optional(),
320
+ })
321
+ .optional(),
322
+ }),
323
+ z.object({
324
+ error: z.enum([
325
+ 'api_error',
326
+ 'rate_limit',
327
+ 'timeout',
328
+ 'invalid_input',
329
+ 'configuration_error',
330
+ 'execution_error',
331
+ 'unknown',
332
+ ]),
333
+ statusCode: z.number().optional(),
334
+ message: z.string(),
335
+ }),
336
+ ]),
337
+ ),
338
+ );
339
+
340
+ export const exaSearchToolFactory = createProviderExecutedToolFactory<
341
+ ExaSearchInput,
342
+ ExaSearchOutput,
343
+ ExaSearchConfig
344
+ >({
345
+ id: 'gateway.exa_search',
346
+ inputSchema: exaSearchInputSchema,
347
+ outputSchema: exaSearchOutputSchema,
348
+ });
349
+
350
+ export const exaSearch = (
351
+ config: ExaSearchConfig = {},
352
+ ): ReturnType<typeof exaSearchToolFactory> => exaSearchToolFactory(config);