24Arys11 commited on
Commit
910ae58
·
1 Parent(s): bea2d8c

New approach: langgraph only

Browse files
agent_builder.py DELETED
@@ -1,321 +0,0 @@
1
- from pathlib import Path
2
- import yaml
3
- from typing import Dict, List, Any
4
-
5
-
6
- class AgentBuilder:
7
- @staticmethod
8
- def initialize(agent_name: str):
9
- """Create agent folder and an empty 'yaml' file
10
-
11
- Args:
12
- agent_name: Name of the agent to create
13
- """
14
- # Create base agents directory if it doesn't exist
15
- agents_base_dir = Path("agents")
16
- agents_base_dir.mkdir(exist_ok=True)
17
-
18
- # Create agent-specific directory
19
- agent_dir = agents_base_dir / agent_name
20
- agent_dir.mkdir(exist_ok=True)
21
-
22
- # Create YAML file with initial content
23
- yaml_file = agent_dir / f"design.yaml"
24
-
25
- # Initial YAML content with nodes list and example node
26
- initial_content = {
27
- "nodes": [
28
- {
29
- "name": "START",
30
- "connections": ["example_node"],
31
- "description": "This is the mandatory initial node `START` !"
32
- },
33
- {
34
- "name": "example_node",
35
- "connections": [],
36
- "description": "This is an example node"
37
- }
38
- ]
39
- }
40
-
41
- # Write the YAML content to the file
42
- with open(yaml_file, "w") as f:
43
- yaml.dump(initial_content, f, default_flow_style=False, sort_keys=False)
44
-
45
- @classmethod
46
- def setup(cls, agent_name: str):
47
- """Create the graph and the test python files as well as a 'puml' diagram
48
-
49
- Args:
50
- agent_name: Name of the agent to set up
51
- """
52
- design_data = cls._validate_design(agent_name)
53
-
54
- cls._create_graph_file(agent_name, design_data)
55
- cls._create_test_file(agent_name)
56
- cls._create_puml_file(agent_name, design_data)
57
-
58
- @classmethod
59
- def _validate_design(cls, agent_name: str) -> Dict[str, List[Dict[str, Any]]]:
60
- """Validate the design.yaml file structure
61
-
62
- Args:
63
- agent_name: Name of the agent to validate
64
-
65
- Returns:
66
- The parsed design data if valid
67
-
68
- Raises:
69
- ValueError: If design file is invalid or missing required elements
70
- """
71
- yaml_path = Path(f"agents/{agent_name}/design.yaml")
72
-
73
- if not yaml_path.exists():
74
- raise ValueError(f"Design file not found at {yaml_path}")
75
-
76
- with open(yaml_path, 'r') as f:
77
- design_data = yaml.safe_load(f)
78
-
79
- # Check if nodes list exists
80
- if not design_data or 'nodes' not in design_data or not isinstance(design_data['nodes'], list):
81
- raise ValueError("Design file must contain a 'nodes' list")
82
-
83
- # Check if START node is defined
84
- start_node_exists = any(node.get('name') == "START" for node in design_data['nodes'])
85
- if not start_node_exists:
86
- raise ValueError("Design file must contain a 'START' node")
87
-
88
- # Validate each node
89
- for i, node in enumerate(design_data['nodes']):
90
- if not isinstance(node, dict):
91
- raise ValueError(f"Node at index {i} must be a dictionary")
92
-
93
- if 'name' not in node:
94
- raise ValueError(f"Node at index {i} is missing a 'name' field")
95
-
96
- if 'description' not in node:
97
- raise ValueError(f"Node '{node.get('name', f'at index {i}')}' is missing a 'description' field")
98
-
99
- if 'connections' not in node or not isinstance(node['connections'], list):
100
- raise ValueError(f"Node '{node.get('name')}' must have a 'connections' list")
101
-
102
- return design_data
103
-
104
- @classmethod
105
- def _create_graph_file(cls, agent_name: str, design_data: Dict[str, List[Dict[str, Any]]]):
106
- """Create the graph.py file with the necessary classes
107
-
108
- Args:
109
- agent_name: Name of the agent
110
- design_data: The validated design data
111
- """
112
- nodes = design_data['nodes']
113
-
114
- # Prepare node methods and conditional edge methods
115
- node_methods = []
116
- edge_methods = []
117
-
118
- # Generate node method for each node
119
- for node in nodes:
120
- node_name = node['name']
121
- node_desc = node['description']
122
-
123
- node_method = f'''
124
- def {node_name}_node(self, state):
125
- """
126
- {node_desc}
127
- """
128
- # TODO: To implement...
129
- pass
130
- '''
131
- if node_name != "START":
132
- node_methods.append(node_method)
133
-
134
- # Check if this node has more than one connection (needs conditional edge)
135
- if len(node['connections']) > 1:
136
- connections_str = ", ".join([f'"{conn}"' for conn in node['connections']])
137
- edge_method = f'''
138
- def {node_name}_edge(self, state):
139
- """
140
- Conditional edge for {node_name} node.
141
- Returns one of: {connections_str}
142
- """
143
- # TODO: To implement...
144
- pass
145
- '''
146
- edge_methods.append(edge_method)
147
-
148
- # Build the file content
149
- file_content = f'''from typing import Dict, Any
150
- from langgraph.graph import StateGraph, END, START
151
- from langgraph.graph.state import CompiledStateGraph
152
-
153
-
154
- class State:
155
- """
156
- State class for the agent graph.
157
- """
158
- # TODO: Define state structure
159
- pass
160
-
161
-
162
- class Nodes:
163
- """
164
- Collection of node functions for the agent graph.
165
- """
166
- {"".join(node_methods)}
167
-
168
-
169
- class Edges:
170
- """
171
- Collection of conditional edge functions for the agent graph.
172
- """
173
- {"".join(edge_methods)}
174
-
175
-
176
- class GraphBuilder:
177
- def __init__(self):
178
- """
179
- Initializes the GraphBuilder.
180
- """
181
- self.nodes = Nodes()
182
- self.edges = Edges()
183
- # TODO: Implement the desired constructor.
184
- pass
185
-
186
- def build_agent_graph(self) -> CompiledStateGraph:
187
- """Build and return the agent graph."""
188
- graph = StateGraph(State)
189
-
190
- # Add all nodes
191
- {cls._generate_add_nodes_code(nodes)}
192
-
193
- # Add edges
194
- {cls._generate_regular_edges_code(nodes)}
195
- {cls._generate_conditional_edges_code(nodes)}
196
- return graph.compile()
197
- '''
198
- # Write to file
199
- graph_file_path = Path(f"agents/{agent_name}/graph.py")
200
- with open(graph_file_path, 'w') as f:
201
- f.write(file_content)
202
-
203
- @staticmethod
204
- def _generate_add_nodes_code(nodes):
205
- """Generate code for adding nodes to the graph"""
206
- code_lines = []
207
- for node in nodes:
208
- if node["name"] != "START":
209
- code_lines.append(f' graph.add_node("{node["name"]}", self.nodes.{node["name"]}_node)')
210
- return "\n".join(code_lines)
211
-
212
- @staticmethod
213
- def _generate_conditional_edges_code(nodes):
214
- """Generate code for adding conditional edges to the graph"""
215
- code_lines = []
216
- for node in nodes:
217
- if len(node['connections']) > 1:
218
- destinations = ", ".join([f'{conn}: {conn}' if conn in ["START", "END"] else f'"{conn}": "{conn}"' for conn in node['connections']])
219
- code_lines.append(f''' graph.add_conditional_edges(
220
- "{node["name"]}",
221
- self.edges.{node["name"]}_edge,
222
- {{
223
- {destinations}
224
- }}
225
- )''')
226
- return "\n".join(code_lines) if code_lines else ""
227
-
228
- @staticmethod
229
- def _generate_regular_edges_code(nodes):
230
- """Generate code for adding regular edges to the graph"""
231
- code_lines = []
232
- for node in nodes:
233
- if len(node['connections']) == 1:
234
- start_key = node["name"] if node["name"] in ["START", "END"] else f'"{node["name"]}"'
235
- end_key = node["connections"][0] if node["connections"][0] in ["START", "END"] else f'"{node["connections"][0]}"'
236
- code_lines.append(f' graph.add_edge({start_key}, {end_key})')
237
- return "\n".join(code_lines) if code_lines else ""
238
-
239
- @staticmethod
240
- def _create_test_file(agent_name: str):
241
- """Create the test.py file
242
-
243
- Args:
244
- agent_name: Name of the agent
245
- """
246
- test_file_content = f'''# Test file for {agent_name} agent
247
- from graph import GraphBuilder
248
-
249
- def test_agent():
250
- """
251
- Test the {agent_name} agent functionality.
252
- """
253
- # Create the graph
254
- builder = GraphBuilder()
255
- graph = builder.build_agent_graph()
256
-
257
- # TODO: Add test code here
258
- print("Testing {agent_name} agent...")
259
-
260
- # Example test
261
- # result = graph.invoke({{"input": "Test input"}})
262
- # print(f"Result: {{result}}")
263
-
264
- if __name__ == "__main__":
265
- test_agent()
266
- '''
267
-
268
- # Write to file
269
- test_file_path = Path(f"agents/{agent_name}/test.py")
270
- with open(test_file_path, 'w') as f:
271
- f.write(test_file_content)
272
-
273
- @staticmethod
274
- def _create_puml_file(agent_name: str, design_data: Dict[str, List[Dict[str, Any]]]):
275
- """Create the design.puml file for diagram visualization
276
-
277
- Args:
278
- agent_name: Name of the agent
279
- design_data: The validated design data
280
- """
281
- nodes = design_data['nodes']
282
-
283
- # Start the PlantUML content
284
- puml_content = f'''@startuml {agent_name}
285
- !define NOT_IMPLEMENTED_NODE_COLOR #IndianRed
286
- !define IMPLEMENTED_NODE_COLOR #Gold
287
- !define TESTED_NODE_COLOR #LawnGreen
288
- !define TERMINAL_NODE_COLOR #DodgerBlue
289
-
290
- '''
291
-
292
- # Add node descriptions
293
- for node in nodes:
294
- puml_content += f'node {node["name"]} {node["status"]}_NODE_COLOR[\n {node["description"]}\n]\n\n'
295
- puml_content += f'node END TERMINAL_NODE_COLOR[\n This is the final Node !\n]\n\n'
296
-
297
- # Add connections
298
- for node in nodes:
299
- node_name = node["name"]
300
- for connection in node["connections"]:
301
- if connection == "END":
302
- puml_content += f'{node_name} --> END\n'
303
- else:
304
- puml_content += f'{node_name} --> {connection}\n'
305
-
306
- # End the PlantUML content
307
- puml_content += '\n@enduml'
308
-
309
- # Write to file
310
- puml_file_path = Path(f"agents/{agent_name}/design.puml")
311
- with open(puml_file_path, 'w') as f:
312
- f.write(puml_content)
313
-
314
- @staticmethod
315
- def validate(agent_name: str):
316
- pass
317
-
318
-
319
- if __name__ == "__main__":
320
- # AgentBuilder.initialize("universal_solver")
321
- AgentBuilder.setup("universal_solver")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
agent_builder_template/design_puml.txt DELETED
@@ -1,15 +0,0 @@
1
- @startuml universal_solver
2
- !define NOT_IMPLEMENTED_NODE_COLOR #IndianRed
3
- !define IMPLEMENTED_NODE_COLOR #Gold
4
- !define TESTED_NODE_COLOR #LawnGreen
5
- !define TERMINAL_NODE_COLOR #DodgerBlue
6
-
7
- >>>>>NODES<<<<<
8
-
9
- node END TERMINAL_NODE_COLOR[
10
- This is the final Node !
11
- ]
12
-
13
- >>>>>EDGES<<<<<
14
-
15
- @enduml
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
agent_builder_template/design_yaml.txt DELETED
@@ -1,11 +0,0 @@
1
- nodes:
2
- - name: START
3
- connections:
4
- - example_node
5
- description: This is the mandatory initial node `START` !
6
- status: TERMINAL
7
-
8
- - name: example_node
9
- connections: [END]
10
- description: This is an example node
11
- status: NOT_IMPLEMENTED
 
 
 
 
 
 
 
 
 
 
 
 
agent_builder_template/graph.txt DELETED
@@ -1,21 +0,0 @@
1
- class State:
2
- """
3
- State class for the agent graph.
4
- """
5
- # TODO: Define state structure
6
- pass
7
-
8
-
9
- class Nodes:
10
- """
11
- Collection of node functions for the agent graph.
12
- """
13
- >>>>>NODE_METHODS<<<<<
14
-
15
-
16
-
17
- class Edges:
18
- """
19
- Collection of conditional edge functions for the agent graph.
20
- """
21
- >>>>>CONDITIONAL_EDGE_METHODS<<<<<
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
agent_builder_template/graph_builder.txt DELETED
@@ -1,24 +0,0 @@
1
- from typing import Dict, Any
2
- from langgraph.graph import StateGraph, END, START
3
- from langgraph.graph.state import CompiledStateGraph
4
-
5
-
6
- class GraphBuilder:
7
- def __init__(self):
8
- """
9
- Initializes the GraphBuilder.
10
- """
11
- self.nodes = Nodes()
12
- self.edges = Edges()
13
- # TODO: Implement the desired constructor.
14
- pass
15
-
16
- def build_agent_graph(self) -> CompiledStateGraph:
17
- """Build and return the agent graph."""
18
- graph = StateGraph(State)
19
-
20
- >>>>>NODES<<<<<
21
-
22
- >>>>>EDGES<<<<<
23
-
24
- return graph.compile()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
agent_builder_template/test.txt DELETED
@@ -1,2 +0,0 @@
1
- from graph import State, Nodes, Edges
2
- from graph_builder import GraphBuilder
 
 
 
