masai-framework 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,205 @@
1
+ import os, json
2
+ from typing import List, Tuple, Type, Union, Literal, Dict, Optional
3
+ from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, PromptTemplate, SystemMessagePromptTemplate
4
+ from pydantic import BaseModel, Field
5
+ from ..GenerativeModel.generativeModels import MASGenerativeModel
6
+ from ..Agents.singular_agent import Agent
7
+ from datetime import datetime, timezone
8
+ from ..pydanticModels.AnswerModel import answermodel
9
+ from dataclasses import dataclass
10
+ from pathlib import Path
11
+ from importlib import resources
12
+ from ..prompts.prompt_templates import get_agent_prompts
13
+
14
+ @dataclass
15
+ class AgentDetails:
16
+ capabilities: List[str] # e.g., ["reasoning", "coding", "science"]
17
+ description: str = "" # Optional additional description
18
+ style: str = "gives very elaborate answers" # Communication style
19
+
20
+ class AgentManager:
21
+ def __init__(self, logging=True, context:dict=None, model_config_path=None):
22
+ """Initialize the AgentManager with an empty registry of agents.
23
+
24
+ The AgentManager class serves as a central registry for creating, managing, and
25
+ coordinating multiple agents in a multi-agent system.
26
+
27
+ Args:
28
+ logging (bool, optional): Enable or disable logging of agent activities.
29
+ Defaults to True.
30
+ context (dict, optional): Additional contextual information to be shared
31
+ with all agents. Defaults to None.
32
+
33
+ Attributes:
34
+ agents (dict[str, Agent]): Dictionary storing agent instances,
35
+ where keys are agent names and values are Agent objects.
36
+ agent_prompts (dict): Dictionary storing system prompts for each agent.
37
+ logging (bool): Flag to control logging behavior.
38
+ context (dict): Shared context available to all agents.
39
+ model_config_path (Path): Path to the model configuration file.
40
+ """
41
+ self.agents = {}
42
+ self.agent_prompts = {}
43
+ self.logging = logging
44
+ self.context = context
45
+
46
+ # model_config_path should be provided by user
47
+ if not model_config_path:
48
+ raise ValueError("model_config_path must be provided")
49
+ self.model_config_path = model_config_path
50
+
51
+ def load_prompts(self) -> Tuple[str, str, str]:
52
+ """Load prompts from module."""
53
+ return get_agent_prompts()
54
+
55
+ def promptformatter(self, router_prompt: str, evaluator_prompt: str, reflector_prompt: str, planner_prompt: str, system_prompt: str) -> Tuple[ChatPromptTemplate, ChatPromptTemplate, ChatPromptTemplate, ChatPromptTemplate]:
56
+ """Format prompts into ChatPromptTemplates."""
57
+ input_variables = ['question', 'history', 'schema','current_time','useful_info','coworking_agents_info','long_context']
58
+ template = """
59
+ INFO:{useful_info}
60
+ \n\nTIME:{current_time},
61
+ \nQUESTION: {question},
62
+ \n\nRESPONSE FORMAT : {schema},
63
+ \n\nAVAILABLE AGENTS:{coworking_agents_info},
64
+ \n\nCHAT HISTORY: {history}
65
+ \n\nLONG CONTEXT: {long_context}
66
+ """
67
+
68
+ human_message_template = HumanMessagePromptTemplate(
69
+ prompt=PromptTemplate(input_variables=input_variables, template=template)
70
+ )
71
+
72
+ system_message_template_1 = SystemMessagePromptTemplate(
73
+ prompt=PromptTemplate(template =system_prompt + "\nFOLLOW THESE INSTRUCTIONS:" + router_prompt )
74
+ )
75
+ system_message_template_2 = SystemMessagePromptTemplate(
76
+ prompt=PromptTemplate(template=system_prompt +"\nFOLLOW THESE INSTRUCTIONS:" + evaluator_prompt )
77
+ )
78
+ system_message_template_3 = SystemMessagePromptTemplate(
79
+ prompt=PromptTemplate(template=system_prompt + "\nFOLLOW THESE INSTRUCTIONS:" + reflector_prompt )
80
+ )
81
+
82
+ system_message_template_4 = SystemMessagePromptTemplate(
83
+ prompt=PromptTemplate(template=system_prompt + "\nFOLLOW THESE INSTRUCTIONS:" + planner_prompt if planner_prompt else "")
84
+ )
85
+
86
+ router_chat_prompt = ChatPromptTemplate.from_messages([system_message_template_1, human_message_template])
87
+ evaluator_chat_prompt = ChatPromptTemplate.from_messages([system_message_template_2, human_message_template])
88
+ reflector_chat_prompt = ChatPromptTemplate.from_messages([system_message_template_3, human_message_template])
89
+ if planner_prompt:
90
+ planner_chat_prompt = ChatPromptTemplate.from_messages([system_message_template_4, human_message_template])
91
+ else:
92
+ planner_chat_prompt = None
93
+ return router_chat_prompt, evaluator_chat_prompt, reflector_chat_prompt, planner_chat_prompt
94
+
95
+ def _load_model_config(self) -> dict:
96
+ """Load model configuration from a JSON file."""
97
+ if not os.path.exists(self.model_config_path):
98
+ raise FileNotFoundError(f"Model config file not found at {self.model_config_path}.")
99
+
100
+ with open(self.model_config_path, "r") as f:
101
+ return json.load(f)
102
+
103
+ def create_agent(self, agent_name: str, tools: List[object], agent_details: AgentDetails,
104
+ memory_order: int = 20, long_context: bool = True,long_context_order: int = 10, shared_memory_order: int = 10,
105
+ plan: bool = False):
106
+ """Create and register a new agent in the AgentManager.
107
+
108
+ Args:
109
+ agent_name (str): Unique identifier for the agent (converted to lowercase).
110
+ tools (List[object]): Tools the agent can use, each with a 'name' attribute.
111
+ agent_details (AgentDetails): Configuration with capabilities, description, and style.
112
+ memory_order (int, optional): Number of past interactions to keep. Defaults to 20.
113
+ long_context (bool, optional): Use long context if True. Defaults to True.
114
+ long_context_order (int, optional): Number of past interactions summary to keep in long context. Defaults to 10.
115
+ shared_memory_order (int, optional): Shared memory size for components. Defaults to 10.
116
+ plan (bool, optional): Include planner if True. Defaults to False.
117
+
118
+ Raises:
119
+ ValueError: If agent_name already exists.
120
+ FileNotFoundError: If prompts file is missing.
121
+ """
122
+ agent_name = agent_name.lower()
123
+ if agent_name in self.agents:
124
+ raise ValueError(f"Agent '{agent_name}' already exists.")
125
+
126
+ # Load and format prompts
127
+ prompts = self.load_prompts()
128
+ system_prompt = self._create_system_prompt(agent_name, agent_details)
129
+ chat_prompts = self.promptformatter(*prompts, system_prompt=system_prompt)
130
+
131
+ # Configure tools and answer format
132
+ tool_mapping = {tool.name: tool for tool in tools}
133
+ AnswerFormat = answermodel(tool_names=list(tool_mapping.keys()) + ['None'], tools=tools)
134
+
135
+ # Initialize LLM models
136
+ model_config = self._load_model_config()
137
+ llm_args = {"temperature": 0.2, "memory_order": memory_order, "extra_context": self.context, "long_context": long_context,"long_context_order":long_context_order}
138
+ llm_router = MASGenerativeModel(model_config["router"]["model_name"], category=model_config["router"]["category"], prompt_template=chat_prompts[0], **llm_args)
139
+ llm_evaluator = MASGenerativeModel(model_config["evaluator"]["model_name"], category=model_config["evaluator"]["category"], prompt_template=chat_prompts[1], **llm_args)
140
+ llm_reflector = MASGenerativeModel(model_config["reflector"]["model_name"], category=model_config["reflector"]["category"], prompt_template=chat_prompts[2], **llm_args)
141
+ llm_planner = MASGenerativeModel(model_config["planner"]["model_name"], category=model_config["planner"]["category"], prompt_template=chat_prompts[3], **llm_args) if plan else None
142
+
143
+ # Create and register agent
144
+ agent = Agent(agent_name, llm_router, llm_evaluator, llm_reflector, llm_planner, tool_mapping, AnswerFormat, self.logging, shared_memory_order=shared_memory_order)
145
+ self.agents[agent_name] = agent
146
+ self.agent_prompts[agent_name] = system_prompt
147
+ def _compile_agents(self,type='decentralized',agent_context:dict=None):
148
+ """Share agent system prompts among all registered agents.
149
+
150
+ This method ensures each agent is aware of other agents' capabilities and characteristics
151
+ by sharing their system prompts. For each agent, it creates a dictionary of all other
152
+ agents' prompts (excluding itself) and stores it in the agent's context.
153
+
154
+ Example:
155
+ If there are agents A, B, and C:
156
+ - Agent A will receive prompts from B and C
157
+ - Agent B will receive prompts from A and C
158
+ - Agent C will receive prompts from A and B
159
+
160
+ Note:
161
+ This method should be called after all agents have been created and before
162
+ starting any agent interactions to ensure proper inter-agent awareness.
163
+ """
164
+ if type=='decentralized':
165
+ for agents in self.agents.values():
166
+ prompts ={}
167
+ for agent_name in self.agent_prompts:
168
+ if agent_name!=agents.agent_name:
169
+ prompts[agent_name]=self.agent_prompts[agent_name]
170
+
171
+ # print(prompts)
172
+ agents.agent_context = prompts
173
+ elif type=='hierarchical':
174
+ for agents in self.agents.values():
175
+ agents.agent_context = agent_context
176
+ return
177
+
178
+ def _create_system_prompt(self, agent_name: str, details: AgentDetails) -> str:
179
+ """Convert AgentDetails into a system prompt."""
180
+ capabilities_str = ", ".join(details.capabilities)
181
+
182
+ prompt_parts = [
183
+ f"Your Name: {agent_name}.\n Your capabilities are {capabilities_str}",
184
+ f"Response Style: {details.style}."
185
+ ]
186
+
187
+ if details.description:
188
+ prompt_parts.append(details.description)
189
+
190
+ return "\n".join(prompt_parts)
191
+
192
+ def get_agent(self, agent_name: str)->Agent:
193
+ """Retrieve an agent by name."""
194
+ if agent_name.lower() not in self.agents:
195
+ raise ValueError(f"No agent found with name '{agent_name}'.")
196
+
197
+ return self.agents[agent_name.lower()]
198
+
199
+ def list_agents(self) -> List[str]:
200
+ """List all registered agents."""
201
+ return list(self.agents.keys())
202
+
203
+
204
+
205
+
@@ -0,0 +1 @@
1
+ from .AgentManager import AgentManager, AgentDetails
@@ -0,0 +1 @@
1
+ from .singular_agent import Agent
@@ -0,0 +1,316 @@
1
+ from langgraph.graph import END, StateGraph, START
2
+ from typing import List, Dict, Any, Literal, TypedDict, Tuple, Union, Type
3
+ from pydantic import BaseModel, Field
4
+ import ast, os
5
+ from dotenv import load_dotenv
6
+ load_dotenv()
7
+ from ..GenerativeModel.generativeModels import MASGenerativeModel,GenerativeModel
8
+ from ..GenerativeModel.baseGenerativeModel.basegenerativeModel import BaseGenerativeModel
9
+ from ..Tools.logging_setup.logger import setup_logger
10
+ from ..Tools.PARSERs.json_parser import parse_tool_input, parse_task_string
11
+ from langchain.schema import Document
12
+
13
+ class State(TypedDict):
14
+ messages: List[Dict[str, str]]
15
+ current_tool: str
16
+ tool_input: str
17
+ tool_output: str
18
+ answer: str
19
+ satisfied: bool
20
+ reasoning: str
21
+ delegate_to_agent:str
22
+ current_node: str
23
+ previous_node: str
24
+ plan: List[str]
25
+ class Agent:
26
+ """Agent Made Out of Routing-Evaluator-Reflector Architecture"""
27
+ _logger=None
28
+ def __init__(self,agent_name,llm_router, llm_evaluator, llm_reflector, llm_planner=None, tool_mapping=None, AnswerFormat:BaseModel=None,logging=True, agent_context=None, shared_memory_order:int=5):
29
+ """Initialize an agent with router-evaluator-reflector architecture and optional planner.
30
+
31
+ The agent uses a state machine workflow to process queries through specialized LLMs:
32
+ - Router: Determines which tool to use or agent to delegate to
33
+ - Evaluator: Evaluates tool outputs and determines next steps
34
+ - Reflector: Reflects on overall progress and generates final answers
35
+ - Planner (optional): Creates execution plans for complex tasks
36
+
37
+ Args:
38
+ agent_name (str): Name identifier for the agent instance
39
+ llm_router (BaseGenerativeModel): Language model for routing decisions - determines which tool to use or agent to delegate to
40
+ llm_evaluator (BaseGenerativeModel): Language model for evaluation - processes tool outputs and determines next steps
41
+ llm_reflector (BaseGenerativeModel): Language model for reflection - analyzes overall progress and generates final answers
42
+ llm_planner (BaseGenerativeModel, optional): Language model for planning complex tasks. Defaults to None.
43
+ tool_mapping (Dict[str, Callable], optional): Mapping of tool names to their function implementations. Defaults to None.
44
+ AnswerFormat (BaseModel, optional): Pydantic model defining the structure of agent responses. Defaults to None.
45
+ logging (bool, optional): Enable/disable logging functionality. Defaults to True.
46
+ agent_context (Dict[str, Any], optional): Additional context information for the agent in multi agent system, providing context about other agents it should interact with. Defaults to None.
47
+ shared_memory_order (int, optional): Number of previous interactions to maintain in shared memory among individual components of an agent. Defaults to 5.
48
+ retain_messages_order (int, optional): Number of previous interactions to maintain in agent's system memory.This includes short term memory of all components within the agent. Defaults to 20.
49
+ """
50
+ self.agent_name = agent_name
51
+ self.llm_evaluator :BaseGenerativeModel =llm_evaluator
52
+ self.llm_router : BaseGenerativeModel=llm_router
53
+ self.llm_reflector : BaseGenerativeModel=llm_reflector
54
+ if llm_planner:
55
+ self.llm_planner = llm_planner
56
+ self.plan=True
57
+ else:
58
+ self.llm_planner = None
59
+ self.plan=False
60
+ self.app = self.agentworkflow()
61
+ self.graph = self.app.get_graph()
62
+ self.tool_mapping : dict= tool_mapping
63
+ self.pydanticmodel : BaseModel = AnswerFormat
64
+ self.logging = logging
65
+ self.shared_memory_order=shared_memory_order
66
+
67
+ if self.logging:
68
+ if Agent._logger is None:
69
+ Agent._logger = setup_logger()
70
+ self.logger = Agent._logger
71
+ else:
72
+ self.logger = None
73
+
74
+
75
+ self.agent_context = agent_context
76
+ self.node:str='evaluator'
77
+ self.retain_messages_order=20
78
+
79
+ def gettoolinput(self, tool_input : dict, tool_name: str)->Union[Dict,str]:
80
+ tool_input = parse_tool_input(tool_input, list((self.tool_mapping[tool_name]).args_schema.schema()['properties'].keys()))
81
+ return tool_input
82
+
83
+ def display(self):
84
+ """Display the graph of the agent"""
85
+ png_data = self.graph.draw_mermaid_png()
86
+ # Save the PNG image to a file
87
+ mermaid_dir = os.path.join('MAS','Database','mermaid')
88
+ os.makedirs(mermaid_dir, exist_ok=True)
89
+ png_file_path = os.path.join(mermaid_dir, "diagram.png")
90
+ with open(png_file_path, "wb") as f:
91
+ f.write(png_data)
92
+
93
+ def _update_state(self,current_state:State, parsed_response,node):
94
+ if not (parsed_response['tool']=="none" or parsed_response['tool']==None):
95
+ current_state["current_tool"] = parsed_response['tool']
96
+ current_state['tool_input'] = self.gettoolinput(parsed_response['tool_input'],current_state['current_tool'])
97
+ if self.logger:
98
+ self.logger.warning("-------------------------------------Tool Input---------------------------------\n\n")
99
+ self.logger.warning(current_state['tool_input'])
100
+
101
+
102
+ if node=='planner':
103
+ current_state['plan']=parse_task_string(parsed_response['answer'])
104
+ for ele in current_state['plan']:
105
+ print(ele)
106
+ self.node=node
107
+ current_state.update({
108
+ "previous_node": node,
109
+ "current_node": node,
110
+ "answer": parsed_response['answer'],
111
+ "satisfied": parsed_response['satisfied'],
112
+ "reasoning": parsed_response['reasoning'],
113
+ "current_tool": parsed_response['tool'],
114
+ "delegate_to_agent": parsed_response['delegate_to_agent']
115
+ })
116
+
117
+ current_state["messages"].append({"role": "assistant", "content": current_state['answer']})
118
+ if len(current_state["messages"])>self.retain_messages_order:
119
+ current_state['messages']=[current_state['messages'][0]].extend(current_state["messages"][-self.retain_messages_order:])
120
+ return current_state
121
+
122
+ def node_handler(self,state: State, llm : MASGenerativeModel, prompt:str, component_context=None, node=None):
123
+
124
+ parsed_response = llm.generate_response_mas(prompt,
125
+ output_structure=self.pydanticmodel,
126
+ agent_context=self.agent_context if self.agent_context else None,
127
+ agent_name=self.agent_name,
128
+ component_context=component_context if component_context else [])
129
+ if self.logger:
130
+ self.logger.info(f"{parsed_response['answer']}")
131
+ current_state = state
132
+ current_state = self._update_state(current_state, parsed_response,node)
133
+ return current_state
134
+
135
+ def checkroutingcondition(self,state):
136
+ if self.logger:
137
+ self.logger.info('----------------------------Deciding Node--------------------------------')
138
+ if state["satisfied"] and not(state["current_tool"]==None or state["current_tool"]== "None"):
139
+ return "continue" #continue when satisfied is true but tool is provided
140
+
141
+ elif state["satisfied"] and (state["current_tool"]==None or state["current_tool"] == "None"):
142
+
143
+ return "end" #end when satisfied and tool is none
144
+
145
+ elif not state["satisfied"] and (state["current_tool"]==None or state["current_tool"]== "None"):
146
+ return "reflection" #reflect when not satisfied and tool is not provided
147
+ elif state["current_tool"]==None or state["current_tool"]== "None":
148
+ return "end" #end when no tool is chosen
149
+
150
+ return "continue"
151
+
152
+
153
+ def router(self,state: State) -> State:
154
+ messages = state["messages"]
155
+ state['current_node']='router'
156
+ if self.node=='evaluator':
157
+ component_context = self.llm_evaluator.chat_history[-self.shared_memory_order:]
158
+ elif self.node=='reflector':
159
+ component_context = self.llm_reflector.chat_history[-self.shared_memory_order:]
160
+ elif self.node=='router':
161
+ component_context=[]
162
+ prompt = messages[0]['content'] if messages else ""
163
+ current_state = self.node_handler(state, self.llm_router, prompt,component_context=component_context,node='router')
164
+ return current_state
165
+
166
+ def execute_tool(self,state: State) -> State:
167
+ tool_name = state["current_tool"]
168
+ tool_input = state["tool_input"]
169
+ tool=self.tool_mapping[tool_name]
170
+
171
+ result = tool.invoke(input=tool_input)
172
+
173
+ if self.logger:
174
+ self.logger.warning("-------------------------------------Tool Output---------------------------------\n\n")
175
+ self.logger.warning(result)
176
+
177
+
178
+ if tool.return_direct:
179
+ state["tool_input"] = 'None'
180
+ state["messages"].append({"role": f"Tool: {tool_name}", "content": str(result)})
181
+ state['current_tool'] = 'None'
182
+ state['answer']= str(result)
183
+ state['reasoning'] = ""
184
+ state['satisfied'] = True
185
+ state['delegate_to_agent']=None
186
+ self.logger.info("RETURNING DIRECT")
187
+ return state
188
+
189
+ state['tool_output'] = str(result)
190
+ state["messages"].append({"role": f"Tool: {tool_name}", "tool_output": state['tool_output']})
191
+ return state
192
+
193
+ # Define the evaluation function
194
+ def evaluator(self,state: State) -> Dict[str, Any]:
195
+ messages = state["messages"]
196
+ state['current_node'] = 'evaluator'
197
+ tool_output = state["tool_output"]
198
+ if state['previous_node']=='reflector':
199
+ component_context=self.llm_reflector.chat_history[-self.shared_memory_order:]
200
+ elif state['previous_node']=='router':
201
+ component_context=self.llm_router.chat_history[-self.shared_memory_order:]
202
+ elif state['previous_node']=='planner':
203
+ component_context=self.llm_planner.chat_history[-self.shared_memory_order:]
204
+ else:
205
+ component_context=[]
206
+
207
+ if state['previous_node']=='planner':
208
+ prompt = f"\n\n<ORIGINAL QUESTION>: {messages[0]['content']}\n\n <PREVIOUS TOOL>:{state['current_tool']}\n\n<TOOL OUTPUT>: {tool_output}\n\n<PLAN>: {state['plan']}\n\n"
209
+ else:
210
+ prompt = f"\n\n<ORIGINAL QUESTION>: {messages[0]['content']}\n\n <PREVIOUS TOOL>:{state['current_tool']}\n\n<TOOL OUTPUT>: {tool_output}\n\n"
211
+ current_state = self.node_handler(state,self.llm_evaluator,prompt,component_context=component_context,node='evaluator')
212
+
213
+ return current_state
214
+
215
+ def reflection(self, state: State):
216
+ messages = state["messages"]
217
+ if self.logger:
218
+ self.logger.info("\n\n--------------------Reasoning and Reflecting-----------------------------\n\n")
219
+ state['current_node'] = 'reflector'
220
+ if state['previous_node']=='router':
221
+ component_context=self.llm_router.chat_history[-self.shared_memory_order:]
222
+ elif state['previous_node']=='evaluator':
223
+ component_context=self.llm_evaluator.chat_history[-self.shared_memory_order:]
224
+ elif state['previous_node']=='planner':
225
+ component_context=self.llm_planner.chat_history[-self.shared_memory_order:]
226
+ else:
227
+ component_context=[]
228
+ if state['previous_node']=='planner':
229
+ prompt = f"""<CURRENT STAGE>: REFLECTION STAGE\n\n <GOAL>: Reflect on gathered component_context, think and arrive at solution. \n\n<LAST USED TOOL>{state['current_tool']}\n\n<TOOL OUTPUT>{state['tool_output']} \n\n<QUESTION> : {messages[0]['content']}\n\n<PLAN>: {state['plan']}"""
230
+ else:
231
+ prompt = f"""<CURRENT STAGE>: REFLECTION STAGE\n\n <GOAL>: Reflect on gathered component_context, think and arrive at solution. \n\n<LAST USED TOOL>{state['current_tool']}\n\n<TOOL OUTPUT>{state['tool_output']} \n\n<QUESTION> : {messages[0]['content']}"""
232
+ current_state = self.node_handler(state, self.llm_reflector,prompt,component_context=component_context,node='reflector')
233
+ if self.logger:
234
+ self.logger.info("--------------------Reflection End-----------------------------")
235
+ return current_state
236
+
237
+ def planner(self,state: State):
238
+ messages = state["messages"]
239
+ state['current_node'] = 'planner'
240
+ if self.node=='evaluator':
241
+ component_context=self.llm_evaluator.chat_history[-self.shared_memory_order:]
242
+ elif self.node=='reflection':
243
+ component_context=self.llm_reflector.chat_history[-self.shared_memory_order:]
244
+ else:
245
+ component_context=[]
246
+ prompt = f"""<CURRENT STAGE>: PLANNER STAGE\n\n <GOAL>: Plan the tasks to accomplish the goal. \n\n<QUESTION> : {messages[0]['content']}"""
247
+ current_state = self.node_handler(state, self.llm_planner,prompt,component_context=component_context,node='planner')
248
+ return current_state
249
+
250
+ def agentworkflow(self):
251
+ workflow = StateGraph(State)
252
+ nodes = ["execute_tool", "evaluator", "reflection"]
253
+ if self.plan:
254
+ nodes.append("planner")
255
+ else:
256
+ nodes.append("router")
257
+
258
+ for node in nodes:
259
+ workflow.add_node(node, getattr(self, node))
260
+
261
+ # Dynamic edges
262
+ if self.plan:
263
+ workflow.add_edge(START, "planner")
264
+ workflow.add_edge("planner", "evaluator")
265
+ else:
266
+ workflow.add_edge(START, "router")
267
+ workflow.add_conditional_edges("router", self.checkroutingcondition, {
268
+ "end": END, "reflection": "reflection", "continue": "execute_tool"
269
+ })
270
+
271
+ for node in ["execute_tool", "evaluator", "reflection"]:
272
+ workflow.add_conditional_edges(node, self.checkroutingcondition, {
273
+ "end": END, "continue": "evaluator" if node == "execute_tool" else "execute_tool", "reflection": "reflection"
274
+ })
275
+
276
+ workflow.set_entry_point("planner" if self.plan else "router")
277
+ return workflow.compile()
278
+ def _sanitize_query(self, query: str) -> str:
279
+ """Sanitize input query by removing/replacing problematic characters."""
280
+ replacements = {
281
+ "'": '', # Remove single quotes
282
+ "\\": "", # Remove backslashes
283
+ """: '"', # Replace smart quotes
284
+ """: '"', # Replace smart quotes
285
+ '"': "" # Remove double quotes
286
+ }
287
+
288
+ sanitized = str(query)
289
+ for old, new in replacements.items():
290
+ sanitized = sanitized.replace(old, new)
291
+ return sanitized
292
+
293
+ def initiate_agent(self, query: str):
294
+ new_query = self._sanitize_query(query)
295
+ if self.logger:
296
+ self.logger.debug(self.agent_name)
297
+ initial_state = State(
298
+ messages=[{"role": "user", "content": new_query}],
299
+ current_tool="",
300
+ tool_input="",
301
+ tool_output="",
302
+ answer="",
303
+ satisfied=False,
304
+ reasoning="",
305
+ delegate_to_agent=None,
306
+ current_node='router',
307
+ previous_node=None,
308
+ )
309
+ response = self.app.invoke(initial_state, {"recursion_limit": 100})
310
+ return response
311
+
312
+
313
+
314
+
315
+
316
+
@@ -0,0 +1 @@
1
+ from .generativeModels import GenerativeModel, MASGenerativeModel