@@ -119,11 +119,12 @@ def __init__(
119119 logger .info ("Email agent initialized successfully" )
120120
121121 def _init_agent (self ):
122- """Initialize the ToolCallingAgent with Azure OpenAI."""
123- # Initialize the model with routing capabilities
124- self .routed_model = RoutedLiteLLMModel () # Store as instance variable to update handle later
122+ """
123+ Initialize the ToolCallingAgent with Azure OpenAI.
124+ """
125+ # Initialize the routed model with the default model group
126+ self .routed_model = RoutedLiteLLMModel ()
125127
126- # Initialize the agent
127128 self .agent = ToolCallingAgent (
128129 model = self .routed_model ,
129130 tools = self .available_tools ,
@@ -138,7 +139,12 @@ def _init_agent(self):
138139 logger .debug ("Agent initialized with routed model configuration" )
139140
140141 def _initialize_search_tools (self ) -> SearchWithFallbackTool :
141- """Initializes and configures the search tools, returning the SearchWithFallbackTool."""
142+ """
143+ Initializes and configures the search tools, returning the SearchWithFallbackTool.
144+
145+ Returns:
146+ SearchWithFallbackTool: The configured search tool with Bing and DuckDuckGo as primary engines and Google as fallback.
147+ """
142148 bing_search_tool = WebSearchTool (engine = "bing" , max_results = 5 )
143149 logger .debug ("Initialized WebSearchTool with Bing engine." )
144150
@@ -167,6 +173,25 @@ def _initialize_search_tools(self) -> SearchWithFallbackTool:
167173 logger .info (f"Initialized SearchWithFallbackTool. Primary engines: { primary_names } , Fallback: { fallback_name } " )
168174 return search_tool
169175
176+ def _get_required_actions (self , mode : str ) -> List [str ]:
177+ """
178+ Get list of required actions based on mode.
179+
180+ Args:
181+ mode: The mode of operation (e.g., "summary", "reply", "research", "full")
182+
183+ Returns:
184+ List[str]: List of actions to be performed by the agent
185+ """
186+ actions = []
187+ if mode in ["summary" , "full" ]:
188+ actions .append ("Generate summary" )
189+ if mode in ["reply" , "full" ]:
190+ actions .append ("Generate reply" )
191+ if mode in ["research" , "full" ]:
192+ actions .append ("Conduct research" )
193+ return actions
194+
170195 def _initialize_google_search_tool (self ) -> Optional [GoogleSearchTool ]:
171196 """
172197 Initialize Google search tool with either SerpAPI or Serper provider.
@@ -195,7 +220,15 @@ def _initialize_google_search_tool(self) -> Optional[GoogleSearchTool]:
195220 return None
196221
197222 def _initialize_deep_research_tool (self , enable_deep_research : bool ) -> Optional [DeepResearchTool ]:
198- """Initializes the DeepResearchTool if API key is available."""
223+ """
224+ Initializes the DeepResearchTool if API key is available.
225+
226+ Args:
227+ enable_deep_research: Flag to enable deep research functionality
228+
229+ Returns:
230+ Optional[DeepResearchTool]: Initialized DeepResearchTool instance or None if API key is not found
231+ """
199232 research_tool : Optional [DeepResearchTool ] = None
200233 if os .getenv ("JINA_API_KEY" ):
201234 research_tool = DeepResearchTool ()
@@ -210,7 +243,18 @@ def _initialize_deep_research_tool(self, enable_deep_research: bool) -> Optional
210243 return research_tool
211244
212245 def _create_task (self , email_request : EmailRequest , email_instructions : ProcessingInstructions ) -> str :
213- """Create a task description for the agent based on email handle instructions."""
246+ """
247+ Create a task description for the agent based on email handle instructions.
248+
249+ Args:
250+ email_request: EmailRequest instance containing email data
251+ email_instructions: EmailHandleInstructions object containing processing configuration
252+
253+ Returns:
254+ str: The task description for the agent
255+ """
256+
257+ # process attachments if specified
214258 attachments = self ._format_attachments (email_request .attachments ) \
215259 if email_instructions .process_attachments and email_request .attachments else []
216260
@@ -224,14 +268,31 @@ def _create_task(self, email_request: EmailRequest, email_instructions: Processi
224268 )
225269
226270 def _format_attachments (self , attachments : List [EmailAttachment ]) -> List [str ]:
227- """Format attachment details for inclusion in the task."""
271+ """
272+ Format attachment details for inclusion in the task.
273+
274+ Args:
275+ attachments: List of EmailAttachment objects
276+
277+ Returns:
278+ List[str]: Formatted attachment details
279+ """
228280 return [
229281 f"- { att .filename } (Type: { att .contentType } , Size: { att .size } bytes)\n "
230282 f' EXACT FILE PATH: "{ att .path } "'
231283 for att in attachments
232284 ]
233285 def _create_email_context (self , email_request : EmailRequest , attachment_details = None ) -> str :
234- """Generate context information from the email request."""
286+ """
287+ Generate context information from the email request.
288+
289+ Args:
290+ email_request: EmailRequest instance containing email data
291+ attachment_details: List of formatted attachment details
292+
293+ Returns:
294+ str: The context information for the agent
295+ """
235296 recipients = ", " .join (email_request .recipients ) if email_request .recipients else "N/A"
236297 attachments_info = f"Available Attachments:\n { chr (10 ).join (attachment_details )} " if attachment_details else "No attachments provided."
237298
@@ -248,7 +309,15 @@ def _create_email_context(self, email_request: EmailRequest, attachment_details=
248309 """
249310
250311 def _create_attachment_task (self , attachment_details : List [str ]) -> str :
251- """Return instructions for processing attachments, if any."""
312+ """
313+ Return instructions for processing attachments, if any.
314+
315+ Args:
316+ attachment_details: List of formatted attachment details
317+
318+ Returns:
319+ str: Instructions for processing attachments
320+ """
252321 return f"Process these attachments:\n { chr (10 ).join (attachment_details )} " if attachment_details else ""
253322
254323 def _create_task_template (
@@ -260,7 +329,22 @@ def _create_task_template(
260329 deep_research_mandatory : bool = False ,
261330 output_template : str = "" ,
262331 ) -> str :
263- """Combine all task components into the final task description."""
332+ """
333+ Combine all task components into the final task description.
334+
335+ Args:
336+ handle: The email handle being processed.
337+ email_context: The context information extracted from the email.
338+ handle_specific_template: Any specific template for the handle.
339+ attachment_task: Instructions for processing attachments.
340+ deep_research_mandatory: Flag indicating if deep research is mandatory.
341+ output_template: The output template to use.
342+
343+ Returns:
344+ str: The complete task description for the agent.
345+ """
346+
347+ # Merge the task components into a single string by listing the sections
264348 sections = [
265349 f"Process this email according to the '{ handle } ' instruction type.\n " ,
266350 email_context ,
@@ -330,7 +414,6 @@ def _process_agent_result(self, final_answer_obj: Any, agent_steps: List) -> Dic
330414 )
331415 tool_name = None # Reset tool_name if extraction failed
332416
333- # Revised Output Extraction
334417 action_out = getattr (step , "action_output" , None )
335418 obs_out = getattr (step , "observations" , None )
336419
@@ -422,7 +505,6 @@ def _process_agent_result(self, final_answer_obj: Any, agent_steps: List) -> Dic
422505 logger .debug (f"[Memory Step { i + 1 } ] Matched tool: deep_research" )
423506 try :
424507 if isinstance (tool_output , dict ):
425- # Store the primary findings content
426508 research_findings_content = tool_output .get ("findings" , "" )
427509 # Store metadata separately
428510 research_metadata = {
@@ -533,7 +615,6 @@ def _process_agent_result(self, final_answer_obj: Any, agent_steps: List) -> Dic
533615
534616 # --- Format the selected content ---
535617 if email_body_content :
536- # Remove signature remnants before formatting
537618 signature_markers = [
538619 "Best regards,\n MXtoAI Assistant" ,
539620 "Best regards," ,
@@ -552,7 +633,6 @@ def _process_agent_result(self, final_answer_obj: Any, agent_steps: List) -> Dic
552633 ).strip ()
553634 logger .debug ("Removed potential signature remnants from email body content." )
554635
555- # Format using ReportFormatter
556636 result ["email_content" ]["text" ] = self .report_formatter .format_report (
557637 email_body_content , format_type = "text" , include_signature = True
558638 )
@@ -637,13 +717,10 @@ def process_email(
637717
638718 """
639719 try :
640- # Update the model's current handle
720+ # create task
641721 self .routed_model .current_handle = email_instructions
642-
643- # Create task with specific instructions
644722 task = self ._create_task (email_request , email_instructions )
645723
646- # Run the agent
647724 try :
648725 logger .info ("Starting agent execution..." )
649726 final_answer_obj = self .agent .run (task )
@@ -670,7 +747,7 @@ def process_email(
670747 if not processed_result .get ("email_content" ) or not processed_result ["email_content" ].get ("text" ):
671748 msg = "No reply text was generated by _process_agent_result"
672749 logger .error (msg )
673- # Populate errors within the existing structure if possible
750+
674751 if "metadata" not in processed_result :
675752 processed_result ["metadata" ] = {}
676753 if "errors" not in processed_result ["metadata" ]:
@@ -680,7 +757,7 @@ def process_email(
680757 processed_result ["metadata" ]["email_sent" ] = {}
681758 processed_result ["metadata" ]["email_sent" ]["status" ] = "error"
682759 processed_result ["metadata" ]["email_sent" ]["error" ] = msg
683- # Return the partially processed result with error flags
760+
684761 return processed_result
685762
686763 logger .info (f"Email processed successfully with handle: { email_instructions .handle } " )
0 commit comments