solver.py → agents.py RENAMED
@@ -1,110 +1,82 @@
1
- from llama_index.core.tools import FunctionTool
 
2
 
3
- from typing import List
4
 
5
- from itf_agent import IAgent
6
- from toolbox import Toolbox
7
- from args import Args
 
 
 
 
 
 
 
 
 
 
 
8
 
9
 
10
  class Summarizer(IAgent):
 
 
 
11
  def __init__(self, temperature, max_tokens):
12
  super().__init__(temperature, max_tokens, "04_summarizer.txt", Args.primary_llm_interface)
13
 
14
 
 
 
 
 
 
 
 
 
15
  class Researcher(IAgent):
 
 
 
16
  def __init__(self, temperature, max_tokens):
17
  super().__init__(temperature, max_tokens, "05_researcher.txt", Args.primary_llm_interface)
18
 
19
- def setup_tools(self) -> List[FunctionTool]:
20
- return [
21
- Toolbox.web_search.duckduckgo_text_search,
22
- Toolbox.web_search.duckduckgo_images_search,
23
- Toolbox.web_search.duckduckgo_videos_search
24
- ]
25
-
26
 
27
  class EncryptionExpert(IAgent):
 
 
 
28
  def __init__(self, temperature, max_tokens):
29
  super().__init__(temperature, max_tokens, "06_encryption_expert.txt", Args.primary_llm_interface)
30
 
31
- def setup_tools(self) -> List[FunctionTool]:
32
- return [
33
- Toolbox.encryption.ascii_encode,
34
- Toolbox.encryption.ascii_decode,
35
- Toolbox.encryption.base64_encode,
36
- Toolbox.encryption.base64_decode,
37
- Toolbox.encryption.caesar_cipher_encode,
38
- Toolbox.encryption.caesar_cipher_decode,
39
- Toolbox.encryption.caesar_cipher_brute_force,
40
- Toolbox.encryption.reverse_string,
41
- Toolbox.math.unit_converter
42
- ]
43
-
44
- def setup_slaves(self) -> List:
45
- reasoner = Reasoner(self.temperature, self.max_tokens)
46
- return [reasoner]
47
-
48
 
49
  class MathExpert(IAgent):
 
 
 
50
  def __init__(self, temperature, max_tokens):
51
  super().__init__(temperature, max_tokens, "07_math_expert.txt", Args.primary_llm_interface)
52
 
53
- def setup_tools(self) -> List[FunctionTool]:
54
- return [
55
- Toolbox.math.symbolic_calc,
56
- Toolbox.math.unit_converter,
57
- ]
58
-
59
- def setup_slaves(self) -> List:
60
- reasoner = Reasoner(self.temperature, self.max_tokens)
61
- return [reasoner]
62
-
63
 
64
  class Reasoner(IAgent):
 
 
 
65
  def __init__(self, temperature, max_tokens):
66
  super().__init__(temperature, max_tokens, "08_reasoner.txt", Args.primary_llm_interface)
67
 
68
 
69
  class ImageHandler(IAgent):
 
 
 
70
  def __init__(self, temperature, max_tokens):
71
  super().__init__(temperature, max_tokens, "09_image_handler.txt", Args.vlm_interface)
72
 
73
- async def query(self, question: str, has_context=True) -> str:
74
- return "Image Handler is not available due to maintainance !"
75
-
76
 
77
  class VideoHandler(IAgent):
 
 
 
78
  def __init__(self, temperature, max_tokens):
79
  super().__init__(temperature, max_tokens, "10_video_handler.txt", Args.vlm_interface)
80
-
81
- async def query(self, question: str, has_context=True) -> str:
82
- return "Video Handler is not available due to maintainance !"
83
-
84
-
85
- class Solver(IAgent):
86
- def __init__(self, temperature, max_tokens):
87
- super().__init__(temperature, max_tokens, "03_solver.txt", Args.primary_llm_interface)
88
-
89
- def setup_slaves(self) -> List:
90
- summarizer = Summarizer(self.temperature, self.max_tokens)
91
- researcher = Researcher(self.temperature, self.max_tokens)
92
- encryption_expert = EncryptionExpert(self.temperature, self.max_tokens)
93
- math_expert = MathExpert(self.temperature, self.max_tokens)
94
- reasoner = Reasoner(self.temperature, self.max_tokens)
95
- image_handler = ImageHandler(self.temperature, self.max_tokens)
96
- video_handler = VideoHandler(self.temperature, self.max_tokens)
97
-
98
- return [
99
- summarizer,
100
- researcher,
101
- encryption_expert,
102
- math_expert,
103
- reasoner,
104
- image_handler,
105
- video_handler
106
- ]
107
-
108
-
109
- # if __name__ == "__main__":
110
- # pass
 
1
+ from args import Args
2
+ from itf_agent import IAgent
3
 
 
4
 
5
+ class Manager(IAgent):
6
+ """
7
+ Orchestrates the workflow by delegating tasks to specialized nodes and integrating their outputs
8
+ """
9
+ def __init__(self, temperature, max_tokens):
10
+ super().__init__(temperature, max_tokens, "01_manager.txt", Args.primary_llm_interface)
11
+
12
+
13
+ class Auditor(IAgent):
14
+ """
15
+ Reviews manager's outputs for accuracy, safety, and quality
16
+ """
17
+ def __init__(self, temperature, max_tokens):
18
+ super().__init__(temperature, max_tokens, "02_auditor.txt", Args.primary_llm_interface)
19
 
20
 
21
  class Summarizer(IAgent):
22
+ """
23
+ Generates concise summaries of conversations or passages.
24
+ """
25
  def __init__(self, temperature, max_tokens):
26
  super().__init__(temperature, max_tokens, "04_summarizer.txt", Args.primary_llm_interface)
27
 
28
 
