From df7bd55dd94d162f776be66480fac299d41b7faa Mon Sep 17 00:00:00 2001
From: sanj <67624670+iodrift@users.noreply.github.com>
Date: Tue, 25 Jun 2024 19:28:02 -0700
Subject: [PATCH] Auto-update: Tue Jun 25 19:28:02 PDT 2024

---
 sijapi/classes.py                |  1 +
 sijapi/config/email.yaml-example |  2 +
 sijapi/routers/email.py          | 88 +++++++++++++++-----------------
 3 files changed, 44 insertions(+), 47 deletions(-)

diff --git a/sijapi/classes.py b/sijapi/classes.py
index 809c8f8..e572c6c 100644
--- a/sijapi/classes.py
+++ b/sijapi/classes.py
@@ -65,6 +65,7 @@ class AutoResponder(BaseModel):
     name: str
     style: str
     context: str
+    ollama_model: str = "llama3"
     whitelist: List[str]
     blacklist: List[str]
     image_prompt: Optional[str] = None
diff --git a/sijapi/config/email.yaml-example b/sijapi/config/email.yaml-example
index c4ad15f..31e16ac 100644
--- a/sijapi/config/email.yaml-example
+++ b/sijapi/config/email.yaml-example
@@ -18,6 +18,7 @@ accounts:
       - name: work
         style: professional
         context: he is currently on leave and will return in late July
+        ollama_model: llama3
         whitelist:
           - '@work.org'
         blacklist:
@@ -56,6 +57,7 @@ accounts:
     autoresponders:
       - name: ai
         style: cryptic
+        ollama_model: llama3
         context: respond to any inquiries with cryptic and vaguely menacing riddles, esoteric assertions, or obscure references.
         image_prompt: using visually evocative words, phrases, and sentence fragments, describe an image inspired by the following prompt
         whitelist:
diff --git a/sijapi/routers/email.py b/sijapi/routers/email.py
index a1179e9..ca27520 100644
--- a/sijapi/routers/email.py
+++ b/sijapi/routers/email.py
@@ -28,7 +28,7 @@ from sijapi import DEBUG, INFO, WARN, ERR, CRITICAL
 from sijapi import PODCAST_DIR, DEFAULT_VOICE, EMAIL_CONFIG
 from sijapi.routers import tts, llm, sd, locate
 from sijapi.utilities import clean_text, assemble_journal_path, extract_text, prefix_lines
-from sijapi.classes import EmailAccount, IMAPConfig, SMTPConfig, IncomingEmail, EmailContact
+from sijapi.classes import EmailAccount, IMAPConfig, SMTPConfig, IncomingEmail, EmailContact, AutoResponder
 
 
 email = APIRouter(tags=["private"])
@@ -39,19 +39,19 @@ def load_email_accounts(yaml_path: str) -> List[EmailAccount]:
     return [EmailAccount(**account) for account in config['accounts']]
 
 
-def get_account_by_email(email: str) -> Optional[EmailAccount]:
+def get_account_by_email(this_email: str) -> Optional[EmailAccount]:
     email_accounts = load_email_accounts(EMAIL_CONFIG)
     for account in email_accounts:
-        if account.imap.username.lower() == email.lower():
+        if account.imap.username.lower() == this_email.lower():
             return account
     return None
 
-def get_imap_details(email: str) -> Optional[IMAPConfig]:
-    account = get_account_by_email(email)
+def get_imap_details(this_email: str) -> Optional[IMAPConfig]:
+    account = get_account_by_email(this_email)
     return account.imap if account else None
 
-def get_smtp_details(email: str) -> Optional[SMTPConfig]:
-    account = get_account_by_email(email)
+def get_smtp_details(this_email: str) -> Optional[SMTPConfig]:
+    account = get_account_by_email(this_email)
     return account.smtp if account else None
 
 
@@ -75,55 +75,49 @@ def get_smtp_connection(account: EmailAccount):
     else:
         return SMTP(account.smtp.host, account.smtp.port)
 
-def get_matching_autoresponders(email: IncomingEmail, account: EmailAccount) -> List[Dict]:
-    matching_profiles = []
 
-    def matches_list(item: str, email: IncomingEmail) -> bool:
+
+
+def get_matching_autoresponders(this_email: IncomingEmail, account: EmailAccount) -> List[AutoResponder]:
+    def matches_list(item: str, this_email: IncomingEmail) -> bool:
         if '@' in item:
-            return item in email.sender
+            return item in this_email.sender
         else:
-            return item.lower() in email.subject.lower() or item.lower() in email.body.lower()
-
+            return item.lower() in this_email.subject.lower() or item.lower() in this_email.body.lower()
+    matching_profiles = []
     for profile in account.autoresponders:
-        whitelist_match = not profile.whitelist or any(matches_list(item, email) for item in profile.whitelist)
-        blacklist_match = any(matches_list(item, email) for item in profile.blacklist)
-
+        whitelist_match = not profile.whitelist or any(matches_list(item, this_email) for item in profile.whitelist)
+        blacklist_match = any(matches_list(item, this_email) for item in profile.blacklist)
         if whitelist_match and not blacklist_match:
