@librechat/agents 2.4.83 → 2.4.85

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.
Files changed (42) hide show
  1. package/dist/cjs/llm/google/utils/common.cjs +13 -0
  2. package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
  3. package/dist/cjs/main.cjs +1 -1
  4. package/dist/cjs/messages/format.cjs +52 -34
  5. package/dist/cjs/messages/format.cjs.map +1 -1
  6. package/dist/cjs/tools/search/firecrawl.cjs +3 -1
  7. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
  8. package/dist/cjs/tools/search/search.cjs +5 -5
  9. package/dist/cjs/tools/search/search.cjs.map +1 -1
  10. package/dist/cjs/tools/search/serper-scraper.cjs +132 -0
  11. package/dist/cjs/tools/search/serper-scraper.cjs.map +1 -0
  12. package/dist/cjs/tools/search/tool.cjs +45 -9
  13. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  14. package/dist/esm/llm/google/utils/common.mjs +13 -0
  15. package/dist/esm/llm/google/utils/common.mjs.map +1 -1
  16. package/dist/esm/main.mjs +1 -1
  17. package/dist/esm/messages/format.mjs +52 -34
  18. package/dist/esm/messages/format.mjs.map +1 -1
  19. package/dist/esm/tools/search/firecrawl.mjs +3 -1
  20. package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
  21. package/dist/esm/tools/search/search.mjs +5 -5
  22. package/dist/esm/tools/search/search.mjs.map +1 -1
  23. package/dist/esm/tools/search/serper-scraper.mjs +129 -0
  24. package/dist/esm/tools/search/serper-scraper.mjs.map +1 -0
  25. package/dist/esm/tools/search/tool.mjs +45 -9
  26. package/dist/esm/tools/search/tool.mjs.map +1 -1
  27. package/dist/types/messages/format.d.ts +23 -20
  28. package/dist/types/tools/search/firecrawl.d.ts +2 -1
  29. package/dist/types/tools/search/search.d.ts +1 -2
  30. package/dist/types/tools/search/serper-scraper.d.ts +59 -0
  31. package/dist/types/tools/search/tool.d.ts +21 -0
  32. package/dist/types/tools/search/types.d.ts +30 -1
  33. package/package.json +1 -1
  34. package/src/llm/google/utils/common.ts +14 -0
  35. package/src/messages/format.ts +67 -39
  36. package/src/messages/formatMessage.test.ts +418 -2
  37. package/src/scripts/search.ts +5 -1
  38. package/src/tools/search/firecrawl.ts +5 -2
  39. package/src/tools/search/search.ts +6 -8
  40. package/src/tools/search/serper-scraper.ts +155 -0
  41. package/src/tools/search/tool.ts +47 -8
  42. package/src/tools/search/types.ts +45 -0
@@ -1,9 +1,8 @@
1
1
  import type * as t from './types';
2
- import { FirecrawlScraper } from './firecrawl';
3
2
  export declare const createSearchAPI: (config: t.SearchConfig) => {
4
3
  getSources: (params: t.GetSourcesParams) => Promise<t.SearchResult>;
5
4
  };