29
+ class Solver(IAgent):
30
+ """
31
+ Central problem-solving node that coordinates with specialized experts based on task requirements
32
+ """
33
+ def __init__(self, temperature, max_tokens):
34
+ super().__init__(temperature, max_tokens, "03_solver.txt", Args.primary_llm_interface)
35
+
36
+
37
  class Researcher(IAgent):
38
+ """
39
+ Retrieves and synthesizes information from various sources to answer knowledge-based questions
40
+ """
41
  def __init__(self, temperature, max_tokens):
42
  super().__init__(temperature, max_tokens, "05_researcher.txt", Args.primary_llm_interface)
43
 
 
 
 
 
 
 
 
44
 
45
  class EncryptionExpert(IAgent):
46
+ """
47
+ Handles encryption/decryption tasks and encoding/decoding operations
48
+ """
49
  def __init__(self, temperature, max_tokens):
50
  super().__init__(temperature, max_tokens, "06_encryption_expert.txt", Args.primary_llm_interface)
51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
  class MathExpert(IAgent):
54
+ """
55
+ Performs mathematical calculations and solves numerical problems
56
+ """
57
  def __init__(self, temperature, max_tokens):
58
  super().__init__(temperature, max_tokens, "07_math_expert.txt", Args.primary_llm_interface)
59
 
 
 
 
 
 
 
 
 
 
 
60
 
61
  class Reasoner(IAgent):
62
+ """
63
+ Performs logical reasoning, inference, and step-by-step problem-solving
64
+ """
65
  def __init__(self, temperature, max_tokens):
66
  super().__init__(temperature, max_tokens, "08_reasoner.txt", Args.primary_llm_interface)
67
 
68
 
69
  class ImageHandler(IAgent):
70
+ """
71
+ Processes, analyzes, and generates information related to images
72
+ """
73
  def __init__(self, temperature, max_tokens):
74
  super().__init__(temperature, max_tokens, "09_image_handler.txt", Args.vlm_interface)
75
 
 
 
 
76
 
77
  class VideoHandler(IAgent):
78
+ """
79
+ Processes, analyzes, and generates information related to videos
80
+ """
81
  def __init__(self, temperature, max_tokens):
82
  super().__init__(temperature, max_tokens, "10_video_handler.txt", Args.vlm_interface)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
agents/universal_solver/design.puml DELETED
@@ -1,32 +0,0 @@
1
- @startuml universal_solver
2
- !define NOT_IMPLEMENTED_NODE_COLOR #IndianRed
3
- !define IMPLEMENTED_NODE_COLOR #Gold
4
- !define TESTED_NODE_COLOR #LawnGreen
5
- !define TERMINAL_NODE_COLOR #DodgerBlue
6
-
7
- node START TERMINAL_NODE_COLOR[
8
- This is the mandatory initial node `START` !
9
- ]
10
-
11
- node example_node_1 NOT_IMPLEMENTED_NODE_COLOR[
12
- This is an example node
13
- ]
14
-
15
- node example_node_2 IMPLEMENTED_NODE_COLOR[
16
- This is another example node
17
- ]
18
-
19
- node example_node_3 TESTED_NODE_COLOR[
20
- This is yet another example node
21
- ]
22
-
23
- node END TERMINAL_NODE_COLOR[
24
- This is the final Node !
25
- ]
26
-
27
- START --> example_node_1
28
- example_node_1 --> example_node_2
29
- example_node_2 --> example_node_3
30
- example_node_3 --> END
31
-
32
- @enduml
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
agents/universal_solver/design.yaml DELETED
@@ -1,21 +0,0 @@
1
- nodes:
2
- - name: START
3
- connections:
4
- - example_node_1
5
- description: This is the mandatory initial node `START` !
6
- status: TERMINAL
7
-
8
- - name: example_node_1
9
- connections: [example_node_2]
10
- description: This is an example node
11
- status: NOT_IMPLEMENTED
12
-
13
- - name: example_node_2
14
- connections: [example_node_3]
15
- description: This is another example node
16
- status: IMPLEMENTED
17
-
18
- - name: example_node_3
19
- connections: [END]
20
- description: This is yet another example node
21
- status: TESTED
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
agents/universal_solver/graph.py DELETED
@@ -1,74 +0,0 @@
1
- from typing import Dict, Any
2
- from langgraph.graph import StateGraph, END, START
3
- from langgraph.graph.state import CompiledStateGraph
4
-
5
-
6
- class State:
7
- """
8
- State class for the agent graph.
9
- """
10
- # TODO: Define state structure
11
- pass
12
-
13
-
14
- class Nodes:
15
- """
16
- Collection of node functions for the agent graph.
17
- """
18
-
19
- def example_node_1_node(self, state):
20
- """
21
- This is an example node
22
- """
23
- # TODO: To implement...
24
- pass
25
-
26
- def example_node_2_node(self, state):
27
- """
28
- This is another example node
29
- """
30
- # TODO: To implement...
31
- pass
32
-
33
- def example_node_3_node(self, state):
34
- """
35
- This is yet another example node
36
- """
37
- # TODO: To implement...
38
- pass
39
-
40
-
41
-
42
- class Edges:
43
- """
44
- Collection of conditional edge functions for the agent graph.
45
- """
46
-
47
-
48
-
49
- class GraphBuilder:
50
- def __init__(self):
51
- """
52
- Initializes the GraphBuilder.
53
- """
54
- self.nodes = Nodes()
55
- self.edges = Edges()
56
- # TODO: Implement the desired constructor.
57
- pass
58
-
59
- def build_agent_graph(self) -> CompiledStateGraph:
60
- """Build and return the agent graph."""
61
- graph = StateGraph(State)
62
-
63
- # Add all nodes
64
- graph.add_node("example_node_1", self.nodes.example_node_1_node)
65
- graph.add_node("example_node_2", self.nodes.example_node_2_node)
66
- graph.add_node("example_node_3", self.nodes.example_node_3_node)
67
-
68
- # Add edges
69
- graph.add_edge(START, "example_node_1")
70
- graph.add_edge("example_node_1", "example_node_2")
71
- graph.add_edge("example_node_2", "example_node_3")
72
- graph.add_edge("example_node_3", END)
73
-
74
- return graph.compile()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
agents/universal_solver/test.py DELETED
@@ -1,20 +0,0 @@
1
- # Test file for universal_solver agent
2
- from graph import GraphBuilder
3
-
4
- def test_agent():
5
- """
6
- Test the universal_solver agent functionality.
7
- """
8
- # Create the graph
9
- builder = GraphBuilder()
10
- graph = builder.build_agent_graph()
11
-
12
- # TODO: Add test code here
13
- print("Testing universal_solver agent...")
14
-
15
- # Example test
16
- # result = graph.invoke({"input": "Test input"})
17
- # print(f"Result: {result}")
18
-
19
- if __name__ == "__main__":
20
- test_agent()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
alfred.py CHANGED
@@ -1,13 +1,8 @@
1
- from langgraph.graph import START, END, StateGraph
2
- from langgraph.graph.state import CompiledStateGraph
3
-
4
- from typing import Dict, Any, TypedDict, Literal, Optional
5
- import logging
6
- import datetime
7
- from pathlib import Path
8
 
9
  from args import Args
10
- from management import Manager, Assistant
 
11
 
12
 
13
  # Maximum number of interactions between Assistant and Manager
@@ -19,173 +14,11 @@ TEMPERATURE = 0.7
19
  MAX_TOKENS = 2000
20
 
21
 
