22import os
33import re
44from datetime import datetime
5- from typing import Any
5+ from typing import Any , List , Dict
66
77from dotenv import load_dotenv
88
1818)
1919
2020from mxtoai ._logging import get_logger
21- from mxtoai .prompts .base_prompts import create_attachment_processing_task , create_email_context , create_task_template
21+ from mxtoai .models import ProcessingInstructions
22+ from mxtoai .prompts .base_prompts import (
23+ LIST_FORMATTING_REQUIREMENTS ,
24+ MARKDOWN_STYLE_GUIDE ,
25+ RESEARCH_GUIDELINES ,
26+ RESPONSE_GUIDELINES ,
27+ )
2228from mxtoai .routed_litellm_model import RoutedLiteLLMModel
23- from mxtoai .schemas import EmailRequest
29+ from mxtoai .schemas import EmailRequest , EmailAttachment
2430from mxtoai .scripts .report_formatter import ReportFormatter
2531from mxtoai .scripts .visual_qa import azure_visualizer
2632from mxtoai .tools .attachment_processing_tool import AttachmentProcessingTool
@@ -131,7 +137,7 @@ def __init__(
131137 )
132138
133139 # Define the list of tools available to the agent
134- self .available_tools : list [Tool ] = [
140+ self .available_tools : List [Tool ] = [
135141 self .attachment_tool ,
136142 self .schedule_tool ,
137143 self .visit_webpage_tool ,
@@ -168,7 +174,7 @@ def _init_agent(self):
168174
169175 logger .debug ("Agent initialized with routed model configuration" )
170176
171- def _get_required_actions (self , mode : str ) -> list [str ]:
177+ def _get_required_actions (self , mode : str ) -> List [str ]:
172178 """Get list of required actions based on mode."""
173179 actions = []
174180 if mode in ["summary" , "full" ]:
@@ -202,7 +208,7 @@ def determine_mode_from_email(to_email: str) -> str:
202208
203209 return mode_mapping .get (email_prefix , "full" )
204210
205- def _get_attachment_types (self , attachments : list [ dict [str , Any ]]) -> list [str ]:
211+ def _get_attachment_types (self , attachments : List [ Dict [str , Any ]]) -> List [str ]:
206212 """Get list of attachment types."""
207213 types = []
208214 for att in attachments :
@@ -211,36 +217,74 @@ def _get_attachment_types(self, attachments: list[dict[str, Any]]) -> list[str]:
211217 types .append (content_type )
212218 return types
213219
214- def _create_task (self , email_request : EmailRequest , email_instructions : "EmailHandleInstructions" ) -> str :
220+ def _create_task (self , email_request : EmailRequest , email_instructions : ProcessingInstructions ) -> str :
215221 """Create a task description for the agent based on email handle instructions."""
216- # Create attachment details with explicit paths if needed
217- attachment_details = []
218- if email_instructions .process_attachments and email_request .attachments :
219- for att in email_request .attachments :
220- # Quote the file path to handle spaces correctly
221- quoted_path = f'"{ att .path } "'
222- attachment_details .append (
223- f"- { att .filename } (Type: { att .contentType } , Size: { att .size } bytes)\n "
224- f" EXACT FILE PATH: { quoted_path } "
225- )
226-
227- # Create the email context section
228- email_context = create_email_context (email_request , attachment_details )
229-
230- # Create attachment processing task if needed
231- attachment_task = create_attachment_processing_task (attachment_details ) if attachment_details else ""
222+ attachments = self ._format_attachments (email_request .attachments ) \
223+ if email_instructions .process_attachments and email_request .attachments else []
232224
233- # Create the complete task template
234- return create_task_template (
225+ return self ._create_task_template (
235226 handle = email_instructions .handle ,
236- email_context = email_context ,
227+ email_context = self . _create_email_context ( email_request , attachments ) ,
237228 handle_specific_template = email_instructions .task_template ,
238- attachment_task = attachment_task ,
229+ attachment_task = self . _create_attachment_task ( attachments ) ,
239230 deep_research_mandatory = email_instructions .deep_research_mandatory ,
240231 output_template = email_instructions .output_template
241232 )
242233
243- def _process_agent_result (self , final_answer_obj : Any , agent_steps : list ) -> dict [str , Any ]:
234+ def _format_attachments (self , attachments : List [EmailAttachment ]) -> List [str ]:
235+ """Format attachment details for inclusion in the task."""
236+ return [
237+ f"- { att .filename } (Type: { att .contentType } , Size: { att .size } bytes)\n "
238+ f' EXACT FILE PATH: "{ att .path } "'
239+ for att in attachments
240+ ]
241+ def _create_email_context (self , email_request : EmailRequest , attachment_details = None ) -> str :
242+ """Generate context information from the email request."""
243+ recipients = ", " .join (email_request .recipients ) if email_request .recipients else "N/A"
244+ attachments_info = f"Available Attachments:\n { chr (10 ).join (attachment_details )} " if attachment_details else "No attachments provided."
245+
246+ return f"""Email Content:
247+ Subject: { email_request .subject }
248+ From: { email_request .from_email }
249+ Email Date: { email_request .date }
250+ Recipients: { recipients }
251+ CC: { email_request .cc or "N/A" }
252+ BCC: { email_request .bcc or "N/A" }
253+ Body: { email_request .textContent or email_request .htmlContent or "" }
254+
255+ { attachments_info }
256+ """
257+
258+ def _create_attachment_task (self , attachment_details : List [str ]) -> str :
259+ """Return instructions for processing attachments, if any."""
260+ return f"Process these attachments:\n { chr (10 ).join (attachment_details )} " if attachment_details else ""
261+
262+ def _create_task_template (
263+ self ,
264+ handle : str ,
265+ email_context : str ,
266+ handle_specific_template : str = "" ,
267+ attachment_task : str = "" ,
268+ deep_research_mandatory : bool = False ,
269+ output_template : str = "" ,
270+ ) -> str :
271+ """Combine all task components into the final task description."""
272+ sections = [
273+ f"Process this email according to the '{ handle } ' instruction type.\n " ,
274+ email_context ,
275+ RESEARCH_GUIDELINES ["mandatory" ] if deep_research_mandatory else RESEARCH_GUIDELINES ["optional" ],
276+ attachment_task ,
277+ handle_specific_template ,
278+ output_template ,
279+ RESPONSE_GUIDELINES ,
280+ MARKDOWN_STYLE_GUIDE ,
281+ LIST_FORMATTING_REQUIREMENTS
282+ ]
283+
284+ return "\n \n " .join (filter (None , sections )) # Filter out any empty strings
285+
286+
287+ def _process_agent_result (self , final_answer_obj : Any , agent_steps : List ) -> Dict [str , Any ]:
244288 """
245289 Process the agent's result into our expected format, using the agent steps.
246290 Prioritizes direct output from the 'deep_research' tool if available.
@@ -604,7 +648,7 @@ def _process_agent_result(self, final_answer_obj: Any, agent_steps: list) -> dic
604648 def process_email (
605649 self ,
606650 email_request : EmailRequest ,
607- email_instructions : "EmailHandleInstructions" , # Type hint as string to avoid circular import
651+ email_instructions : ProcessingInstructions , # Type hint as string to avoid circular import
608652 ) -> dict [str , Any ]:
609653 """
610654 Process an email using the agent based on the provided email handle instructions.
0 commit comments