6
- export declare const createSourceProcessor: (config?: t.ProcessSourcesConfig, scraperInstance?: FirecrawlScraper) => {
5
+ export declare const createSourceProcessor: (config?: t.ProcessSourcesConfig, scraperInstance?: t.BaseScraper) => {
7
6
  processSources: (fields: t.ProcessSourcesFields) => Promise<t.SearchResultData>;
8
7
  topResults: number;
9
8
  };
@@ -0,0 +1,59 @@
1
+ import type * as t from './types';
2
+ /**
3
+ * Serper scraper implementation
4
+ * Uses the Serper Scrape API (https://scrape.serper.dev) to scrape web pages
5
+ *
6
+ * Features:
7
+ * - Simple API with single endpoint
8
+ * - Returns both text and markdown content
9
+ * - Includes metadata from scraped pages
10
+ * - Credits-based pricing model
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const scraper = createSerperScraper({
15
+ * apiKey: 'your-serper-api-key',
16
+ * includeMarkdown: true,
17
+ * timeout: 10000
18
+ * });
19
+ *
20
+ * const [url, response] = await scraper.scrapeUrl('https://example.com');
21
+ * if (response.success) {
22
+ * const [content] = scraper.extractContent(response);
23
+ * console.log(content);
24
+ * }
25
+ * ```
26
+ */
27
+ export declare class SerperScraper implements t.BaseScraper {
28
+ private apiKey;
29
+ private apiUrl;
30
+ private timeout;
31
+ private logger;
32
+ private includeMarkdown;
33
+ constructor(config?: t.SerperScraperConfig);
34
+ /**
35
+ * Scrape a single URL
36
+ * @param url URL to scrape
37
+ * @param options Scrape options
38
+ * @returns Scrape response
39
+ */
40
+ scrapeUrl(url: string, options?: t.SerperScrapeOptions): Promise<[string, t.SerperScrapeResponse]>;
41
+ /**
42
+ * Extract content from scrape response
43
+ * @param response Scrape response
44
+ * @returns Extracted content or empty string if not available
45
+ */
46
+ extractContent(response: t.SerperScrapeResponse): [string, undefined | t.References];
47
+ /**
48
+ * Extract metadata from scrape response
49
+ * @param response Scrape response
50
+ * @returns Metadata object
51
+ */
52
+ extractMetadata(response: t.SerperScrapeResponse): Record<string, string | number | boolean | null | undefined>;
53
+ }
54
+ /**
55
+ * Create a Serper scraper instance
56
+ * @param config Scraper configuration
57
+ * @returns Serper scraper instance
58
+ */
59
+ export declare const createSerperScraper: (config?: t.SerperScraperConfig) => SerperScraper;
@@ -6,6 +6,27 @@ import { DATE_RANGE } from './schema';
6
6
  * Creates a search tool with a schema that dynamically includes the country field
7
7
  * only when the searchProvider is 'serper'.
8
8
  *
9
+ * Supports multiple scraper providers:
10
+ * - Firecrawl (default): Full-featured web scraping with multiple formats
11
+ * - Serper: Lightweight scraping using Serper's scrape API
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * // Using Firecrawl scraper (default)
16
+ * const searchTool = createSearchTool({
17
+ * searchProvider: 'serper',
18
+ * scraperProvider: 'firecrawl',
19
+ * firecrawlApiKey: 'your-firecrawl-key'
20
+ * });
21
+ *
22
+ * // Using Serper scraper
23
+ * const searchTool = createSearchTool({
24
+ * searchProvider: 'serper',
25
+ * scraperProvider: 'serper',
26
+ * serperApiKey: 'your-serper-key'
27
+ * });
28
+ * ```
29
+ *
9
30
  * @param config - The search tool configuration
10
31
  * @returns A DynamicStructuredTool with a schema that depends on the searchProvider
11
32
  */
@@ -4,6 +4,7 @@ import type { RunnableConfig } from '@langchain/core/runnables';
4
4
  import type { BaseReranker } from './rerankers';
5
5
  import { DATE_RANGE } from './schema';
6
6
  export type SearchProvider = 'serper' | 'searxng';
7
+ export type ScraperProvider = 'firecrawl' | 'serper';
7
8
  export type RerankerType = 'infinity' | 'jina' | 'cohere' | 'none';
8
9
  export interface Highlight {
9
10
  score: number;
@@ -85,8 +86,16 @@ export interface ProcessSourcesConfig {
85
86
  export interface FirecrawlConfig {
86
87
  firecrawlApiKey?: string;
87
88
  firecrawlApiUrl?: string;
89
+ firecrawlVersion?: string;
88
90
  firecrawlOptions?: FirecrawlScraperConfig;
89
91
  }
92
+ export interface SerperScraperConfig {
93
+ apiKey?: string;
94
+ apiUrl?: string;
95
+ timeout?: number;
96
+ logger?: Logger;
97
+ includeMarkdown?: boolean;
98
+ }
90
99
  export interface ScraperContentResult {
91
100
  content: string;
92
101
  }
@@ -133,7 +142,9 @@ export interface SearchToolConfig extends SearchConfig, ProcessSourcesConfig, Fi
133
142
  jinaApiUrl?: string;
134
143
  cohereApiKey?: string;
135
144
  rerankerType?: RerankerType;
145
+ scraperProvider?: ScraperProvider;
136
146
  scraperTimeout?: number;
147
+ serperScraperOptions?: SerperScraperConfig;
137
148
  onSearchResults?: (results: SearchResult, runnableConfig?: RunnableConfig) => void;
138
149
  onGetHighlights?: (link: string) => void;
139
150
  }
@@ -147,8 +158,15 @@ export type UsedReferences = {
147
158
  originalIndex: number;
148
159
  reference: MediaReference;
149
160
  }[];
161
+ /** Base Scraper Interface */
162
+ export interface BaseScraper {
163
+ scrapeUrl(url: string, options?: unknown): Promise<[string, FirecrawlScrapeResponse | SerperScrapeResponse]>;
164
+ extractContent(response: FirecrawlScrapeResponse | SerperScrapeResponse): [string, undefined | References];
165
+ extractMetadata(response: FirecrawlScrapeResponse | SerperScrapeResponse): ScrapeMetadata | Record<string, string | number | boolean | null | undefined>;
166
+ }
150
167
  /** Firecrawl */
151
- export type FirecrawlScrapeOptions = Omit<FirecrawlScraperConfig, 'apiKey' | 'apiUrl' | 'logger'>;
168
+ export type FirecrawlScrapeOptions = Omit<FirecrawlScraperConfig, 'apiKey' | 'apiUrl' | 'version' | 'logger'>;
169
+ export type SerperScrapeOptions = Omit<SerperScraperConfig, 'apiKey' | 'apiUrl' | 'logger'>;
152
170
  export interface ScrapeMetadata {
153
171
  sourceURL?: string;
154
172
  url?: string;
@@ -214,9 +232,20 @@ export interface FirecrawlScrapeResponse {
214
232
  };
215
233
  error?: string;
216
234
  }
235
+ export interface SerperScrapeResponse {
236
+ success: boolean;
237
+ data?: {
238
+ text?: string;
239
+ markdown?: string;
240
+ metadata?: Record<string, string | number | boolean | null | undefined>;
241
+ credits?: number;
242
+ };
243
+ error?: string;
244
+ }
217
245
  export interface FirecrawlScraperConfig {
218
246
  apiKey?: string;
219
247
  apiUrl?: string;
248
+ version?: string;
220
249
  formats?: string[];
221
250
  timeout?: number;
222
251
  logger?: Logger;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "2.4.83",
3
+ "version": "2.4.85",
4
4
  "main": "./dist/cjs/main.cjs",
5
5
  "module": "./dist/esm/main.mjs",
6
6
  "types": "./dist/types/index.d.ts",
@@ -301,6 +301,20 @@ function _convertLangChainContentToPart(
301
301
  mimeType,
302
302
  },
303
303
  };
304
+ } else if (
305
+ content.type === 'document' ||
306
+ content.type === 'audio' ||
307
+ content.type === 'video'
308
+ ) {
309
+ if (!isMultimodalModel) {
310
+ throw new Error(`This model does not support ${content.type}s`);
311
+ }
312
+ return {
313
+ inlineData: {
314
+ data: content.data,
315
+ mimeType: content.mimeType,
316
+ },
317
+ };
304
318
  } else if (content.type === 'media') {
305
319
  return messageContentMedia(content);
306
320
  } else if (content.type === 'tool_use') {
@@ -17,28 +17,28 @@ import type {
17
17
  } from '@/types';
18
18
  import { Providers, ContentTypes } from '@/common';
19
19
 
20
- interface VisionMessageParams {
20
+ interface MediaMessageParams {
21
21
  message: {
22
22
  role: string;
23
23
  content: string;
24
24
  name?: string;
25
25
  [key: string]: any;
26
26
  };
27
- image_urls: MessageContentImageUrl[];
27
+ mediaParts: MessageContentComplex[];
28
28
  endpoint?: Providers;
29
29
  }
30
30
 
31
31
  /**
32
- * Formats a message to OpenAI Vision API payload format.
32
+ * Formats a message with media content (images, documents, videos, audios) to API payload format.
33
33
  *
34
- * @param {VisionMessageParams} params - The parameters for formatting.
35
- * @returns {Object} - The formatted message.
34
+ * @param params - The parameters for formatting.
35
+ * @returns - The formatted message.
36
36
  */
37
- export const formatVisionMessage = ({
37
+ export const formatMediaMessage = ({
38
38
  message,
39
- image_urls,
40
39
  endpoint,
41
- }: VisionMessageParams): {
40
+ mediaParts,
41
+ }: MediaMessageParams): {
42
42
  role: string;
43
43
  content: MessageContentComplex[];
44
44
  name?: string;
@@ -57,7 +57,7 @@ export const formatVisionMessage = ({
57
57
 
58
58
  if (endpoint === Providers.ANTHROPIC) {
59
59
  result.content = [
60
- ...image_urls,
60
+ ...mediaParts,
61
61
  { type: ContentTypes.TEXT, text: message.content },
62
62
  ] as MessageContentComplex[];
63
63
  return result;
@@ -65,7 +65,7 @@ export const formatVisionMessage = ({
65
65
 
66
66
  result.content = [
67
67
  { type: ContentTypes.TEXT, text: message.content },
68
- ...image_urls,
68
+ ...mediaParts,
69
69
  ] as MessageContentComplex[];
70
70
 
71
71
  return result;
@@ -78,6 +78,9 @@ interface MessageInput {
78
78
  text?: string;
79
79
  content?: string | MessageContentComplex[];
80
80
  image_urls?: MessageContentImageUrl[];
81
+ documents?: MessageContentComplex[];
82
+ videos?: MessageContentComplex[];
83
+ audios?: MessageContentComplex[];
81
84
  lc_id?: string[];
82
85
  [key: string]: any;
83
86
  }
@@ -100,14 +103,14 @@ interface FormattedMessage {
100
103
  /**
101
104
  * Formats a message to OpenAI payload format based on the provided options.
102
105
  *
103
- * @param {FormatMessageParams} params - The parameters for formatting.
104
- * @returns {FormattedMessage | HumanMessage | AIMessage | SystemMessage} - The formatted message.
106
+ * @param params - The parameters for formatting.
107
+ * @returns - The formatted message.
105
108
  */
106
109
  export const formatMessage = ({
107
110
  message,
108
111
  userName,
109
- assistantName,
110
112
  endpoint,
113
+ assistantName,
111
114
  langChain = false,
112
115
  }: FormatMessageParams):
113
116
  | FormattedMessage
@@ -135,21 +138,7 @@ export const formatMessage = ({
135
138
  content,
136
139
  };
137
140
 
138
- const { image_urls } = message;
139
- if (Array.isArray(image_urls) && image_urls.length > 0 && role === 'user') {
140
- return formatVisionMessage({
141
- message: {
142
- ...formattedMessage,
143
- content:
144
- typeof formattedMessage.content === 'string'
145
- ? formattedMessage.content
146
- : '',
147
- },
148
- image_urls,
149
- endpoint,
150
- });
151
- }
152
-
141
+ // Set name fields first
153
142
  if (_name != null && _name) {
154
143
  formattedMessage.name = _name;
155
144
  }
@@ -179,6 +168,45 @@ export const formatMessage = ({
179
168
  }
180
169
  }
181
170
 
171
+ const { image_urls, documents, videos, audios } = message;
172
+ const mediaParts: MessageContentComplex[] = [];
173
+
174
+ if (Array.isArray(documents) && documents.length > 0) {
175
+ mediaParts.push(...documents);
176
+ }
177
+
178
+ if (Array.isArray(videos) && videos.length > 0) {
179
+ mediaParts.push(...videos);
180
+ }
181
+
182
+ if (Array.isArray(audios) && audios.length > 0) {
183
+ mediaParts.push(...audios);
184
+ }
185
+
186
+ if (Array.isArray(image_urls) && image_urls.length > 0) {
187
+ mediaParts.push(...image_urls);
188
+ }
189
+
190
+ if (mediaParts.length > 0 && role === 'user') {
191
+ const mediaMessage = formatMediaMessage({
192
+ message: {
193
+ ...formattedMessage,
194
+ content:
195
+ typeof formattedMessage.content === 'string'
196
+ ? formattedMessage.content
197
+ : '',
198
+ },
199
+ mediaParts,
200
+ endpoint,
201
+ });
202
+
203
+ if (!langChain) {
204
+ return mediaMessage;
205
+ }
206
+
207
+ return new HumanMessage(mediaMessage);
208
+ }
209
+
182
210
  if (!langChain) {
183
211
  return formattedMessage;
184
212
  }
@@ -195,9 +223,9 @@ export const formatMessage = ({
195
223
  /**
196
224
  * Formats an array of messages for LangChain.
197
225
  *
198
- * @param {Array<MessageInput>} messages - The array of messages to format.
199
- * @param {Omit<FormatMessageParams, 'message' | 'langChain'>} formatOptions - The options for formatting each message.
200
- * @returns {Array<HumanMessage | AIMessage | SystemMessage>} - The array of formatted LangChain messages.
226
+ * @param messages - The array of messages to format.
227
+ * @param formatOptions - The options for formatting each message.
228
+ * @returns - The array of formatted LangChain messages.
201
229
  */
202
230
  export const formatLangChainMessages = (
203
231
  messages: Array<MessageInput>,
@@ -228,8 +256,8 @@ interface LangChainMessage {
228
256
  /**
229
257
  * Formats a LangChain message object by merging properties from `lc_kwargs` or `kwargs` and `additional_kwargs`.
230
258
  *
231
- * @param {LangChainMessage} message - The message object to format.
232
- * @returns {Record<string, any>} The formatted LangChain message.
259
+ * @param message - The message object to format.
260
+ * @returns - The formatted LangChain message.
233
261
  */
234
262
  export const formatFromLangChain = (
235
263
  message: LangChainMessage
@@ -357,10 +385,10 @@ function formatAssistantMessage(
357
385
  /**
358
386
  * Formats an array of messages for LangChain, handling tool calls and creating ToolMessage instances.
359
387
  *
360
- * @param {TPayload} payload - The array of messages to format.
361
- * @param {Record<number, number>} [indexTokenCountMap] - Optional map of message indices to token counts.
362
- * @param {Set<string>} [tools] - Optional set of tool names that are allowed in the request.
363
- * @returns {Object} - Object containing formatted messages and updated indexTokenCountMap if provided.
388
+ * @param payload - The array of messages to format.
389
+ * @param indexTokenCountMap - Optional map of message indices to token counts.
390
+ * @param tools - Optional set of tool names that are allowed in the request.
391
+ * @returns - Object containing formatted messages and updated indexTokenCountMap if provided.
364
392
  */
365
393
  export const formatAgentMessages = (
366
394
  payload: TPayload,
@@ -539,8 +567,8 @@ export const formatAgentMessages = (
539
567
 
540
568
  /**
541
569
  * Formats an array of messages for LangChain, making sure all content fields are strings
542
- * @param {Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>} payload - The array of messages to format.
543
- * @returns {Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>} - The array of formatted LangChain messages, including ToolMessages for tool calls.
570
+ * @param payload - The array of messages to format.
571
+ * @returns - The array of formatted LangChain messages, including ToolMessages for tool calls.
544
572
  */
545
573
  export const formatContentStrings = (
546
574
  payload: Array<BaseMessage>