""" AI Email Analysis Service — Barrel Re-export Split into: - mail/ai_sender.py — Sender classification (domain + LLM) - mail/ai_deadline.py — Deadline extraction (regex + LLM) - mail/ai_category.py — Category classification + response suggestions The AIEmailService class and get_ai_email_service() are defined here to maintain the original public API. """ import logging from typing import Optional, List, Tuple from datetime import datetime import httpx from .models import ( EmailCategory, SenderType, TaskPriority, SenderClassification, DeadlineExtraction, EmailAnalysisResult, ResponseSuggestion, get_priority_from_sender_type, ) from .mail_db import update_email_ai_analysis from .ai_sender import classify_sender, LLM_GATEWAY_URL from .ai_deadline import extract_deadlines from .ai_category import classify_category, suggest_response logger = logging.getLogger(__name__) class AIEmailService: """ AI-powered email analysis service. Features: - Domain-based sender classification (fast, no LLM) - LLM-based sender classification (fallback) - Deadline extraction using regex + LLM - Category classification - Response suggestions """ def __init__(self): self._http_client = None async def get_http_client(self) -> httpx.AsyncClient: """Get or create HTTP client for LLM gateway.""" if self._http_client is None: self._http_client = httpx.AsyncClient(timeout=30.0) return self._http_client async def classify_sender( self, sender_email: str, sender_name: Optional[str] = None, subject: Optional[str] = None, body_preview: Optional[str] = None, ) -> SenderClassification: """Classify the sender of an email.""" client = await self.get_http_client() return await classify_sender( client, sender_email, sender_name, subject, body_preview ) async def extract_deadlines( self, subject: str, body_text: str, ) -> List[DeadlineExtraction]: """Extract deadlines from email content.""" client = await self.get_http_client() return await extract_deadlines(client, subject, body_text) async def classify_category( self, subject: str, body_preview: str, sender_type: SenderType, ) -> Tuple[EmailCategory, float]: """Classify email into a category.""" client = await self.get_http_client() return await classify_category(client, subject, body_preview, sender_type) async def analyze_email( self, email_id: str, sender_email: str, sender_name: Optional[str], subject: str, body_text: Optional[str], body_preview: Optional[str], ) -> EmailAnalysisResult: """Run full analysis pipeline on an email.""" # 1. Classify sender sender_classification = await self.classify_sender( sender_email, sender_name, subject, body_preview ) # 2. Extract deadlines deadlines = await self.extract_deadlines(subject, body_text or "") # 3. Classify category category, category_confidence = await self.classify_category( subject, body_preview or "", sender_classification.sender_type ) # 4. Determine priority suggested_priority = get_priority_from_sender_type(sender_classification.sender_type) # Upgrade priority if deadlines are found if deadlines: nearest_deadline = min(d.deadline_date for d in deadlines) days_until = (nearest_deadline - datetime.now()).days if days_until <= 1: suggested_priority = TaskPriority.URGENT elif days_until <= 3: suggested_priority = TaskPriority.HIGH elif days_until <= 7: suggested_priority = max(suggested_priority, TaskPriority.MEDIUM) # 5. Summary (optional) summary = None # 6. Determine if task should be auto-created auto_create_task = ( len(deadlines) > 0 or sender_classification.sender_type in [ SenderType.KULTUSMINISTERIUM, SenderType.LANDESSCHULBEHOERDE, SenderType.RLSB, ] ) # 7. Store analysis in database await update_email_ai_analysis( email_id=email_id, category=category.value, sender_type=sender_classification.sender_type.value, sender_authority_name=sender_classification.authority_name, detected_deadlines=[ { "date": d.deadline_date.isoformat(), "description": d.description, "is_firm": d.is_firm, } for d in deadlines ], suggested_priority=suggested_priority.value, ai_summary=summary, ) return EmailAnalysisResult( email_id=email_id, category=category, category_confidence=category_confidence, sender_classification=sender_classification, deadlines=deadlines, suggested_priority=suggested_priority, summary=summary, suggested_actions=[], auto_create_task=auto_create_task, ) async def suggest_response( self, subject: str, body_text: str, sender_type: SenderType, category: EmailCategory, ) -> List[ResponseSuggestion]: """Generate response suggestions for an email.""" client = await self.get_http_client() return await suggest_response( client, subject, body_text, sender_type, category ) # Global instance _ai_service: Optional[AIEmailService] = None def get_ai_email_service() -> AIEmailService: """Get or create the global AIEmailService instance.""" global _ai_service if _ai_service is None: _ai_service = AIEmailService() return _ai_service