@librechat/agents 2.4.322 → 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 (258) 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 +55 -9
  51. package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
  52. package/dist/cjs/tools/search/format.cjs +6 -6
  53. package/dist/cjs/tools/search/format.cjs.map +1 -1
  54. package/dist/cjs/tools/search/rerankers.cjs +7 -29
  55. package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
  56. package/dist/cjs/tools/search/search.cjs +86 -16
  57. package/dist/cjs/tools/search/search.cjs.map +1 -1
  58. package/dist/cjs/tools/search/tool.cjs +4 -2
  59. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  60. package/dist/cjs/tools/search/utils.cjs +1 -1
  61. package/dist/cjs/tools/search/utils.cjs.map +1 -1
  62. package/dist/cjs/utils/events.cjs +31 -0
  63. package/dist/cjs/utils/events.cjs.map +1 -0
  64. package/dist/cjs/utils/title.cjs +57 -21
  65. package/dist/cjs/utils/title.cjs.map +1 -1
  66. package/dist/cjs/utils/tokens.cjs +54 -7
  67. package/dist/cjs/utils/tokens.cjs.map +1 -1
  68. package/dist/esm/agents/AgentContext.mjs +216 -0
  69. package/dist/esm/agents/AgentContext.mjs.map +1 -0
  70. package/dist/esm/common/enum.mjs +15 -6
  71. package/dist/esm/common/enum.mjs.map +1 -1
  72. package/dist/esm/events.mjs +10 -6
  73. package/dist/esm/events.mjs.map +1 -1
  74. package/dist/esm/graphs/Graph.mjs +311 -214
  75. package/dist/esm/graphs/Graph.mjs.map +1 -1
  76. package/dist/esm/graphs/MultiAgentGraph.mjs +320 -0
  77. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -0
  78. package/dist/esm/llm/anthropic/index.mjs +54 -9
  79. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  80. package/dist/esm/llm/anthropic/types.mjs.map +1 -1
  81. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +52 -6
  82. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  83. package/dist/esm/llm/anthropic/utils/message_outputs.mjs +22 -2
  84. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  85. package/dist/esm/llm/anthropic/utils/tools.mjs +27 -0
  86. package/dist/esm/llm/anthropic/utils/tools.mjs.map +1 -0
  87. package/dist/esm/llm/google/index.mjs +142 -0
  88. package/dist/esm/llm/google/index.mjs.map +1 -0
  89. package/dist/esm/llm/google/utils/common.mjs +471 -0
  90. package/dist/esm/llm/google/utils/common.mjs.map +1 -0
  91. package/dist/esm/llm/ollama/index.mjs +65 -0
  92. package/dist/esm/llm/ollama/index.mjs.map +1 -0
  93. package/dist/esm/llm/ollama/utils.mjs +155 -0
  94. package/dist/esm/llm/ollama/utils.mjs.map +1 -0
  95. package/dist/esm/llm/openai/index.mjs +388 -4
  96. package/dist/esm/llm/openai/index.mjs.map +1 -1
  97. package/dist/esm/llm/openai/utils/index.mjs +666 -0
  98. package/dist/esm/llm/openai/utils/index.mjs.map +1 -0
  99. package/dist/esm/llm/providers.mjs +5 -5
  100. package/dist/esm/llm/providers.mjs.map +1 -1
  101. package/dist/esm/llm/text.mjs +14 -3
  102. package/dist/esm/llm/text.mjs.map +1 -1
  103. package/dist/esm/llm/vertexai/index.mjs +328 -0
  104. package/dist/esm/llm/vertexai/index.mjs.map +1 -0
  105. package/dist/esm/main.mjs +6 -5
  106. package/dist/esm/main.mjs.map +1 -1
  107. package/dist/esm/run.mjs +121 -83
  108. package/dist/esm/run.mjs.map +1 -1
  109. package/dist/esm/stream.mjs +87 -54
  110. package/dist/esm/stream.mjs.map +1 -1
  111. package/dist/esm/tools/ToolNode.mjs +10 -4
  112. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  113. package/dist/esm/tools/handlers.mjs +119 -15
  114. package/dist/esm/tools/handlers.mjs.map +1 -1
  115. package/dist/esm/tools/search/anthropic.mjs +37 -0
  116. package/dist/esm/tools/search/anthropic.mjs.map +1 -0
  117. package/dist/esm/tools/search/firecrawl.mjs +55 -9
  118. package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
  119. package/dist/esm/tools/search/format.mjs +7 -7
  120. package/dist/esm/tools/search/format.mjs.map +1 -1
  121. package/dist/esm/tools/search/rerankers.mjs +7 -29
  122. package/dist/esm/tools/search/rerankers.mjs.map +1 -1
  123. package/dist/esm/tools/search/search.mjs +86 -16
  124. package/dist/esm/tools/search/search.mjs.map +1 -1
  125. package/dist/esm/tools/search/tool.mjs +4 -2
  126. package/dist/esm/tools/search/tool.mjs.map +1 -1
  127. package/dist/esm/tools/search/utils.mjs +1 -1
  128. package/dist/esm/tools/search/utils.mjs.map +1 -1
  129. package/dist/esm/utils/events.mjs +29 -0
  130. package/dist/esm/utils/events.mjs.map +1 -0
  131. package/dist/esm/utils/title.mjs +57 -22
  132. package/dist/esm/utils/title.mjs.map +1 -1
  133. package/dist/esm/utils/tokens.mjs +54 -8
  134. package/dist/esm/utils/tokens.mjs.map +1 -1
  135. package/dist/types/agents/AgentContext.d.ts +91 -0
  136. package/dist/types/common/enum.d.ts +15 -6
  137. package/dist/types/events.d.ts +5 -4
  138. package/dist/types/graphs/Graph.d.ts +64 -67
  139. package/dist/types/graphs/MultiAgentGraph.d.ts +37 -0
  140. package/dist/types/graphs/index.d.ts +1 -0
  141. package/dist/types/llm/anthropic/index.d.ts +11 -0
  142. package/dist/types/llm/anthropic/types.d.ts +9 -3
  143. package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -1
  144. package/dist/types/llm/anthropic/utils/output_parsers.d.ts +4 -4
  145. package/dist/types/llm/anthropic/utils/tools.d.ts +3 -0
  146. package/dist/types/llm/google/index.d.ts +13 -0
  147. package/dist/types/llm/google/types.d.ts +32 -0
  148. package/dist/types/llm/google/utils/common.d.ts +19 -0
  149. package/dist/types/llm/google/utils/tools.d.ts +10 -0
  150. package/dist/types/llm/google/utils/zod_to_genai_parameters.d.ts +14 -0
  151. package/dist/types/llm/ollama/index.d.ts +7 -0
  152. package/dist/types/llm/ollama/utils.d.ts +7 -0
  153. package/dist/types/llm/openai/index.d.ts +72 -3
  154. package/dist/types/llm/openai/types.d.ts +10 -0
  155. package/dist/types/llm/openai/utils/index.d.ts +20 -0
  156. package/dist/types/llm/text.d.ts +1 -1
  157. package/dist/types/llm/vertexai/index.d.ts +293 -0
  158. package/dist/types/messages/reducer.d.ts +9 -0
  159. package/dist/types/run.d.ts +19 -12
  160. package/dist/types/scripts/ant_web_search.d.ts +1 -0
  161. package/dist/types/scripts/args.d.ts +2 -1
  162. package/dist/types/scripts/handoff-test.d.ts +1 -0
  163. package/dist/types/scripts/multi-agent-conditional.d.ts +1 -0
  164. package/dist/types/scripts/multi-agent-parallel.d.ts +1 -0
  165. package/dist/types/scripts/multi-agent-sequence.d.ts +1 -0
  166. package/dist/types/scripts/multi-agent-test.d.ts +1 -0
  167. package/dist/types/stream.d.ts +10 -3
  168. package/dist/types/tools/CodeExecutor.d.ts +2 -2
  169. package/dist/types/tools/ToolNode.d.ts +1 -1
  170. package/dist/types/tools/handlers.d.ts +17 -4
  171. package/dist/types/tools/search/anthropic.d.ts +16 -0
  172. package/dist/types/tools/search/firecrawl.d.ts +15 -0
  173. package/dist/types/tools/search/rerankers.d.ts +0 -1
  174. package/dist/types/tools/search/types.d.ts +30 -9
  175. package/dist/types/types/graph.d.ts +95 -15
  176. package/dist/types/types/llm.d.ts +24 -10
  177. package/dist/types/types/run.d.ts +46 -8
  178. package/dist/types/types/stream.d.ts +16 -2
  179. package/dist/types/types/tools.d.ts +1 -1
  180. package/dist/types/utils/events.d.ts +6 -0
  181. package/dist/types/utils/title.d.ts +2 -1
  182. package/dist/types/utils/tokens.d.ts +24 -0
  183. package/package.json +33 -17
  184. package/src/agents/AgentContext.ts +315 -0
  185. package/src/common/enum.ts +14 -5
  186. package/src/events.ts +24 -13
  187. package/src/graphs/Graph.ts +495 -312
  188. package/src/graphs/MultiAgentGraph.ts +381 -0
  189. package/src/graphs/index.ts +2 -1
  190. package/src/llm/anthropic/Jacob_Lee_Resume_2023.pdf +0 -0
  191. package/src/llm/anthropic/index.ts +78 -13
  192. package/src/llm/anthropic/llm.spec.ts +491 -115
  193. package/src/llm/anthropic/types.ts +39 -3
  194. package/src/llm/anthropic/utils/message_inputs.ts +67 -11
  195. package/src/llm/anthropic/utils/message_outputs.ts +21 -2
  196. package/src/llm/anthropic/utils/output_parsers.ts +25 -6
  197. package/src/llm/anthropic/utils/tools.ts +29 -0
  198. package/src/llm/google/index.ts +218 -0
  199. package/src/llm/google/types.ts +43 -0
  200. package/src/llm/google/utils/common.ts +646 -0
  201. package/src/llm/google/utils/tools.ts +160 -0
  202. package/src/llm/google/utils/zod_to_genai_parameters.ts +86 -0
  203. package/src/llm/ollama/index.ts +89 -0
  204. package/src/llm/ollama/utils.ts +193 -0
  205. package/src/llm/openai/index.ts +600 -14
  206. package/src/llm/openai/types.ts +24 -0
  207. package/src/llm/openai/utils/index.ts +912 -0
  208. package/src/llm/openai/utils/isReasoningModel.test.ts +90 -0
  209. package/src/llm/providers.ts +10 -9
  210. package/src/llm/text.ts +26 -7
  211. package/src/llm/vertexai/index.ts +360 -0
  212. package/src/messages/reducer.ts +80 -0
  213. package/src/run.ts +181 -112
  214. package/src/scripts/ant_web_search.ts +158 -0
  215. package/src/scripts/args.ts +12 -8
  216. package/src/scripts/cli4.ts +29 -21
  217. package/src/scripts/cli5.ts +29 -21
  218. package/src/scripts/code_exec.ts +54 -23
  219. package/src/scripts/code_exec_files.ts +48 -17
  220. package/src/scripts/code_exec_simple.ts +46 -27
  221. package/src/scripts/handoff-test.ts +135 -0
  222. package/src/scripts/image.ts +52 -20
  223. package/src/scripts/multi-agent-conditional.ts +220 -0
  224. package/src/scripts/multi-agent-example-output.md +110 -0
  225. package/src/scripts/multi-agent-parallel.ts +337 -0
  226. package/src/scripts/multi-agent-sequence.ts +212 -0
  227. package/src/scripts/multi-agent-test.ts +186 -0
  228. package/src/scripts/search.ts +1 -9
  229. package/src/scripts/simple.ts +25 -10
  230. package/src/scripts/tools.ts +48 -18
  231. package/src/specs/anthropic.simple.test.ts +150 -34
  232. package/src/specs/azure.simple.test.ts +325 -0
  233. package/src/specs/openai.simple.test.ts +140 -33
  234. package/src/specs/openrouter.simple.test.ts +107 -0
  235. package/src/specs/prune.test.ts +4 -9
  236. package/src/specs/reasoning.test.ts +80 -44
  237. package/src/specs/token-memoization.test.ts +39 -0
  238. package/src/stream.test.ts +94 -0
  239. package/src/stream.ts +139 -60
  240. package/src/tools/ToolNode.ts +21 -7
  241. package/src/tools/handlers.ts +192 -18
  242. package/src/tools/search/anthropic.ts +51 -0
  243. package/src/tools/search/firecrawl.ts +69 -20
  244. package/src/tools/search/format.ts +6 -8
  245. package/src/tools/search/rerankers.ts +7 -40
  246. package/src/tools/search/search.ts +97 -16
  247. package/src/tools/search/tool.ts +5 -2
  248. package/src/tools/search/types.ts +30 -10
  249. package/src/tools/search/utils.ts +1 -1
  250. package/src/types/graph.ts +272 -103
  251. package/src/types/llm.ts +25 -12
  252. package/src/types/run.ts +51 -13
  253. package/src/types/stream.ts +22 -1
  254. package/src/types/tools.ts +16 -10
  255. package/src/utils/events.ts +32 -0
  256. package/src/utils/llmConfig.ts +19 -7
  257. package/src/utils/title.ts +104 -30
  258. package/src/utils/tokens.ts +69 -10
