@librechat/agents 3.1.75-dev.1 → 3.1.76
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/dist/cjs/llm/openai/index.cjs +43 -0
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +19 -10
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +67 -10
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +55 -66
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/tavily-scraper.cjs +189 -0
- package/dist/cjs/tools/search/tavily-scraper.cjs.map +1 -0
- package/dist/cjs/tools/search/tavily-search.cjs +372 -0
- package/dist/cjs/tools/search/tavily-search.cjs.map +1 -0
- package/dist/cjs/tools/search/tool.cjs +26 -4
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/search/utils.cjs +10 -3
- package/dist/cjs/tools/search/utils.cjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +43 -0
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +19 -10
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +67 -10
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +55 -66
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/tavily-scraper.mjs +186 -0
- package/dist/esm/tools/search/tavily-scraper.mjs.map +1 -0
- package/dist/esm/tools/search/tavily-search.mjs +370 -0
- package/dist/esm/tools/search/tavily-search.mjs.map +1 -0
- package/dist/esm/tools/search/tool.mjs +26 -4
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/search/utils.mjs +10 -3
- package/dist/esm/tools/search/utils.mjs.map +1 -1
- package/dist/types/messages/format.d.ts +4 -1
- package/dist/types/tools/search/tavily-scraper.d.ts +19 -0
- package/dist/types/tools/search/tavily-search.d.ts +4 -0
- package/dist/types/tools/search/types.d.ts +99 -5
- package/dist/types/tools/search/utils.d.ts +2 -2
- package/package.json +1 -1
- package/src/llm/custom-chat-models.smoke.test.ts +175 -1
- package/src/llm/openai/index.ts +124 -0
- package/src/llm/openai/utils/index.ts +23 -14
- package/src/llm/openai/utils/messages.test.ts +159 -0
- package/src/messages/format.ts +90 -13
- package/src/messages/formatAgentMessages.test.ts +166 -1
- package/src/tools/search/search.ts +83 -73
- package/src/tools/search/tavily-scraper.ts +235 -0
- package/src/tools/search/tavily-search.ts +424 -0
- package/src/tools/search/tavily.test.ts +965 -0
- package/src/tools/search/tool.ts +36 -26
- package/src/tools/search/types.ts +134 -11
- package/src/tools/search/utils.ts +13 -5
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import type * as t from './types';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_TAVILY_TIMEOUT = 15000;
|
|
5
|
+
|
|
6
|
+
const TAVILY_COUNTRY_ALIASES: Record<string, string> = {
|
|
7
|
+
ba: 'bosnia and herzegovina',
|
|
8
|
+
cg: 'congo',
|
|
9
|
+
cz: 'czech republic',
|
|
10
|
+
gb: 'united kingdom',
|
|
11
|
+
mm: 'myanmar',
|
|
12
|
+
tr: 'turkey',
|
|
13
|
+
tt: 'trinidad and tobago',
|
|
14
|
+
uk: 'united kingdom',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const TAVILY_SUPPORTED_COUNTRIES = new Set([
|
|
18
|
+
'afghanistan',
|
|
19
|
+
'albania',
|
|
20
|
+
'algeria',
|
|
21
|
+
'andorra',
|
|
22
|
+
'angola',
|
|
23
|
+
'argentina',
|
|
24
|
+
'armenia',
|
|
25
|
+
'australia',
|
|
26
|
+
'austria',
|
|
27
|
+
'azerbaijan',
|
|
28
|
+
'bahamas',
|
|
29
|
+
'bahrain',
|
|
30
|
+
'bangladesh',
|
|
31
|
+
'barbados',
|
|
32
|
+
'belarus',
|
|
33
|
+
'belgium',
|
|
34
|
+
'belize',
|
|
35
|
+
'benin',
|
|
36
|
+
'bhutan',
|
|
37
|
+
'bolivia',
|
|
38
|
+
'bosnia and herzegovina',
|
|
39
|
+
'botswana',
|
|
40
|
+
'brazil',
|
|
41
|
+
'brunei',
|
|
42
|
+
'bulgaria',
|
|
43
|
+
'burkina faso',
|
|
44
|
+
'burundi',
|
|
45
|
+
'cambodia',
|
|
46
|
+
'cameroon',
|
|
47
|
+
'canada',
|
|
48
|
+
'cape verde',
|
|
49
|
+
'central african republic',
|
|
50
|
+
'chad',
|
|
51
|
+
'chile',
|
|
52
|
+
'china',
|
|
53
|
+
'colombia',
|
|
54
|
+
'comoros',
|
|
55
|
+
'congo',
|
|
56
|
+
'costa rica',
|
|
57
|
+
'croatia',
|
|
58
|
+
'cuba',
|
|
59
|
+
'cyprus',
|
|
60
|
+
'czech republic',
|
|
61
|
+
'denmark',
|
|
62
|
+
'djibouti',
|
|
63
|
+
'dominican republic',
|
|
64
|
+
'ecuador',
|
|
65
|
+
'egypt',
|
|
66
|
+
'el salvador',
|
|
67
|
+
'equatorial guinea',
|
|
68
|
+
'eritrea',
|
|
69
|
+
'estonia',
|
|
70
|
+
'ethiopia',
|
|
71
|
+
'fiji',
|
|
72
|
+
'finland',
|
|
73
|
+
'france',
|
|
74
|
+
'gabon',
|
|
75
|
+
'gambia',
|
|
76
|
+
'georgia',
|
|
77
|
+
'germany',
|
|
78
|
+
'ghana',
|
|
79
|
+
'greece',
|
|
80
|
+
'guatemala',
|
|
81
|
+
'guinea',
|
|
82
|
+
'haiti',
|
|
83
|
+
'honduras',
|
|
84
|
+
'hungary',
|
|
85
|
+
'iceland',
|
|
86
|
+
'india',
|
|
87
|
+
'indonesia',
|
|
88
|
+
'iran',
|
|
89
|
+
'iraq',
|
|
90
|
+
'ireland',
|
|
91
|
+
'israel',
|
|
92
|
+
'italy',
|
|
93
|
+
'jamaica',
|
|
94
|
+
'japan',
|
|
95
|
+
'jordan',
|
|
96
|
+
'kazakhstan',
|
|
97
|
+
'kenya',
|
|
98
|
+
'kuwait',
|
|
99
|
+
'kyrgyzstan',
|
|
100
|
+
'latvia',
|
|
101
|
+
'lebanon',
|
|
102
|
+
'lesotho',
|
|
103
|
+
'liberia',
|
|
104
|
+
'libya',
|
|
105
|
+
'liechtenstein',
|
|
106
|
+
'lithuania',
|
|
107
|
+
'luxembourg',
|
|
108
|
+
'madagascar',
|
|
109
|
+
'malawi',
|
|
110
|
+
'malaysia',
|
|
111
|
+
'maldives',
|
|
112
|
+
'mali',
|
|
113
|
+
'malta',
|
|
114
|
+
'mauritania',
|
|
115
|
+
'mauritius',
|
|
116
|
+
'mexico',
|
|
117
|
+
'moldova',
|
|
118
|
+
'monaco',
|
|
119
|
+
'mongolia',
|
|
120
|
+
'montenegro',
|
|
121
|
+
'morocco',
|
|
122
|
+
'mozambique',
|
|
123
|
+
'myanmar',
|
|
124
|
+
'namibia',
|
|
125
|
+
'nepal',
|
|
126
|
+
'netherlands',
|
|
127
|
+
'new zealand',
|
|
128
|
+
'nicaragua',
|
|
129
|
+
'niger',
|
|
130
|
+
'nigeria',
|
|
131
|
+
'north korea',
|
|
132
|
+
'north macedonia',
|
|
133
|
+
'norway',
|
|
134
|
+
'oman',
|
|
135
|
+
'pakistan',
|
|
136
|
+
'panama',
|
|
137
|
+
'papua new guinea',
|
|
138
|
+
'paraguay',
|
|
139
|
+
'peru',
|
|
140
|
+
'philippines',
|
|
141
|
+
'poland',
|
|
142
|
+
'portugal',
|
|
143
|
+
'qatar',
|
|
144
|
+
'romania',
|
|
145
|
+
'russia',
|
|
146
|
+
'rwanda',
|
|
147
|
+
'saudi arabia',
|
|
148
|
+
'senegal',
|
|
149
|
+
'serbia',
|
|
150
|
+
'singapore',
|
|
151
|
+
'slovakia',
|
|
152
|
+
'slovenia',
|
|
153
|
+
'somalia',
|
|
154
|
+
'south africa',
|
|
155
|
+
'south korea',
|
|
156
|
+
'south sudan',
|
|
157
|
+
'spain',
|
|
158
|
+
'sri lanka',
|
|
159
|
+
'sudan',
|
|
160
|
+
'sweden',
|
|
161
|
+
'switzerland',
|
|
162
|
+
'syria',
|
|
163
|
+
'taiwan',
|
|
164
|
+
'tajikistan',
|
|
165
|
+
'tanzania',
|
|
166
|
+
'thailand',
|
|
167
|
+
'togo',
|
|
168
|
+
'trinidad and tobago',
|
|
169
|
+
'tunisia',
|
|
170
|
+
'turkey',
|
|
171
|
+
'turkmenistan',
|
|
172
|
+
'uganda',
|
|
173
|
+
'ukraine',
|
|
174
|
+
'united arab emirates',
|
|
175
|
+
'united kingdom',
|
|
176
|
+
'united states',
|
|
177
|
+
'uruguay',
|
|
178
|
+
'uzbekistan',
|
|
179
|
+
'venezuela',
|
|
180
|
+
'vietnam',
|
|
181
|
+
'yemen',
|
|
182
|
+
'zambia',
|
|
183
|
+
'zimbabwe',
|
|
184
|
+
]);
|
|
185
|
+
|
|
186
|
+
const TAVILY_REGION_NAMES = new Intl.DisplayNames(['en'], {
|
|
187
|
+
type: 'region',
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const normalizeTavilyCountryName = (country: string): string =>
|
|
191
|
+
country
|
|
192
|
+
.toLowerCase()
|
|
193
|
+
.replace(/\s*\([^)]*\)/g, '')
|
|
194
|
+
.replace(/\s*&\s*/g, ' and ')
|
|
195
|
+
.normalize('NFD')
|
|
196
|
+
.replace(/\p{Diacritic}/gu, '');
|
|
197
|
+
|
|
198
|
+
const normalizeTavilyCountry = (country?: string): string | undefined => {
|
|
199
|
+
const normalizedCountry = country?.trim().toLowerCase();
|
|
200
|
+
if (normalizedCountry == null || normalizedCountry === '') {
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const countryAlias = TAVILY_COUNTRY_ALIASES[normalizedCountry];
|
|
205
|
+
if (countryAlias != null) {
|
|
206
|
+
return TAVILY_SUPPORTED_COUNTRIES.has(countryAlias)
|
|
207
|
+
? countryAlias
|
|
208
|
+
: undefined;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (/^[a-z]{2}$/.test(normalizedCountry)) {
|
|
212
|
+
const regionName = TAVILY_REGION_NAMES.of(normalizedCountry.toUpperCase());
|
|
213
|
+
if (regionName == null) {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
const countryName = normalizeTavilyCountryName(regionName);
|
|
217
|
+
return TAVILY_SUPPORTED_COUNTRIES.has(countryName)
|
|
218
|
+
? countryName
|
|
219
|
+
: undefined;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const countryName = normalizeTavilyCountryName(normalizedCountry);
|
|
223
|
+
return TAVILY_SUPPORTED_COUNTRIES.has(countryName) ? countryName : undefined;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const normalizeTavilyTimeRange = (
|
|
227
|
+
timeRange?: t.TavilyTimeRangeInput
|
|
228
|
+
): t.TavilyTimeRange | undefined => {
|
|
229
|
+
switch (timeRange) {
|
|
230
|
+
case 'h':
|
|
231
|
+
case 'd':
|
|
232
|
+
return 'day';
|
|
233
|
+
case 'w':
|
|
234
|
+
return 'week';
|
|
235
|
+
case 'm':
|
|
236
|
+
return 'month';
|
|
237
|
+
case 'y':
|
|
238
|
+
return 'year';
|
|
239
|
+
case 'day':
|
|
240
|
+
case 'week':
|
|
241
|
+
case 'month':
|
|
242
|
+
case 'year':
|
|
243
|
+
return timeRange;
|
|
244
|
+
default:
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const getHostname = (link: string): string => {
|
|
250
|
+
try {
|
|
251
|
+
return new URL(link).hostname;
|
|
252
|
+
} catch {
|
|
253
|
+
return link;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
export const createTavilyAPI = (
|
|
258
|
+
apiKey?: string,
|
|
259
|
+
apiUrl?: string,
|
|
260
|
+
options?: t.TavilySearchOptions
|
|
261
|
+
): {
|
|
262
|
+
getSources: (params: t.GetSourcesParams) => Promise<t.SearchResult>;
|
|
263
|
+
} => {
|
|
264
|
+
const config = {
|
|
265
|
+
apiKey: apiKey ?? process.env.TAVILY_API_KEY,
|
|
266
|
+
apiUrl:
|
|
267
|
+
apiUrl ??
|
|
268
|
+
process.env.TAVILY_SEARCH_URL ??
|
|
269
|
+
'https://api.tavily.com/search',
|
|
270
|
+
timeout: options?.timeout ?? DEFAULT_TAVILY_TIMEOUT,
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
if (config.apiKey == null || config.apiKey === '') {
|
|
274
|
+
throw new Error('TAVILY_API_KEY is required for Tavily API');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const getSources = async ({
|
|
278
|
+
query,
|
|
279
|
+
date,
|
|
280
|
+
country,
|
|
281
|
+
numResults = 8,
|
|
282
|
+
type,
|
|
283
|
+
news,
|
|
284
|
+
}: t.GetSourcesParams): Promise<t.SearchResult> => {
|
|
285
|
+
if (!query.trim()) {
|
|
286
|
+
return { success: false, error: 'Query cannot be empty' };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
try {
|
|
290
|
+
const timeRange =
|
|
291
|
+
normalizeTavilyTimeRange(options?.timeRange) ??
|
|
292
|
+
(date != null ? (normalizeTavilyTimeRange(date) ?? 'day') : undefined);
|
|
293
|
+
const topic =
|
|
294
|
+
news === true || type === 'news'
|
|
295
|
+
? 'news'
|
|
296
|
+
: (options?.topic ?? 'general');
|
|
297
|
+
const maxResults = options?.maxResults ?? numResults;
|
|
298
|
+
const searchDepth = options?.searchDepth ?? 'basic';
|
|
299
|
+
|
|
300
|
+
const payload: t.TavilySearchPayload = {
|
|
301
|
+
query,
|
|
302
|
+
search_depth: searchDepth,
|
|
303
|
+
topic,
|
|
304
|
+
max_results: Math.min(Math.max(1, maxResults), 20),
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
if (
|
|
308
|
+
options?.safeSearch != null &&
|
|
309
|
+
searchDepth !== 'fast' &&
|
|
310
|
+
searchDepth !== 'ultra-fast'
|
|
311
|
+
) {
|
|
312
|
+
payload.safe_search = options.safeSearch;
|
|
313
|
+
}
|
|
314
|
+
if (timeRange != null) {
|
|
315
|
+
payload.time_range = timeRange;
|
|
316
|
+
}
|
|
317
|
+
const tavilyCountry =
|
|
318
|
+
topic === 'general' ? normalizeTavilyCountry(country) : undefined;
|
|
319
|
+
if (tavilyCountry != null) {
|
|
320
|
+
payload.country = tavilyCountry;
|
|
321
|
+
}
|
|
322
|
+
if (type === 'images' || options?.includeImages) {
|
|
323
|
+
payload.include_images = true;
|
|
324
|
+
}
|
|
325
|
+
if (options?.includeAnswer != null) {
|
|
326
|
+
payload.include_answer = options.includeAnswer;
|
|
327
|
+
}
|
|
328
|
+
if (options?.includeRawContent != null) {
|
|
329
|
+
payload.include_raw_content = options.includeRawContent;
|
|
330
|
+
}
|
|
331
|
+
if (
|
|
332
|
+
options?.includeDomains != null &&
|
|
333
|
+
options.includeDomains.length > 0
|
|
334
|
+
) {
|
|
335
|
+
payload.include_domains = options.includeDomains;
|
|
336
|
+
}
|
|
337
|
+
if (
|
|
338
|
+
options?.excludeDomains != null &&
|
|
339
|
+
options.excludeDomains.length > 0
|
|
340
|
+
) {
|
|
341
|
+
payload.exclude_domains = options.excludeDomains;
|
|
342
|
+
}
|
|
343
|
+
if (options?.includeImageDescriptions != null) {
|
|
344
|
+
payload.include_image_descriptions = options.includeImageDescriptions;
|
|
345
|
+
}
|
|
346
|
+
if (options?.includeFavicon != null) {
|
|
347
|
+
payload.include_favicon = options.includeFavicon;
|
|
348
|
+
}
|
|
349
|
+
if (options?.chunksPerSource != null && searchDepth === 'advanced') {
|
|
350
|
+
payload.chunks_per_source = options.chunksPerSource;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const response = await axios.post<t.TavilySearchResponse>(
|
|
354
|
+
config.apiUrl,
|
|
355
|
+
payload,
|
|
356
|
+
{
|
|
357
|
+
headers: {
|
|
358
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
359
|
+
'Content-Type': 'application/json',
|
|
360
|
+
},
|
|
361
|
+
timeout: config.timeout,
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
const data = response.data;
|
|
366
|
+
|
|
367
|
+
const organicResults: t.OrganicResult[] = (data.results ?? []).map(
|
|
368
|
+
(result: t.TavilySearchResult) => ({
|
|
369
|
+
title: result.title ?? '',
|
|
370
|
+
link: result.url ?? '',
|
|
371
|
+
snippet: result.content ?? '',
|
|
372
|
+
date: result.published_date,
|
|
373
|
+
})
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
const imageResults: t.ImageResult[] = Array.isArray(data.images)
|
|
377
|
+
? data.images.slice(0, 6).reduce<t.ImageResult[]>((acc, image) => {
|
|
378
|
+
const imageUrl = typeof image === 'string' ? image : image.url;
|
|
379
|
+
if (imageUrl == null || imageUrl === '') {
|
|
380
|
+
return acc;
|
|
381
|
+
}
|
|
382
|
+
acc.push({
|
|
383
|
+
imageUrl,
|
|
384
|
+
title: typeof image === 'string' ? undefined : image.description,
|
|
385
|
+
position: acc.length + 1,
|
|
386
|
+
});
|
|
387
|
+
return acc;
|
|
388
|
+
}, [])
|
|
389
|
+
: [];
|
|
390
|
+
|
|
391
|
+
const newsResults: t.NewsResult[] =
|
|
392
|
+
topic === 'news'
|
|
393
|
+
? organicResults.map((r) => ({
|
|
394
|
+
title: r.title,
|
|
395
|
+
link: r.link,
|
|
396
|
+
snippet: r.snippet,
|
|
397
|
+
date: r.date,
|
|
398
|
+
source: getHostname(r.link),
|
|
399
|
+
}))
|
|
400
|
+
: [];
|
|
401
|
+
|
|
402
|
+
const results: t.SearchResultData = {
|
|
403
|
+
organic: organicResults,
|
|
404
|
+
images: imageResults,
|
|
405
|
+
topStories: [],
|
|
406
|
+
videos: [],
|
|
407
|
+
news: newsResults,
|
|
408
|
+
answerBox: data.answer != null ? { snippet: data.answer } : undefined,
|
|
409
|
+
relatedSearches: [],
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
return { success: true, data: results };
|
|
413
|
+
} catch (error) {
|
|
414
|
+
const errorMessage =
|
|
415
|
+
error instanceof Error ? error.message : String(error);
|
|
416
|
+
return {
|
|
417
|
+
success: false,
|
|
418
|
+
error: `Tavily API request failed: ${errorMessage}`,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
return { getSources };
|
|
424
|
+
};
|