Examples

This section provides examples of how to use pymailai in different scenarios.

OpenAI Integration

Basic Example

This example shows how to create a simple email agent that processes incoming emails using OpenAI’s GPT model and sends back AI-generated responses.

OpenAI Email Agent Example
 1"""Example of using PyMailAI with OpenAI's completion API."""
 2
 3import asyncio
 4import os
 5from typing import Optional
 6
 7from openai import OpenAI
 8from pymailai import EmailAgent, EmailConfig
 9from pymailai.message import EmailData
10
11# Configure OpenAI
12client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
13
14
15async def process_with_openai(message: EmailData) -> Optional[EmailData]:
16    """Process email content using OpenAI's completion API.
17
18    This example shows how to:
19    1. Extract the prompt from an incoming email
20    2. Send it to OpenAI's API
21    3. Send the response back via email
22    """
23    try:
24        # Get completion from OpenAI
25        completion = client.chat.completions.create(
26            model="gpt-4o-mini",
27            messages=[
28                {"role": "system", "content": "You are a helpful assistant."},
29                {"role": "user", "content": message.body_text}
30            ]
31        )
32
33        # Extract the response
34        ai_response = completion.choices[0].message.content
35
36        # Create email response
37        return EmailData(
38            message_id="",  # Will be generated by email server
39            subject=f"Re: {message.subject}",
40            from_address=message.to_addresses[0],  # Reply from the recipient
41            to_addresses=[message.from_address],   # Reply to the sender
42            cc_addresses=[],
43            body_text=ai_response,
44            body_html=None,
45            timestamp=message.timestamp,
46            in_reply_to=message.message_id,
47            references=[message.message_id] if message.references is None
48            else message.references + [message.message_id]
49        )
50    except Exception as e:
51        print(f"Error processing message: {e}")
52        return None
53
54
55async def main():
56    # Configure email settings
57    config = EmailConfig(
58        imap_server=os.getenv("IMAP_SERVER", "imap.gmail.com"),
59        smtp_server=os.getenv("SMTP_SERVER", "smtp.gmail.com"),
60        email=os.getenv("EMAIL_ADDRESS"),
61        password=os.getenv("EMAIL_PASSWORD"),
62        smtp_port=465  # Use implicit SSL/TLS port
63    )
64
65    # Create and run email agent
66    async with EmailAgent(config, message_handler=process_with_openai) as agent:
67        print(f"OpenAI Email Agent started. Monitoring {config.email}")
68        print("Send an email to get an AI response!")
69        print("Press Ctrl+C to stop...")
70
71        try:
72            while True:
73                await asyncio.sleep(1)
74        except KeyboardInterrupt:
75            print("\nStopping email agent...")
76
77if __name__ == "__main__":
78    asyncio.run(main())

Advanced Example with Email Threading

This example shows how to create an email agent that maintains proper email threading, including quoted original messages and correct threading metadata.

OpenAI Email Agent with Threading Example
 1"""Example of using PyMailAI with OpenAI's completion API and proper email threading."""
 2
 3import asyncio
 4import os
 5from typing import Optional
 6from dotenv import load_dotenv
 7
 8from openai import OpenAI
 9from pymailai import EmailAgent, EmailConfig
