@ai-sdk/gateway 3.0.137 → 3.0.139

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.
@@ -566,6 +566,93 @@ for await (const part of result.fullStream) {
566
566
  }
567
567
  ```
568
568
 
569
+ #### Exa Search
570
+
571
+ 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.
572
+
573
+ ```ts
574
+ import { gateway, generateText } from 'ai';
575
+
576
+ const result = await generateText({
577
+ model: 'openai/gpt-5.4-nano',
578
+ prompt:
579
+ 'Find the latest AI regulation updates and summarize the key changes.',
580
+ tools: {
581
+ exa_search: gateway.tools.exaSearch(),
582
+ },
583
+ });
584
+
585
+ console.log(result.text);
586
+ console.log('Tool calls:', JSON.stringify(result.toolCalls, null, 2));
587
+ console.log('Tool results:', JSON.stringify(result.toolResults, null, 2));
588
+ ```
589
+
590
+ You can also configure the search with optional parameters:
591
+
592
+ ```ts
593
+ import { gateway, generateText } from 'ai';
594
+
595
+ const result = await generateText({
596
+ model: 'openai/gpt-5.4-nano',
597
+ prompt: 'Find recent AI regulation news from trusted sources.',
598
+ tools: {
599
+ exa_search: gateway.tools.exaSearch({
600
+ type: 'fast',
601
+ numResults: 5,
602
+ category: 'news',
603
+ includeDomains: ['reuters.com', 'bbc.com', 'nytimes.com'],
604
+ contents: {
605
+ highlights: true,
606
+ maxAgeHours: 24,
607
+ },
608
+ }),
609
+ },
610
+ });
611
+
612
+ console.log(result.text);
613
+ console.log('Tool calls:', JSON.stringify(result.toolCalls, null, 2));
614
+ console.log('Tool results:', JSON.stringify(result.toolResults, null, 2));
615
+ ```
616
+
617
+ The Exa Search tool supports the following optional configuration options:
618
+
619
+ - **type** _'auto' | 'fast' | 'instant'_
620
+
621
+ Search mode. `'auto'` is the default balance of speed and quality.
622
+
623
+ - **numResults** _number_
624
+
625
+ Maximum number of results to return (1-100, default: 10).
626
+
627
+ - **category** _'company' | 'people' | 'research paper' | 'news' | 'personal site' | 'financial report'_
628
+
629
+ Focus results on a specific content type.
630
+
631
+ - **includeDomains** / **excludeDomains** _string[]_
632
+
633
+ Restrict or exclude search results by domain.
634
+
635
+ - **startPublishedDate** / **endPublishedDate** _string_
636
+
637
+ Filter results by ISO 8601 publication date.
638
+
639
+ - **contents** _object_
640
+
641
+ Control result extraction and freshness:
642
+ - `text` - Return full page text, optionally capped with `maxCharacters`
643
+ - `highlights` - Return token-efficient excerpts
644
+ - `maxAgeHours` - Control cached content freshness
645
+ - `livecrawlTimeout` - Livecrawl timeout in milliseconds
646
+ - `subpages` / `subpageTarget` - Crawl related subpages
647
+ - `extras.links` / `extras.imageLinks` - Extract links from pages
648
+
649
+ The tool works with both `generateText` and `streamText`.
650
+
651
+ This initial Gateway integration supports Exa's plain Search modes and
652
+ token-efficient content controls. Deep synthesis modes and generated summaries
653
+ are intentionally not exposed yet because they have separate pricing from
654
+ standard Search.
655
+
569
656
  #### Parallel Search
570
657
 
571
658
  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": "3.0.137",
4
+ "version": "3.0.139",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
7
7
  "main": "./dist/index.js",
@@ -31,8 +31,8 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@vercel/oidc": "3.2.0",
34
- "@ai-sdk/provider": "3.0.11",
35
- "@ai-sdk/provider-utils": "4.0.31"
34
+ "@ai-sdk/provider": "3.0.12",
35
+ "@ai-sdk/provider-utils": "4.0.33"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "18.15.11",
@@ -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
  *
@@ -46,6 +46,7 @@ export class GatewayVideoModel implements Experimental_VideoModelV3 {
46
46
  duration,
47
47
  fps,
48
48
  seed,
49
+ generateAudio,
49
50
  image,
50
51
  providerOptions,
51
52
  headers,
@@ -79,6 +80,7 @@ export class GatewayVideoModel implements Experimental_VideoModelV3 {
79
80
  ...(duration && { duration }),
80
81
  ...(fps && { fps }),
81
82
  ...(seed && { seed }),
83
+ ...(generateAudio !== undefined && { generateAudio }),
82
84
  ...(providerOptions && { providerOptions }),
83
85
  ...(image && { image: maybeEncodeVideoFile(image) }),
84
86
  },
@@ -0,0 +1,352 @@
1
+ import {
2
+ createProviderToolFactoryWithOutputSchema,
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 = createProviderToolFactoryWithOutputSchema<
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);