@librechat/agents 2.4.50 → 2.4.52

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.
@@ -287,34 +287,111 @@ const createSearXNGAPI = (
287
287
 
288
288
  const data = response.data;
289
289
 
290
+ // Helper function to identify news results since SearXNG doesn't provide that classification by default
291
+ const isNewsResult = (result: t.SearXNGResult): boolean => {
292
+ const url = result.url?.toLowerCase() ?? '';
293
+ const title = result.title?.toLowerCase() ?? '';
294
+
295
+ // News-related keywords in title/content
296
+ const newsKeywords = [
297
+ 'breaking news',
298
+ 'latest news',
299
+ 'top stories',
300
+ 'news today',
301
+ 'developing story',
302
+ 'trending news',
303
+ 'news',
304
+ ];
305
+
306
+ // Check if title/content contains news keywords
307
+ const hasNewsKeywords = newsKeywords.some(
308
+ (keyword) => title.toLowerCase().includes(keyword) // just title probably fine, content parsing is overkill for what we need: || content.includes(keyword)
309
+ );
310
+
311
+ // Check if URL contains news-related paths
312
+ const hasNewsPath =
313
+ url.includes('/news/') ||
314
+ url.includes('/world/') ||
315
+ url.includes('/politics/') ||
316
+ url.includes('/breaking/');
317
+
318
+ return hasNewsKeywords || hasNewsPath;
319
+ };
320
+
290
321
  // Transform SearXNG results to match SerperAPI format
291
322
  const organicResults = (data.results ?? [])
292
323
  .slice(0, numResults)
293
- .map((result: t.SearXNGResult) => ({
294
- title: result.title ?? '',
295
- link: result.url ?? '',
296
- snippet: result.content ?? '',
297
- date: result.publishedDate ?? '',
298
- }));
324
+ .map((result: t.SearXNGResult, index: number) => {
325
+ let attribution = '';
326
+ try {
327
+ attribution = new URL(result.url ?? '').hostname;
328
+ } catch {
329
+ attribution = '';
330
+ }
331
+
332
+ return {
333
+ position: index + 1,
334
+ title: result.title ?? '',
335
+ link: result.url ?? '',
336
+ snippet: result.content ?? '',
337
+ date: result.publishedDate ?? '',
338
+ attribution,
339
+ };
340
+ });
299
341
 
300
- // Extract image results if available
301
342
  const imageResults = (data.results ?? [])
302
343
  .filter((result: t.SearXNGResult) => result.img_src)
303
344
  .slice(0, 6)
304
- .map((result: t.SearXNGResult) => ({
345
+ .map((result: t.SearXNGResult, index: number) => ({
305
346
  title: result.title ?? '',
306
347
  imageUrl: result.img_src ?? '',
348
+ position: index + 1,
349
+ source: new URL(result.url ?? '').hostname,
350
+ domain: new URL(result.url ?? '').hostname,
351
+ link: result.url ?? '',
307
352
  }));
308
353
 
309
- // Format results to match SerperAPI structure
354
+ // Extract news results from organic results
355
+ const newsResults = (data.results ?? [])
356
+ .filter(isNewsResult)
357
+ .map((result: t.SearXNGResult, index: number) => {
358
+ let attribution = '';
359
+ try {
360
+ attribution = new URL(result.url ?? '').hostname;
361
+ } catch {
362
+ attribution = '';
363
+ }
364
+
365
+ return {
366
+ title: result.title ?? '',
367
+ link: result.url ?? '',
368
+ snippet: result.content ?? '',
369
+ date: result.publishedDate ?? '',
370
+ source: attribution,
371
+ imageUrl: result.img_src ?? '',
372
+ position: index + 1,
373
+ };
374
+ });
375
+
376
+ const topStories = newsResults.slice(0, 5);
377
+
378
+ const relatedSearches = Array.isArray(data.suggestions)
379
+ ? data.suggestions.map((suggestion: string) => ({ query: suggestion }))
380
+ : [];
381
+
310
382
  const results: t.SearchResultData = {
311
383
  organic: organicResults,
312
384
  images: imageResults,
313
- topStories: [],
314
- // Use undefined instead of null for optional properties
315
- relatedSearches: data.suggestions ?? [],
385
+ topStories: topStories, // Use first 5 extracted news as top stories
386
+ relatedSearches,
316
387
  videos: [],
317
- news: [],
388
+ news: newsResults,
389
+ // Add empty arrays for other Serper fields to maintain parity
390
+ places: [],
391
+ shopping: [],
392
+ peopleAlsoAsk: [],
393
+ knowledgeGraph: undefined,
394
+ answerBox: undefined,
318
395
  };
319
396
 
320
397
  return { success: true, data: results };
@@ -581,6 +581,15 @@ export interface SearXNGResult {
581
581
  content?: string;
582
582
  publishedDate?: string;
583
583
  img_src?: string;
584
+ score?: number;
585
+ engine?: string;
586
+ category?: string;
587
+ thumbnail?: string;
588
+ priority?: string;
589
+ engines?: string[];
590
+ positions?: number[];
591
+ template?: string;
592
+ parsed_url?: string[];
584
593
  }
585
594
 
586
595
  export type ProcessSourcesFields = {