@librechat/agents 3.0.0-rc8 → 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 (229) 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 +108 -17
  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 +108 -17
  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/graphs/MultiAgentGraph.d.ts +11 -1
  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 +8 -2
  142. package/src/agents/AgentContext.ts +8 -0
  143. package/src/graphs/Graph.ts +31 -2
  144. package/src/graphs/MultiAgentGraph.ts +125 -18
  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 +278 -0
  168. package/src/scripts/multi-agent-conditional.ts +4 -4
  169. package/src/scripts/multi-agent-document-review-chain.ts +197 -0
  170. package/src/scripts/multi-agent-hybrid-flow.ts +310 -0
  171. package/src/scripts/multi-agent-parallel.ts +10 -8
  172. package/src/scripts/multi-agent-sequence.ts +3 -3
  173. package/src/scripts/multi-agent-supervisor.ts +5 -3
  174. package/src/scripts/multi-agent-test.ts +2 -2
  175. package/src/scripts/search.ts +5 -1
  176. package/src/scripts/simple.ts +8 -0
  177. package/src/scripts/test-custom-prompt-key.ts +4 -4
  178. package/src/scripts/test-handoff-input.ts +3 -3
  179. package/src/scripts/test-multi-agent-list-handoff.ts +2 -2
  180. package/src/scripts/tools.ts +4 -1
  181. package/src/specs/agent-handoffs.test.ts +889 -0
  182. package/src/stream.ts +9 -2
  183. package/src/tools/search/firecrawl.ts +5 -2
  184. package/src/tools/search/jina-reranker.test.ts +126 -0
  185. package/src/tools/search/rerankers.ts +11 -5
  186. package/src/tools/search/search.ts +6 -8
  187. package/src/tools/search/serper-scraper.ts +155 -0
  188. package/src/tools/search/tool.ts +49 -8
  189. package/src/tools/search/types.ts +46 -0
  190. package/src/types/graph.ts +6 -1
  191. package/src/types/messages.ts +4 -0
  192. package/src/utils/handlers.ts +107 -0
  193. package/src/utils/index.ts +2 -1
  194. package/src/utils/llmConfig.ts +35 -1
  195. package/src/utils/misc.ts +33 -21
  196. package/src/utils/title.ts +80 -40
  197. package/dist/types/scripts/ant_web_search.d.ts +0 -1
  198. package/dist/types/scripts/args.d.ts +0 -7
  199. package/dist/types/scripts/caching.d.ts +0 -1
  200. package/dist/types/scripts/cli.d.ts +0 -1
  201. package/dist/types/scripts/cli2.d.ts +0 -1
  202. package/dist/types/scripts/cli3.d.ts +0 -1
  203. package/dist/types/scripts/cli4.d.ts +0 -1
  204. package/dist/types/scripts/cli5.d.ts +0 -1
  205. package/dist/types/scripts/code_exec.d.ts +0 -1
  206. package/dist/types/scripts/code_exec_files.d.ts +0 -1
  207. package/dist/types/scripts/code_exec_simple.d.ts +0 -1
  208. package/dist/types/scripts/content.d.ts +0 -1
  209. package/dist/types/scripts/empty_input.d.ts +0 -1
  210. package/dist/types/scripts/handoff-test.d.ts +0 -1
  211. package/dist/types/scripts/image.d.ts +0 -1
  212. package/dist/types/scripts/memory.d.ts +0 -1
  213. package/dist/types/scripts/multi-agent-conditional.d.ts +0 -1
  214. package/dist/types/scripts/multi-agent-parallel.d.ts +0 -1
  215. package/dist/types/scripts/multi-agent-sequence.d.ts +0 -1
  216. package/dist/types/scripts/multi-agent-supervisor.d.ts +0 -1
  217. package/dist/types/scripts/multi-agent-test.d.ts +0 -1
  218. package/dist/types/scripts/search.d.ts +0 -1
  219. package/dist/types/scripts/simple.d.ts +0 -1
  220. package/dist/types/scripts/stream.d.ts +0 -1
  221. package/dist/types/scripts/test-custom-prompt-key.d.ts +0 -2
  222. package/dist/types/scripts/test-handoff-input.d.ts +0 -1
  223. package/dist/types/scripts/test-multi-agent-list-handoff.d.ts +0 -2
  224. package/dist/types/scripts/test-tools-before-handoff.d.ts +0 -1
  225. package/dist/types/scripts/thinking.d.ts +0 -1
  226. package/dist/types/scripts/tools.d.ts +0 -1
  227. package/dist/types/specs/spec.utils.d.ts +0 -1
  228. package/src/scripts/multi-agent-example-output.md +0 -110
  229. /package/dist/types/{scripts/abort.d.ts → instrumentation.d.ts} +0 -0
