illuma-agents 1.0.2 → 1.0.3

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