File size: 14,212 Bytes
e4f6727
910ae58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e4f6727
 
 
 
 
 
 
 
 
 
 
910ae58
 
 
 
e4f6727
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
910ae58
 
 
 
 
 
 
 
 
e4f6727
 
 
 
 
 
 
 
 
 
 
 
910ae58
 
e4f6727
 
 
 
910ae58
 
 
 
 
 
 
 
 
e4f6727
 
 
 
 
 
 
 
 
 
 
910ae58
 
e4f6727
 
910ae58
 
 
 
 
 
 
 
 
e4f6727
 
 
 
 
 
 
 
 
 
 
910ae58
 
e4f6727
 
910ae58
 
 
 
 
 
 
 
 
e4f6727
3d648f2
 
 
e4f6727
 
 
 
 
 
 
910ae58
 
e4f6727
910ae58
 
 
 
 
 
 
 
 
e4f6727
 
 
 
 
 
 
 
 
 
 
910ae58
 
e4f6727
910ae58
 
98ada6c
910ae58
98ada6c
910ae58
98ada6c
910ae58
 
e4f6727
 
 
 
 
 
 
 
 
 
 
910ae58
98ada6c
e4f6727
910ae58
 
 
 
 
 
 
 
 
e4f6727
 
 
 
 
 
 
 
 
 
 
 
910ae58
 
e4f6727
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
910ae58
 
 
 
 
e4f6727
910ae58
 
e4f6727
 
 
 
 
 
 
 
 
 
 
 
910ae58
e4f6727
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
910ae58
 
 
 
 
e4f6727
 
910ae58
e4f6727
910ae58
 
 
 
 
 
3d648f2
 
910ae58
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
from args import Args
from graph import State, Nodes, Edges
from graph_builder import GraphBuilder

import unittest