@@ -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,
@@ -23,7 +23,17 @@ import { Constants } from '@/common';
23
23
 
24
24
  /**
25
25
  * MultiAgentGraph extends StandardGraph to support dynamic multi-agent workflows
26
- * with handoffs, fan-in/fan-out, and other composable patterns
26
+ * with handoffs, fan-in/fan-out, and other composable patterns.
27
+ *
28
+ * Key behavior:
29
+ * - Agents with ONLY handoff edges: Can dynamically route to any handoff destination
30
+ * - Agents with ONLY direct edges: Always follow their direct edges
31
+ * - Agents with BOTH: Use Command for exclusive routing (handoff OR direct, not both)
32
+ * - If handoff occurs: Only the handoff destination executes
33
+ * - If no handoff: Direct edges execute (potentially in parallel)
34
+ *
35
+ * This enables the common pattern where an agent either delegates (handoff)
36
+ * OR continues its workflow (direct edges), but not both simultaneously.
27
37
  */
28
38
  export class MultiAgentGraph extends StandardGraph {
29
39
  private edges: t.GraphEdge[];
@@ -328,41 +338,123 @@ export class MultiAgentGraph extends StandardGraph {
328
338
  }
329
339
  }
330
340
 
331
- /** If agent has handoff destinations, add END to possible ends
332
- * If agent only has direct destinations, it naturally ends without explicit END
333
- */
334
- const destinations = new Set([...handoffDestinations]);
341
+ /** Check if this agent has BOTH handoff and direct edges */
342
+ const hasHandoffEdges = handoffDestinations.size > 0;
343
+ const hasDirectEdges = directDestinations.size > 0;
344
+ const needsCommandRouting = hasHandoffEdges && hasDirectEdges;
345
+
346
+ /** Collect all possible destinations for this agent */
347
+ const allDestinations = new Set([
348
+ ...handoffDestinations,
349
+ ...directDestinations,
350
+ ]);
335
351
  if (handoffDestinations.size > 0 || directDestinations.size === 0) {
336
- destinations.add(END);
352
+ allDestinations.add(END);
337
353
  }
338
354
 
339
355
  /** Agent subgraph (includes agent + tools) */
340
356
  const agentSubgraph = this.createAgentSubgraph(agentId);
341
357
 