10from pymailai.message import EmailData
11
12# Load environment variables
13load_dotenv()
14
15# Configure OpenAI
16client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
17
18
19async def process_with_openai(message: EmailData) -> Optional[EmailData]:
20    """Process email content using OpenAI's completion API.
21
22    This example shows how to:
23    1. Extract the prompt from an incoming email
24    2. Send it to OpenAI's API
25    3. Send the response back via email with proper threading
26    """
27    try:
28        # Get completion from OpenAI
29        completion = client.chat.completions.create(
30            model="gpt-4o-mini",
31            messages=[
32                {"role": "system", "content": "You are a helpful assistant."},
33                {"role": "user", "content": message.body_text}
34            ]
35        )
36
37        # Extract the response
38        ai_response = completion.choices[0].message.content
39
40        # Create a properly threaded reply that includes the original message
41        reply = message.create_reply(
42            reply_text=ai_response,
43            include_history=True  # This ensures the original message is quoted
44        )
45
46        # Set the from address since create_reply doesn't set it
47        reply.from_address = message.to_addresses[0]
48
49        return reply
50
51    except Exception as e:
52        print(f"Error processing message: {e}")
53        return None
54
55
56async def main():
57    # Configure email settings
58    config = EmailConfig(
59        imap_server=os.getenv("IMAP_SERVER"),
60        smtp_server=os.getenv("SMTP_SERVER"),
61        email=os.getenv("EMAIL_ADDRESS"),
62        password=os.getenv("EMAIL_PASSWORD"),
63        smtp_port=465
64    )
65
66    # Create and run email agent
67    async with EmailAgent(config, message_handler=process_with_openai) as agent:
68        print(f"OpenAI Email Agent started. Monitoring {config.email}")
69        print("Send an email to get an AI response with proper threading!")
70        print("The response will include the original message and appear")
71        print("properly threaded in your email client.")
72        print("Press Ctrl+C to stop...")
73
74        try:
75            while True:
76                await asyncio.sleep(1)
77        except KeyboardInterrupt:
78            print("\nStopping email agent...")
79
80if __name__ == "__main__":
81    asyncio.run(main())

Key features of the threading example:

  • Uses create_reply() to generate properly formatted responses

  • Includes the original message as quoted text

  • Sets correct threading metadata (References and In-Reply-To headers)

  • Ensures emails appear properly threaded in email clients

Email Threading Best Practices

When creating email responses, it’s important to maintain proper threading for a better user experience:

  1. Include the original message as quoted text using create_reply()

  2. Preserve threading metadata for email client compatibility

  3. Maintain conversation context with proper message quoting

  4. Keep CC recipients in the loop

Example of creating a threaded reply:

# Create a properly threaded reply
reply = message.create_reply(
    reply_text="Your response here",
    include_history=True  # Include quoted original message
)
reply.from_address = "your-email@example.com"

Running the Examples

To run these examples:

  1. Install the required dependencies:

    pip install pymailai[openai]
    
  2. Set up the required environment variables:

    export OPENAI_API_KEY="your-openai-api-key"
    export EMAIL_ADDRESS="your-email@example.com"
    export EMAIL_PASSWORD="your-email-password"
    # Optional: Configure custom email servers
    export EMAIL_IMAP_SERVER="imap.gmail.com"
    export EMAIL_SMTP_SERVER="smtp.gmail.com"
    
  3. Run the example:

    python examples/openai_completion.py
    

The agent will monitor the specified email account and:

  1. Process any new incoming emails

  2. Send the email content to OpenAI’s API

  3. Send back the AI-generated response to the original sender

Anthropic Integration

This example shows how to create an email agent that processes incoming emails using Anthropic’s Claude model and sends back AI-generated responses.

Anthropic Email Agent Example
 1"""Example of using PyMailAI with Anthropic's Claude API."""
 2
 3import asyncio
 4import os
 5from typing import Optional
 6
 7import anthropic
 8from pymailai import EmailAgent, EmailConfig
 9from pymailai.message import EmailData
