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.
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.
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 responsesIncludes 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:
Include the original message as quoted text using
create_reply()
Preserve threading metadata for email client compatibility
Maintain conversation context with proper message quoting
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:
Install the required dependencies:
pip install pymailai[openai]
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"
Run the example:
python examples/openai_completion.py
The agent will monitor the specified email account and:
Process any new incoming emails
Send the email content to OpenAI’s API
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.
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:
Install the required dependencies:
pip install pymailai[anthropic]
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"
Run the example:
python examples/anthropic_completion.py
The agent will monitor the specified email account and:
Process any new incoming emails
Send the email content to Anthropic’s API
Send back the AI-generated response to the original sender
Gmail Authentication
pymailai supports two methods for Gmail authentication:
OAuth2 (for personal Gmail accounts)
Service Account (for Google Workspace accounts)
OAuth2 Authentication
This example shows how to use Gmail OAuth2 authentication for personal Gmail accounts.
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:
Set up a Google Cloud Project and enable the Gmail API:
Go to the Google Cloud Console
Create a new project or select an existing one
Enable the Gmail API
Create OAuth2 credentials: - Click “Create Credentials” and select “OAuth client ID” - Choose “Desktop application” as the application type - Download the credentials JSON file
Configure the OAuth consent screen: - Add your email address as a test user - Set the necessary scopes (gmail.modify, gmail.compose, gmail.send)
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.
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:
Set up Google Cloud Project:
Go to the Google Cloud Console
Create a new project or select an existing one
Enable the Gmail API
Create a service account: - Click “Create Service Account” - Download the JSON key file
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
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.
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:
Install the required dependencies:
pip install pymailai[ollama]
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
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
Run the example:
python examples/ollama_completion.py
The agent will monitor the specified email account and:
Process any new incoming emails
Send the email content to the local Ollama instance
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.