@@ -4,12 +4,19 @@
4
4
  import { config } from 'dotenv';
5
5
  config();
6
6
  import { Calculator } from '@langchain/community/tools/calculator';
7
- import { HumanMessage, BaseMessage, UsageMetadata } from '@langchain/core/messages';
8
- import type { StandardGraph } from '@/graphs';
7
+ import {
8
+ HumanMessage,
9
+ BaseMessage,
10
+ UsageMetadata,
11
+ } from '@langchain/core/messages';
9
12
  import type * as t from '@/types';
10
- import { ToolEndHandler, ModelEndHandler, createMetadataAggregator } from '@/events';
13
+ import {
14
+ ToolEndHandler,
15
+ ModelEndHandler,
16
+ createMetadataAggregator,
17
+ } from '@/events';
18
+ import { ContentTypes, GraphEvents, Providers, TitleMethod } from '@/common';
11
19
  import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
12
- import { ContentTypes, GraphEvents, Providers } from '@/common';
13
20
  import { capitalizeFirstLetter } from './spec.utils';
14
21
  import { getLLMConfig } from '@/utils/llmConfig';
15
22
  import { getArgs } from '@/scripts/args';
@@ -17,7 +24,7 @@ import { Run } from '@/run';
17
24
 
18
25
  const provider = Providers.ANTHROPIC;