10
11# Configure Anthropic client
12client = anthropic.AsyncAnthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
13
14async def process_with_anthropic(message: EmailData) -> Optional[EmailData]:
15    """Process email content using Anthropic's Claude API.
16
17    This example shows how to:
18    1. Extract the prompt from an incoming email
19    2. Send it to Anthropic's API
20    3. Send the response back via email
21    """
22    try:
23        # Get completion from Anthropic
24        message_content = await client.messages.create(
25            model="claude-3-opus-20240229",
26            max_tokens=1024,
27            messages=[
28                {"role": "user", "content": message.body_text}
29            ]
30        )
31
32        # Extract the response
33        ai_response = message_content.content[0].text
34
35        # Create email response
36        return EmailData(
37            message_id="",  # Will be generated by email server
38            subject=f"Re: {message.subject}",
39            from_address=message.to_addresses[0],  # Reply from the recipient
40            to_addresses=[message.from_address],   # Reply to the sender
41            cc_addresses=[],
42            body_text=ai_response,
43            body_html=None,
44            timestamp=message.timestamp,
45            in_reply_to=message.message_id,
46            references=[message.message_id] if message.references is None
47                      else message.references + [message.message_id]
48        )
49    except Exception as e:
50        print(f"Error processing message: {e}")
51        return None
52
53async def main():
54    # Configure email settings
55    config = EmailConfig(
56        imap_server=os.getenv("IMAP_SERVER", "imap.gmail.com"),
57        smtp_server=os.getenv("SMTP_SERVER", "smtp.gmail.com"),
58        email=os.getenv("EMAIL_ADDRESS"),
59        password=os.getenv("EMAIL_PASSWORD")
60    )
61
62    # Create and run email agent
63    async with EmailAgent(config, message_handler=process_with_anthropic) as agent:
64        print(f"Anthropic Email Agent started. Monitoring {config.email}")
65        print("Send an email to get an AI response!")
66        print("Press Ctrl+C to stop...")
67
68        try:
69            while True:
70                await asyncio.sleep(1)
71        except KeyboardInterrupt:
72            print("\nStopping email agent...")
73
74if __name__ == "__main__":
75    asyncio.run(main())

To run this example:

  1. Install the required dependencies:

    pip install pymailai[anthropic]
    
  2. Set up the required environment variables:

    export ANTHROPIC_API_KEY="your-anthropic-api-key"
    export EMAIL_ADDRESS="your-email@example.com"
    export EMAIL_PASSWORD="your-email-password"
    # Optional: Configure custom email servers
    export EMAIL_IMAP_SERVER="imap.gmail.com"
    export EMAIL_SMTP_SERVER="smtp.gmail.com"
    
  3. Run the example:

    python examples/anthropic_completion.py
    

The agent will monitor the specified email account and:

  1. Process any new incoming emails

  2. Send the email content to Anthropic’s API

  3. Send back the AI-generated response to the original sender

Gmail Authentication

pymailai supports two methods for Gmail authentication:

  1. OAuth2 (for personal Gmail accounts)

  2. Service Account (for Google Workspace accounts)

OAuth2 Authentication

This example shows how to use Gmail OAuth2 authentication for personal Gmail accounts.

Gmail OAuth2 Example
 1"""Example of using PyMailAI with Gmail credentials from a file."""
 2
 3import asyncio
 4import os
 5from pathlib import Path
 6
 7from pymailai import EmailAgent
 8from pymailai.gmail import GmailCredentials
 9from pymailai.message import EmailData
10
11async def echo_handler(message: EmailData) -> EmailData:
12    """Simple echo handler that replies with the received message."""
13    return EmailData(
14        message_id="",
15        subject=f"Re: {message.subject}",
16        from_address=message.to_addresses[0],
17        to_addresses=[message.from_address],
18        cc_addresses=[],
19        body_text=f"Received your message:\n\n{message.body_text}",
20        body_html=None,
21        timestamp=message.timestamp,
22        in_reply_to=message.message_id,
23        references=[message.message_id]
24    )
25
26async def main():
27    # Load Gmail credentials from file
28    creds_path = Path.home() / ".config" / "pymailai" / "gmail_creds.json"
29
30    # If credentials file doesn't exist, create it from environment variables
31    if not creds_path.exists():
32        creds_path.parent.mkdir(parents=True, exist_ok=True)
33        creds = GmailCredentials.from_oauth_credentials(
34            client_id=os.getenv("GMAIL_CLIENT_ID", ""),
35            client_secret=os.getenv("GMAIL_CLIENT_SECRET", ""),
36            refresh_token=os.getenv("GMAIL_REFRESH_TOKEN", ""),
37            save_path=creds_path
38        )
39    else:
40        creds = GmailCredentials(creds_path)
41
42    # Convert to EmailConfig
43    email_address = os.getenv("GMAIL_ADDRESS", "")
44    config = creds.to_email_config(email_address)
45
46    # Create and run email agent
47    async with EmailAgent(config, message_handler=echo_handler) as agent:
48        print(f"Gmail Agent started. Monitoring {config.email}")
49        print("Send an email to get an echo response!")
50        print("Press Ctrl+C to stop...")
51
52        try:
53            while True:
54                await asyncio.sleep(1)
55        except KeyboardInterrupt:
56            print("\nStopping email agent...")
57
58if __name__ == "__main__":
59    asyncio.run(main())

