@librechat/agents 2.4.321 → 3.0.0-rc1

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 (266) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +218 -0
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -0
  3. package/dist/cjs/common/enum.cjs +14 -5
  4. package/dist/cjs/common/enum.cjs.map +1 -1
  5. package/dist/cjs/events.cjs +10 -6
  6. package/dist/cjs/events.cjs.map +1 -1
  7. package/dist/cjs/graphs/Graph.cjs +309 -212
  8. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  9. package/dist/cjs/graphs/MultiAgentGraph.cjs +322 -0
  10. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -0
  11. package/dist/cjs/llm/anthropic/index.cjs +54 -9
  12. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  13. package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
  14. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +52 -6
  15. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  16. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +22 -2
  17. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
  18. package/dist/cjs/llm/anthropic/utils/tools.cjs +29 -0
  19. package/dist/cjs/llm/anthropic/utils/tools.cjs.map +1 -0
  20. package/dist/cjs/llm/google/index.cjs +144 -0
  21. package/dist/cjs/llm/google/index.cjs.map +1 -0
  22. package/dist/cjs/llm/google/utils/common.cjs +477 -0
  23. package/dist/cjs/llm/google/utils/common.cjs.map +1 -0
  24. package/dist/cjs/llm/ollama/index.cjs +67 -0
  25. package/dist/cjs/llm/ollama/index.cjs.map +1 -0
  26. package/dist/cjs/llm/ollama/utils.cjs +158 -0
  27. package/dist/cjs/llm/ollama/utils.cjs.map +1 -0
  28. package/dist/cjs/llm/openai/index.cjs +389 -3
  29. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  30. package/dist/cjs/llm/openai/utils/index.cjs +672 -0
  31. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -0
  32. package/dist/cjs/llm/providers.cjs +15 -15
  33. package/dist/cjs/llm/providers.cjs.map +1 -1
  34. package/dist/cjs/llm/text.cjs +14 -3
  35. package/dist/cjs/llm/text.cjs.map +1 -1
  36. package/dist/cjs/llm/vertexai/index.cjs +330 -0
  37. package/dist/cjs/llm/vertexai/index.cjs.map +1 -0
  38. package/dist/cjs/main.cjs +11 -0
  39. package/dist/cjs/main.cjs.map +1 -1
  40. package/dist/cjs/run.cjs +120 -81
  41. package/dist/cjs/run.cjs.map +1 -1
  42. package/dist/cjs/stream.cjs +85 -51
  43. package/dist/cjs/stream.cjs.map +1 -1
  44. package/dist/cjs/tools/ToolNode.cjs +10 -4
  45. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  46. package/dist/cjs/tools/handlers.cjs +119 -13
  47. package/dist/cjs/tools/handlers.cjs.map +1 -1
  48. package/dist/cjs/tools/search/anthropic.cjs +40 -0
  49. package/dist/cjs/tools/search/anthropic.cjs.map +1 -0
  50. package/dist/cjs/tools/search/firecrawl.cjs +61 -13
  51. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
  52. package/dist/cjs/tools/search/format.cjs +9 -3
  53. package/dist/cjs/tools/search/format.cjs.map +1 -1
  54. package/dist/cjs/tools/search/rerankers.cjs +35 -50
  55. package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
  56. package/dist/cjs/tools/search/schema.cjs +70 -0
  57. package/dist/cjs/tools/search/schema.cjs.map +1 -0
  58. package/dist/cjs/tools/search/search.cjs +145 -38
  59. package/dist/cjs/tools/search/search.cjs.map +1 -1
  60. package/dist/cjs/tools/search/tool.cjs +165 -48
  61. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  62. package/dist/cjs/tools/search/utils.cjs +34 -5
  63. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  64. package/dist/cjs/utils/events.cjs +31 -0
  65. package/dist/cjs/utils/events.cjs.map +1 -0
  66. package/dist/cjs/utils/title.cjs +57 -21
  67. package/dist/cjs/utils/title.cjs.map +1 -1
  68. package/dist/cjs/utils/tokens.cjs +54 -7
  69. package/dist/cjs/utils/tokens.cjs.map +1 -1
  70. package/dist/esm/agents/AgentContext.mjs +216 -0
  71. package/dist/esm/agents/AgentContext.mjs.map +1 -0
  72. package/dist/esm/common/enum.mjs +15 -6
  73. package/dist/esm/common/enum.mjs.map +1 -1
  74. package/dist/esm/events.mjs +10 -6
  75. package/dist/esm/events.mjs.map +1 -1
  76. package/dist/esm/graphs/Graph.mjs +311 -214
  77. package/dist/esm/graphs/Graph.mjs.map +1 -1
  78. package/dist/esm/graphs/MultiAgentGraph.mjs +320 -0
  79. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -0
  80. package/dist/esm/llm/anthropic/index.mjs +54 -9
  81. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  82. package/dist/esm/llm/anthropic/types.mjs.map +1 -1
  83. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +52 -6
  84. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  85. package/dist/esm/llm/anthropic/utils/message_outputs.mjs +22 -2
  86. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  87. package/dist/esm/llm/anthropic/utils/tools.mjs +27 -0
  88. package/dist/esm/llm/anthropic/utils/tools.mjs.map +1 -0
  89. package/dist/esm/llm/google/index.mjs +142 -0
  90. package/dist/esm/llm/google/index.mjs.map +1 -0
  91. package/dist/esm/llm/google/utils/common.mjs +471 -0
  92. package/dist/esm/llm/google/utils/common.mjs.map +1 -0
  93. package/dist/esm/llm/ollama/index.mjs +65 -0
  94. package/dist/esm/llm/ollama/index.mjs.map +1 -0
  95. package/dist/esm/llm/ollama/utils.mjs +155 -0
  96. package/dist/esm/llm/ollama/utils.mjs.map +1 -0
  97. package/dist/esm/llm/openai/index.mjs +388 -4
  98. package/dist/esm/llm/openai/index.mjs.map +1 -1
  99. package/dist/esm/llm/openai/utils/index.mjs +666 -0
  100. package/dist/esm/llm/openai/utils/index.mjs.map +1 -0
  101. package/dist/esm/llm/providers.mjs +5 -5
  102. package/dist/esm/llm/providers.mjs.map +1 -1
  103. package/dist/esm/llm/text.mjs +14 -3
  104. package/dist/esm/llm/text.mjs.map +1 -1
  105. package/dist/esm/llm/vertexai/index.mjs +328 -0
  106. package/dist/esm/llm/vertexai/index.mjs.map +1 -0
  107. package/dist/esm/main.mjs +6 -5
  108. package/dist/esm/main.mjs.map +1 -1
  109. package/dist/esm/run.mjs +121 -83
  110. package/dist/esm/run.mjs.map +1 -1
  111. package/dist/esm/stream.mjs +87 -54
  112. package/dist/esm/stream.mjs.map +1 -1
  113. package/dist/esm/tools/ToolNode.mjs +10 -4
  114. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  115. package/dist/esm/tools/handlers.mjs +119 -15
  116. package/dist/esm/tools/handlers.mjs.map +1 -1
  117. package/dist/esm/tools/search/anthropic.mjs +37 -0
  118. package/dist/esm/tools/search/anthropic.mjs.map +1 -0
  119. package/dist/esm/tools/search/firecrawl.mjs +61 -13
  120. package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
  121. package/dist/esm/tools/search/format.mjs +10 -4
  122. package/dist/esm/tools/search/format.mjs.map +1 -1
  123. package/dist/esm/tools/search/rerankers.mjs +35 -50
  124. package/dist/esm/tools/search/rerankers.mjs.map +1 -1
  125. package/dist/esm/tools/search/schema.mjs +61 -0
  126. package/dist/esm/tools/search/schema.mjs.map +1 -0
  127. package/dist/esm/tools/search/search.mjs +146 -39
  128. package/dist/esm/tools/search/search.mjs.map +1 -1
  129. package/dist/esm/tools/search/tool.mjs +164 -47
  130. package/dist/esm/tools/search/tool.mjs.map +1 -1
  131. package/dist/esm/tools/search/utils.mjs +33 -6
  132. package/dist/esm/tools/search/utils.mjs.map +1 -1
  133. package/dist/esm/utils/events.mjs +29 -0
  134. package/dist/esm/utils/events.mjs.map +1 -0
  135. package/dist/esm/utils/title.mjs +57 -22
  136. package/dist/esm/utils/title.mjs.map +1 -1
  137. package/dist/esm/utils/tokens.mjs +54 -8
  138. package/dist/esm/utils/tokens.mjs.map +1 -1
  139. package/dist/types/agents/AgentContext.d.ts +91 -0
  140. package/dist/types/common/enum.d.ts +15 -6
  141. package/dist/types/events.d.ts +5 -4
  142. package/dist/types/graphs/Graph.d.ts +64 -67
  143. package/dist/types/graphs/MultiAgentGraph.d.ts +37 -0
  144. package/dist/types/graphs/index.d.ts +1 -0
  145. package/dist/types/llm/anthropic/index.d.ts +11 -0
  146. package/dist/types/llm/anthropic/types.d.ts +9 -3
  147. package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -1
  148. package/dist/types/llm/anthropic/utils/output_parsers.d.ts +4 -4
  149. package/dist/types/llm/anthropic/utils/tools.d.ts +3 -0
  150. package/dist/types/llm/google/index.d.ts +13 -0
  151. package/dist/types/llm/google/types.d.ts +32 -0
  152. package/dist/types/llm/google/utils/common.d.ts +19 -0
  153. package/dist/types/llm/google/utils/tools.d.ts +10 -0
  154. package/dist/types/llm/google/utils/zod_to_genai_parameters.d.ts +14 -0
  155. package/dist/types/llm/ollama/index.d.ts +7 -0
  156. package/dist/types/llm/ollama/utils.d.ts +7 -0
  157. package/dist/types/llm/openai/index.d.ts +72 -3
  158. package/dist/types/llm/openai/types.d.ts +10 -0
  159. package/dist/types/llm/openai/utils/index.d.ts +20 -0
  160. package/dist/types/llm/text.d.ts +1 -1
  161. package/dist/types/llm/vertexai/index.d.ts +293 -0
  162. package/dist/types/messages/reducer.d.ts +9 -0
  163. package/dist/types/run.d.ts +19 -12
  164. package/dist/types/scripts/ant_web_search.d.ts +1 -0
  165. package/dist/types/scripts/args.d.ts +2 -1
  166. package/dist/types/scripts/handoff-test.d.ts +1 -0
  167. package/dist/types/scripts/multi-agent-conditional.d.ts +1 -0
  168. package/dist/types/scripts/multi-agent-parallel.d.ts +1 -0
  169. package/dist/types/scripts/multi-agent-sequence.d.ts +1 -0
  170. package/dist/types/scripts/multi-agent-test.d.ts +1 -0
  171. package/dist/types/stream.d.ts +10 -3
  172. package/dist/types/tools/CodeExecutor.d.ts +2 -2
  173. package/dist/types/tools/ToolNode.d.ts +1 -1
  174. package/dist/types/tools/handlers.d.ts +17 -4
  175. package/dist/types/tools/search/anthropic.d.ts +16 -0
  176. package/dist/types/tools/search/firecrawl.d.ts +16 -0
  177. package/dist/types/tools/search/rerankers.d.ts +8 -5
  178. package/dist/types/tools/search/schema.d.ts +16 -0
  179. package/dist/types/tools/search/tool.d.ts +13 -0
  180. package/dist/types/tools/search/types.d.ts +64 -9
  181. package/dist/types/tools/search/utils.d.ts +9 -2
  182. package/dist/types/types/graph.d.ts +95 -15
  183. package/dist/types/types/llm.d.ts +24 -10
  184. package/dist/types/types/run.d.ts +46 -8
  185. package/dist/types/types/stream.d.ts +16 -2
  186. package/dist/types/types/tools.d.ts +1 -1
  187. package/dist/types/utils/events.d.ts +6 -0
  188. package/dist/types/utils/title.d.ts +2 -1
  189. package/dist/types/utils/tokens.d.ts +24 -0
  190. package/package.json +35 -18
  191. package/src/agents/AgentContext.ts +315 -0
  192. package/src/common/enum.ts +14 -5
  193. package/src/events.ts +24 -13
  194. package/src/graphs/Graph.ts +495 -312
  195. package/src/graphs/MultiAgentGraph.ts +381 -0
  196. package/src/graphs/index.ts +2 -1
  197. package/src/llm/anthropic/Jacob_Lee_Resume_2023.pdf +0 -0
  198. package/src/llm/anthropic/index.ts +78 -13
  199. package/src/llm/anthropic/llm.spec.ts +491 -115
  200. package/src/llm/anthropic/types.ts +39 -3
  201. package/src/llm/anthropic/utils/message_inputs.ts +67 -11
  202. package/src/llm/anthropic/utils/message_outputs.ts +21 -2
  203. package/src/llm/anthropic/utils/output_parsers.ts +25 -6
  204. package/src/llm/anthropic/utils/tools.ts +29 -0
  205. package/src/llm/google/index.ts +218 -0
  206. package/src/llm/google/types.ts +43 -0
  207. package/src/llm/google/utils/common.ts +646 -0
  208. package/src/llm/google/utils/tools.ts +160 -0
  209. package/src/llm/google/utils/zod_to_genai_parameters.ts +86 -0
  210. package/src/llm/ollama/index.ts +89 -0
  211. package/src/llm/ollama/utils.ts +193 -0
  212. package/src/llm/openai/index.ts +600 -14
  213. package/src/llm/openai/types.ts +24 -0
  214. package/src/llm/openai/utils/index.ts +912 -0
  215. package/src/llm/openai/utils/isReasoningModel.test.ts +90 -0
  216. package/src/llm/providers.ts +10 -9
  217. package/src/llm/text.ts +26 -7
  218. package/src/llm/vertexai/index.ts +360 -0
  219. package/src/messages/reducer.ts +80 -0
  220. package/src/run.ts +181 -112
  221. package/src/scripts/ant_web_search.ts +158 -0
  222. package/src/scripts/args.ts +12 -8
  223. package/src/scripts/cli4.ts +29 -21
  224. package/src/scripts/cli5.ts +29 -21
  225. package/src/scripts/code_exec.ts +54 -23
  226. package/src/scripts/code_exec_files.ts +48 -17
  227. package/src/scripts/code_exec_simple.ts +46 -27
  228. package/src/scripts/handoff-test.ts +135 -0
  229. package/src/scripts/image.ts +52 -20
  230. package/src/scripts/multi-agent-conditional.ts +220 -0
  231. package/src/scripts/multi-agent-example-output.md +110 -0
  232. package/src/scripts/multi-agent-parallel.ts +337 -0
  233. package/src/scripts/multi-agent-sequence.ts +212 -0
  234. package/src/scripts/multi-agent-test.ts +186 -0
  235. package/src/scripts/search.ts +4 -12
  236. package/src/scripts/simple.ts +25 -10
  237. package/src/scripts/tools.ts +48 -18
  238. package/src/specs/anthropic.simple.test.ts +150 -34
  239. package/src/specs/azure.simple.test.ts +325 -0
  240. package/src/specs/openai.simple.test.ts +140 -33
  241. package/src/specs/openrouter.simple.test.ts +107 -0
  242. package/src/specs/prune.test.ts +4 -9
  243. package/src/specs/reasoning.test.ts +80 -44
  244. package/src/specs/token-memoization.test.ts +39 -0
  245. package/src/stream.test.ts +94 -0
  246. package/src/stream.ts +139 -60
  247. package/src/tools/ToolNode.ts +21 -7
  248. package/src/tools/handlers.ts +192 -18
  249. package/src/tools/search/anthropic.ts +51 -0
  250. package/src/tools/search/firecrawl.ts +78 -24
  251. package/src/tools/search/format.ts +10 -5
  252. package/src/tools/search/rerankers.ts +50 -62
  253. package/src/tools/search/schema.ts +63 -0
  254. package/src/tools/search/search.ts +167 -34
  255. package/src/tools/search/tool.ts +222 -46
  256. package/src/tools/search/types.ts +65 -10
  257. package/src/tools/search/utils.ts +37 -5
  258. package/src/types/graph.ts +272 -103
  259. package/src/types/llm.ts +25 -12
  260. package/src/types/run.ts +51 -13
  261. package/src/types/stream.ts +22 -1
  262. package/src/types/tools.ts +16 -10
  263. package/src/utils/events.ts +32 -0
  264. package/src/utils/llmConfig.ts +20 -8
  265. package/src/utils/title.ts +104 -30
  266. package/src/utils/tokens.ts +69 -10
