@librechat/agents 3.0.0-rc9 → 3.0.0

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 (195) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +6 -2
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +23 -2
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/graphs/MultiAgentGraph.cjs +5 -5
  6. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  7. package/dist/cjs/instrumentation.cjs +21 -0
  8. package/dist/cjs/instrumentation.cjs.map +1 -0
  9. package/dist/cjs/llm/anthropic/index.cjs +21 -2
  10. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  11. package/dist/cjs/llm/google/index.cjs +3 -0
  12. package/dist/cjs/llm/google/index.cjs.map +1 -1
  13. package/dist/cjs/llm/google/utils/common.cjs +13 -0
  14. package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
  15. package/dist/cjs/llm/ollama/index.cjs +3 -0
  16. package/dist/cjs/llm/ollama/index.cjs.map +1 -1
  17. package/dist/cjs/llm/openai/index.cjs +18 -3
  18. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  19. package/dist/cjs/llm/openai/utils/index.cjs +6 -1
  20. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  21. package/dist/cjs/llm/openrouter/index.cjs +5 -1
  22. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  23. package/dist/cjs/llm/vertexai/index.cjs +1 -1
  24. package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
  25. package/dist/cjs/main.cjs +8 -2
  26. package/dist/cjs/main.cjs.map +1 -1
  27. package/dist/cjs/messages/cache.cjs +49 -0
  28. package/dist/cjs/messages/cache.cjs.map +1 -0
  29. package/dist/cjs/messages/content.cjs +53 -0
  30. package/dist/cjs/messages/content.cjs.map +1 -0
  31. package/dist/cjs/messages/core.cjs +5 -1
  32. package/dist/cjs/messages/core.cjs.map +1 -1
  33. package/dist/cjs/messages/format.cjs +50 -59
  34. package/dist/cjs/messages/format.cjs.map +1 -1
  35. package/dist/cjs/messages/prune.cjs +28 -0
  36. package/dist/cjs/messages/prune.cjs.map +1 -1
  37. package/dist/cjs/run.cjs +57 -5
  38. package/dist/cjs/run.cjs.map +1 -1
  39. package/dist/cjs/stream.cjs +7 -0
  40. package/dist/cjs/stream.cjs.map +1 -1
  41. package/dist/cjs/tools/ToolNode.cjs +2 -0
  42. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  43. package/dist/cjs/tools/search/firecrawl.cjs +3 -1
  44. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
  45. package/dist/cjs/tools/search/rerankers.cjs +8 -6
  46. package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
  47. package/dist/cjs/tools/search/search.cjs +5 -5
  48. package/dist/cjs/tools/search/search.cjs.map +1 -1
  49. package/dist/cjs/tools/search/serper-scraper.cjs +132 -0
  50. package/dist/cjs/tools/search/serper-scraper.cjs.map +1 -0
  51. package/dist/cjs/tools/search/tool.cjs +46 -9
  52. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  53. package/dist/cjs/utils/handlers.cjs +70 -0
  54. package/dist/cjs/utils/handlers.cjs.map +1 -0
  55. package/dist/cjs/utils/misc.cjs +8 -1
  56. package/dist/cjs/utils/misc.cjs.map +1 -1
  57. package/dist/cjs/utils/title.cjs +54 -25
  58. package/dist/cjs/utils/title.cjs.map +1 -1
  59. package/dist/esm/agents/AgentContext.mjs +6 -2
  60. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  61. package/dist/esm/graphs/Graph.mjs +23 -2
  62. package/dist/esm/graphs/Graph.mjs.map +1 -1
  63. package/dist/esm/graphs/MultiAgentGraph.mjs +5 -5
  64. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  65. package/dist/esm/instrumentation.mjs +19 -0
  66. package/dist/esm/instrumentation.mjs.map +1 -0
  67. package/dist/esm/llm/anthropic/index.mjs +21 -2
  68. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  69. package/dist/esm/llm/google/index.mjs +3 -0
  70. package/dist/esm/llm/google/index.mjs.map +1 -1
  71. package/dist/esm/llm/google/utils/common.mjs +13 -0
  72. package/dist/esm/llm/google/utils/common.mjs.map +1 -1
  73. package/dist/esm/llm/ollama/index.mjs +3 -0
  74. package/dist/esm/llm/ollama/index.mjs.map +1 -1
  75. package/dist/esm/llm/openai/index.mjs +18 -3
  76. package/dist/esm/llm/openai/index.mjs.map +1 -1
  77. package/dist/esm/llm/openai/utils/index.mjs +6 -1
  78. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  79. package/dist/esm/llm/openrouter/index.mjs +5 -1
  80. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  81. package/dist/esm/llm/vertexai/index.mjs +1 -1
  82. package/dist/esm/llm/vertexai/index.mjs.map +1 -1
  83. package/dist/esm/main.mjs +5 -2
  84. package/dist/esm/main.mjs.map +1 -1
  85. package/dist/esm/messages/cache.mjs +47 -0
  86. package/dist/esm/messages/cache.mjs.map +1 -0
  87. package/dist/esm/messages/content.mjs +51 -0
  88. package/dist/esm/messages/content.mjs.map +1 -0
  89. package/dist/esm/messages/core.mjs +5 -1
  90. package/dist/esm/messages/core.mjs.map +1 -1
  91. package/dist/esm/messages/format.mjs +50 -58
  92. package/dist/esm/messages/format.mjs.map +1 -1
  93. package/dist/esm/messages/prune.mjs +28 -0
  94. package/dist/esm/messages/prune.mjs.map +1 -1
  95. package/dist/esm/run.mjs +57 -5
  96. package/dist/esm/run.mjs.map +1 -1
  97. package/dist/esm/stream.mjs +7 -0
  98. package/dist/esm/stream.mjs.map +1 -1
  99. package/dist/esm/tools/ToolNode.mjs +2 -0
  100. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  101. package/dist/esm/tools/search/firecrawl.mjs +3 -1
  102. package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
  103. package/dist/esm/tools/search/rerankers.mjs +8 -6
  104. package/dist/esm/tools/search/rerankers.mjs.map +1 -1
  105. package/dist/esm/tools/search/search.mjs +5 -5
  106. package/dist/esm/tools/search/search.mjs.map +1 -1
  107. package/dist/esm/tools/search/serper-scraper.mjs +129 -0
  108. package/dist/esm/tools/search/serper-scraper.mjs.map +1 -0
  109. package/dist/esm/tools/search/tool.mjs +46 -9
  110. package/dist/esm/tools/search/tool.mjs.map +1 -1
  111. package/dist/esm/utils/handlers.mjs +68 -0
  112. package/dist/esm/utils/handlers.mjs.map +1 -0
  113. package/dist/esm/utils/misc.mjs +8 -2
  114. package/dist/esm/utils/misc.mjs.map +1 -1
  115. package/dist/esm/utils/title.mjs +54 -25
  116. package/dist/esm/utils/title.mjs.map +1 -1
  117. package/dist/types/agents/AgentContext.d.ts +4 -1
  118. package/dist/types/instrumentation.d.ts +1 -0
  119. package/dist/types/llm/anthropic/index.d.ts +3 -0
  120. package/dist/types/llm/google/index.d.ts +1 -0
  121. package/dist/types/llm/ollama/index.d.ts +1 -0
  122. package/dist/types/llm/openai/index.d.ts +4 -0
  123. package/dist/types/llm/openrouter/index.d.ts +4 -2
  124. package/dist/types/llm/vertexai/index.d.ts +1 -1
  125. package/dist/types/messages/cache.d.ts +8 -0
  126. package/dist/types/messages/content.d.ts +7 -0
  127. package/dist/types/messages/format.d.ts +22 -25
  128. package/dist/types/messages/index.d.ts +2 -0
  129. package/dist/types/run.d.ts +2 -1
  130. package/dist/types/tools/search/firecrawl.d.ts +2 -1
  131. package/dist/types/tools/search/rerankers.d.ts +4 -1
  132. package/dist/types/tools/search/search.d.ts +1 -2
  133. package/dist/types/tools/search/serper-scraper.d.ts +59 -0
  134. package/dist/types/tools/search/tool.d.ts +25 -4
  135. package/dist/types/tools/search/types.d.ts +31 -1
  136. package/dist/types/types/graph.d.ts +3 -1
  137. package/dist/types/types/messages.d.ts +4 -0
  138. package/dist/types/utils/handlers.d.ts +34 -0
  139. package/dist/types/utils/index.d.ts +1 -0
  140. package/dist/types/utils/misc.d.ts +1 -0
  141. package/package.json +6 -2
  142. package/src/agents/AgentContext.ts +8 -0
  143. package/src/graphs/Graph.ts +31 -2
  144. package/src/graphs/MultiAgentGraph.ts +5 -5
  145. package/src/instrumentation.ts +22 -0
  146. package/src/llm/anthropic/index.ts +23 -2
  147. package/src/llm/anthropic/llm.spec.ts +1 -1
  148. package/src/llm/google/index.ts +4 -0
  149. package/src/llm/google/utils/common.ts +14 -0
  150. package/src/llm/ollama/index.ts +3 -0
  151. package/src/llm/openai/index.ts +17 -4
  152. package/src/llm/openai/utils/index.ts +7 -1
  153. package/src/llm/openrouter/index.ts +15 -6
  154. package/src/llm/vertexai/index.ts +2 -2
  155. package/src/messages/cache.test.ts +262 -0
  156. package/src/messages/cache.ts +56 -0
  157. package/src/messages/content.test.ts +362 -0
  158. package/src/messages/content.ts +63 -0
  159. package/src/messages/core.ts +5 -2
  160. package/src/messages/format.ts +65 -71
  161. package/src/messages/formatMessage.test.ts +418 -2
  162. package/src/messages/index.ts +2 -0
  163. package/src/messages/prune.ts +51 -0
  164. package/src/run.ts +82 -10
  165. package/src/scripts/ant_web_search.ts +1 -1
  166. package/src/scripts/handoff-test.ts +1 -1
  167. package/src/scripts/multi-agent-chain.ts +4 -4
  168. package/src/scripts/multi-agent-conditional.ts +4 -4
  169. package/src/scripts/multi-agent-document-review-chain.ts +4 -4
  170. package/src/scripts/multi-agent-parallel.ts +10 -8
  171. package/src/scripts/multi-agent-sequence.ts +3 -3
  172. package/src/scripts/multi-agent-supervisor.ts +5 -3
  173. package/src/scripts/multi-agent-test.ts +2 -2
  174. package/src/scripts/search.ts +5 -1
  175. package/src/scripts/simple.ts +8 -0
  176. package/src/scripts/test-custom-prompt-key.ts +4 -4
  177. package/src/scripts/test-handoff-input.ts +3 -3
  178. package/src/scripts/test-multi-agent-list-handoff.ts +2 -2
  179. package/src/scripts/tools.ts +4 -1
  180. package/src/specs/agent-handoffs.test.ts +889 -0
  181. package/src/stream.ts +9 -2
  182. package/src/tools/search/firecrawl.ts +5 -2
  183. package/src/tools/search/jina-reranker.test.ts +126 -0
  184. package/src/tools/search/rerankers.ts +11 -5
  185. package/src/tools/search/search.ts +6 -8
  186. package/src/tools/search/serper-scraper.ts +155 -0
  187. package/src/tools/search/tool.ts +49 -8
  188. package/src/tools/search/types.ts +46 -0
  189. package/src/types/graph.ts +6 -1
  190. package/src/types/messages.ts +4 -0
  191. package/src/utils/handlers.ts +107 -0
  192. package/src/utils/index.ts +2 -1
  193. package/src/utils/llmConfig.ts +35 -1
  194. package/src/utils/misc.ts +33 -21
  195. package/src/utils/title.ts +80 -40