To use Gmail with OAuth2 credentials:

  1. Set up a Google Cloud Project and enable the Gmail API:

    1. Go to the Google Cloud Console

    2. Create a new project or select an existing one

    3. Enable the Gmail API

    4. Create OAuth2 credentials: - Click “Create Credentials” and select “OAuth client ID” - Choose “Desktop application” as the application type - Download the credentials JSON file

    5. Configure the OAuth consent screen: - Add your email address as a test user - Set the necessary scopes (gmail.modify, gmail.compose, gmail.send)

  2. Get a refresh token using the provided helper script:

    # First time setup - this will create the credentials file
    export GMAIL_CLIENT_ID="your-client-id"
    export GMAIL_CLIENT_SECRET="your-client-secret"
    export GMAIL_REFRESH_TOKEN="your-refresh-token"
    export GMAIL_ADDRESS="your-gmail@gmail.com"
    
    # Run the example to save credentials
    python examples/gmail_credentials.py
    

Service Account Authentication

This example shows how to use Gmail service account authentication for Google Workspace accounts.

Gmail Service Account Example
  1"""Example of using Gmail with service account authentication."""
  2
  3import asyncio
  4import logging
  5from datetime import datetime
  6from typing import Optional
  7
  8from pymailai.agent import EmailAgent
  9from pymailai.gmail import ServiceAccountCredentials
 10from pymailai.gmail_client import GmailClient
 11from pymailai.message import EmailData
 12
 13# Configure logging
 14logging.basicConfig(level=logging.INFO)
 15logger = logging.getLogger(__name__)
 16
 17# Path to the service account key file downloaded from Google Cloud Console
 18SERVICE_ACCOUNT_FILE = "credentials.json"
 19
 20# The email address of the user to impersonate (must be set up in Google Workspace)
 21USER_EMAIL = "do@example.com"
 22
 23# Scopes required for the Gmail API
 24SCOPES = ["https://www.googleapis.com/auth/gmail.modify"]
 25
 26# Optional: Custom email address for replies (if different from USER_EMAIL)
 27CUSTOM_EMAIL = None  # e.g. "custom@example.com"
 28
 29# Optional: Feedback email configuration
 30FEEDBACK_EMAIL = "feedback@example.com"
 31FEEDBACK_MESSAGE = "\n\nPS: You can always send your feedback to feedback@example.com"
 32
 33
 34async def echo_handler(message: EmailData) -> Optional[EmailData]:
 35    """Handle incoming emails by echoing their content back."""
 36    logger.info(f"Processing email from: {message.from_address}, subject: {message.subject}")
 37
 38    # Create reply with original content
 39    reply_text = f"""
 40I received your email with subject: {message.subject}
 41
 42Here's what you sent:
 43-------------------
 44{message.body_text}
 45-------------------
 46
 47{FEEDBACK_MESSAGE if FEEDBACK_EMAIL else ""}
 48"""
 49
 50    # Create reply email with proper HTML formatting
 51    reply_html = f"""
 52<p>I received your email with subject: {message.subject}</p>
 53
 54<p>Here's what you sent:</p>
 55<blockquote style="border-left: 2px solid #ccc; margin-left: 0; padding-left: 1em;">
 56{message.body_html if message.body_html else message.body_text}
 57</blockquote>
 58
 59{f'<p>{FEEDBACK_MESSAGE}</p>' if FEEDBACK_EMAIL else ''}
 60"""
 61
 62    logger.info("Creating reply email")
 63    reply = EmailData(
 64        message_id="",  # Will be generated by Gmail
 65        subject=f"Re: {message.subject}",
 66        from_address="",  # Will be set by Gmail
 67        to_addresses=[message.from_address],
 68        cc_addresses=[],
 69        body_text=reply_text,
 70        body_html=reply_html,
 71        timestamp=datetime.now(),
 72        in_reply_to=message.message_id
 73    )
 74
 75    logger.info(f"Created reply to: {reply.to_addresses[0]}, subject: {reply.subject}")
 76    return reply
 77
 78
 79async def main():
 80    """Run the email echo service using service account authentication."""
 81    try:
 82        # Load service account credentials and set up delegation
 83        # The service account JSON is downloaded from Google Cloud Console
 84        # and contains fields like:
 85        # {
 86        #   "type": "service_account",
 87        #   "project_id": "your-project",
 88        #   "private_key_id": "...",
 89        #   "private_key": "-----BEGIN PRIVATE KEY-----\n...",
 90        #   "client_email": "service@project.iam.gserviceaccount.com",
 91        #   "client_id": "...",
 92        #   "auth_uri": "https://accounts.google.com/o/oauth2/auth",
 93        #   "token_uri": "https://oauth2.googleapis.com/token",
 94        #   ...
 95        # }
 96
 97        # Set up Gmail service account authentication
 98        creds = ServiceAccountCredentials(
 99            credentials_path=SERVICE_ACCOUNT_FILE,
100            delegated_email=USER_EMAIL,
101            scopes=SCOPES
102        )
103
104        # Create Gmail client
105        service = creds.get_gmail_service()
106        logger.info(f"Gmail service initialized for {USER_EMAIL}")
107
108        client = GmailClient(service)
109        logger.info("Gmail client created")
110
111        # Create and start the email agent
112        agent = EmailAgent(client, message_handler=echo_handler)
113        logger.info("Email agent created with echo handler")
114
115        async with agent:
116            # Keep the agent running
117            while True:
118                await asyncio.sleep(1)
119
120    except KeyboardInterrupt:
121        logger.info("Shutting down email echo service")
122    except Exception as e:
123        logger.error(f"Service error: {e}")
124
125
126if __name__ == "__main__":
127    asyncio.run(main())