class TestAlfredAgent(unittest.TestCase):
    """Test suite for the Alfred agent"""
    
    def setUp(self):
        """Set up test fixtures"""
        self.nodes = Nodes()
        self.edges = Edges()
        self.builder = GraphBuilder()
        self.graph = self.builder.build_agent_graph()

    def test_manager_node(self):
        """
        Test the manager node functionality.
        
        Orchestrates the workflow by delegating tasks to specialized nodes and integrating their outputs
        """
        nodes = Nodes()
        
        # Create a test state
        test_state: State = {
            "initial_query": "query",
            "messages": ["query"],  # Manager's context
            "task_progress": [],  # Solver's context
            "audit_interval": 2,
            "manager_queries": 0,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        
        # Test the node function
        print(f"Testing 'manager' node...")
        nodes.manager_node(test_state)

        # Assert that manager_queries has been incremented
        self.assertEqual(test_state["manager_queries"], 1, "Manager queries should be incremented from 0 to 1")
        # Assert that a new message has been added to the messages list
        self.assertEqual(len(test_state["messages"]), 2, "Messages list should contain 2 items: the initial query and a new message from the manager node")

        # Test audit interval behaviour
        test_state = nodes.manager_node(test_state)

        # Assert that manager_queries has been incremented
        self.assertEqual(test_state["manager_queries"], 2, "Manager queries should be incremented from 1 to 2")
        # Assert that a new message has been added to the messages list
        self.assertEqual(len(test_state["messages"]), 2, "Messages list should contain 2 items: the initial 2 messages and no additional message as it is the audit interval")

        already_tested_messages = test_state["messages"]
        expected_state: State = {
            "initial_query": "query",
            "messages": already_tested_messages,  # Manager's context
            "task_progress": [test_state["messages"][-1]],  # Solver's context
            "audit_interval": 2,
            "manager_queries": 2,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }

        self.assertEqual(test_state, expected_state, "The state after manager node execution should match the expected state with manager_queries=2 and no additional messages added during audit interval")
        print(f"State after node execution: {test_state}")

    def test_final_answer_node(self):
        """
        Test the final_answer node functionality.
        
        Formats and delivers the final response to the user
        """
        nodes = Nodes()
        # Prepare a state with messages and required fields
        test_state: State = {
            "initial_query": "What is the capital of France?",
            "messages": ["What is the capital of France?", "The capital of France is Paris."],
            "task_progress": [],
            "audit_interval": 2,
            "manager_queries": 2,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        print(f"Testing 'final_answer' node...")
        nodes.final_answer_node(test_state)
        # The last message should be the instruction
        self.assertIn("Formulate a definitive final answer", test_state["messages"][-1])
        # The final_response should be set and not None
        self.assertIsNotNone(test_state["final_response"])
        print(f"State after node execution: {test_state}")

    def test_auditor_node(self):
        """
        Test the auditor node functionality.
        
        Reviews manager's outputs for accuracy, safety, and quality
        """
        nodes = Nodes()
        test_state: State = {
            "initial_query": "What is the capital of France?",
            "messages": ["What is the capital of France?", "The capital of France is Paris."],
            "task_progress": [],
            "audit_interval": 2,
            "manager_queries": 2,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        print(f"Testing 'auditor' node...")
        nodes.auditor_node(test_state)
        # Auditor appends a message
        self.assertGreaterEqual(len(test_state["messages"]), 3)
        print(f"State after node execution: {test_state}")

    def test_solver_node(self):
        """
        Test the solver node functionality.
        
        Central problem-solving node that coordinates with specialized experts based on task requirements
        """
        nodes = Nodes()
        test_state: State = {
            "initial_query": "What is the capital of France?",
            "messages": ["What is the capital of France?"],
            "task_progress": ["Solve: What is the capital of France?"],
            "audit_interval": 2,
            "manager_queries": 1,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        print(f"Testing 'solver' node...")
        nodes.solver_node(test_state)
        # Solver appends to task_progress
        self.assertGreaterEqual(len(test_state["task_progress"]), 2)
        print(f"State after node execution: {test_state}")

    def test_researcher_node(self):
        """
        Test the researcher node functionality.
        
        Retrieves and synthesizes information from various sources to answer knowledge-based questions
        """
        nodes = Nodes()
        test_state: State = {
            "initial_query": "What are the latest news headlines about artificial intelligence published this week?",
            "messages": ["What are the latest news headlines about artificial intelligence published this week?"],
            "task_progress": ["What are the latest news headlines about artificial intelligence published this week?"],
            "audit_interval": 2,
            "manager_queries": 1,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        print(f"Testing 'researcher' node...")
        nodes.researcher_node(test_state)
        self.assertGreaterEqual(len(test_state["task_progress"]), 2)
        print(f"State after node execution: {test_state}")

    def test_reasoner_node(self):
        """
        Test the reasoner node functionality.
        
        Performs logical reasoning, inference, and step-by-step problem-solving
        """
        nodes = Nodes()
        test_state: State = {
            "initial_query": "What is the capital of France?",
            "messages": ["What is the capital of France?"],
            "task_progress": ["Reason: What is the capital of France?"],
            "audit_interval": 2,
            "manager_queries": 1,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        print(f"Testing 'reasoner' node...")
        nodes.reasoner_node(test_state)
        self.assertGreaterEqual(len(test_state["task_progress"]), 2)
        print(f"State after node execution: {test_state}")

    def test_viewer_node(self):
        """
        Test the viewer node functionality.
        
        Processes, analyzes, and generates vision related information
        """
        nodes = Nodes()
        test_state: State = {
            "initial_query": "Describe the image.",
            "messages": ["Describe the image."],
            "task_progress": ["View: Describe the image."],
            "audit_interval": 2,
            "manager_queries": 1,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        print(f"Testing 'image_handler' node...")
        nodes.viewer_node(test_state)
        self.assertGreaterEqual(len(test_state["task_progress"]), 2)
        print(f"State after node execution: {test_state}")

    def test_manager_edge(self):
        """
        Test the conditional edge for manager node.
        
        This edge should return one of: "solver", "auditor", "final_answer"
        """
        edges = Edges()
        # Test for final_answer by FINAL ANSWER in last message
        test_state: State = {
            "initial_query": "Q",
            "messages": ["Q", "FINAL ANSWER: Paris"],
            "task_progress": [],
            "audit_interval": 2,
            "manager_queries": 2,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        print(f"Testing 'manager' conditional edge...")
        result = edges.manager_edge(test_state)
        self.assertEqual(result, "final_answer")

        # Test for final_answer by max_interactions
        test_state2: State = {
            "initial_query": "Q",
            "messages": ["Q", "Some message"],
            "task_progress": [],
            "audit_interval": 2,
            "manager_queries": 4,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        result2 = edges.manager_edge(test_state2)
        self.assertEqual(result2, "final_answer")

        # Test for auditor
        test_state3: State = {
            "initial_query": "Q",
            "messages": ["Q", "Some message"],
            "task_progress": [],
            "audit_interval": 2,
            "manager_queries": 2,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        result3 = edges.manager_edge(test_state3)
        self.assertEqual(result3, "auditor")

        # Test for solver
        test_state4: State = {
            "initial_query": "Q",
            "messages": ["Q", "Some message"],
            "task_progress": [],
            "audit_interval": 2,
            "manager_queries": 1,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        result4 = edges.manager_edge(test_state4)
        self.assertEqual(result4, "solver")
        print(f"Edge decision: {result4}")

    def test_solver_edge(self):
        """
        Test the conditional edge for solver node.
        
        This edge should return one of: "manager", "researcher", "reasoner", "viewer"
        """
        edges = Edges()
        # researcher
        test_state: State = {
            "initial_query": "Q",
            "messages": ["Q"],
            "task_progress": ["to: researcher"],
            "audit_interval": 2,
            "manager_queries": 1,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        result = edges.solver_edge(test_state)
        self.assertEqual(result, "researcher")

        # reasoner
        test_state2: State = {
            "initial_query": "Q",
            "messages": ["Q"],
            "task_progress": ["to: reasoner"],
            "audit_interval": 2,
            "manager_queries": 1,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        result2 = edges.solver_edge(test_state2)
        self.assertEqual(result2, "reasoner")

        # viewer
        test_state3: State = {
            "initial_query": "Q",
            "messages": ["Q"],
            "task_progress": ["to: viewer"],
            "audit_interval": 2,
            "manager_queries": 1,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        result3 = edges.solver_edge(test_state3)
        self.assertEqual(result3, "viewer")

        # manager
        test_state4: State = {
            "initial_query": "Q",
            "messages": ["Q"],
            "task_progress": ["to: manager"],
            "audit_interval": 2,
            "manager_queries": 1,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        result4 = edges.solver_edge(test_state4)
        self.assertEqual(result4, "manager")

        # unspecified (should append instruction and return manager)
        test_state5: State = {
            "initial_query": "Q",
            "messages": ["Q"],
            "task_progress": ["no receiver"],
            "audit_interval": 2,
            "manager_queries": 1,
            "solver_queries": 0,
            "max_interactions": 4,
            "max_solving_effort": 4,
            "final_response": None
        }
        result5 = edges.solver_edge(test_state5)
        self.assertEqual(result5, "manager")
        print(f"Edge decision: {result5}")

    def test_full_workflow(self):
        """
        Test the Alfred agent full workflow.
        """
        # This is a placeholder for a full workflow test.
        # For a real test, you would simulate the entire agent graph.
        print("Testing Alfred complete workflow...")
        # Example test (pseudo, as actual invoke may require more setup)
        # result = self.graph.invoke({"input": "Test input"})
        # self.assertIsNotNone(result)
        # print(f"Workflow result: {result}")


if __name__ == "__main__":
    # test = TestAlfredAgent()
    # test.test_researcher_node()
    unittest.main()