19
26
  describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
20
- jest.setTimeout(30000);
27
+ jest.setTimeout(60000);
21
28
  let run: Run<t.IState>;
22
29
  let runningHistory: BaseMessage[];
23
30
  let collectedUsage: UsageMetadata[];
@@ -36,7 +43,8 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
36
43
  beforeEach(async () => {
37
44
  conversationHistory = [];
38
45
  collectedUsage = [];
39
- const { contentParts: cp, aggregateContent: ac } = createContentAggregator();
46
+ const { contentParts: cp, aggregateContent: ac } =
47
+ createContentAggregator();
40
48
  contentParts = cp as t.MessageContentComplex[];
41
49
  aggregateContent = ac;
42
50
  });
@@ -49,36 +57,62 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
49
57
  onRunStepSpy.mockReset();
50
58
  });
51
59
 
52
- const setupCustomHandlers = (): Record<string | GraphEvents, t.EventHandler> => ({
60
+ const setupCustomHandlers = (): Record<
61
+ string | GraphEvents,
62
+ t.EventHandler
63
+ > => ({
53
64
  [GraphEvents.TOOL_END]: new ToolEndHandler(),
54
65
  [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
55
66
  [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
56
67
  [GraphEvents.ON_RUN_STEP_COMPLETED]: {
57
- handle: (event: GraphEvents.ON_RUN_STEP_COMPLETED, data: t.StreamEventData): void => {
58
- aggregateContent({ event, data: data as unknown as { result: t.ToolEndEvent; } });
59
- }
68
+ handle: (
69
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
70
+ data: t.StreamEventData
71
+ ): void => {
72
+ aggregateContent({
73
+ event,
74
+ data: data as unknown as { result: t.ToolEndEvent },
75
+ });
76
+ },
60
77
  },
61
78
  [GraphEvents.ON_RUN_STEP]: {
62
- handle: (event: GraphEvents.ON_RUN_STEP, data: t.StreamEventData, metadata, graph): void => {
79
+ handle: (
80
+ event: GraphEvents.ON_RUN_STEP,
81
+ data: t.StreamEventData,
82
+ metadata,
83
+ graph
84
+ ): void => {
63
85
  onRunStepSpy(event, data, metadata, graph);
64
86
  aggregateContent({ event, data: data as t.RunStep });
65
- }
87
+ },
66
88
  },
67
89
  [GraphEvents.ON_RUN_STEP_DELTA]: {
68
- handle: (event: GraphEvents.ON_RUN_STEP_DELTA, data: t.StreamEventData): void => {
90
+ handle: (
91
+ event: GraphEvents.ON_RUN_STEP_DELTA,
92
+ data: t.StreamEventData
93
+ ): void => {
69
94
  aggregateContent({ event, data: data as t.RunStepDeltaEvent });
70
- }
95
+ },
71
96
  },
72
97
  [GraphEvents.ON_MESSAGE_DELTA]: {
73
- handle: (event: GraphEvents.ON_MESSAGE_DELTA, data: t.StreamEventData, metadata, graph): void => {
98
+ handle: (
99
+ event: GraphEvents.ON_MESSAGE_DELTA,
100
+ data: t.StreamEventData,
101
+ metadata,
102
+ graph
103
+ ): void => {
74
104
  onMessageDeltaSpy(event, data, metadata, graph);
75
105
  aggregateContent({ event, data: data as t.MessageDeltaEvent });
76
- }
106
+ },
77
107
  },
78
108
  [GraphEvents.TOOL_START]: {
79
- handle: (_event: string, _data: t.StreamEventData, _metadata?: Record<string, unknown>): void => {
109
+ handle: (
110
+ _event: string,
111
+ _data: t.StreamEventData,
112
+ _metadata?: Record<string, unknown>
113
+ ): void => {
80
114
  // Handle tool start
81
- }
115
+ },
82
116
  },
83
117
  });
84
118
 
@@ -93,7 +127,8 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
93
127
  type: 'standard',
94
128
  llmConfig,
95
129
  tools: [new Calculator()],
96
- instructions: 'You are a friendly AI assistant. Always address the user by their name.',
130
+ instructions:
131
+ 'You are a friendly AI assistant. Always address the user by their name.',
97
132
  additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
98
133
  },
99
134
  returnContent: true,
@@ -109,7 +144,9 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
109
144
 
110
145
  const finalContentParts = await run.processStream(inputs, config);
111
146
  expect(finalContentParts).toBeDefined();
112
- const allTextParts = finalContentParts?.every((part) => part.type === ContentTypes.TEXT);
147
+ const allTextParts = finalContentParts?.every(
148
+ (part) => part.type === ContentTypes.TEXT
149
+ );
113
150
  expect(allTextParts).toBe(true);
114
151
  expect(collectedUsage.length).toBeGreaterThan(0);
115
152
  expect(collectedUsage[0].input_tokens).toBeGreaterThan(0);
@@ -117,26 +154,34 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
117
154
 
118
155
  const finalMessages = run.getRunMessages();
119
156
  expect(finalMessages).toBeDefined();
120
- conversationHistory.push(...finalMessages ?? []);
157
+ conversationHistory.push(...(finalMessages ?? []));
121
158
  expect(conversationHistory.length).toBeGreaterThan(1);
122
159
  runningHistory = conversationHistory.slice();
123
160
 
124
161
  expect(onMessageDeltaSpy).toHaveBeenCalled();
125
162
  expect(onMessageDeltaSpy.mock.calls.length).toBeGreaterThan(1);
126
- expect((onMessageDeltaSpy.mock.calls[0][3] as StandardGraph).provider).toBeDefined();
163
+ expect(onMessageDeltaSpy.mock.calls[0][3]).toBeDefined(); // Graph exists
127
164
 
128
165
  expect(onRunStepSpy).toHaveBeenCalled();
129
166
  expect(onRunStepSpy.mock.calls.length).toBeGreaterThan(0);
130
- expect((onRunStepSpy.mock.calls[0][3] as StandardGraph).provider).toBeDefined();
167
+ expect(onRunStepSpy.mock.calls[0][3]).toBeDefined(); // Graph exists
131
168
 
132
169
  const { handleLLMEnd, collected } = createMetadataAggregator();
133
170
  const titleResult = await run.generateTitle({
171
+ provider,
134
172
  inputText: userMessage,
173
+ titleMethod: TitleMethod.STRUCTURED,
135
174
  contentParts,
175
+ clientOptions: {
176
+ ...llmConfig,
177
+ model: 'claude-3-5-haiku-latest',
178
+ },
136
179
  chainOptions: {
137
- callbacks: [{
138
- handleLLMEnd,
139
- }],
180
+ callbacks: [
181
+ {
182
+ handleLLMEnd,
183
+ },
184
+ ],
140
185
  },
141
186
  });
142
187
 
@@ -146,9 +191,71 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
146
191
  expect(collected).toBeDefined();
147
192
  });
148
193
 
194
+ test(`${capitalizeFirstLetter(provider)}: should generate title using completion method`, async () => {
195
+ const { userName, location } = await getArgs();
196
+ const llmConfig = getLLMConfig(provider);
197
+ const customHandlers = setupCustomHandlers();
198
+
199
+ run = await Run.create<t.IState>({
200
+ runId: 'test-run-id-completion',
201
+ graphConfig: {
202
+ type: 'standard',
203
+ llmConfig,
204
+ tools: [new Calculator()],
205
+ instructions:
206
+ 'You are a friendly AI assistant. Always address the user by their name.',
207
+ additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
208
+ },
209
+ returnContent: true,
210
+ customHandlers,
211
+ });
212
+
213
+ const userMessage =
214
+ 'Can you help me calculate the area of a circle with radius 5?';
215
+ conversationHistory = [];
216
+ conversationHistory.push(new HumanMessage(userMessage));
217
+
218
+ const inputs = {
219
+ messages: conversationHistory,
220
+ };
221
+
222
+ const finalContentParts = await run.processStream(inputs, config);
223
+ expect(finalContentParts).toBeDefined();
224
+
225
+ const { handleLLMEnd, collected } = createMetadataAggregator();
226
+ const titleResult = await run.generateTitle({
227
+ provider,
228
+ inputText: userMessage,
229
+ titleMethod: TitleMethod.COMPLETION, // Using completion method
230
+ contentParts,
231
+ clientOptions: {
232
+ ...llmConfig,
233
+ model: 'claude-3-5-haiku-latest',
234
+ },
235
+ chainOptions: {
236
+ callbacks: [
237
+ {
238
+ handleLLMEnd,
239
+ },
240
+ ],
241
+ },
242
+ });
243
+
244
+ expect(titleResult).toBeDefined();
245
+ expect(titleResult.title).toBeDefined();
246
+ expect(titleResult.title).not.toBe('');
247
+ // Completion method doesn't return language
248
+ expect(titleResult.language).toBeUndefined();
249
+ expect(collected).toBeDefined();
250
+ console.log(`Completion method generated title: "${titleResult.title}"`);
251
+ });
252
+
149
253
  test(`${capitalizeFirstLetter(provider)}: should follow-up`, async () => {
150
254
  console.log('Previous conversation length:', runningHistory.length);
151
- console.log('Last message:', runningHistory[runningHistory.length - 1].content);
255
+ console.log(
256
+ 'Last message:',
257
+ runningHistory[runningHistory.length - 1].content
258
+ );
152
259
  const { userName, location } = await getArgs();
153
260
  const llmConfig = getLLMConfig(provider);
154
261
  const customHandlers = setupCustomHandlers();
@@ -159,7 +266,8 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
159
266
  type: 'standard',
160
267
  llmConfig,
161
268
  tools: [new Calculator()],
162
- instructions: 'You are a friendly AI assistant. Always address the user by their name.',
269
+ instructions:
270
+ 'You are a friendly AI assistant. Always address the user by their name.',
163
271
  additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
164
272
  },
165
273
  returnContent: true,
@@ -175,7 +283,9 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
175
283
 
176
284
  const finalContentParts = await run.processStream(inputs, config);
177
285
  expect(finalContentParts).toBeDefined();
178
- const allTextParts = finalContentParts?.every((part) => part.type === ContentTypes.TEXT);
286
+ const allTextParts = finalContentParts?.every(
287
+ (part) => part.type === ContentTypes.TEXT
288
+ );
179
289
  expect(allTextParts).toBe(true);
180
290
  expect(collectedUsage.length).toBeGreaterThan(0);
181
291
  expect(collectedUsage[0].input_tokens).toBeGreaterThan(0);
@@ -184,7 +294,10 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
184
294
  const finalMessages = run.getRunMessages();
185
295
  expect(finalMessages).toBeDefined();
186
296
  expect(finalMessages?.length).toBeGreaterThan(0);
187
- console.log(`${capitalizeFirstLetter(provider)} follow-up message:`, finalMessages?.[finalMessages.length - 1]?.content);
297
+ console.log(
298
+ `${capitalizeFirstLetter(provider)} follow-up message:`,
299
+ finalMessages?.[finalMessages.length - 1]?.content
300
+ );
188
301
 
189
302
  expect(onMessageDeltaSpy).toHaveBeenCalled();
190
303
  expect(onMessageDeltaSpy.mock.calls.length).toBeGreaterThan(1);
@@ -196,9 +309,12 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
196
309
  test('should handle errors appropriately', async () => {
197
310
  // Test error scenarios
198
311
  await expect(async () => {
199
- await run.processStream({
200
- messages: [],
201
- }, {} as any);
312
+ await run.processStream(
313
+ {
314
+ messages: [],
315
+ },
316
+ {} as any
317
+ );
202
318
  }).rejects.toThrow();
203
319
  });
204
- });
320
+ });
@@ -0,0 +1,325 @@
1
+ /* eslint-disable no-console */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ // src/scripts/cli.test.ts
4
+ import { config } from 'dotenv';
5
+ config();
6
+ import { Calculator } from '@langchain/community/tools/calculator';
7
+ import {
8
+ HumanMessage,
9
+ BaseMessage,
10
+ UsageMetadata,
11
+ } from '@langchain/core/messages';
12
+ import type * as t from '@/types';
13
+ import {
14
+ ToolEndHandler,
15
+ ModelEndHandler,
16
+ createMetadataAggregator,
17
+ } from '@/events';
18
+ import { ContentTypes, GraphEvents, Providers, TitleMethod } from '@/common';
19
+ import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
20
+ import { capitalizeFirstLetter } from './spec.utils';
21
+ import { getLLMConfig } from '@/utils/llmConfig';
22
+ import { getArgs } from '@/scripts/args';
23
+ import { Run } from '@/run';
24
+
25
+ // Auto-skip this suite if Azure env vars are not present
26
+ const requiredAzureEnv = [
27
+ 'AZURE_OPENAI_API_KEY',
28
+ 'AZURE_OPENAI_API_INSTANCE',
29
+ 'AZURE_OPENAI_API_DEPLOYMENT',
30
+ 'AZURE_OPENAI_API_VERSION',
31
+ ];
32
+ const hasAzure = requiredAzureEnv.every(
33
+ (k) => (process.env[k] ?? '').trim() !== ''
34
+ );
35
+ const describeIfAzure = hasAzure ? describe : describe.skip;
36
+
37
+ const provider = Providers.AZURE;
38
+ describeIfAzure(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
39
+ jest.setTimeout(30000);
40
+ let run: Run<t.IState>;
41
+ let runningHistory: BaseMessage[];
42
+ let collectedUsage: UsageMetadata[];
43
+ let conversationHistory: BaseMessage[];
44
+ let aggregateContent: t.ContentAggregator;
45
+ let contentParts: t.MessageContentComplex[];
46
+
47
+ const config = {
48
+ configurable: {
49
+ thread_id: 'conversation-num-1',
50
+ },
51
+ streamMode: 'values',
52
+ version: 'v2' as const,
53
+ };
54
+
55
+ beforeEach(async () => {
56
+ conversationHistory = [];
57
+ collectedUsage = [];
58
+ const { contentParts: cp, aggregateContent: ac } =
59
+ createContentAggregator();
60
+ contentParts = cp as t.MessageContentComplex[];
61
+ aggregateContent = ac;
62
+ });
63
+
64
+ const onMessageDeltaSpy = jest.fn();
65
+ const onRunStepSpy = jest.fn();
66
+
67
+ afterAll(() => {
68
+ onMessageDeltaSpy.mockReset();
69
+ onRunStepSpy.mockReset();
70
+ });
71
+
72
+ const setupCustomHandlers = (): Record<
73
+ string | GraphEvents,
74
+ t.EventHandler
75
+ > => ({
76
+ [GraphEvents.TOOL_END]: new ToolEndHandler(),
77
+ [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
78
+ [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
79
+ [GraphEvents.ON_RUN_STEP_COMPLETED]: {
80
+ handle: (
81
+ event: GraphEvents.ON_RUN_STEP_COMPLETED,
82
+ data: t.StreamEventData
83
+ ): void => {
84
+ aggregateContent({
85
+ event,
86
+ data: data as unknown as { result: t.ToolEndEvent },
87
+ });
88
+ },
89
+ },
90
+ [GraphEvents.ON_RUN_STEP]: {
91
+ handle: (
92
+ event: GraphEvents.ON_RUN_STEP,
93
+ data: t.StreamEventData,
94
+ metadata,
95
+ graph
96
+ ): void => {
97
+ onRunStepSpy(event, data, metadata, graph);
98
+ aggregateContent({ event, data: data as t.RunStep });
99
+ },
100
+ },
101
+ [GraphEvents.ON_RUN_STEP_DELTA]: {
102
+ handle: (
103
+ event: GraphEvents.ON_RUN_STEP_DELTA,
104
+ data: t.StreamEventData
105
+ ): void => {
106
+ aggregateContent({ event, data: data as t.RunStepDeltaEvent });
107
+ },
108
+ },
109
+ [GraphEvents.ON_MESSAGE_DELTA]: {
110
+ handle: (
111
+ event: GraphEvents.ON_MESSAGE_DELTA,
112
+ data: t.StreamEventData,
113
+ metadata,
114
+ graph
115
+ ): void => {
116
+ onMessageDeltaSpy(event, data, metadata, graph);
117
+ aggregateContent({ event, data: data as t.MessageDeltaEvent });
118
+ },
119
+ },
120
+ [GraphEvents.TOOL_START]: {
121
+ handle: (
122
+ _event: string,
123
+ _data: t.StreamEventData,
124
+ _metadata?: Record<string, unknown>
125
+ ): void => {
126
+ // Handle tool start
127
+ },
128
+ },
129
+ });
130
+
131
+ test(`${capitalizeFirstLetter(provider)}: should process a simple message, generate title`, async () => {
132
+ const { userName, location } = await getArgs();
133
+ const llmConfig = getLLMConfig(provider);
134
+ const customHandlers = setupCustomHandlers();
135
+
136
+ run = await Run.create<t.IState>({
137
+ runId: 'test-run-id',
138
+ graphConfig: {
139
+ type: 'standard',
140
+ llmConfig,
141
+ tools: [new Calculator()],
142
+ instructions:
143
+ 'You are a friendly AI assistant. Always address the user by their name.',
144
+ additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
145
+ },
146
+ returnContent: true,
147
+ customHandlers,
148
+ });
149
+
150
+ const userMessage = 'hi';
151
+ conversationHistory.push(new HumanMessage(userMessage));
152
+
153
+ const inputs = {
154
+ messages: conversationHistory,
155
+ };
156
+
157
+ const finalContentParts = await run.processStream(inputs, config);
158
+ expect(finalContentParts).toBeDefined();
159
+ const allTextParts = finalContentParts?.every(
160
+ (part) => part.type === ContentTypes.TEXT
161
+ );
162
+ expect(allTextParts).toBe(true);
163
+ expect(collectedUsage.length).toBeGreaterThan(0);
164
+ expect(collectedUsage[0].input_tokens).toBeGreaterThan(0);
165
+ expect(collectedUsage[0].output_tokens).toBeGreaterThan(0);
166
+
167
+ const finalMessages = run.getRunMessages();
168
+ expect(finalMessages).toBeDefined();
169
+ conversationHistory.push(...(finalMessages ?? []));
170
+ expect(conversationHistory.length).toBeGreaterThan(1);
171
+ runningHistory = conversationHistory.slice();
172
+
173
+ expect(onMessageDeltaSpy).toHaveBeenCalled();
174
+ expect(onMessageDeltaSpy.mock.calls.length).toBeGreaterThan(1);
175
+ expect(onMessageDeltaSpy.mock.calls[0][3]).toBeDefined(); // Graph exists
176
+
177
+ expect(onRunStepSpy).toHaveBeenCalled();
178
+ expect(onRunStepSpy.mock.calls.length).toBeGreaterThan(0);
179
+ expect(onRunStepSpy.mock.calls[0][3]).toBeDefined(); // Graph exists
180
+
181
+ const { handleLLMEnd, collected } = createMetadataAggregator();
182
+ const titleResult = await run.generateTitle({
183
+ provider,
184
+ inputText: userMessage,
185
+ titleMethod: TitleMethod.STRUCTURED,
186
+ contentParts,
187
+ clientOptions: llmConfig,
188
+ chainOptions: {
189
+ callbacks: [
190
+ {
191
+ handleLLMEnd,
192
+ },
193
+ ],
194
+ },
195
+ });
196
+
197
+ expect(titleResult).toBeDefined();
198
+ expect(titleResult.title).toBeDefined();
199
+ expect(titleResult.language).toBeDefined();
200
+ expect(collected).toBeDefined();
201
+ });
202
+
203
+ test(`${capitalizeFirstLetter(provider)}: should generate title using completion method`, async () => {
204
+ const { userName, location } = await getArgs();
205
+ const llmConfig = getLLMConfig(provider);
206
+ const customHandlers = setupCustomHandlers();
207
+
208
+ run = await Run.create<t.IState>({
209
+ runId: 'test-run-id-completion',
210
+ graphConfig: {
211
+ type: 'standard',
212
+ llmConfig,
213
+ tools: [new Calculator()],
214
+ instructions:
215
+ 'You are a friendly AI assistant. Always address the user by their name.',
216
+ additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
217
+ },
218
+ returnContent: true,
219
+ customHandlers,
220
+ });
221
+
222
+ const userMessage = 'What is the weather like today?';
223
+ conversationHistory = [];
224
+ conversationHistory.push(new HumanMessage(userMessage));
225
+
226
+ const inputs = {
227
+ messages: conversationHistory,
228
+ };
229
+
230
+ const finalContentParts = await run.processStream(inputs, config);
231
+ expect(finalContentParts).toBeDefined();
232
+
233
+ const { handleLLMEnd, collected } = createMetadataAggregator();
234
+ const titleResult = await run.generateTitle({
235
+ provider,
236
+ inputText: userMessage,
237
+ titleMethod: TitleMethod.COMPLETION, // Using completion method
238
+ contentParts,
239
+ clientOptions: llmConfig,
240
+ chainOptions: {
241
+ callbacks: [
242
+ {
243
+ handleLLMEnd,
244
+ },
245
+ ],
246
+ },
247
+ });
248
+
249
+ expect(titleResult).toBeDefined();
250
+ expect(titleResult.title).toBeDefined();
251
+ expect(titleResult.title).not.toBe('');
252
+ // Completion method doesn't return language
253
+ expect(titleResult.language).toBeUndefined();
254
+ expect(collected).toBeDefined();
255
+ console.log(`Completion method generated title: "${titleResult.title}"`);
256
+ });
257
+
258
+ test(`${capitalizeFirstLetter(provider)}: should follow-up`, async () => {
259
+ console.log('Previous conversation length:', runningHistory.length);
260
+ console.log(
261
+ 'Last message:',
262
+ runningHistory[runningHistory.length - 1].content
263
+ );
264
+ const { userName, location } = await getArgs();
265
+ const llmConfig = getLLMConfig(provider);
266
+ const customHandlers = setupCustomHandlers();
267
+
268
+ run = await Run.create<t.IState>({
269
+ runId: 'test-run-id',
270
+ graphConfig: {
271
+ type: 'standard',
272
+ llmConfig,
273
+ tools: [new Calculator()],
274
+ instructions:
275
+ 'You are a friendly AI assistant. Always address the user by their name.',
276
+ additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
277
+ },
278
+ returnContent: true,
279
+ customHandlers,
280
+ });
281
+
282
+ conversationHistory = runningHistory.slice();
283
+ conversationHistory.push(new HumanMessage('how are you?'));
284
+
285
+ const inputs = {
286
+ messages: conversationHistory,
287
+ };
288
+
289
+ const finalContentParts = await run.processStream(inputs, config);
290
+ expect(finalContentParts).toBeDefined();
291
+ const allTextParts = finalContentParts?.every(
292
+ (part) => part.type === ContentTypes.TEXT
293
+ );
294
+ expect(allTextParts).toBe(true);
295
+ expect(collectedUsage.length).toBeGreaterThan(0);
296
+ expect(collectedUsage[0].input_tokens).toBeGreaterThan(0);
297
+ expect(collectedUsage[0].output_tokens).toBeGreaterThan(0);
298
+
299
+ const finalMessages = run.getRunMessages();
300
+ expect(finalMessages).toBeDefined();
301
+ expect(finalMessages?.length).toBeGreaterThan(0);
302
+ console.log(
303
+ `${capitalizeFirstLetter(provider)} follow-up message:`,
304
+ finalMessages?.[finalMessages.length - 1]?.content
305
+ );
306
+
307
+ expect(onMessageDeltaSpy).toHaveBeenCalled();
308
+ expect(onMessageDeltaSpy.mock.calls.length).toBeGreaterThan(1);
309
+
310
+ expect(onRunStepSpy).toHaveBeenCalled();
311
+ expect(onRunStepSpy.mock.calls.length).toBeGreaterThan(0);
312
+ });
313
+
314
+ test('should handle errors appropriately', async () => {
315
+ // Test error scenarios
316
+ await expect(async () => {
317
+ await run.processStream(
318
+ {
319
+ messages: [],
320
+ },
321
+ {} as any
322
+ );
323
+ }).rejects.toThrow();
324
+ });
325
+ });