@@ -1,24 +1,24 @@
1
- import { AIMessage, ToolMessage, BaseMessage, HumanMessage, SystemMessage } from '@langchain/core/messages';
1
+ import { AIMessage, ToolMessage, HumanMessage, SystemMessage } from '@langchain/core/messages';
2
2
  import type { MessageContentImageUrl } from '@langchain/core/messages';
3
3
  import type { MessageContentComplex, TPayload } from '@/types';
4
4
  import { Providers } from '@/common';
5
- interface VisionMessageParams {
5
+ interface MediaMessageParams {
6
6
  message: {
7
7
  role: string;
8
8
  content: string;
9
9
  name?: string;
10
10
  [key: string]: any;
11
11
  };
12
- image_urls: MessageContentImageUrl[];
12
+ mediaParts: MessageContentComplex[];
13
13
  endpoint?: Providers;
14
14
  }
15
15
  /**
16
- * Formats a message to OpenAI Vision API payload format.
16
+ * Formats a message with media content (images, documents, videos, audios) to API payload format.
17
17
  *
18
- * @param {VisionMessageParams} params - The parameters for formatting.
19
- * @returns {Object} - The formatted message.
18
+ * @param params - The parameters for formatting.
19
+ * @returns - The formatted message.
20
20
  */
21
- export declare const formatVisionMessage: ({ message, image_urls, endpoint, }: VisionMessageParams) => {
21
+ export declare const formatMediaMessage: ({ message, endpoint, mediaParts, }: MediaMessageParams) => {
22
22
  role: string;
23
23
  content: MessageContentComplex[];
24
24
  name?: string;
@@ -31,6 +31,9 @@ interface MessageInput {
31
31
  text?: string;
32
32
  content?: string | MessageContentComplex[];
33
33
  image_urls?: MessageContentImageUrl[];
34
+ documents?: MessageContentComplex[];
35
+ videos?: MessageContentComplex[];
36
+ audios?: MessageContentComplex[];
34
37
  lc_id?: string[];
35
38
  [key: string]: any;
36
39
  }
@@ -50,16 +53,16 @@ interface FormattedMessage {
50
53
  /**
51
54
  * Formats a message to OpenAI payload format based on the provided options.
52
55
  *
53
- * @param {FormatMessageParams} params - The parameters for formatting.
54
- * @returns {FormattedMessage | HumanMessage | AIMessage | SystemMessage} - The formatted message.
56
+ * @param params - The parameters for formatting.
57
+ * @returns - The formatted message.
55
58
  */
56
- export declare const formatMessage: ({ message, userName, assistantName, endpoint, langChain, }: FormatMessageParams) => FormattedMessage | HumanMessage | AIMessage | SystemMessage;
59
+ export declare const formatMessage: ({ message, userName, endpoint, assistantName, langChain, }: FormatMessageParams) => FormattedMessage | HumanMessage | AIMessage | SystemMessage;
57
60
  /**
58
61
  * Formats an array of messages for LangChain.
59
62
  *
60
- * @param {Array<MessageInput>} messages - The array of messages to format.
61
- * @param {Omit<FormatMessageParams, 'message' | 'langChain'>} formatOptions - The options for formatting each message.
62
- * @returns {Array<HumanMessage | AIMessage | SystemMessage>} - The array of formatted LangChain messages.
63
+ * @param messages - The array of messages to format.
64
+ * @param formatOptions - The options for formatting each message.
65
+ * @returns - The array of formatted LangChain messages.
63
66
  */
64
67
  export declare const formatLangChainMessages: (messages: Array<MessageInput>, formatOptions: Omit<FormatMessageParams, "message" | "langChain">) => Array<HumanMessage | AIMessage | SystemMessage>;
65
68
  interface LangChainMessage {
@@ -76,28 +79,22 @@ interface LangChainMessage {
76
79
  /**
77
80
  * Formats a LangChain message object by merging properties from `lc_kwargs` or `kwargs` and `additional_kwargs`.
78
81
  *
79
- * @param {LangChainMessage} message - The message object to format.
80
- * @returns {Record<string, any>} The formatted LangChain message.
82
+ * @param message - The message object to format.
83
+ * @returns - The formatted LangChain message.
81
84
  */
82
85
  export declare const formatFromLangChain: (message: LangChainMessage) => Record<string, any>;
83
86
  /**
84
87
  * Formats an array of messages for LangChain, handling tool calls and creating ToolMessage instances.
85
88
  *
86
- * @param {TPayload} payload - The array of messages to format.
87
- * @param {Record<number, number>} [indexTokenCountMap] - Optional map of message indices to token counts.
88
- * @param {Set<string>} [tools] - Optional set of tool names that are allowed in the request.
89
- * @returns {Object} - Object containing formatted messages and updated indexTokenCountMap if provided.
89
+ * @param payload - The array of messages to format.
90
+ * @param indexTokenCountMap - Optional map of message indices to token counts.
91
+ * @param tools - Optional set of tool names that are allowed in the request.
92
+ * @returns - Object containing formatted messages and updated indexTokenCountMap if provided.
90
93
  */
91
94
  export declare const formatAgentMessages: (payload: TPayload, indexTokenCountMap?: Record<number, number>, tools?: Set<string>) => {
92
95
  messages: Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>;
93
96
  indexTokenCountMap?: Record<number, number>;
94
97
  };
95
- /**
96
- * Formats an array of messages for LangChain, making sure all content fields are strings
97
- * @param {Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>} payload - The array of messages to format.
98
- * @returns {Array<HumanMessage | AIMessage | SystemMessage | ToolMessage>} - The array of formatted LangChain messages, including ToolMessages for tool calls.
99
- */
100
- export declare const formatContentStrings: (payload: Array<BaseMessage>) => Array<BaseMessage>;
101
98
  /**
102
99
  * Adds a value at key 0 for system messages and shifts all key indices by one in an indexTokenCountMap.
103
100
  * This is useful when adding a system message at the beginning of a conversation.
@@ -2,3 +2,5 @@ export * from './core';
2
2
  export * from './ids';
3
3
  export * from './prune';
4
4
  export * from './format';
5
+ export * from './cache';
6
+ export * from './content';
@@ -1,4 +1,5 @@
1
- import type { BaseMessage, MessageContentComplex } from '@langchain/core/messages';
1
+ import './instrumentation';
2
+ import type { MessageContentComplex, BaseMessage } from '@langchain/core/messages';
2
3
  import type { RunnableConfig } from '@langchain/core/runnables';
3
4
  import type * as t from '@/types';
4
5
  import { MultiAgentGraph } from '@/graphs/MultiAgentGraph';
@@ -3,9 +3,10 @@ import type * as t from './types';
3
3
  * Firecrawl scraper implementation
4
4
  * Uses the Firecrawl API to scrape web pages
5
5
  */
6
- export declare class FirecrawlScraper {
6
+ export declare class FirecrawlScraper implements t.BaseScraper {
7
7
  private apiKey;
8
8
  private apiUrl;
9
+ private version;
9
10
  private defaultFormats;
10
11
  private timeout;
11
12
  private logger;
@@ -7,8 +7,10 @@ export declare abstract class BaseReranker {
7
7
  protected getDefaultRanking(documents: string[], topK: number): t.Highlight[];
8
8
  }
9
9
  export declare class JinaReranker extends BaseReranker {
10
- constructor({ apiKey, logger, }: {
10
+ private apiUrl;
11
+ constructor({ apiKey, apiUrl, logger, }: {
11
12
  apiKey?: string;
13
+ apiUrl?: string;
12
14
  logger?: t.Logger;
13
15
  });
14
16
  rerank(query: string, documents: string[], topK?: number): Promise<t.Highlight[]>;
@@ -30,6 +32,7 @@ export declare class InfinityReranker extends BaseReranker {
30
32
  export declare const createReranker: (config: {
31
33
  rerankerType: t.RerankerType;
32
34
  jinaApiKey?: string;
35
+ jinaApiUrl?: string;
33
36
  cohereApiKey?: string;
34
37
  logger?: t.Logger;
35
38
  }) => BaseReranker | undefined;
@@ -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
  */
@@ -18,16 +39,16 @@ export declare const createSearchTool: (config?: t.SearchToolConfig) => DynamicS
18
39
  news: z.ZodOptional<z.ZodBoolean>;
19
40
  }, "strip", z.ZodTypeAny, {
20
41
  query: string;
21
- date?: DATE_RANGE | undefined;
22
- images?: boolean | undefined;
23
42
  videos?: boolean | undefined;
43
+ images?: boolean | undefined;
24
44
  news?: boolean | undefined;
45
+ date?: DATE_RANGE | undefined;
25
46
  country?: unknown;
26
47
  }, {
27
48
  query: string;
28
- date?: DATE_RANGE | undefined;
29
- images?: boolean | undefined;
30
49
  videos?: boolean | undefined;
50
+ images?: boolean | undefined;
31
51
  news?: boolean | undefined;
52
+ date?: DATE_RANGE | undefined;
32
53
  country?: unknown;
33
54
  }>>;
@@ -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
  }
@@ -130,9 +139,12 @@ export interface SearchToolConfig extends SearchConfig, ProcessSourcesConfig, Fi
130
139
  logger?: Logger;
131
140
  safeSearch?: SafeSearchLevel;
132
141
  jinaApiKey?: string;
142
+ jinaApiUrl?: string;
133
143
  cohereApiKey?: string;
134
144
  rerankerType?: RerankerType;
145
+ scraperProvider?: ScraperProvider;
135
146
  scraperTimeout?: number;
147
+ serperScraperOptions?: SerperScraperConfig;
136
148
  onSearchResults?: (results: SearchResult, runnableConfig?: RunnableConfig) => void;
137
149
  onGetHighlights?: (link: string) => void;
138
150
  }
@@ -146,8 +158,15 @@ export type UsedReferences = {
146
158
  originalIndex: number;
147
159
  reference: MediaReference;
148
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
+ }
149
167
  /** Firecrawl */
150
- 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'>;
151
170
  export interface ScrapeMetadata {
152
171
  sourceURL?: string;
153
172
  url?: string;
@@ -213,9 +232,20 @@ export interface FirecrawlScrapeResponse {
213
232
  };
214
233
  error?: string;
215
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
+ }
216
245
  export interface FirecrawlScraperConfig {
217
246
  apiKey?: string;
218
247
  apiUrl?: string;
248
+ version?: string;
219
249
  formats?: string[];
220
250
  timeout?: number;
221
251
  logger?: Logger;
@@ -218,7 +218,7 @@ export type GraphEdge = {
218
218
  * For handoff edges: Description for the input parameter that the handoff tool accepts,
219
219
  * allowing the supervisor to pass specific instructions/context to the transferred agent.
220
220
  */
221
- prompt?: string | ((messages: BaseMessage[], runStartIndex: number) => string | undefined);
221
+ prompt?: string | ((messages: BaseMessage[], runStartIndex: number) => string | Promise<string> | undefined);
222
222
  /**
223
223
  * When true, excludes messages from startIndex when adding prompt.
224
224
  * Automatically set to true when {results} variable is used in prompt.
@@ -246,4 +246,6 @@ export interface AgentInputs {
246
246
  clientOptions?: ClientOptions;
247
247
  additional_instructions?: string;
248
248
  reasoningKey?: 'reasoning_content' | 'reasoning';
249
+ /** Format content blocks as strings (for legacy compatibility i.e. Ollama/Azure Serverless) */
250
+ useLegacyContent?: boolean;
249
251
  }
@@ -0,0 +1,4 @@
1
+ import type Anthropic from '@anthropic-ai/sdk';
2
+ import type { BaseMessage } from '@langchain/core/messages';
3
+ export type AnthropicMessages = Array<AnthropicMessage | BaseMessage>;
4
+ export type AnthropicMessage = Anthropic.MessageParam;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Multi-Agent Handler Utilities
3
+ *
4
+ * Provides a simple helper to create handlers with content aggregation for multi-agent scripts.
5
+ *
6
+ * Usage:
7
+ * ```typescript
8
+ * const { contentParts, aggregateContent, handlers } = createHandlers();
9
+ *
10
+ * // With callbacks
11
+ * const { contentParts, aggregateContent, handlers } = createHandlers({
12
+ * onRunStep: (event, data) => console.log('Step:', data),
13
+ * onRunStepCompleted: (event, data) => console.log('Completed:', data)
14
+ * });
15
+ * ```
16
+ */
17
+ import { GraphEvents } from '@/common';
18
+ import { createContentAggregator } from '@/stream';
19
+ import type * as t from '@/types';
20
+ interface HandlerCallbacks {
21
+ onRunStep?: (event: GraphEvents.ON_RUN_STEP, data: t.StreamEventData) => void;
22
+ onRunStepCompleted?: (event: GraphEvents.ON_RUN_STEP_COMPLETED, data: t.StreamEventData) => void;
23
+ onRunStepDelta?: (event: GraphEvents.ON_RUN_STEP_DELTA, data: t.StreamEventData) => void;
24
+ onMessageDelta?: (event: GraphEvents.ON_MESSAGE_DELTA, data: t.StreamEventData) => void;
25
+ }
26
+ /**
27
+ * Creates handlers with content aggregation for multi-agent scripts
28
+ */
29
+ export declare function createHandlers(callbacks?: HandlerCallbacks): {
30
+ contentParts: Array<t.MessageContentComplex | undefined>;
31
+ aggregateContent: ReturnType<typeof createContentAggregator>['aggregateContent'];
32
+ handlers: Record<string, t.EventHandler>;
33
+ };
34
+ export {};
@@ -1,5 +1,6 @@
1
1
  export * from './graph';
2
2
  export * from './llm';
3
3
  export * from './misc';
4
+ export * from './handlers';
4
5
  export * from './run';
5
6
  export * from './tokens';
@@ -1,3 +1,4 @@
1
+ export declare function isPresent(value: string | null | undefined): value is string;
1
2
  /**
2
3
  * Recursively unescapes all string values in an object
3
4
  * @param obj The object to unescape
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "3.0.00-rc9",
3
+ "version": "3.0.00",
4
4
  "main": "./dist/cjs/main.cjs",
5
5
  "module": "./dist/esm/main.mjs",
6
6
  "types": "./dist/types/index.d.ts",
@@ -51,7 +51,7 @@
51
51
  "caching": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/caching.ts --name 'Jo' --location 'New York, NY'",
52
52
  "thinking": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/thinking.ts --name 'Jo' --location 'New York, NY'",
53
53
  "memory": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/memory.ts --provider 'openAI' --name 'Jo' --location 'New York, NY'",
54
- "tool-test": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/tools.ts --provider 'bedrock' --name 'Jo' --location 'New York, NY'",
54
+ "tool-test": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/tools.ts --provider 'openrouter' --name 'Jo' --location 'New York, NY'",
55
55
  "search": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/search.ts --provider 'anthropic' --name 'Jo' --location 'New York, NY'",
56
56
  "ant_web_search": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/ant_web_search.ts --name 'Jo' --location 'New York, NY'",
57
57
  "abort": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/abort.ts --provider 'openAI' --name 'Jo' --location 'New York, NY'",
@@ -105,6 +105,10 @@
105
105
  "@langchain/ollama": "^0.2.3",
106
106
  "@langchain/openai": "0.5.18",
107
107
  "@langchain/xai": "^0.0.3",
108
+ "@langfuse/langchain": "^4.3.0",
109
+ "@langfuse/otel": "^4.3.0",
110
+ "@langfuse/tracing": "^4.3.0",
111
+ "@opentelemetry/sdk-node": "^0.207.0",
108
112
  "cheerio": "^1.0.0",
109
113
  "dotenv": "^16.4.7",
110
114
  "https-proxy-agent": "^7.0.6",
@@ -37,6 +37,7 @@ export class AgentContext {
37
37
  streamBuffer,
38
38
  maxContextTokens,
39
39
  reasoningKey,
40
+ useLegacyContent,
40
41
  } = agentConfig;
41
42
 
42
43
  const agentContext = new AgentContext({
@@ -53,6 +54,7 @@ export class AgentContext {
53
54
  toolEnd,
54
55
  instructionTokens: 0,
55
56
  tokenCounter,
57
+ useLegacyContent,
56
58
  });
57
59
 
58
60
  if (tokenCounter) {
@@ -123,6 +125,8 @@ export class AgentContext {
123
125
  >;
124
126
  /** Promise for token calculation initialization */
125
127
  tokenCalculationPromise?: Promise<void>;
128
+ /** Format content blocks as strings (for legacy compatibility) */
129
+ useLegacyContent: boolean = false;
126
130
 
127
131
  constructor({
128
132
  agentId,
@@ -138,6 +142,7 @@ export class AgentContext {
138
142
  reasoningKey,
139
143
  toolEnd,
140
144
  instructionTokens,
145
+ useLegacyContent,
141
146
  }: {
142
147
  agentId: string;
143
148
  provider: Providers;
@@ -152,6 +157,7 @@ export class AgentContext {
152
157
  reasoningKey?: 'reasoning_content' | 'reasoning';
153
158
  toolEnd?: boolean;
154
159
  instructionTokens?: number;
160
+ useLegacyContent?: boolean;
155
161
  }) {
156
162
  this.agentId = agentId;
157
163
  this.provider = provider;
@@ -173,6 +179,8 @@ export class AgentContext {
173
179
  this.instructionTokens = instructionTokens;
174
180
  }
175
181
 
182
+ this.useLegacyContent = useLegacyContent ?? false;
183
+
176
184
  this.systemRunnable = this.createSystemRunnable();
177
185
  }
178
186
 
@@ -41,7 +41,9 @@ import {
41
41
  convertMessagesToContent,
42
42
  modifyDeltaProperties,
43
43
  formatArtifactPayload,
44
+ formatContentStrings,
44
45
  createPruneMessages,
46
+ addCacheControl,
45
47
  } from '@/messages';
46
48
  import {
47
49
  resetIfNotEmpty,
@@ -299,6 +301,8 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
299
301
  agentContext.currentTokenType === 'think_and_text'
300
302
  ) {
301
303
  keyList.push('reasoning');
304
+ } else if (agentContext.tokenTypeSwitch === 'content') {
305
+ keyList.push('post-reasoning');
302
306
  }
303
307
 
304
308
  if (this.invokedToolIds != null && this.invokedToolIds.size > 0) {
@@ -580,7 +584,12 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
580
584
  null) ||
581
585
  (agentContext.provider === Providers.BEDROCK &&
582
586
  (agentContext.clientOptions as t.BedrockAnthropicInput)
583
- .additionalModelRequestFields?.['thinking'] != null);
587
+ .additionalModelRequestFields?.['thinking'] != null) ||
588
+ (agentContext.provider === Providers.OPENAI &&
589
+ (
590
+ (agentContext.clientOptions as t.OpenAIClientOptions).modelKwargs
591
+ ?.thinking as t.AnthropicClientOptions['thinking']
592
+ )?.type === 'enabled');
584
593
 
585
594
  agentContext.pruneMessages = createPruneMessages({
586
595
  startIndex: this.startIndex,
@@ -601,7 +610,11 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
601
610
  messagesToUse = context;
602
611
  }
603
612
 
604
- const finalMessages = messagesToUse;
613
+ let finalMessages = messagesToUse;
614
+ if (agentContext.useLegacyContent) {
615
+ finalMessages = formatContentStrings(finalMessages);
616
+ }
617
+
605
618
  const lastMessageX =
606
619
  finalMessages.length >= 2
607
620
  ? finalMessages[finalMessages.length - 2]
@@ -635,6 +648,22 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
635
648
  formatArtifactPayload(finalMessages);
636
649
  }
637
650
 
651
+ if (agentContext.provider === Providers.ANTHROPIC) {
652
+ const anthropicOptions = agentContext.clientOptions as
653
+ | t.AnthropicClientOptions
654
+ | undefined;
655
+ const defaultHeaders = anthropicOptions?.clientOptions
656
+ ?.defaultHeaders as Record<string, string> | undefined;
657
+ const anthropicBeta = defaultHeaders?.['anthropic-beta'];
658
+
659
+ if (
660
+ typeof anthropicBeta === 'string' &&
661
+ anthropicBeta.includes('prompt-caching')
662
+ ) {
663
+ finalMessages = addCacheControl<BaseMessage>(finalMessages);
664
+ }
665
+ }
666
+
638
667
  if (
639
668
  agentContext.lastStreamCall != null &&
640
669
  agentContext.streamBuffer != null
@@ -1,11 +1,11 @@
1
1
  import { z } from 'zod';
2
2
  import { tool } from '@langchain/core/tools';
3
+ import { PromptTemplate } from '@langchain/core/prompts';
3
4
  import {
4
5
  ToolMessage,
5
6
  HumanMessage,
6
7
  getBufferString,
7
8
  } from '@langchain/core/messages';
8
- import { ChatPromptTemplate } from '@langchain/core/prompts';
9
9
  import {
10
10
  END,
11
11
  START,
@@ -508,16 +508,16 @@ export class MultiAgentGraph extends StandardGraph {
508
508
  let effectiveExcludeResults = excludeResults;
509
509
 
510
510
  if (typeof prompt === 'function') {
511
- promptText = prompt(state.messages, this.startIndex);
511
+ promptText = await prompt(state.messages, this.startIndex);
512
512
  } else if (prompt != null) {
513
513
  if (prompt.includes('{results}')) {
514
514
  const resultsMessages = state.messages.slice(this.startIndex);
515
515
  const resultsString = getBufferString(resultsMessages);
516
- const promptTemplate = ChatPromptTemplate.fromTemplate(prompt);
517
- const formattedPromptValue = await promptTemplate.invoke({
516
+ const promptTemplate = PromptTemplate.fromTemplate(prompt);
517
+ const result = await promptTemplate.invoke({
518
518
  results: resultsString,
519
519
  });
520
- promptText = formattedPromptValue.messages[0].content.toString();
520
+ promptText = result.value;
521
521
  effectiveExcludeResults =
522
522
  excludeResults !== false && promptText !== '';
523
523
  } else {
@@ -0,0 +1,22 @@
1
+ import { NodeSDK } from '@opentelemetry/sdk-node';
2
+ import { LangfuseSpanProcessor } from '@langfuse/otel';
3
+ import { isPresent } from '@/utils/misc';
4
+
5
+ if (
6
+ isPresent(process.env.LANGFUSE_SECRET_KEY) &&
7
+ isPresent(process.env.LANGFUSE_PUBLIC_KEY) &&
8
+ isPresent(process.env.LANGFUSE_BASE_URL)
9
+ ) {
10
+ const langfuseSpanProcessor = new LangfuseSpanProcessor({
11
+ publicKey: process.env.LANGFUSE_PUBLIC_KEY,
12
+ secretKey: process.env.LANGFUSE_SECRET_KEY,
13
+ baseUrl: process.env.LANGFUSE_BASE_URL,
14
+ environment: process.env.NODE_ENV ?? 'development',
15
+ });
16
+
17
+ const sdk = new NodeSDK({
18
+ spanProcessors: [langfuseSpanProcessor],
19
+ });
20
+
21
+ sdk.start();
22
+ }