22
- class State(TypedDict):
23
- """State for the agent graph."""
24
- initial_query: str
25
- current_message: Optional[str]
26
- nr_interactions: int
27
- final_response: Optional[str]
28
-
29
-
30
- class GraphBuilder:
31
- def __init__(self):
32
- """
33
- Initializes the GraphBuilder.
34
- """
35
- self.assistant_agent = Assistant(TEMPERATURE, MAX_TOKENS)
36
- self.manager_agent = Manager(TEMPERATURE, MAX_TOKENS, MAX_DEPTH)
37
- self.final_answer_hint = "Final answer:"
38
-
39
- def clear_chat_history(self):
40
- self.assistant_agent.clear_context()
41
- self.manager_agent.clear_context()
42
-
43
- async def assistant_node(self, state: State) -> State:
44
- """
45
- Assistant agent that evaluates the query and decides whether to give a final answer
46
- or continue the conversation with the Manager.
47
-
48
- Uses the existing Assistant implementation.
49
- """
50
- if Args.LOGGER is None:
51
- raise RuntimeError("LOGGER must be defined before running the assistant_node.")
52
-
53
- Args.LOGGER.log(logging.INFO, "********** assistant_node **********")
54
-
55
- if state["current_message"] is None:
56
- # First time, just forward the query to the manager
57
- response = state["initial_query"]
58
- else:
59
- assistent_input = f"This is the initial user query:\n\n{state["initial_query"]}\n\nThe manager provided the following answer:\n\n{state["current_message"]}\n"
60
- response = await self.assistant_agent.query(assistent_input)
61
-
62
- # Check if this is a final answer
63
- if self.final_answer_hint in response:
64
- # Extract the text after final answer hint
65
- final_response = response.split(self.final_answer_hint)[-1]
66
- final_response = final_response.strip()
67
- state["final_response"] = final_response
68
-
69
- state["current_message"] = response
70
- state["nr_interactions"] += 1
71
-
72
- return state
73
-
74
- async def manager_node(self, state: State) -> State:
75
- """
76
- Manager agent that handles the queries from the Assistant and provides responses.
77
-
78
- Uses the existing Manager implementation.
79
- """
80
- if Args.LOGGER is None:
81
- raise RuntimeError("LOGGER must be defined before running the manager_node.")
82
-
83
- Args.LOGGER.log(logging.INFO, "********** manager_node **********")
84
-
85
- if state["current_message"] is None:
86
- raise ValueError("manager_node called with no current_message in state")
87
-
88
- response = await self.manager_agent.query(state["current_message"])
89
-
90
- state["current_message"] = response
91
-
92
- return state
93
-
94
- async def final_answer_node(self, state: State) -> State:
95
- """
96
- Final answer node that formats and returns the final response.
97
-
98
- If there's already a final answer in the state, it uses that.
99
- Otherwise, it asks the assistant to formulate a final answer.
100
- """
101
- if Args.LOGGER is None:
102
- raise RuntimeError("LOGGER must be defined before running the final_answer_node.")
103
-
104
- Args.LOGGER.log(logging.INFO, "********** final_answer_node **********")
105
-
106
- # If we already have a final answer, use it
107
- final_response = state.get("final_response")
108
- if final_response is not None:
109
- Args.LOGGER.log(logging.INFO, f"==========\nFinal response:\n{final_response}\n==========")
110
- return state
111
-
112
- # Otherwise, have the assistant formulate a final answer
113
- prompt = f"Based on the conversation so far, provide a final answer to the original query:\n\n{state['initial_query']}"
114
- state["current_message"] = prompt
115
- response = await self.assistant_agent.query(prompt)
116
-
117
- # Format the response
118
- if self.final_answer_hint not in response:
119
- Args.LOGGER.log(logging.WARNING, f"Final_answer_hint '{self.final_answer_hint}' not in response !")
120
- response = f"{self.final_answer_hint}{response}"
121
-
122
- # Extract the text after final answer hint
123
- final_response = response.split(self.final_answer_hint)[-1]
124
- final_response = final_response.strip()
125
- state["final_response"] = final_response
126
- Args.LOGGER.log(logging.INFO, f"==========\nFinal response:\n{final_response}\n==========")
127
-
128
- return state
129
-
130
- def should_continue(self, state: State) -> Literal["manager", "final_answer"]:
131
- """
132
- Decides whether to continue to the Manager or to provide a final answer.
133
-
134
- Returns:
135
- "manager": If the Assistant has decided to continue the conversation
136
- "final_answer": If the Assistant has decided to provide a final answer
137
- """
138
- if Args.LOGGER is None:
139
- raise RuntimeError("LOGGER must be defined before running the should_continue edge.")
140
-
141
- Args.LOGGER.log(logging.INFO, "++++++++++ should_continue edge ++++++++++")
142
-
143
- if state["current_message"] is None:
144
- raise ValueError("should_continue conditional edge was reached with no current_message in state")
145
-
146
- message = state["current_message"]
147
-
148
- if state["nr_interactions"] >= MAX_INTERACTIONS or self.final_answer_hint in message:
149
- Args.LOGGER.log(logging.INFO, "++++++++++ should_continue edge decision: final_answer ++++++++++")
150
- return "final_answer"
151
- Args.LOGGER.log(logging.INFO, "++++++++++ should_continue edge decision: manager ++++++++++")
152
- return "manager"
153
-
154
- def build_agent_graph(self) -> CompiledStateGraph:
155
- """Build and return the agent graph."""
156
- graph = StateGraph(State)
157
-
158
- # Add the nodes with sync wrappers
159
- graph.add_node("assistant", self.assistant_node)
160
- graph.add_node("manager", self.manager_node)
161
- graph.add_node("final_answer", self.final_answer_node)
162
-
163
- # Add the edges
164
- graph.add_edge(START, "assistant")
165
-
166
- graph.add_conditional_edges(
167
- "assistant",
168
- self.should_continue,
169
- {
170
- "manager": "manager",
171
- "final_answer": "final_answer"
172
- }
173
- )
174
-
175
- graph.add_edge("manager", "assistant")
176
-
177
- graph.add_edge("final_answer", END)
178
-
179
- return graph.compile()
180
-
181
-
182
  class Alfred:
183
 
184
  def __init__(self):
185
  print("Agent initialized.")
186
 
187
- Args.LOGGER = self.set_logger()
188
-
189
  self.graph_builder = GraphBuilder()
190
  self.agent_graph = self.graph_builder.build_agent_graph()
191
 
@@ -200,54 +33,19 @@ class Alfred:
200
  async def process_query(self, query: str) -> Dict[str, Any]:
201
  """
202
  Process a query through the agent graph.
203
-
204
  Args:
205
  query: The initial query to process
206
-
207
  Returns:
208
  The final state of the graph execution
209
  """
210
  initial_state: State = {
211
  "initial_query": query,
212
- "current_message": None,
213
  "nr_interactions": 0,
214
  "final_response": None
215
  }
216
- self.graph_builder.clear_chat_history()
217
 
218
  result = await self.agent_graph.ainvoke(initial_state)
219
  return result
220
-
221
- def set_logger(self) -> logging.Logger:
222
- """
223
- Configure and return a logger with a file handler that writes to logs/<current date-time>.txt
224
-
225
- Returns:
226
- logging.Logger: Configured logger instance
227
- """
228
- # Create logger
229
- logger = logging.getLogger("Alfred")
230
- logger.setLevel(logging.INFO)
231
-
232
- # Create formatter
233
- formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
234
-
235
- # Create logs directory if it doesn't exist
236
- logs_dir = Path('logs')
237
- logs_dir.mkdir(exist_ok=True)
238
-
239
- # Generate log filename with current date-time
240
- current_time = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
241
- log_filename = f"{current_time}.txt"
242
- log_filepath = logs_dir / log_filename
243
-
244
- # Create file handler with UTF-8 encoding to handle all Unicode characters
245
- file_handler = logging.FileHandler(log_filepath, encoding='utf-8')
246
- file_handler.setLevel(logging.INFO)
247
- file_handler.setFormatter(formatter)
248
-
249
- # Add handler to logger
250
- logger.addHandler(file_handler)
251
-
252
- logger.info(f"Logging started at {current_time}")
253
- return logger
 
1
+ from typing import Dict, Any
 
 
 
 
 
 
2
 
3
  from args import Args
4
+ from graph import State
5
+ from graph_builder import GraphBuilder
6
 
7
 
8
  # Maximum number of interactions between Assistant and Manager
 
14
  MAX_TOKENS = 2000
15
 
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  class Alfred:
18
 
19
  def __init__(self):
20
  print("Agent initialized.")
21
 
 
 
22
  self.graph_builder = GraphBuilder()
23
  self.agent_graph = self.graph_builder.build_agent_graph()
24
 
 
33
  async def process_query(self, query: str) -> Dict[str, Any]:
34
  """
35
  Process a query through the agent graph.
36
+
37
  Args:
38
  query: The initial query to process
39
+
40
  Returns:
41
  The final state of the graph execution
42
  """
43
  initial_state: State = {
44
  "initial_query": query,
45
+ "messages": [],
46
  "nr_interactions": 0,
47
  "final_response": None
48
  }
 
49
 
50
  result = await self.agent_graph.ainvoke(initial_state)
51
  return result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
args.py CHANGED
@@ -1,7 +1,7 @@
1
 
2
  from enum import Enum
3
- from logging import Logger
4
- from typing import Optional
5
 
6
 
7
  class LLMInterface(Enum):
@@ -12,7 +12,7 @@ class LLMInterface(Enum):
12
 
13
 
14
  class Args:
15
- LOGGER: Optional[Logger] = None
16
  primary_llm_interface=LLMInterface.OPENAILIKE
17
  # secondary_llm_interface=LLMInterface.HUGGINGFACE
18
  vlm_interface=LLMInterface.HUGGINGFACE
 
1
 
2
  from enum import Enum
3
+
4
+ from logger import Logger
5
 
6
 
7
  class LLMInterface(Enum):
 
12
 
13
 
14
  class Args:
15
+ LOGGER = Logger.set_logger()
16
  primary_llm_interface=LLMInterface.OPENAILIKE
17
  # secondary_llm_interface=LLMInterface.HUGGINGFACE
18
  vlm_interface=LLMInterface.HUGGINGFACE