@@ -1,88 +1,246 @@
1
- /* eslint-disable no-console */
2
1
  import { z } from 'zod';
3
2
  import { tool, DynamicStructuredTool } from '@langchain/core/tools';
4
3
  import type { RunnableConfig } from '@langchain/core/runnables';
5
4
  import type * as t from './types';
5
+ import {
6
+ DATE_RANGE,
7
+ querySchema,
8
+ dateSchema,
9
+ countrySchema,
10
+ imagesSchema,
11
+ videosSchema,
12
+ newsSchema,
13
+ } from './schema';
6
14
  import { createSearchAPI, createSourceProcessor } from './search';
7
15
  import { createFirecrawlScraper } from './firecrawl';
8
16
  import { expandHighlights } from './highlights';
9
17
  import { formatResultsForLLM } from './format';
18
+ import { createDefaultLogger } from './utils';
10
19
  import { createReranker } from './rerankers';
11
20
  import { Constants } from '@/common';
12
21
 
13
- const DEFAULT_QUERY_DESCRIPTION = `
14
- GUIDELINES:
15
- - Start broad, then narrow: Begin with key concepts, then refine with specifics
16
- - Think like sources: Use terminology experts would use in the field
17
- - Consider perspective: Frame queries from different viewpoints for better results
18
- - Quality over quantity: A precise 3-4 word query often beats lengthy sentences
19
-
20
- TECHNIQUES (combine for power searches):
21
- - EXACT PHRASES: Use quotes ("climate change report")
22
- - EXCLUDE TERMS: Use minus to remove unwanted results (-wikipedia)
23
- - SITE-SPECIFIC: Restrict to websites (site:edu research)
24
- - FILETYPE: Find specific documents (filetype:pdf study)
25
- - OR OPERATOR: Find alternatives (electric OR hybrid cars)
26
- - DATE RANGE: Recent information (data after:2020)
27
- - WILDCARDS: Use * for unknown terms (how to * bread)
28
- - SPECIFIC QUESTIONS: Use who/what/when/where/why/how
29
- - DOMAIN TERMS: Include technical terminology for specialized topics
30
- - CONCISE TERMS: Prioritize keywords over sentences
31
- `.trim();
32
-
33
- const DEFAULT_COUNTRY_DESCRIPTION = `Country code to localize search results.
34
- Use standard 2-letter country codes: "us", "uk", "ca", "de", "fr", "jp", "br", etc.
35
- Provide this when the search should return results specific to a particular country.
36
- Examples:
37
- - "us" for United States (default)
38
- - "de" for Germany
39
- - "in" for India
40
- `.trim();
22
+ /**
23
+ * Executes parallel searches and merges the results
24
+ */
25
+ async function executeParallelSearches({
26
+ searchAPI,
27
+ query,
28
+ date,
29
+ country,
30
+ safeSearch,
31
+ images,
32
+ videos,
33
+ news,
34
+ logger,
35
+ }: {
36
+ searchAPI: ReturnType<typeof createSearchAPI>;
37
+ query: string;
38
+ date?: DATE_RANGE;
39
+ country?: string;
40
+ safeSearch: t.SearchToolConfig['safeSearch'];
41
+ images: boolean;
42
+ videos: boolean;
43
+ news: boolean;
44
+ logger: t.Logger;
45
+ }): Promise<t.SearchResult> {
46
+ // Prepare all search tasks to run in parallel
47
+ const searchTasks: Promise<t.SearchResult>[] = [
48
+ // Main search
49
+ searchAPI.getSources({
50
+ query,
51
+ date,
52
+ country,
53
+ safeSearch,
54
+ }),
55
+ ];
56
+
57
+ if (images) {
58
+ searchTasks.push(
59
+ searchAPI
60
+ .getSources({
61
+ query,
62
+ date,
63
+ country,
64
+ safeSearch,
65
+ type: 'images',
66
+ })
67
+ .catch((error) => {
68
+ logger.error('Error fetching images:', error);
69
+ return {
70
+ success: false,
71
+ error: `Images search failed: ${error instanceof Error ? error.message : String(error)}`,
72
+ };
73
+ })
74
+ );
75
+ }
76
+ if (videos) {
77
+ searchTasks.push(
78
+ searchAPI
79
+ .getSources({
80
+ query,
81
+ date,
82
+ country,
83
+ safeSearch,
84
+ type: 'videos',
85
+ })
86
+ .catch((error) => {
87
+ logger.error('Error fetching videos:', error);
88
+ return {
89
+ success: false,
90
+ error: `Videos search failed: ${error instanceof Error ? error.message : String(error)}`,
91
+ };
92
+ })
93
+ );
94
+ }
95
+ if (news) {
96
+ searchTasks.push(
97
+ searchAPI
98
+ .getSources({
99
+ query,
100
+ date,
101
+ country,
102
+ safeSearch,
103
+ type: 'news',
104
+ })
105
+ .catch((error) => {
106
+ logger.error('Error fetching news:', error);
107
+ return {
108
+ success: false,
109
+ error: `News search failed: ${error instanceof Error ? error.message : String(error)}`,
110
+ };
111
+ })
112
+ );
113
+ }
114
+
115
+ // Run all searches in parallel
116
+ const results = await Promise.all(searchTasks);
117
+
118
+ // Get the main search result (first result)
119
+ const mainResult = results[0];
120
+ if (!mainResult.success) {
121
+ throw new Error(mainResult.error ?? 'Search failed');
122
+ }
123
+
124
+ // Merge additional results with the main results
125
+ const mergedResults = { ...mainResult.data };
126
+
127
+ // Convert existing news to topStories if present
128
+ if (mergedResults.news !== undefined && mergedResults.news.length > 0) {
129
+ const existingNewsAsTopStories = mergedResults.news
130
+ .filter((newsItem) => newsItem.link !== undefined && newsItem.link !== '')
131
+ .map((newsItem) => ({
132
+ title: newsItem.title ?? '',
133
+ link: newsItem.link ?? '',
134
+ source: newsItem.source ?? '',
135
+ date: newsItem.date ?? '',
136
+ imageUrl: newsItem.imageUrl ?? '',
137
+ processed: false,
138
+ }));
139
+ mergedResults.topStories = [
140
+ ...(mergedResults.topStories ?? []),
141
+ ...existingNewsAsTopStories,
142
+ ];
143
+ delete mergedResults.news;
144
+ }
145
+
146
+ results.slice(1).forEach((result) => {
147
+ if (result.success && result.data !== undefined) {
148
+ if (result.data.images !== undefined && result.data.images.length > 0) {
149
+ mergedResults.images = [
150
+ ...(mergedResults.images ?? []),
151
+ ...result.data.images,
152
+ ];
153
+ }
154
+ if (result.data.videos !== undefined && result.data.videos.length > 0) {
155
+ mergedResults.videos = [
156
+ ...(mergedResults.videos ?? []),
157
+ ...result.data.videos,
158
+ ];
159
+ }
160
+ if (result.data.news !== undefined && result.data.news.length > 0) {
161
+ const newsAsTopStories = result.data.news.map((newsItem) => ({
162
+ ...newsItem,
163
+ link: newsItem.link ?? '',
164
+ }));
165
+ mergedResults.topStories = [
166
+ ...(mergedResults.topStories ?? []),
167
+ ...newsAsTopStories,
168
+ ];
169
+ }
170
+ }
171
+ });
172
+
173
+ return { success: true, data: mergedResults };
174
+ }
41
175
 