342
- /** Wrapper function that handles agentMessages channel */
358
+ /** Wrapper function that handles agentMessages channel and conditional routing */
343
359
  const agentWrapper = async (
344
360
  state: t.MultiAgentGraphState
345
- ): Promise<t.MultiAgentGraphState> => {
361
+ ): Promise<t.MultiAgentGraphState | Command> => {
362
+ let result: t.MultiAgentGraphState;
363
+
346
364
  if (state.agentMessages != null && state.agentMessages.length > 0) {
365
+ /**
366
+ * When using agentMessages (excludeResults=true), we need to update
367
+ * the token map to account for the new prompt message
368
+ */
369
+ const agentContext = this.agentContexts.get(agentId);
370
+ if (agentContext && agentContext.tokenCounter) {
371
+ // The agentMessages contains:
372
+ // 1. Filtered messages (0 to startIndex) - already have token counts
373
+ // 2. New prompt message - needs token counting
374
+
375
+ const freshTokenMap: Record<string, number> = {};
376
+
377
+ // Copy existing token counts for filtered messages (0 to startIndex)
378
+ for (let i = 0; i < this.startIndex; i++) {
379
+ const tokenCount = agentContext.indexTokenCountMap[i];
380
+ if (tokenCount !== undefined) {
381
+ freshTokenMap[i] = tokenCount;
382
+ }
383
+ }
384
+
385
+ // Calculate tokens only for the new prompt message (last message)
386
+ const promptMessageIndex = state.agentMessages.length - 1;
387
+ if (promptMessageIndex >= this.startIndex) {
388
+ const promptMessage = state.agentMessages[promptMessageIndex];
389
+ freshTokenMap[promptMessageIndex] =
390
+ agentContext.tokenCounter(promptMessage);
391
+ }
392
+
393
+ // Update the agent's token map with instructions added
394
+ agentContext.updateTokenMapWithInstructions(freshTokenMap);
395
+ }
396
+
347
397
  /** Temporary state with messages replaced by `agentMessages` */
348
398
  const transformedState: t.MultiAgentGraphState = {
349
399
  ...state,
350
400
  messages: state.agentMessages,
351
401
  };
352
- const result = await agentSubgraph.invoke(transformedState);
353
- return {
402
+ result = await agentSubgraph.invoke(transformedState);
403
+ result = {
354
404
  ...result,
355
405
  /** Clear agentMessages for next agent */
356
406
  agentMessages: [],
357
407
  };
358
408
  } else {
359
- return await agentSubgraph.invoke(state);
409
+ result = await agentSubgraph.invoke(state);
360
410
  }
411
+
412
+ /** If agent has both handoff and direct edges, use Command for exclusive routing */
413
+ if (needsCommandRouting) {
414
+ /** Check if a handoff occurred */
415
+ const lastMessage = result.messages[
416
+ result.messages.length - 1
417
+ ] as BaseMessage | null;
418
+ if (
419
+ lastMessage != null &&
420
+ lastMessage.getType() === 'tool' &&
421
+ typeof lastMessage.name === 'string' &&
422
+ lastMessage.name.startsWith(Constants.LC_TRANSFER_TO_)
423
+ ) {
424
+ /** Handoff occurred - extract destination and navigate there exclusively */
425
+ const handoffDest = lastMessage.name.replace(
426
+ Constants.LC_TRANSFER_TO_,
427
+ ''
428
+ );
429
+ return new Command({
430
+ update: result,
431
+ goto: handoffDest,
432
+ });
433
+ } else {
434
+ /** No handoff - proceed with direct edges */
435
+ const directDests = Array.from(directDestinations);
436
+ if (directDests.length === 1) {
437
+ return new Command({
438
+ update: result,
439
+ goto: directDests[0],
440
+ });
441
+ } else if (directDests.length > 1) {
442
+ /** Multiple direct destinations - they'll run in parallel */
443
+ return new Command({
444
+ update: result,
445
+ goto: directDests,
446
+ });
447
+ }
448
+ }
449
+ }
450
+
451
+ /** No special routing needed - return state normally */
452
+ return result;
361
453
  };
362
454
 
363
455
  /** Wrapped agent as a node with its possible destinations */
364
456
  builder.addNode(agentId, agentWrapper, {
365
- ends: Array.from(destinations),
457
+ ends: Array.from(allDestinations),
366
458
  });
367
459
  }
368
460
 
