Blog

Automating Emails Using AI Agents & Tools: AWS Bedrock, Converse API, and Google OAuth 2.0

Published: Oct 2, 2024 by Sai Jahnavi Bachu, AI/ML Developer

 


 

This not only saves time but also reduces stress during urgent situations when crafting an email might be challenging.

Have you ever found yourself struggling to compose an important email during a stressful or urgent situation? You’re not alone. Many people face challenges when it comes to crafting well-worded emails, especially under pressure. One day, I unexpectedly fell ill and needed to request an emergency sick leave from work. It was my first time doing so, and I had no idea how to properly write an email to inform my manager. Feeling unwell and anxious, I struggled to find the right words. Then it hit me — what if I could use AI to help craft the email for me?

This thought sparked an idea: creating an AI-powered agent that could generate any kind of email based on simple prompts. By leveraging technologies like AI agents, tools, and large language models (LLMs), I realized I could simplify the process of writing important emails, especially in stressful situations. This experience made me understand that I wasn’t alone — many people might struggle with composing emails during unexpected circumstances. To address this, I decided to create an AI-powered email agent that can generate and send emails on our behalf.

To build this AI email agent, I integrated several powerful technologies:

AWS Bedrock’s Large Language Models (LLMs): For generating coherent and contextually appropriate email content.

Converse API: To interact with the Bedrock AI models in a conversational manner.

Google OAuth 2.0: For secure authentication with Gmail, enabling the application to send emails on the user’s behalf.

Let’s dig into the code

Importing required libraries:

import base64
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from email.mime.text import MIMEText
import boto3
from boto3 import client
from datetime import datetime, timedelta, timezone
from botocore.config import Config

Setting Up Google OAuth 2.0 Scope:

SCOPES = ['https://www.googleapis.com/auth/gmail.send']

Google OAuth 2.0 Authentication Function:

To authenticate with Gmail, I created the gmail_authenticate function. This function handles the OAuth 2.0 flow, refreshing tokens as needed, and saving credentials to a token.json file

def gmail_authenticate():
    creds = None
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request()) 
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'client_secrets.json', SCOPES)
            creds = flow.run_local_server(port=0)
        with open('token.json', 'w') as token:
            token.write(creds.to_json())
    return creds
Setting Up Google API Credentials

To get the client secret, I needed to create a project in Google Cloud Console:

Visit Google Cloud Console: Go to https://console.cloud.google.com/ and sign in with your Google account.

Create a New Project: Click the project dropdown at the top, select “New Project”, name it, and click “Create”.

Enable Gmail API: In the left menu, go to APIs & Services > Library, search for “Gmail API”, select it, and click “Enable”.

Configure OAuth Consent Screen: Navigate to APIs & Services > OAuth consent screen, choose “External” as the app is for personal use, and provide the required app details.

Add Scopes: Add the scope https://www.googleapis.com/auth/gmail.send to allow sending emails.

Add Test Users: Under “Test users”, add your email address to grant access during testing.

Create OAuth Client ID: Go to Credentials, click “Create Credentials”, and select “OAuth client ID”.

Set Application Type: Choose “Desktop app”, name it, and click “Create”.

Download Client Secret: After creation, click “Download JSON” to get the client_secrets.json file.

Place Client Secret in Project: Save the client_secrets.json file in your project directory alongside your Python script.

When you run your Python script for the first time, it will prompt you to authenticate. A browser window will open, asking you to log in with your Google account and grant the requested permissions. After granting permission, a token.json file will be created in your directory. This file stores your access and refresh tokens.

Function to Send Emails via Gmail:

Next, I defined the send_email_gmail function, which uses the Gmail API to send emails.

def send_email_gmail(recipient_email, sender_email subject, body):
    creds = gmail_authenticate()
    service = build('gmail', 'v1', credentials=creds)
    message = MIMEText(body)
    message['to'] = recipient_email
    message['from'] = sender_email
    message['subject'] = subject
    raw = base64.urlsafe_b64encode(message.as_bytes()).decode()
    try:
        message = service.users().messages().send(
            userId="me", body={'raw': raw}
        ).execute()
        return(f'Email sent successfully!')
    except Exception as error:
        return(f'An error occurred: {error}')

Defining the Tool Configuration:

I set up the tool configuration to specify how the AI agent should interact with the email-sending functionality.

toolConfig = {
  'tools': [
    {
      'toolSpec': {
        'name': 'email',
        'description': 'Sends personalized emails to the users',            
        'inputSchema': {
          'json': {
            'type': 'object',
            'properties': {
              'receiver_email': {
                'type': 'string',
                'description': 'The email of the receiver. Ask the user to input the receiver email.'
              },
              'sender_email': {
                'type': 'string',
                'description': 'The email of the sender. Ask the user to input the senders email.'
              },
              'subject':{
                'type': 'string',
                'description': 'you should write appropriate email subject depending on the topic.',
              },
              'body':{
                'type': 'string',
                'description': 'you should write appropriate email body depending on the topic.',
              }
            },
            'required': ['receiver_email', 'sender_email', 'subject', 'body']
          }
        }
      }
    },
  ],
  'toolChoice': {
    'auto': {}
  }
}

AWS Bedrock Client Initialization:

At present, only few FM’s support converse API and claude is one of it.

client = boto3.client(
    service_name='bedrock-runtime', 
    region_name='us-east-1',
    config=config
) 
modelId = 'us.anthropic.claude-3-5-sonnet-20240620-v1:0'

Defining the Converse API AI Agent Chat Function:

The simple_chat function handles the conversation with the AI agent, processing user messages, and interacting with the tool when necessary. When the AI agent decides to use the email tool, it provides the necessary inputs as per the tool inputSchema. These inputs are extracted and then passed to the send_email_gmail function.

def simple_chat(user_message):
    system_prompt = """You are a professional email writer. Based on the topic user gives you, you should write email subject and body."""
    if 'chat_messages' not in st.session_state:
        st.session_state.chat_messages = []
  messages = st.session_state.chat_messages
  messages.append({"role": "user", "content": [{"text": user_message}]})
  converse_api_params = {
            "modelId": modelId,
            "system": [{"text": system_prompt}],
            "messages": messages,
            "inferenceConfig": {"maxTokens": 4096 , "temperature": 0.7},
            "toolConfig" : toolConfig,
        }
  response = client.converse(**converse_api_params)
  messages.append({"role": "assistant", "content": response['output']['message']['content']})
  if response['stopReason'] == "tool_use":
    if tool_name == "email":
      tool_result = send_email_gmail(
                  tool_inputs["receiver_email"],
                  tool_inputs["sender_email"],
                  tool_inputs["subject"],
                  tool_inputs["body"]
                  )

      result_content = {
                 "toolResult": {
                 "toolUseId": tool_id,
                  "content": [{"text": str(tool_result)}]
                 }
                 }
      tool_results_content.append(result_content)

       messages.append({
                    "role": "user",
                    "content": tool_results_content
                })
     else:
       st.session_state.chat_messages = messages
       return response['output']['message']['content'][0]['text']

Reach me out on LinkedIn for complete code

Bringing It All to Life with Streamlit, I used Streamlit to create an interactive web interface. This lets me chat with the bot in a user-friendly environment.

Security and Privacy Considerations

It’s important to handle sensitive data responsibly. The application uses OAuth 2.0 for authentication, ensuring that your credentials are securely managed. The client_secrets.json and token.json files should be kept secure and not shared publicly. Additionally, the app only requests the necessary scope (gmail.send) to minimize access to your Google account.

Future Enhancements

Expanding Functionality: Adding features like scheduling emails or integrating calendar invites.

Here is the demo:

View another article