42
176
  function createSearchProcessor({
43
177
  searchAPI,
178
+ safeSearch,
44
179
  sourceProcessor,
45
180
  onGetHighlights,
181
+ logger,
46
182
  }: {
183
+ safeSearch: t.SearchToolConfig['safeSearch'];
47
184
  searchAPI: ReturnType<typeof createSearchAPI>;
48
185
  sourceProcessor: ReturnType<typeof createSourceProcessor>;
49
186
  onGetHighlights: t.SearchToolConfig['onGetHighlights'];
187
+ logger: t.Logger;
50
188
  }) {
51
189
  return async function ({
52
190
  query,
191
+ date,
53
192
  country,
54
193
  proMode = true,
55
194
  maxSources = 5,
56
195
  onSearchResults,
196
+ images = false,
197
+ videos = false,
198
+ news = false,
57
199
  }: {
58
200
  query: string;
59
201
  country?: string;
60
- maxSources?: number;
202
+ date?: DATE_RANGE;
61
203
  proMode?: boolean;
204
+ maxSources?: number;
62
205
  onSearchResults: t.SearchToolConfig['onSearchResults'];
206
+ images?: boolean;
207
+ videos?: boolean;
208
+ news?: boolean;
63
209
  }): Promise<t.SearchResultData> {
64
210
  try {
65
- const result = await searchAPI.getSources({ query, country });
66
- onSearchResults?.(result);
211
+ // Execute parallel searches and merge results
212
+ const searchResult = await executeParallelSearches({
213
+ searchAPI,
214
+ query,
215
+ date,
216
+ country,
217
+ safeSearch,
218
+ images,
219
+ videos,
220
+ news,
221
+ logger,
222
+ });
67
223
 
68
- if (!result.success) {
69
- throw new Error(result.error ?? 'Search failed');
70
- }
224
+ onSearchResults?.(searchResult);
71
225
 
72
226
  const processedSources = await sourceProcessor.processSources({
73
227
  query,
74
- result,
228
+ news,
229
+ result: searchResult,
75
230
  proMode,
76
231
  onGetHighlights,
77
232
  numElements: maxSources,
78
233
  });
234
+
79
235
  return expandHighlights(processedSources);
80
236
  } catch (error) {
81
- console.error('Error in search:', error);
237
+ logger.error('Error in search:', error);
82
238
  return {
83
239
  organic: [],
84
240
  topStories: [],
85
241
  images: [],
242
+ videos: [],
243
+ news: [],
86
244
  relatedSearches: [],
87
245
  error: error instanceof Error ? error.message : String(error),
88
246
  };
@@ -116,11 +274,15 @@ function createTool({
116
274
  }): DynamicStructuredTool<typeof schema> {
117
275
  return tool<typeof schema>(
118
276
  async (params, runnableConfig) => {
119
- const { query, country: _c } = params;
277
+ const { query, date, country: _c, images, videos, news } = params;
120
278
  const country = typeof _c === 'string' && _c ? _c : undefined;
121
279
  const searchResult = await search({
122
280
  query,
281
+ date,
123
282
  country,
283
+ images,
284
+ videos,
285
+ news,
124
286
  onSearchResults: createOnSearchResults({
125
287
  runnableConfig,
126
288
  onSearchResults: _onSearchResults,
@@ -181,28 +343,36 @@ export const createSearchTool = (
181
343
  topResults = 5,
182
344
  strategies = ['no_extraction'],
183
345
  filterContent = true,
346
+ safeSearch = 1,
184
347
  firecrawlApiKey,
185
348
  firecrawlApiUrl,
186
- firecrawlFormats = ['markdown', 'html'],
349
+ firecrawlOptions,
350
+ scraperTimeout,
187
351
  jinaApiKey,
188
352
  cohereApiKey,
189
353
  onSearchResults: _onSearchResults,
190
354
  onGetHighlights,
191
355
  } = config;
192
356
 
193
- const querySchema = z.string().describe(DEFAULT_QUERY_DESCRIPTION);
357
+ const logger = config.logger || createDefaultLogger();
358
+
194
359
  const schemaObject: {
195
360
  query: z.ZodString;
361
+ date: z.ZodOptional<z.ZodNativeEnum<typeof DATE_RANGE>>;
196
362
  country?: z.ZodOptional<z.ZodString>;
363
+ images: z.ZodOptional<z.ZodBoolean>;
364
+ videos: z.ZodOptional<z.ZodBoolean>;
365
+ news: z.ZodOptional<z.ZodBoolean>;
197
366
  } = {
198
367
  query: querySchema,
368
+ date: dateSchema,
369
+ images: imagesSchema,
370
+ videos: videosSchema,
371
+ news: newsSchema,
199
372
  };
200
373
 
201
374
  if (searchProvider === 'serper') {
202
- schemaObject.country = z
203
- .string()
204
- .optional()
205
- .describe(DEFAULT_COUNTRY_DESCRIPTION);
375
+ schemaObject.country = countrySchema;
206
376
  }
207
377
 
208
378
  const toolSchema = z.object(schemaObject);
@@ -215,19 +385,22 @@ export const createSearchTool = (
215
385
  });
216
386
 
217
387
  const firecrawlScraper = createFirecrawlScraper({
388
+ ...firecrawlOptions,
218
389
  apiKey: firecrawlApiKey ?? process.env.FIRECRAWL_API_KEY,
219
390
  apiUrl: firecrawlApiUrl,
220
- formats: firecrawlFormats,
391
+ timeout: scraperTimeout ?? firecrawlOptions?.timeout,
392
+ formats: firecrawlOptions?.formats ?? ['markdown', 'rawHtml'],
221
393
  });
222
394
 
223
395
  const selectedReranker = createReranker({
224
396
  rerankerType,
225
397
  jinaApiKey,
226
398
  cohereApiKey,
399
+ logger,
227
400
  });
228
401
 
229
402
  if (!selectedReranker) {
230
- console.warn('No reranker selected. Using default ranking.');
403
+ logger.warn('No reranker selected. Using default ranking.');
231
404
  }
232
405
 
233
406
  const sourceProcessor = createSourceProcessor(
@@ -236,14 +409,17 @@ export const createSearchTool = (
236
409
  topResults,
237
410
  strategies,
238
411
  filterContent,
412
+ logger,
239
413
  },
240
414
  firecrawlScraper
241
415
  );
242
416
 
243
417
  const search = createSearchProcessor({
244
418
  searchAPI,
419
+ safeSearch,
245
420
  sourceProcessor,
246
421
  onGetHighlights,
422
+ logger,
247
423
  });
248
424
 
249
425
  return createTool({
@@ -1,6 +1,8 @@
1
1
  import { z } from 'zod';
2
+ import type { Logger as WinstonLogger } from 'winston';
2
3
  import type { RunnableConfig } from '@langchain/core/runnables';
3
4
  import type { BaseReranker } from './rerankers';
5
+ import { DATE_RANGE } from './schema';
4
6
 
5
7
  export type SearchProvider = 'serper' | 'searxng';
6
8
  export type RerankerType = 'infinity' | 'jina' | 'cohere' | 'none';
@@ -86,12 +88,13 @@ export interface ProcessSourcesConfig {
86
88
  strategies?: string[];
87
89
  filterContent?: boolean;
88
90
  reranker?: BaseReranker;
91
+ logger?: Logger;
89
92
  }
90
93
 
91
94
  export interface FirecrawlConfig {
92
95
  firecrawlApiKey?: string;
93
96
  firecrawlApiUrl?: string;
94
- firecrawlFormats?: string[];
97
+ firecrawlOptions?: FirecrawlScraperConfig;
95
98
  }
96
99
 
97
100
  export interface ScraperContentResult {
@@ -135,13 +138,19 @@ export interface CohereRerankerResponse {
135
138
  };
136
139
  }
137
140
 
141
+ export type SafeSearchLevel = 0 | 1 | 2;
142
+
143
+ export type Logger = WinstonLogger;
138
144
  export interface SearchToolConfig
139
145
  extends SearchConfig,
140
146
  ProcessSourcesConfig,
141
147
  FirecrawlConfig {
148
+ logger?: Logger;
149
+ safeSearch?: SafeSearchLevel;
142
150
  jinaApiKey?: string;
143
151
  cohereApiKey?: string;
144
152
  rerankerType?: RerankerType;
153
+ scraperTimeout?: number;
145
154
  onSearchResults?: (
146
155
  results: SearchResult,
147
156
  runnableConfig?: RunnableConfig
@@ -161,15 +170,10 @@ export type UsedReferences = {
161
170
  }[];
162
171
 
163
172
  /** Firecrawl */
164
-
165
- export interface FirecrawlScrapeOptions {
166
- formats?: string[];
167
- includeTags?: string[];
168
- excludeTags?: string[];
169
- headers?: Record<string, string>;
170
- waitFor?: number;
171
- timeout?: number;
172
- }
173
+ export type FirecrawlScrapeOptions = Omit<
174
+ FirecrawlScraperConfig,
175
+ 'apiKey' | 'apiUrl' | 'logger'
176
+ >;
173
177
 
174
178
  export interface ScrapeMetadata {
175
179
  // Core source information
@@ -251,12 +255,34 @@ export interface FirecrawlScraperConfig {
251
255
  apiUrl?: string;
252
256
  formats?: string[];
253
257
  timeout?: number;
258
+ logger?: Logger;
259
+ includeTags?: string[];
260
+ excludeTags?: string[];
261
+ waitFor?: number;
262
+ maxAge?: number;
263
+ mobile?: boolean;
264
+ skipTlsVerification?: boolean;
265
+ blockAds?: boolean;
266
+ removeBase64Images?: boolean;
267
+ parsePDF?: boolean;
268
+ storeInCache?: boolean;
269
+ zeroDataRetention?: boolean;
270
+ headers?: Record<string, string>;
271
+ location?: { country?: string; languages?: string[] };
272
+ onlyMainContent?: boolean;
273
+ changeTrackingOptions?: object;
254
274
  }
255
275
 
256
276
  export type GetSourcesParams = {
257
277
  query: string;
278
+ date?: DATE_RANGE;
258
279
  country?: string;
259
280
  numResults?: number;
281
+ safeSearch?: SearchToolConfig['safeSearch'];
282
+ images?: boolean;
283
+ videos?: boolean;
284
+ news?: boolean;
285
+ type?: 'search' | 'images' | 'videos' | 'news';
260
286
  };
261
287
 
262
288
  /** Serper API */
@@ -435,6 +461,13 @@ export interface SerperSearchInput {
435
461
  */
436
462
  autocorrect?: boolean;
437
463
  page?: number;
464
+ /**
465
+ * Date range for search results
466
+ * Options: "h" (past hour), "d" (past 24 hours), "w" (past week),
467
+ * "m" (past month), "y" (past year)
468
+ * `qdr:${DATE_RANGE}`
469
+ */
470
+ tbs?: string;
438
471
  }
439
472
 
440
473
  export type SerperResultData = {
@@ -558,12 +591,22 @@ export interface SearXNGResult {
558
591
  content?: string;
559
592
  publishedDate?: string;
560
593
  img_src?: string;
594
+ score?: number;
595
+ engine?: string;
596
+ category?: string;
597
+ thumbnail?: string;
598
+ priority?: string;
599
+ engines?: string[];
600
+ positions?: number[];
601
+ template?: string;
602
+ parsed_url?: string[];
561
603
  }
562
604
 
563
605
  export type ProcessSourcesFields = {
564
606
  result: SearchResult;
565
607
  numElements: number;
566
608
  query: string;
609
+ news: boolean;
567
610
  proMode: boolean;
568
611
  onGetHighlights: SearchToolConfig['onGetHighlights'];
569
612
  };
@@ -571,16 +614,28 @@ export type ProcessSourcesFields = {
571
614
  export type SearchToolSchema = z.ZodObject<
572
615
  {
573
616
  query: z.ZodString;
617
+ date: z.ZodOptional<z.ZodNativeEnum<typeof DATE_RANGE>>;
574
618
  country?: z.ZodOptional<z.ZodString>;
619
+ images: z.ZodOptional<z.ZodBoolean>;
620
+ videos: z.ZodOptional<z.ZodBoolean>;
621
+ news: z.ZodOptional<z.ZodBoolean>;
575
622
  },
576
623
  'strip',
577
624
  z.ZodTypeAny,
578
625
  {
579
626
  query: string;
627
+ date?: DATE_RANGE;
580
628
  country?: unknown;
629
+ images?: boolean;
630
+ videos?: boolean;
631
+ news?: boolean;
581
632
  },
582
633
  {
583
634
  query: string;
635
+ date?: DATE_RANGE;
584
636
  country?: unknown;
637
+ images?: boolean;
638
+ videos?: boolean;
639
+ news?: boolean;
585
640
  }
586
641
  >;
@@ -1,9 +1,36 @@
1
1
  /* eslint-disable no-console */
2
+
2
3
  import type * as t from './types';
3
4
 
5
+ /**
6
+ * Singleton instance of the default logger
7
+ */
8
+ let defaultLoggerInstance: t.Logger | null = null;
9
+
10
+ /**
11
+ * Creates a default logger that maps to console methods
12
+ * Uses a singleton pattern to avoid creating multiple instances
13
+ * @returns A default logger that implements the Logger interface
14
+ */
15
+ export const createDefaultLogger = (): t.Logger => {
16
+ if (!defaultLoggerInstance) {
17
+ defaultLoggerInstance = {
18
+ error: console.error,
19
+ warn: console.warn,
20
+ info: console.info,
21
+ debug: console.debug,
22
+ } as t.Logger;
23
+ }
24
+ return defaultLoggerInstance;
25
+ };
26
+
27
+ export const fileExtRegex =
28
+ /\.(pdf|jpe?g|png|gif|svg|webp|bmp|ico|tiff?|avif|heic|doc[xm]?|xls[xm]?|ppt[xm]?|zip|rar|mp[34]|mov|avi|wav)(?:\?.*)?$/i;
29
+
4
30
  export const getDomainName = (
5
31
  link: string,
6
- metadata?: t.ScrapeMetadata
32
+ metadata?: t.ScrapeMetadata,
33
+ logger?: t.Logger
7
34
  ): string | undefined => {
8
35
  try {
9
36
  const url = metadata?.sourceURL ?? metadata?.url ?? (link || '');
@@ -13,7 +40,11 @@ export const getDomainName = (
13
40
  }
14
41
  } catch (e) {
15
42
  // URL parsing failed
16
- console.error('Error parsing URL:', e);
43
+ if (logger) {
44
+ logger.error('Error parsing URL:', e);
45
+ } else {
46
+ console.error('Error parsing URL:', e);
47
+ }
17
48
  }
18
49
 
19
50
  return;
@@ -21,9 +52,10 @@ export const getDomainName = (
21
52
 
22
53
  export function getAttribution(
23
54
  link: string,
24
- metadata?: t.ScrapeMetadata
55
+ metadata?: t.ScrapeMetadata,
56
+ logger?: t.Logger
25
57
  ): string | undefined {
26
- if (!metadata) return getDomainName(link, metadata);
58
+ if (!metadata) return getDomainName(link, metadata, logger);
27
59
 
28
60
  const twitterSite = metadata['twitter:site'];
29
61
  const twitterSiteFormatted =
@@ -43,5 +75,5 @@ export function getAttribution(
43
75
  return attribution;
44
76
  }
45
77
 
46
- return getDomainName(link, metadata);
78
+ return getDomainName(link, metadata, logger);
47
79
  }