design.puml ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @startuml alfred
2
+ !define NOT_IMPLEMENTED_NODE_COLOR #IndianRed
3
+ !define IMPLEMENTED_NODE_COLOR #Gold
4
+ !define TESTED_NODE_COLOR #LawnGreen
5
+ !define TERMINAL_NODE_COLOR #DodgerBlue
6
+
7
+ node START TERMINAL_NODE_COLOR[
8
+ START
9
+ ]
10
+
11
+ node manager NOT_IMPLEMENTED_NODE_COLOR[
12
+ manager
13
+ ]
14
+
15
+ node final_answer NOT_IMPLEMENTED_NODE_COLOR[
16
+ final_answer
17
+ ]
18
+
19
+ node auditor NOT_IMPLEMENTED_NODE_COLOR[
20
+ auditor
21
+ ]
22
+
23
+ node solver NOT_IMPLEMENTED_NODE_COLOR[
24
+ solver
25
+ ]
26
+
27
+ node researcher NOT_IMPLEMENTED_NODE_COLOR[
28
+ researcher
29
+ ]
30
+
31
+ node encryption_expert NOT_IMPLEMENTED_NODE_COLOR[
32
+ encryption_expert
33
+ ]
34
+
35
+ node encryption_advisor NOT_IMPLEMENTED_NODE_COLOR[
36
+ encryption_advisor
37
+ ]
38
+
39
+ node math_expert NOT_IMPLEMENTED_NODE_COLOR[
40
+ math_expert
41
+ ]
42
+
43
+ node math_advisor NOT_IMPLEMENTED_NODE_COLOR[
44
+ math_advisor
45
+ ]
46
+
47
+ node reasoner NOT_IMPLEMENTED_NODE_COLOR[
48
+ reasoner
49
+ ]
50
+
51
+ node image_handler NOT_IMPLEMENTED_NODE_COLOR[
52
+ image_handler
53
+ ]
54
+
55
+ node video_handler NOT_IMPLEMENTED_NODE_COLOR[
56
+ video_handler
57
+ ]
58
+
59
+ node END TERMINAL_NODE_COLOR[
60
+ END
61
+ ]
62
+
63
+ START --> manager
64
+ manager --> solver
65
+ manager --> auditor
66
+ manager --> final_answer
67
+ final_answer --> END
68
+ auditor --> manager
69
+ solver --> manager
70
+ solver --> researcher
71
+ solver --> encryption_expert
72
+ solver --> math_expert
73
+ solver --> reasoner
74
+ solver --> image_handler
75
+ solver --> video_handler
76
+ researcher --> solver
77
+ encryption_expert --> solver
78
+ encryption_expert --> encryption_advisor
79
+ encryption_advisor --> encryption_expert
80
+ math_expert --> solver
81
+ math_expert --> math_advisor
82
+ math_advisor --> math_expert
83
+ reasoner --> solver
84
+ image_handler --> solver
85
+ video_handler --> solver
86
+
87
+ @enduml
design.yaml ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ nodes:
2
+ - name: START
3
+ connections:
4
+ - manager
5
+ description: This is the mandatory initial node `START` !
6
+ status: TERMINAL
7
+
8
+ - name: manager
9
+ connections: [solver, auditor, final_answer]
10
+ description: Orchestrates the workflow by delegating tasks to specialized nodes and integrating their outputs
11
+ status: NOT_IMPLEMENTED
12
+
13
+ - name: final_answer
14
+ connections: [END]
15
+ description: Formats and delivers the final response to the user
16
+ status: NOT_IMPLEMENTED
17
+
18
+ - name: auditor
19
+ connections: [manager]
20
+ description: Reviews manager's outputs for accuracy, safety, and quality
21
+ status: NOT_IMPLEMENTED
22
+
23
+ - name: solver
24
+ connections: [manager, researcher, encryption_expert, math_expert, reasoner, image_handler, video_handler]
25
+ description: Central problem-solving node that coordinates with specialized experts based on task requirements
26
+ status: NOT_IMPLEMENTED
27
+
28
+ - name: researcher
29
+ connections: [solver]
30
+ description: Retrieves and synthesizes information from various sources to answer knowledge-based questions
31
+ status: NOT_IMPLEMENTED
32
+
33
+ - name: encryption_expert
34
+ connections: [solver, encryption_advisor]
35
+ description: Handles encryption/decryption tasks and encoding/decoding operations
36
+ status: NOT_IMPLEMENTED
37
+
38
+ - name: encryption_advisor
39
+ connections: [encryption_expert]
40
+ description: Provides specialized guidance on complex encryption problems to the encryption expert
41
+ status: NOT_IMPLEMENTED
42
+
43
+ - name: math_expert
44
+ connections: [solver, math_advisor]
45
+ description: Performs mathematical calculations and solves numerical problems
46
+ status: NOT_IMPLEMENTED
47
+
48
+ - name: math_advisor
49
+ connections: [math_expert]
50
+ description: Provides specialized guidance on complex mathematical problems to the math expert
51
+ status: NOT_IMPLEMENTED
52
+
53
+ - name: reasoner
54
+ connections: [solver]
55
+ description: Performs logical reasoning, inference, and step-by-step problem-solving
56
+ status: NOT_IMPLEMENTED
57
+
58
+ - name: image_handler
59
+ connections: [solver]
60
+ description: Processes, analyzes, and generates information related to images
61
+ status: NOT_IMPLEMENTED
62
+
63
+ - name: video_handler
64
+ connections: [solver]
65
+ description: Processes, analyzes, and generates information related to videos
66
+ status: NOT_IMPLEMENTED
diagrams/architecture.puml DELETED
@@ -1,147 +0,0 @@
1
- @startuml Architecture Diagram
2
-
3
- ' Define color variables
4
- !define CANVAS_COLOR #FAEBD7
5
- !define DEFAULT_NODE_COLOR #Gold
6
- !define TODO_COLOR #FireBrick
7
- !define ARROW_COLOR #666666
8
- !define BORDER_COLOR #999999
9
- !define TOOLBOX_MATH_COLOR #LightBlue
10
- !define TOOLBOX_WEBSEARCH_COLOR #LightGreen
11
- !define TOOLBOX_ENCRYPTION_COLOR #PaleVioletRed
12
- !define POWERS_ARROW_COLOR #4285F4
13
- !define TRIGGER_ARROW_COLOR #OrangeRed
14
- !define FINAL_ANSWER_COLOR #Orange
15
-
16
- ' Style settings
17
- skinparam handwritten true
18
- skinparam backgroundColor white
19
- skinparam componentStyle uml2
20
- skinparam defaultFontName Arial
21
- skinparam arrowColor ARROW_COLOR
22
- skinparam componentBorderColor BORDER_COLOR
23
- skinparam component {
24
- BackgroundColor DEFAULT_NODE_COLOR
25
- }
26
-
27
- ' Custom style for toolbox functions
28
- skinparam label {
29
- FontSize 10
30
- FontColor black
31
- }
32
-
33
- ' Components
34
- package "app.py" {
35
- [UI] as UI
36
- [Application] as Application
37
- }
38
-
39
- package "alfred.py" {
40
- [Alfred] as Alfred
41
- [GraphBuilder] as GraphBuilder
42
- }
43
-
44
- package "management.py" {
45
- [Assistant] as Assistant
46
- [Manager] as Manager
47
- }
48
-
49
- ' Relationships
50
- UI -d-> Application
51
- Application -d-> Alfred
52
- Alfred -d-> GraphBuilder
53
- GraphBuilder -d-> Assistant
54
- GraphBuilder -d-> Manager
55
-
56
- ' LangGraph Flow and LlamaIndex Flow positioning
57
- component "LangGraph Flow" as LangGraphFlow CANVAS_COLOR {
58
- [Query] as Query
59
- [Assistant Node] as AssistantNode
60
- [Manager Node] as ManagerNode
61
- [<b>Final Answer</b>] as FinalAnswer FINAL_ANSWER_COLOR
62
-
63
- Query -r-> AssistantNode
64
- AssistantNode -r-> ManagerNode : requests
65
- ManagerNode -l-> AssistantNode : solution
66
- ManagerNode -d-> break_up_recursion
67
- break_up_recursion -u-> ManagerNode
68
- AssistantNode -d-> FinalAnswer : problem solved
69
- }
70
-
71
- ' Position LlamaIndex Flow with solver.py and toolbox.py - more compact
72
- component "LlamaIndex Flow" as LlamaIndexFlow CANVAS_COLOR {
73
- package "solver.py" {
74
- [Solver] as Solver TODO_COLOR
75
- [Summarizer] as Summarizer
76
- [Researcher] as Researcher TODO_COLOR
77
- [EncryptionExpert] as EncryptionExpert TODO_COLOR
78
- [MathExpert] as MathExpert TODO_COLOR
79
- [Reasoner] as Reasoner TODO_COLOR
80
- [ImageHandler] as ImageHandler TODO_COLOR
81
- [VideoHandler] as VideoHandler TODO_COLOR
82
- }
83
-
84
- package "toolbox.py" {
85
- node Math TOOLBOX_MATH_COLOR [
86
- <b>Math</b>
87
- ----
88
- symbolic_calc
89
- ____
90
- unit_converter
91
- ]
92
- node WebSearch TOOLBOX_WEBSEARCH_COLOR [
93
- <b>WebSearch</b>
94
- ----
95
- duckduckgo_text_search
96
- ____
97
- duckduckgo_images_search
98
- ____
99
- duckduckgo_videos_search
100
- ]
101
- node Encryption TOOLBOX_ENCRYPTION_COLOR [
102
- <b>Encryption</b>
103
- ----
104
- <font color=FireBrick><b>ascii_encode/decode</b></font>
105
- ____
106
- <font color=FireBrick><b>chr_to_int/int_to_chr</b></font>
107
- ____
108
- base64_encode/decode
109
- ____
110
- caesar_cipher_encode/decode/<font color=FireBrick><b>brute_force</b></font>
111
- ____
112
- reverse_string
113
- ]
114
- }
115
-
116
- Solver -r-> Summarizer
117
- Solver -d-> Researcher
118
- Solver -d-> EncryptionExpert
119
- Solver -d-> MathExpert
120
- Solver -d-> Reasoner
121
- Solver -d-> ImageHandler
122
- Solver -d-> VideoHandler
123
-
124
- Researcher -d-> WebSearch
125
- MathExpert -d-> Math
126
- EncryptionExpert -d-> Encryption
127
- }
128
-
129
- ' Adjust the label position to avoid overlap
130
- GraphBuilder -d-> LangGraphFlow : builds
131
-
132
- ' Curved connections from Manager to Solver/Summarizer
133
- Manager -d-> Solver
134
- Manager -d-> Summarizer
135
-
136
- ' Bidirectional connection between ManagerNode and Solver
137
- ManagerNode -r-> Solver : requests
138
- Solver -l-> ManagerNode : provides solution
139
-
140
- ' Agents powering the Nodes (dashed, blue color)
141
- Assistant -[POWERS_ARROW_COLOR,dashed]d-> AssistantNode : powers
142
- Manager -[POWERS_ARROW_COLOR,dashed]d-> ManagerNode : powers
143
-
144
- ' Adding a direct connection from Alfred to Query
145
- Alfred -[TRIGGER_ARROW_COLOR]d..> Query
146
-
147
- @enduml
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
diagrams/diagram.puml DELETED
@@ -1,87 +0,0 @@
1
- @startuml
2
-
3
- !include style.puml
4
-
5
- ' left to right direction
6
-
7
- ' Hide the class indicators (C)
8
- hide circle
9
- hide empty members
10
-
11
- ' Agent Definitions (Use class notation for agents with tools as fields)
12
-
13
-
14
- class "Assistant" as Assistant <<M>> {
15
- }
16
-
17
- class "Manager" as Manager <<M>> {
18
- - Solver() {trivial task}
19
- - Manager() {complex task}
20
- }
21
-
22
- class "Solver" as Solver {
23
- - Researcher()
24
- - EncryptionExpert()
25
- - MathExpert()
26
- - Reasoner()
27
- - ImageHandler()
28
- - VideoHandler()
29
- }
30
-
31
- class "Researcher" as Researcher {
32
- + DuckDuckGoSearchToolSpec
33
- }
34
-
35
- class "EncryptionExpert" as EncryptionExpert {
36
- + ASCII Encode
37
- + ASCII Decode
38
- + ChrToInt Encode
39
- + ChrToInt Decode
40
- + Base64 Encode
41
- + Base64 Decode
42
- + Caesar Cipher Encode
43
- + Caesar Cipher Decode
44
- + Caesar Cipher Brute Force
45
- + Reverse String
46
- - MathExpert()
47
- - Reasoner()
48
- }
49
-
50
- class "MathExpert" as MathExpert {
51
- + Symbolic Math Calculator
52
- + Unit Converter
53
- - Reasoner()
54
- }
55
-
56
- class "Reasoner" as Reasoner {
57
- }
58
-
59
- class "ImageHandler" as ImageHandler {
60
- }
61
-
62
- class "VideoHandler" as VideoHandler {
63
- }
64
-
65
- ' Agent-to-Agent Connections
66
- Query --> Assistant
67
- Assistant --> Manager : request
68
- Manager --> Assistant : solution
69
- Assistant --> Final_Answer : problem solved
70
-
71
- Manager ..> Manager : complex task
72
- Manager ..> Solver : trivial task
73
- Solver ..> Manager : solution
74
-
75
- Solver .. Researcher : query
76
- Solver .. EncryptionExpert : query
77
- Solver .. MathExpert : query
78
- Solver .. Reasoner : query
79
- Solver .. ImageHandler : query
80
- Solver .. VideoHandler : query
81
-
82
- EncryptionExpert .. MathExpert : query
83
- EncryptionExpert .. Reasoner : query
84
- MathExpert .. Reasoner : query
85
-
86
-
87
- @enduml
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
diagrams/style.puml DELETED
@@ -1,7 +0,0 @@
1
- skinparam class {
2
- BackgroundColor #E0F7FA
3
- BorderColor #D84315
4
- FontColor #E65100
5
- FontStyle bold
6
- HeaderBackgroundColor #FFE0B2
7
- }
 
 
 
 
 
 
 
 
graph.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_core.messages import AIMessage, HumanMessage
2
+ from langgraph.graph import START, END, StateGraph
3
+
4
+ from typing import Any, Dict, List, Literal, Optional, TypedDict
5
+ import logging
6
+ from pathlib import Path
7
+
8
+ from args import Args
9
+
10
+
11
+ class State(TypedDict):
12
+ """State class for the agent graph."""
13
+ initial_query: str
14
+ messages: List[Dict[str, Any]]
15
+ nr_interactions: int
16
+ final_response: Optional[str]
17
+
18
+
19
+ class Nodes:
20
+ """
21
+ Collection of node functions for the agent graph.
22
+ """
23
+ def manager_node(self, state: State) -> State:
24
+ """
25
+ Orchestrates the workflow by delegating tasks to specialized nodes and integrating their outputs
26
+ """
27
+ # TODO: To implement...
28
+ pass
29
+
30
+ def final_answer_node(self, state: State) -> State:
31
+ """
32
+ Formats and delivers the final response to the user
33
+ """
34
+ # TODO: To implement...
35
+ pass
36
+
37
+ def auditor_node(self, state: State) -> State:
38
+ """
39
+ Reviews manager's outputs for accuracy, safety, and quality
40
+ """
41
+ # TODO: To implement...
42
+ pass
43
+
44
+ def solver_node(self, state: State) -> State:
45
+ """
46
+ Central problem-solving node that coordinates with specialized experts based on task requirements
47
+ """
48
+ # TODO: To implement...
49
+ pass
50
+
51
+ def researcher_node(self, state: State) -> State:
52
+ """
53
+ Retrieves and synthesizes information from various sources to answer knowledge-based questions
54
+ """
55
+ # TODO: To implement...
56
+ pass
57
+
58
+ def encryption_expert_node(self, state: State) -> State:
59
+ """
60
+ Handles encryption/decryption tasks and encoding/decoding operations
61
+ """
62
+ # TODO: To implement...
63
+ pass
64
+
65
+ def encryption_advisor_node(self, state: State) -> State:
66
+ """
67
+ Provides specialized guidance on complex encryption problems to the encryption expert
68
+ """
69
+ # TODO: To implement...
70
+ pass
71
+
72
+ def math_expert_node(self, state: State) -> State:
73
+ """
74
+ Performs mathematical calculations and solves numerical problems
75
+ """
76
+ # TODO: To implement...
77
+ pass
78
+
79
+ def math_advisor_node(self, state: State) -> State:
80
+ """
81
+ Provides specialized guidance on complex mathematical problems to the math expert
82
+ """
83
+ # TODO: To implement...
84
+ pass
85
+
86
+ def reasoner_node(self, state: State) -> State:
87
+ """
88
+ Performs logical reasoning, inference, and step-by-step problem-solving
89
+ """
90
+ # TODO: To implement...
91
+ pass
92
+
93
+ def image_handler_node(self, state: State) -> State:
94
+ """
95
+ Processes, analyzes, and generates information related to images
96
+ """
97
+ # TODO: To implement...
98
+ pass
99
+
100
+ def video_handler_node(self, state: State) -> State:
101
+ """
102
+ Processes, analyzes, and generates information related to videos
103
+ """
104
+ # TODO: To implement...
105
+ pass
106
+
107
+
108
+ class Edges:
109
+ """
110
+ Collection of conditional edge functions for the agent graph.
111
+ """
112
+ def manager_edge(self, state: State) -> Literal["solver", "auditor", "final_answer"]:
113
+ """
114
+ Conditional edge for manager node.
115
+ Returns one of: "solver", "auditor", "final_answer"
116
+ """
117
+ # TODO: To implement...
118
+ pass
119
+
120
+ def solver_edge(self, state: State) -> Literal["manager", "researcher", "encryption_expert", "math_expert", "reasoner", "image_handler", "video_handler"]:
121
+ """
122
+ Conditional edge for solver node.
123
+ Returns one of: "manager", "researcher", "encryption_expert", "math_expert", "reasoner", "image_handler", "video_handler"
124
+ """
125
+ # TODO: To implement...
126
+ pass
127
+
128
+ def encryption_expert_edge(self, state: State) -> Literal["solver", "encryption_advisor"]:
129
+ """
130
+ Conditional edge for encryption_expert node.
131
+ Returns one of: "solver", "encryption_advisor"
132
+ """
133
+ # TODO: To implement...
134
+ pass
135
+
136
+ def math_expert_edge(self, state: State) -> Literal["solver", "math_advisor"]:
137
+ """
138
+ Conditional edge for math_expert node.
139
+ Returns one of: "solver", "math_advisor"
140
+ """
141
+ # TODO: To implement...
142
+ pass
graph_builder.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from IPython.display import Image, display
2
+ from langgraph.graph import StateGraph, END, START
3
+ from langgraph.graph.state import CompiledStateGraph
4
+ from graph import State, Nodes, Edges
5
+
6
+
7
+ class GraphBuilder:
8
+ def __init__(self):
9
+ """
10
+ Initializes the GraphBuilder.
11
+ """
12
+ self.nodes = Nodes()
13
+ self.edges = Edges()
14
+ self.compiled_graph = None
15
+
16
+ def build_agent_graph(self) -> CompiledStateGraph:
17
+ """Build and return the agent graph."""
18
+ graph = StateGraph(State)
19
+
20
+ graph.add_node("manager", self.nodes.manager_node)
21
+ graph.add_node("final_answer", self.nodes.final_answer_node)
22
+ graph.add_node("auditor", self.nodes.auditor_node)
23
+ graph.add_node("solver", self.nodes.solver_node)
24
+ graph.add_node("researcher", self.nodes.researcher_node)
25
+ graph.add_node("encryption_expert", self.nodes.encryption_expert_node)
26
+ graph.add_node("encryption_advisor", self.nodes.encryption_advisor_node)
27
+ graph.add_node("math_expert", self.nodes.math_expert_node)
28
+ graph.add_node("math_advisor", self.nodes.math_advisor_node)
29
+ graph.add_node("reasoner", self.nodes.reasoner_node)
30
+ graph.add_node("image_handler", self.nodes.image_handler_node)
31
+ graph.add_node("video_handler", self.nodes.video_handler_node)
32
+
33
+ graph.add_edge(START, "manager")
34
+ graph.add_edge("final_answer", END)
35
+ graph.add_edge("auditor", "manager")
36
+ graph.add_edge("researcher", "solver")
37
+ graph.add_edge("encryption_advisor", "encryption_expert")
38
+ graph.add_edge("math_advisor", "math_expert")
39
+ graph.add_edge("reasoner", "solver")
40
+ graph.add_edge("image_handler", "solver")
41
+ graph.add_edge("video_handler", "solver")
42
+
43
+ graph.add_conditional_edges(
44
+ "manager",
45
+ self.edges.manager_edge,
46
+ {
47
+ "solver": "solver", "auditor": "auditor", "final_answer": "final_answer"
48
+ }
49
+ )
50
+ graph.add_conditional_edges(
51
+ "solver",
52
+ self.edges.solver_edge,
53
+ {
54
+ "manager": "manager", "researcher": "researcher", "encryption_expert": "encryption_expert", "math_expert": "math_expert", "reasoner": "reasoner", "image_handler": "image_handler", "video_handler": "video_handler"
55
+ }
56
+ )
57
+ graph.add_conditional_edges(
58
+ "encryption_expert",
59
+ self.edges.encryption_expert_edge,
60
+ {
61
+ "solver": "solver", "encryption_advisor": "encryption_advisor"
62
+ }
63
+ )
64
+ graph.add_conditional_edges(
65
+ "math_expert",
66
+ self.edges.math_expert_edge,
67
+ {
68
+ "solver": "solver", "math_advisor": "math_advisor"
69
+ }
70
+ )
71
+
72
+ return graph.compile()
73
+
74
+ def display_graph(self):
75
+ if self.compiled_graph is None:
76
+ raise ValueError("Graph has not been compiled yet. Call build_agent_graph() first.")
77
+
78
+ display(Image(self.compiled_graph.get_graph().draw_mermaid_png()))
logger.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import datetime
3
+ from pathlib import Path
4
+
5
+
6
+ class Logger:
7
+ @staticmethod
8
+ def set_logger() -> logging.Logger:
9
+ """
10
+ Configure and return a logger with a file handler that writes to logs/<current date-time>.txt
11
+
12
+ Returns:
13
+ logging.Logger: Configured logger instance
14
+ """
15
+ # Create logger
16
+ logger = logging.getLogger("Alfred")
17
+ logger.setLevel(logging.INFO)
18
+
19
+ # Create formatter
20
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
21
+
22
+ # Create logs directory if it doesn't exist
23
+ logs_dir = Path('logs')
24
+ logs_dir.mkdir(exist_ok=True)
25
+
26
+ # Generate log filename with current date-time
27
+ current_time = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
28
+ log_filename = f"{current_time}.txt"
29
+ log_filepath = logs_dir / log_filename
30
+
31
+ # Create file handler with UTF-8 encoding to handle all Unicode characters
32
+ file_handler = logging.FileHandler(log_filepath, encoding='utf-8')
33
+ file_handler.setLevel(logging.INFO)
34
+ file_handler.setFormatter(formatter)
35
+
36
+ # Add handler to logger
37
+ logger.addHandler(file_handler)
38
+
39
+ logger.info(f"Logging started at {current_time}")
40
+ return logger
management.py DELETED
@@ -1,106 +0,0 @@
1
- from llama_index.core.tools import FunctionTool
2
-
3
- from typing import List
4
-
5
- from itf_agent import IAgent
6
- from solver import Solver, Summarizer
7
- from args import Args
8
-
9
-
10
- class Assistant(IAgent):
11
- def __init__(self, temperature, max_tokens):
12
- super().__init__(temperature, max_tokens, "01_assistant.txt", Args.primary_llm_interface)
13
-
14
- class Manager(IAgent):
15
- def __init__(self, temperature, max_tokens, max_depth):
16
- super().__init__(temperature, max_tokens, "02_manager.txt", Args.primary_llm_interface)
17
-
18
- self.max_depth = max_depth
19
- self.current_depth = 0
20
-
21
- # We track the current query to forward it to the team when needed.
22
- self.current_query = ""
23
-
24
- self.solver = Solver(temperature, max_tokens)
25
- self.summarizer = Summarizer(temperature, max_tokens)
26
-
27
- def setup_tools(self) -> List:
28
- return [
29
- FunctionTool.from_defaults(
30
- name="require_break_up",
31
- description="Break a complex task into simpler subtasks. Use when a task needs to be divided into manageable parts.",
32
- fn=self.require_break_up
33
- ),
34
- FunctionTool.from_defaults(
35
- name="require_solution",
36
- description="Request direct solutions for specific tasks. Use when a task is simple enough to be solved directly.",
37
- fn=self.require_solution
38
- ),
39
- FunctionTool.from_defaults(
40
- name="forward_query",
41
- description="Request direct solutions for the current query. Use as a first attempt and to make the team aware of the task's context.",
42
- fn=self.forward_query
43
- )
44
- ]
45
-
46
- async def query(self, question: str, has_context=True) -> str:
47
- self.current_query = question
48
- return await super().query(question, has_context)
49
-
50
- async def require_break_up(self, tasks: List[str], try_solving = False) -> str:
51
- """
52
- Break down complex tasks into simpler subtasks recursively up to max_depth.
53
-
54
- Args:
55
- tasks: List of tasks to break down
56
- try_solving: Whether to attempt solving tasks at max depth (default: False)
57
-
58
- Returns:
59
- Summarized report of the task breakdown
60
- """
61
- print(f"-> require_break_up tool used (input: {tasks}) !")
62
- if not tasks:
63
- return "Error: No tasks provided to break up. Please provide at least one task."
64
-
65
- self.current_depth += 1
66
-
67
- observation = ""
68
- if self.current_depth < self.max_depth:
69
- for task in tasks:
70
- solution = await self.query(task, has_context=False)
71
- response = f"For task:\n\n{task}\n\nThe following break up has been provided:\n\n{solution}\n\n"
72
- observation += response
73
- elif try_solving:
74
- for task in tasks:
75
- response = await self.solver.query(task)
76
- else:
77
- observation = "Maximum depth for `break_up` tool has been reached ! At this point, you may try to break up the task yourself or try `require_solution`."
78
-
79
- self.current_depth -= 1
80
- report = await self.summarizer.query(observation.strip())
81
- return report
82
-
83
- async def require_solution(self, tasks: List[str]) -> str:
84
- """
85
- Request direct solutions for the provided tasks using the Solver.
86
-
87
- Args:
88
- tasks: List of tasks to solve
89
-
90
- Returns:
91
- Summarized report of solutions for all tasks
92
- """
93
- print(f"-> require_solution tool used with input: {tasks} !")
94
- if not tasks:
95
- return "Error: No tasks provided to solve. Please provide at least one task."
96
-
97
- observation = ""
98
- for task in tasks:
99
- solution = await self.solver.query(task)
100
- observation += solution
101
-
102
- report = await self.summarizer.query(observation.strip())
103
- return report
104
-
105
- async def forward_query(self) -> str:
106
- return await self.require_solution([self.current_query])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
system_prompts/{02_manager.txt → 01_manager.txt} RENAMED
File without changes
system_prompts/{01_assistant.txt → 02_auditor.txt} RENAMED
File without changes
test.py ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from graph import State, Nodes, Edges
2
+ from graph_builder import GraphBuilder
3
+
4
+ import unittest
5
+
6
+
7
+ class TestAlfredAgent(unittest.TestCase):
8
+ """Test suite for the Alfred agent"""
9
+
10
+ def setUp(self):
11
+ """Set up test fixtures"""
12
+ self.nodes = Nodes()
13
+ self.edges = Edges()
14
+ self.builder = GraphBuilder()
15
+ self.graph = self.builder.build_agent_graph()
16
+
17
+ def test_manager_node(self):
18
+ """
19
+ Test the manager node functionality.
20
+
21
+ Orchestrates the workflow by delegating tasks to specialized nodes and integrating their outputs
22
+ """
23
+ # Create an instance of Nodes class
24
+ nodes = Nodes()
25
+
26
+ # Create a test state
27
+ test_state = {} # TODO: Initialize with appropriate test data
28
+
29
+ # Test the node function
30
+ print(f"Testing 'manager' node...")
31
+ nodes.manager_node(test_state)
32
+
33
+ # TODO: Add assertions to verify the state changes
34
+ print(f"State after node execution: {test_state}")
35
+
36
+ def test_final_answer_node(self):
37
+ """
38
+ Test the final_answer node functionality.
39
+
40
+ Formats and delivers the final response to the user
41
+ """
42
+ # Create an instance of Nodes class
43
+ nodes = Nodes()
44
+
45
+ # Create a test state
46
+ test_state = {} # TODO: Initialize with appropriate test data
47
+
48
+ # Test the node function
49
+ print(f"Testing 'final_answer' node...")
50
+ nodes.final_answer_node(test_state)
51
+
52
+ # TODO: Add assertions to verify the state changes
53
+ print(f"State after node execution: {test_state}")
54
+
55
+ def test_auditor_node(self):
56
+ """
57
+ Test the auditor node functionality.
58
+
59
+ Reviews manager's outputs for accuracy, safety, and quality
60
+ """
61
+ # Create an instance of Nodes class
62
+ nodes = Nodes()
63
+
64
+ # Create a test state
65
+ test_state = {} # TODO: Initialize with appropriate test data
66
+
67
+ # Test the node function
68
+ print(f"Testing 'auditor' node...")
69
+ nodes.auditor_node(test_state)
70
+
71
+ # TODO: Add assertions to verify the state changes
72
+ print(f"State after node execution: {test_state}")
73
+
74
+ def test_solver_node(self):
75
+ """
76
+ Test the solver node functionality.
77
+
78
+ Central problem-solving node that coordinates with specialized experts based on task requirements
79
+ """
80
+ # Create an instance of Nodes class
81
+ nodes = Nodes()
82
+
83
+ # Create a test state
84
+ test_state = {} # TODO: Initialize with appropriate test data
85
+
86
+ # Test the node function
87
+ print(f"Testing 'solver' node...")
88
+ nodes.solver_node(test_state)
89
+
90
+ # TODO: Add assertions to verify the state changes
91
+ print(f"State after node execution: {test_state}")
92
+
93
+ def test_researcher_node(self):
94
+ """
95
+ Test the researcher node functionality.
96
+
97
+ Retrieves and synthesizes information from various sources to answer knowledge-based questions
98
+ """
99
+ # Create an instance of Nodes class
100
+ nodes = Nodes()
101
+
102
+ # Create a test state
103
+ test_state = {} # TODO: Initialize with appropriate test data
104
+
105
+ # Test the node function
106
+ print(f"Testing 'researcher' node...")
107
+ nodes.researcher_node(test_state)
108
+
109
+ # TODO: Add assertions to verify the state changes
110
+ print(f"State after node execution: {test_state}")
111
+
112
+ def test_encryption_expert_node(self):
113
+ """
114
+ Test the encryption_expert node functionality.
115
+
116
+ Handles encryption/decryption tasks and encoding/decoding operations
117
+ """
118
+ # Create an instance of Nodes class
119
+ nodes = Nodes()
120
+
121
+ # Create a test state
122
+ test_state = {} # TODO: Initialize with appropriate test data
123
+
124
+ # Test the node function
125
+ print(f"Testing 'encryption_expert' node...")
126
+ nodes.encryption_expert_node(test_state)
127
+
128
+ # TODO: Add assertions to verify the state changes
129
+ print(f"State after node execution: {test_state}")
130
+
131
+ def test_encryption_advisor_node(self):
132
+ """
133
+ Test the encryption_advisor node functionality.
134
+
135
+ Provides specialized guidance on complex encryption problems to the encryption expert
136
+ """
137
+ # Create an instance of Nodes class
138
+ nodes = Nodes()
139
+
140
+ # Create a test state
141
+ test_state = {} # TODO: Initialize with appropriate test data
142
+
143
+ # Test the node function
144
+ print(f"Testing 'encryption_advisor' node...")
145
+ nodes.encryption_advisor_node(test_state)
146
+
147
+ # TODO: Add assertions to verify the state changes
148
+ print(f"State after node execution: {test_state}")
149
+
150
+ def test_math_expert_node(self):
151
+ """
152
+ Test the math_expert node functionality.
153
+
154
+ Performs mathematical calculations and solves numerical problems
155
+ """
156
+ # Create an instance of Nodes class
157
+ nodes = Nodes()
158
+
159
+ # Create a test state
160
+ test_state = {} # TODO: Initialize with appropriate test data
161
+
162
+ # Test the node function
163
+ print(f"Testing 'math_expert' node...")
164
+ nodes.math_expert_node(test_state)
165
+
166
+ # TODO: Add assertions to verify the state changes
167
+ print(f"State after node execution: {test_state}")
168
+
169
+ def test_math_advisor_node(self):
170
+ """
171
+ Test the math_advisor node functionality.
172
+
173
+ Provides specialized guidance on complex mathematical problems to the math expert
174
+ """
175
+ # Create an instance of Nodes class
176
+ nodes = Nodes()
177
+
178
+ # Create a test state
179
+ test_state = {} # TODO: Initialize with appropriate test data
180
+
181
+ # Test the node function
182
+ print(f"Testing 'math_advisor' node...")
183
+ nodes.math_advisor_node(test_state)
184
+
185
+ # TODO: Add assertions to verify the state changes
186
+ print(f"State after node execution: {test_state}")
187
+
188
+ def test_reasoner_node(self):
189
+ """
190
+ Test the reasoner node functionality.
191
+
192
+ Performs logical reasoning, inference, and step-by-step problem-solving
193
+ """
194
+ # Create an instance of Nodes class
195
+ nodes = Nodes()
196
+
197
+ # Create a test state
198
+ test_state = {} # TODO: Initialize with appropriate test data
199
+
200
+ # Test the node function
201
+ print(f"Testing 'reasoner' node...")
202
+ nodes.reasoner_node(test_state)
203
+
204
+ # TODO: Add assertions to verify the state changes
205
+ print(f"State after node execution: {test_state}")
206
+
207
+ def test_image_handler_node(self):
208
+ """
209
+ Test the image_handler node functionality.
210
+
211
+ Processes, analyzes, and generates information related to images
212
+ """
213
+ # Create an instance of Nodes class
214
+ nodes = Nodes()
215
+
216
+ # Create a test state
217
+ test_state = {} # TODO: Initialize with appropriate test data
218
+
219
+ # Test the node function
220
+ print(f"Testing 'image_handler' node...")
221
+ nodes.image_handler_node(test_state)
222
+
223
+ # TODO: Add assertions to verify the state changes
224
+ print(f"State after node execution: {test_state}")
225
+
226
+ def test_video_handler_node(self):
227
+ """
228
+ Test the video_handler node functionality.
229
+
230
+ Processes, analyzes, and generates information related to videos
231
+ """
232
+ # Create an instance of Nodes class
233
+ nodes = Nodes()
234
+
235
+ # Create a test state
236
+ test_state = {} # TODO: Initialize with appropriate test data
237
+
238
+ # Test the node function
239
+ print(f"Testing 'video_handler' node...")
240
+ nodes.video_handler_node(test_state)
241
+
242
+ # TODO: Add assertions to verify the state changes
243
+ print(f"State after node execution: {test_state}")
244
+
245
+ def test_manager_edge(self):
246
+ """
247
+ Test the conditional edge for manager node.
248
+
249
+ This edge should return one of: "solver", "auditor", "final_answer"
250
+ """
251
+ # Create an instance of Edges class
252
+ edges = Edges()
253
+
254
+ # Create a test state
255
+ test_state = {} # TODO: Initialize with appropriate test data
256
+
257
+ # Test the edge function
258
+ print(f"Testing 'manager' conditional edge...")
259
+ result = edges.manager_edge(test_state)
260
+
261
+ # TODO: Add assertions to verify the result
262
+ print(f"Edge decision: {result}")
263
+ assert result in ["solver", "auditor", "final_answer"], f"Edge result '{result}' not in expected values"
264
+
265
+ def test_solver_edge(self):
266
+ """
267
+ Test the conditional edge for solver node.
268
+
269
+ This edge should return one of: "manager", "researcher", "encryption_expert", "math_expert", "reasoner", "image_handler", "video_handler"
270
+ """
271
+ # Create an instance of Edges class
272
+ edges = Edges()
273
+
274
+ # Create a test state
275
+ test_state = {} # TODO: Initialize with appropriate test data
276
+
277
+ # Test the edge function
278
+ print(f"Testing 'solver' conditional edge...")
279
+ result = edges.solver_edge(test_state)
280
+
281
+ # TODO: Add assertions to verify the result
282
+ print(f"Edge decision: {result}")
283
+ assert result in ["manager", "researcher", "encryption_expert", "math_expert", "reasoner", "image_handler", "video_handler"], f"Edge result '{result}' not in expected values"
284
+
285
+ def test_encryption_expert_edge(self):
286
+ """
287
+ Test the conditional edge for encryption_expert node.
288
+
289
+ This edge should return one of: "solver", "encryption_advisor"
290
+ """
291
+ # Create an instance of Edges class
292
+ edges = Edges()
293
+
294
+ # Create a test state
295
+ test_state = {} # TODO: Initialize with appropriate test data
296
+
297
+ # Test the edge function
298
+ print(f"Testing 'encryption_expert' conditional edge...")
299
+ result = edges.encryption_expert_edge(test_state)
300
+
301
+ # TODO: Add assertions to verify the result
302
+ print(f"Edge decision: {result}")
303
+ assert result in ["solver", "encryption_advisor"], f"Edge result '{result}' not in expected values"
304
+
305
+ def test_math_expert_edge(self):
306
+ """
307
+ Test the conditional edge for math_expert node.
308
+
309
+ This edge should return one of: "solver", "math_advisor"
310
+ """
311
+ # Create an instance of Edges class
312
+ edges = Edges()
313
+
314
+ # Create a test state
315
+ test_state = {} # TODO: Initialize with appropriate test data
316
+
317
+ # Test the edge function
318
+ print(f"Testing 'math_expert' conditional edge...")
319
+ result = edges.math_expert_edge(test_state)
320
+
321
+ # TODO: Add assertions to verify the result
322
+ print(f"Edge decision: {result}")
323
+ assert result in ["solver", "math_advisor"], f"Edge result '{result}' not in expected values"
324
+
325
+ def test_full_workflow(self):
326
+ """
327
+ Test the Alfred agent full workflow.
328
+ """
329
+ # TODO: Add test code here
330
+ print("Testing Alfred complete workflow...")
331
+
332
+ # Example test
333
+ # result = self.graph.invoke({"input": "Test input"})
334
+ # self.assertIsNotNone(result)
335
+ # print(f"Workflow result: {result}")
336
+
337
+
338
+ if __name__ == "__main__":
339
+ unittest.main()
test_agents.py DELETED
@@ -1,17 +0,0 @@
1
- # OUTDATED !
2
- from agents_stage_2 import MathAgent, MainAgent
3
- import asyncio
4
-
5
- def test_math_agent():
6
- math_agent = MathAgent(temperature=0.7, max_tokens=100)
7
- print(math_agent.get_system_prompt())
8
- asyncio.run(math_agent.query("What is 345 times 281?"))
9
-
10
- def test_main_agent():
11
- main_agent = MainAgent(temperature=0.7, max_tokens=100)
12
- print(main_agent.get_system_prompt())
13
- asyncio.run(main_agent.query("What is 345 times 281?"))
14
-
15
- if __name__ == "__main__":
16
- # test_math_agent()
17
- test_main_agent()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
toolbox.py CHANGED
@@ -1,5 +1,4 @@
1
  from llama_index.core.tools import FunctionTool
2
- # from llama_index.tools.duckduckgo import DuckDuckGoSearchToolSpec # I encountered some issues... Sometimes it doesn't work !
3
 
4
 
5
  from duckduckgo_search import DDGS
@@ -136,7 +135,6 @@ class _WebSearch:
136
 
137
 
138
  class _WebSearchToolbox:
139
- # duckduckgo_tools = DuckDuckGoSearchToolSpec().to_tool_list() # I encountered some issues... Sometimes it doesn't work !
140
  duckduckgo_text_search = FunctionTool.from_defaults(
141
  name="duckduckgo_text_search",
142
  description="DuckDuckGo text search",
 
1
  from llama_index.core.tools import FunctionTool
 
2
 
3
 
4
  from duckduckgo_search import DDGS
 
135
 
136
 
137
  class _WebSearchToolbox:
 
138
  duckduckgo_text_search = FunctionTool.from_defaults(
139
  name="duckduckgo_text_search",
140
  description="DuckDuckGo text search",