-            matching_profiles.append({
-                'USER_FULLNAME': account.fullname,
-                'RESPONSE_STYLE': profile.style,
-                'AUTORESPONSE_CONTEXT': profile.context,
-                'IMG_GEN_PROMPT': profile.image_prompt,
-                'USER_BIO': account.bio
-            })
-
+            matching_profiles.append(profile)
     return matching_profiles
 
 
-async def generate_auto_response_body(email: IncomingEmail, profile: Dict) -> str:
+
+async def generate_auto_response_body(this_email: IncomingEmail, profile: AutoResponder, account: EmailAccount) -> str:
     now = await locate.localize_datetime(dt_datetime.now())
-    then = await locate.localize_datetime(email.datetime_received)
+    then = await locate.localize_datetime(this_email.datetime_received)
     age = now - then
     usr_prompt = f'''
-Generate a personalized auto-response to the following email:
-From: {email.sender}
-Sent: {age} ago
-Subject: "{email.subject}"
-Body:
-{email.body}
-
-Respond on behalf of {profile['USER_FULLNAME']}, who is unable to respond personally because {profile['AUTORESPONSE_CONTEXT']}.
-Keep the response {profile['RESPONSE_STYLE']} and to the point, but responsive to the sender's inquiry.
-Do not mention or recite this context information in your response.
-'''
-    
-    sys_prompt = f"You are an AI assistant helping {profile['USER_FULLNAME']} with email responses. {profile['USER_FULLNAME']} is described as: {profile['USER_BIO']}"
-    
+    Generate a personalized auto-response to the following email:
+    From: {this_email.sender}
+    Sent: {age} ago
+    Subject: "{this_email.subject}"
+    Body:
+    {this_email.body}
+    Respond on behalf of {account.fullname}, who is unable to respond personally because {profile.context}.
+    Keep the response {profile.style} and to the point, but responsive to the sender's inquiry.
+    Do not mention or recite this context information in your response.
+    '''
+    sys_prompt = f"You are an AI assistant helping {account.fullname} with email responses. {account.fullname} is described as: {account.bio}"
     try:
-        response = await llm.query_ollama(usr_prompt, sys_prompt, 400)
+        # async def query_ollama(usr: str, sys: str = LLM_SYS_MSG, model: str = DEFAULT_LLM, max_tokens: int = 200):
+        response = await llm.query_ollama(usr_prompt, sys_prompt, profile.ollama_model, 400)
+
         DEBUG(f"query_ollama response: {response}")
         
         if isinstance(response, str):
+            response += "\n\n"
             return response
         elif isinstance(response, dict):
             if "message" in response and "content" in response["message"]:
@@ -138,7 +132,7 @@ Do not mention or recite this context information in your response.
         
     except Exception as e:
         ERR(f"Error generating auto-response: {str(e)}")
-        return f"Thank you for your email regarding '{email.subject}'. We are currently experiencing technical difficulties with our auto-response system. We will review your email and respond as soon as possible. We apologize for any inconvenience."
+        return f"Thank you for your email regarding '{this_email.subject}'. We are currently experiencing technical difficulties with our auto-response system. We will review your email and respond as soon as possible. We apologize for any inconvenience."
 
 
 def clean_email_content(html_content):
@@ -252,7 +246,7 @@ tags:
             
         markdown_content += f'''
 ---
-{email.body}
+{this_email.body}
 '''
             
         with open(md_path, 'w', encoding='utf-8') as md_file:
@@ -269,9 +263,9 @@ tags:
 async def autorespond(this_email: IncomingEmail, account: EmailAccount):
     matching_profiles = get_matching_autoresponders(this_email, account)
     for profile in matching_profiles:
-        DEBUG(f"Auto-responding to {this_email.subject} with profile: {profile['USER_FULLNAME']}")
+        DEBUG(f"Auto-responding to {this_email.subject} with profile: {profile.name}")
         auto_response_subject = f"Auto-Response Re: {this_email.subject}"
-        auto_response_body = await generate_auto_response_body(this_email, profile)
+        auto_response_body = await generate_auto_response_body(this_email, profile, account)
         DEBUG(f"Auto-response: {auto_response_body}")
         await send_auto_response(this_email.sender, auto_response_subject, auto_response_body, profile, account)
 
@@ -284,8 +278,8 @@ async def send_auto_response(to_email, subject, body, profile, account):
         message['Subject'] = subject
         message.attach(MIMEText(body, 'plain'))
 
-        if profile['IMG_GEN_PROMPT']:
-            jpg_path = await sd.workflow(profile['IMG_GEN_PROMPT'], earlyout=False, downscale_to_fit=True)
+        if profile.image_prompt:
+            jpg_path = await sd.workflow(profile.image_prompt, earlyout=False, downscale_to_fit=True)
             if jpg_path and os.path.exists(jpg_path):
                 with open(jpg_path, 'rb') as img_file:
                     img = MIMEImage(img_file.read(), name=os.path.basename(jpg_path))