@@ -416,16 +508,16 @@ export class MultiAgentGraph extends StandardGraph {
416
508
  let effectiveExcludeResults = excludeResults;
417
509
 
418
510
  if (typeof prompt === 'function') {
419
- promptText = prompt(state.messages, this.startIndex);
511
+ promptText = await prompt(state.messages, this.startIndex);
420
512
  } else if (prompt != null) {
421
513
  if (prompt.includes('{results}')) {
422
514
  const resultsMessages = state.messages.slice(this.startIndex);
423
515
  const resultsString = getBufferString(resultsMessages);
424
- const promptTemplate = ChatPromptTemplate.fromTemplate(prompt);
425
- const formattedPromptValue = await promptTemplate.invoke({
516
+ const promptTemplate = PromptTemplate.fromTemplate(prompt);
517
+ const result = await promptTemplate.invoke({
426
518
  results: resultsString,
427
519
  });
428
- promptText = formattedPromptValue.messages[0].content.toString();
520
+ promptText = result.value;
429
521
  effectiveExcludeResults =
430
522
  excludeResults !== false && promptText !== '';
431
523
  } else {
@@ -474,10 +566,25 @@ export class MultiAgentGraph extends StandardGraph {
474
566
  /** @ts-ignore */
475
567
  builder.addEdge(wrapperNodeId, destination);
476
568
  } else {
477
- /** No prompt instructions, add direct edges */
569
+ /** No prompt instructions, add direct edges (skip if source uses Command routing) */
478
570
  for (const edge of edges) {
479
571
  const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
480
572
  for (const source of sources) {
573
+ /** Check if this source node has both handoff and direct edges */
574
+ const sourceHandoffEdges = this.handoffEdges.filter((e) => {
575
+ const eSources = Array.isArray(e.from) ? e.from : [e.from];
576
+ return eSources.includes(source);
577
+ });
578
+ const sourceDirectEdges = this.directEdges.filter((e) => {
579
+ const eSources = Array.isArray(e.from) ? e.from : [e.from];
580
+ return eSources.includes(source);
581
+ });
582
+
583
+ /** Skip adding edge if source uses Command routing (has both types) */
584
+ if (sourceHandoffEdges.length > 0 && sourceDirectEdges.length > 0) {
585
+ continue;
586
+ }
587
+
481
588
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
482
589
  /** @ts-ignore */
483
590
  builder.addEdge(source, destination);
@@ -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
+ }
@@ -135,12 +135,18 @@ export class CustomAnthropic extends ChatAnthropicMessages {
135
135
  private message_delta: AnthropicMessageDeltaEvent | undefined;
136
136
  private tools_in_params?: boolean;
137
137
  private emitted_usage?: boolean;
138
+ top_k: number | undefined;
138
139
  constructor(fields?: CustomAnthropicInput) {
139
140
  super(fields);
140
141
  this.resetTokenEvents();
142
+ this.setDirectFields(fields);
141
143
  this._lc_stream_delay = fields?._lc_stream_delay ?? 25;
142
144
  }
143
145
 
146
+ static lc_name(): 'LibreChatAnthropic' {
147
+ return 'LibreChatAnthropic';
148
+ }
149
+
144
150
  /**
145
151
  * Get the parameters used to invoke the model
146
152
  */
@@ -158,7 +164,7 @@ export class CustomAnthropic extends ChatAnthropicMessages {
158
164
  | undefined = handleToolChoice(options?.tool_choice);
159
165
 
160
166
  if (this.thinking.type === 'enabled') {
161
- if (this.topK !== -1 && (this.topK as number | undefined) != null) {
167
+ if (this.top_k !== -1 && (this.top_k as number | undefined) != null) {
162
168
  throw new Error('topK is not supported when thinking is enabled');
163
169
  }
164
170
  if (this.topP !== -1 && (this.topP as number | undefined) != null) {
@@ -187,7 +193,7 @@ export class CustomAnthropic extends ChatAnthropicMessages {
187
193
  return {
188
194
  model: this.model,
189
195
  temperature: this.temperature,
190
- top_k: this.topK,
196
+ top_k: this.top_k,
191
197
  top_p: this.topP,
192
198
  stop_sequences: options?.stop ?? this.stopSequences,
193
199
  stream: this.streaming,
@@ -244,6 +250,21 @@ export class CustomAnthropic extends ChatAnthropicMessages {
244
250
  this.tools_in_params = undefined;
245
251
  }
246
252
 
253
+ setDirectFields(fields?: CustomAnthropicInput): void {
254
+ this.temperature = fields?.temperature ?? undefined;
255
+ this.topP = fields?.topP ?? undefined;
256
+ this.top_k = fields?.topK;
257
+ if (this.temperature === -1 || this.temperature === 1) {
258
+ this.temperature = undefined;
259
+ }
260
+ if (this.topP === -1) {
261
+ this.topP = undefined;
262
+ }
263
+ if (this.top_k === -1) {
264
+ this.top_k = undefined;
265
+ }
266
+ }
267
+
247
268
  private createGenerationChunk({
248
269
  token,
249
270
  chunk,
@@ -58,7 +58,7 @@ async function invoke(
58
58
  const extendedThinkingModelName = 'claude-3-7-sonnet-20250219';
59
59
 
60
60
  // use this for tests involving citations
61
- const citationsModelName = 'claude-3-5-sonnet-20241022';
61
+ const citationsModelName = 'claude-sonnet-4-5-20250929';
62
62
 
63
63
  // use this for tests involving PDF documents
64
64
  const pdfModelName = 'claude-3-5-haiku-20241022';
@@ -107,6 +107,10 @@ export class CustomChatGoogleGenerativeAI extends ChatGoogleGenerativeAI {
107
107
  this.streamUsage = fields.streamUsage ?? this.streamUsage;
108
108
  }
109
109
 
110
+ static lc_name(): 'LibreChatGoogleGenerativeAI' {
111
+ return 'LibreChatGoogleGenerativeAI';
112
+ }
113
+
110
114
  invocationParams(
111
115
  options?: this['ParsedCallOptions']
112
116
  ): Omit<GenerateContentRequest, 'contents'> {
@@ -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') {
@@ -13,6 +13,9 @@ import {
13
13
  } from './utils';
14
14
 
15
15
  export class ChatOllama extends BaseChatOllama {
16
+ static lc_name(): 'LibreChatOllama' {
17
+ return 'LibreChatOllama';
18
+ }
16
19
  async *_streamResponseChunks(
17
20
  messages: BaseMessage[],
18
21
  options: this['ParsedCallOptions'],
@@ -210,6 +210,9 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
210
210
  public get exposedClient(): CustomOpenAIClient {
211
211
  return this.client;
212
212
  }
213
+ static lc_name(): string {
214
+ return 'LibreChatOpenAI';
215
+ }
213
216
  protected _getClientOptions(
214
217
  options?: OpenAICoreRequestOptions
215
218
  ): OpenAICoreRequestOptions {
@@ -245,10 +248,6 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
245
248
  getReasoningParams(
246
249
  options?: this['ParsedCallOptions']
247
250
  ): OpenAIClient.Reasoning | undefined {
248
- if (!isReasoningModel(this.model)) {
249
- return;
250
- }
251
-
252
251
  // apply options in reverse order of importance -- newer options supersede older options
253
252
  let reasoning: OpenAIClient.Reasoning | undefined;
254
253
  if (this.reasoning !== undefined) {
@@ -360,6 +359,10 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
360
359
  } else if ('reasoning' in delta) {
361
360
  chunk.additional_kwargs.reasoning_content = delta.reasoning;
362
361
  }
362
+ if ('provider_specific_fields' in delta) {
363
+ chunk.additional_kwargs.provider_specific_fields =
364
+ delta.provider_specific_fields;
365
+ }
363
366
  defaultRole = delta.role ?? defaultRole;
364
367
  const newTokenIndices = {
365
368
  prompt: options.promptIndex ?? 0,
@@ -463,6 +466,9 @@ export class AzureChatOpenAI extends OriginalAzureChatOpenAI {
463
466
  public get exposedClient(): CustomOpenAIClient {
464
467
  return this.client;
465
468
  }
469
+ static lc_name(): 'LibreChatAzureOpenAI' {
470
+ return 'LibreChatAzureOpenAI';
471
+ }
466
472
  /**
467
473
  * Returns backwards compatible reasoning parameters from constructor params and call options
468
474
  * @internal
@@ -607,6 +613,9 @@ export class ChatDeepSeek extends OriginalChatDeepSeek {
607
613
  public get exposedClient(): CustomOpenAIClient {
608
614
  return this.client;
609
615
  }
616
+ static lc_name(): 'LibreChatDeepSeek' {
617
+ return 'LibreChatDeepSeek';
618
+ }
610
619
  protected _getClientOptions(
611
620
  options?: OpenAICoreRequestOptions
612
621
  ): OpenAICoreRequestOptions {
@@ -679,6 +688,10 @@ export class ChatXAI extends OriginalChatXAI {
679
688
  }
680
689
  }
681
690
 
691
+ static lc_name(): 'LibreChatXAI' {
692
+ return 'LibreChatXAI';
693
+ }
694
+
682
695
  public get exposedClient(): CustomOpenAIClient {
683
696
  return this.client;
684
697
  }
@@ -298,10 +298,16 @@ export function _convertMessagesToOpenAIParams(
298
298
  role = 'developer';
299
299
  }
300
300
 
301
+ let hasAnthropicThinkingBlock: boolean = false;
302
+
301
303
  const content =
302
304
  typeof message.content === 'string'
303
305
  ? message.content
304
306
  : message.content.map((m) => {
307
+ if ('type' in m && m.type === 'thinking') {
308
+ hasAnthropicThinkingBlock = true;
309
+ return m;
310
+ }
305
311
  if (isDataContentBlock(m)) {
306
312
  return convertToProviderContentBlock(
307
313
  m,
@@ -326,7 +332,7 @@ export function _convertMessagesToOpenAIParams(
326
332
  completionParam.tool_calls = message.tool_calls.map(
327
333
  convertLangChainToolCallToOpenAI
328
334
  );
329
- completionParam.content = '';
335
+ completionParam.content = hasAnthropicThinkingBlock ? content : '';
330
336
  } else {
331
337
  if (message.additional_kwargs.tool_calls != null) {
332
338
  completionParam.tool_calls = message.additional_kwargs.tool_calls;
@@ -1,27 +1,36 @@
1
- import type { ChatOpenAICallOptions, OpenAIClient } from '@langchain/openai';
1
+ import { ChatOpenAI } from '@/llm/openai';
2
2
  import type {
3
- AIMessageChunk,
4
- HumanMessageChunk,
5
- SystemMessageChunk,
6
3
  FunctionMessageChunk,
4
+ SystemMessageChunk,
5
+ HumanMessageChunk,
7
6
  ToolMessageChunk,
8
7
  ChatMessageChunk,
8
+ AIMessageChunk,
9
9
  } from '@langchain/core/messages';
10
- import { ChatOpenAI } from '@/llm/openai';
10
+ import type {
11
+ ChatOpenAICallOptions,
12
+ OpenAIChatInput,
13
+ OpenAIClient,
14
+ } from '@langchain/openai';
11
15
 
12
16
  export interface ChatOpenRouterCallOptions extends ChatOpenAICallOptions {
13
17
  include_reasoning?: boolean;
18
+ modelKwargs?: OpenAIChatInput['modelKwargs'];
14
19
  }
15
20
  export class ChatOpenRouter extends ChatOpenAI {
16
21
  constructor(_fields: Partial<ChatOpenRouterCallOptions>) {
17
- const { include_reasoning, ...fields } = _fields;
22
+ const { include_reasoning, modelKwargs = {}, ...fields } = _fields;
18
23
  super({
19
24
  ...fields,
20
25
  modelKwargs: {
26
+ ...modelKwargs,
21
27
  include_reasoning,
22
28
  },
23
29
  });
24
30
  }
31
+ static lc_name(): 'LibreChatOpenRouter' {
32
+ return 'LibreChatOpenRouter';
33
+ }
25
34
  protected override _convertOpenAIDeltaToBaseMessageChunk(
26
35
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
36
  delta: Record<string, any>,
@@ -313,8 +313,8 @@ export class ChatVertexAI extends ChatGoogle {
313
313
  lc_namespace = ['langchain', 'chat_models', 'vertexai'];
314
314
  dynamicThinkingBudget = false;
315
315
 
316
- static lc_name(): 'ChatVertexAI' {
317
- return 'ChatVertexAI';
316
+ static lc_name(): 'LibreChatVertexAI' {
317
+ return 'LibreChatVertexAI';
318
318
  }
319
319
 
320
320
  constructor(fields?: VertexAIClientOptions) {