To use Gmail with service account authentication:

  1. Set up Google Cloud Project:

    1. Go to the Google Cloud Console

    2. Create a new project or select an existing one

    3. Enable the Gmail API

    4. Create a service account: - Click “Create Service Account” - Download the JSON key file

    5. Configure domain-wide delegation: - Go to Google Workspace Admin Console - Security -> API Controls -> Domain-wide Delegation - Add the service account’s client ID - Add scope: https://www.googleapis.com/auth/gmail.modify

  2. Run the example:

    # Update SERVICE_ACCOUNT_FILE and USER_EMAIL in the example
    python examples/gmail_service_account.py
    

The service account provides: - Better security through domain-wide delegation - No need for user interaction - Support for multiple user impersonation - Direct Gmail API access

Logging Configuration

pymailai includes comprehensive logging for debugging and monitoring. You can configure the logging level and output format:

import logging

# Configure logging for the entire package
logging.basicConfig(
    level=logging.INFO,  # or logging.DEBUG for more detailed output
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# Or configure logging for specific components
logger = logging.getLogger('pymailai.client')
logger.setLevel(logging.DEBUG)

The following components have dedicated loggers:

  • pymailai.client: Email client operations (SMTP/IMAP connections)

  • pymailai.agent: AI agent activities (message processing)

  • pymailai.gmail: Gmail-specific operations (OAuth2, service accounts)

  • pymailai.gmail_client: Gmail API operations

Example log output at INFO level:

2024-03-20 10:15:30,123 - pymailai.gmail - INFO - Gmail service initialized for user@domain.com
2024-03-20 10:15:30,456 - pymailai.gmail_client - INFO - Found 1 unread messages
2024-03-20 10:15:30,789 - pymailai.agent - INFO - Processing message from: sender@example.com
2024-03-20 10:15:31,012 - pymailai.gmail_client - INFO - Message sent successfully with ID: msg-123

Ollama Integration

This example shows how to create an email agent that processes incoming emails using Ollama’s local LLM models and sends back AI-generated responses.

Ollama Email Agent Example
 1"""Example of using PyMailAI with Ollama's chat API."""
 2
 3import asyncio
 4import os
 5from typing import Optional
 6
 7from ollama import chat, ChatResponse
 8from pymailai import EmailAgent, EmailConfig
 9from pymailai.message import EmailData
10
11
12async def process_with_ollama(message: EmailData) -> Optional[EmailData]:
13    """Process email content using Ollama's chat API.
14
15    This example shows how to:
16    1. Extract the prompt from an incoming email
17    2. Send it to Ollama's API
18    3. Send the response back via email
19    """
20    try:
21        # Get completion from Ollama
22        response: ChatResponse = chat(
23            model="llama3.2",  # Latest Llama version
24            messages=[
25                {
26                    "role": "system",
27                    "content": "You are a helpful assistant.",
28                },
29                {
30                    "role": "user",
31                    "content": message.body_text,
32                },
33            ],
34        )
35
36        # Extract the response
37        ai_response = response.message.content
38
39        # Create email response
40        return EmailData(
41            message_id="",  # Will be generated by email server
42            subject=f"Re: {message.subject}",
43            from_address=message.to_addresses[0],  # Reply from the recipient
44            to_addresses=[message.from_address],  # Reply to the sender
45            cc_addresses=[],
46            body_text=ai_response,
47            body_html=None,
48            timestamp=message.timestamp,
49            in_reply_to=message.message_id,
50            references=[message.message_id]
51            if message.references is None
52            else message.references + [message.message_id],
53        )
54    except Exception as e:
55        print(f"Error processing message: {e}")
56        return None
57
58
59async def main():
60    # Configure email settings - requires explicit server configuration
61    config = EmailConfig(
62        imap_server=os.getenv("IMAP_SERVER"),
63        smtp_server=os.getenv("SMTP_SERVER"),
64        email=os.getenv("EMAIL_ADDRESS"),
65        password=os.getenv("EMAIL_PASSWORD"),
66        smtp_port=int(os.getenv("SMTP_PORT", "465")),  # SSL/TLS port by default
67        imap_port=int(os.getenv("IMAP_PORT", "993")),  # SSL/TLS port by default
68    )
69
70    # Create and run email agent
71    async with EmailAgent(config, message_handler=process_with_ollama) as agent:
72        print(f"Ollama Email Agent started. Monitoring {config.email}")
73        print("Send an email to get an AI response!")
74        print("Press Ctrl+C to stop...")
75
76        try:
77            while True:
78                await asyncio.sleep(1)
79        except KeyboardInterrupt:
80            print("\nStopping email agent...")
81
82
83if __name__ == "__main__":
84    asyncio.run(main())

To run this example:

  1. Install the required dependencies:

    pip install pymailai[ollama]
    
  2. Install and start Ollama:

    Follow the instructions at https://ollama.ai to install Ollama for your platform. Then pull your desired model:

    ollama pull llama3.2  # Latest Llama version
    
  3. Set up the required environment variables:

    export EMAIL_ADDRESS="your-email@example.com"
    export EMAIL_PASSWORD="your-email-password"
    export EMAIL_IMAP_SERVER="your-imap-server"
    export EMAIL_SMTP_SERVER="your-smtp-server"
    export EMAIL_IMAP_PORT="993"  # Default SSL/TLS port for IMAP
    export EMAIL_SMTP_PORT="465"  # Default SSL/TLS port for SMTP
    
  4. Run the example:

    python examples/ollama_completion.py
    

The agent will monitor the specified email account and:

  1. Process any new incoming emails

  2. Send the email content to the local Ollama instance

  3. Send back the AI-generated response to the original sender

Simple AI Agent

For a simpler example without external AI providers, check out the examples/simple_ai_agent.py file in the repository. This example shows the basic structure of creating a custom message handler.