DEV Community

Cover image for ✨I built an AI bot under 65 lines of code that checks my new emails and send people invites📧🚀
Sunil Kumar Dash for Composio

Posted on • Updated on

✨I built an AI bot under 65 lines of code that checks my new emails and send people invites📧🚀

Lately, my calendar has become so hectic that I could really use some help managing it. In fact, I realized it’s a very common problem among my colleagues at Composio.
We get tons of e-mails for multiple events, and managing them has become a nightmare.

I set out to create an automation that could read my incoming emails, understand their content, determine if an event needed to be scheduled, and finally draft an email with the invitation link.

I got an email GIF

Traditional methods wouldn’t be effective for this task, given the challenges involved:

  • Requiring contextual understanding of emails.
  • Needing seamless integration with Gmail and Google Calendar.

I opted for a large language model (LLM) to tackle the first challenge. The real complexity, however, lies in integrating Gmail and Calendar with LLMs, especially since these services can be notoriously finicky.

Now, imagine a library that handles Google OAuth for you, simplifying the integration of API endpoints with LLMs. It would be a game-changer, right?

That's precisely what we offer at Composio.


Composio - The First AI Integration Platform

Here’s a quick introduction about us.

Composio is an open-source tooling infrastructure for building robust and reliable AI applications. We provide over 100+ tools and integrations across industry verticals from CRM, HRM, and Sales to Productivity, Dev, and Social Media.

We handle user authentication and authorization for all these applications and make connecting all the API endpoints with various AI models and frameworks simple. So, you spend more time delivering shareholder value than troubleshooting unnecessary bugs.

Guy Struggling gif

Please help us with a star. 🥹

It would help us to create more articles like this 💖

Star the Composio.dev repository ⭐


LangGraph - Framework for Building Production-ready AI Apps

To complete the project, you will need another dependency, LangGraph.

LangGraph is an open-source framework for building stateful AI workflows. It defines workflows using a modular graphical architecture, making it easier to control each step in the workflow.
Key Components

  • State:
    • A shared data structure that represents the current snapshot of your application.
    • It can be any Python type, but is typically a TypedDict or Pydantic BaseModel.
  • Nodes:
    • Python functions that encode the logic of your agents.
    • They receive the current State as input, perform some computation or side-effect, and return an updated State.
  • Edges:
    • Python functions that determine which Node to execute next based on the current State.
    • They can be conditional branches or fixed transitions.

It is highly recommended that you go through this guide before moving ahead.

In this article, you will build an end-to-end AI Bot that can read incoming emails, process them, decide if they need to be scheduled, and finally draft an invitation email to the recipient with a Calendar link.


Overall Workflow

Let's have a look at the overall workflow.

  • Connect Gmail and Google Calendar with Composio.
  • Enable trigger in Composio to receive mail.
  • Create the AI bot with LangGraph.
  • The bot polls Gmail for incoming emails.
  • The emails are passed to the bot for further analysis.
  • If the email contains event scheduling information:
    • Yes: The bot fetches free slots from the Calendar and drafts a suitable email with a scheduled event invitation link.
    • No: Ignores.

Email Scheduling Agent


Prerequisites to Building the AI Bot

You will need the following requirements to complete the project.

  • Composio user account. Create one here for free.
  • OpenAI API credits. You may use open-source LLMs like Llama from Groq as well.

Let’s Build it!

Setting Up Environment

The first rule of Python development is always to create a virtual environment.

python -m venv gmail-bot

cd gmail-bot
source bit/activate
Enter fullscreen mode Exit fullscreen mode

Now, install the dependencies.

pip install composio-core 
pip install composio-langgraph
pip install python-dotenv

Enter fullscreen mode Exit fullscreen mode
  • composio-core: Core library for all Composio’s functions.
  • composio-langgraph: Composio’s LangGraph plug-in.
  • python-dotenv: To set environment variables from the .env file.

Create a .env file and set the OpenAI API key.

OPENAI_API_KEY="YOUR_KEY"
Enter fullscreen mode Exit fullscreen mode

Integrate Gmail and Google Calendar

To integrate Gmail and Calendar, we will use Composio’s dedicated CLI, but log in to your user account before that.

composio login
Enter fullscreen mode Exit fullscreen mode

Finish the login flow to proceed further. Now, Refresh apps.

composio apps update
Enter fullscreen mode Exit fullscreen mode

Integrate Gmail

composio add gmail
Enter fullscreen mode Exit fullscreen mode

Integrate Google Calendaar

composio add googlecalendar
Enter fullscreen mode Exit fullscreen mode

Finish the integration flow to add these services to your user account.

The best thing about Composio is you can monitor these integrations on your dashboard.

Composio dashboard for integration

Next, enable the trigger to receive emails when someone sends them.

composio triggers enable gmail_new_gmail_message
Enter fullscreen mode Exit fullscreen mode

You can even monitor triggers from the dashboard.


Building the AI Bot

As we discussed before, LangGraph defines workflows with Nodes and Edges.

This workflow will have four key components: START, END, a Tool Node and a Scheduler Node.

  • START: Entry point to the workflow.
  • Tool Node: Handles invoking the tools available to the LLM.
  • Scheduler Node: Manages processing text inputs from emails, tool calls, and more.
  • END: Exit point of the workflow.

When the Scheduler Node determines that a tool needs to be called, it hands over control to the Tool Node, which then executes the tool and sends the results and control back to the Scheduler Node.

Once the execution is complete, the Scheduler Node terminates the process.

Here is a high-level schematic diagram.

LangGraph Workflow diagram

Now, let’s hop on to Coding the workflow.


Importing Libraries

First, import the required libraries and set the environment variable from the .env using.

from typing import Literal, Annotated, Sequence, TypedDict
import operator
import re
from datetime import datetime
from dotenv import load_dotenv
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, ToolMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
from langgraph.graph import END, START, StateGraph
from langgraph.prebuilt import ToolNode
from composio_langgraph import Action, ComposioToolSet
from composio.client.collections import TriggerEventData

# Setup
load_dotenv()
Enter fullscreen mode Exit fullscreen mode

Now, define the constants.

# Constants
SCHEDULER_AGENT_NAME = "Scheduler"
TOOL_NODE_NAME = "ToolNode"
DATE_TIME = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
Enter fullscreen mode Exit fullscreen mode

Defining Tools

Next, we will define the tools and Tool Node that we will use to schedule events and draft emails.

# Initialize LLM and tools
llm = ChatOpenAI(model="gpt-4-turbo")
composio_toolset = ComposioToolSet()

schedule_tools = composio_toolset.get_actions(
    actions=[
        Action.GOOGLECALENDAR_FIND_FREE_SLOTS,
        Action.GOOGLECALENDAR_CREATE_EVENT,
    ]
)
email_tools = composio_toolset.get_actions(actions=[Action.GMAIL_CREATE_EMAIL_DRAFT])
tools = [*schedule_tools, *email_tools]

tool_node = ToolNode(tools)
Enter fullscreen mode Exit fullscreen mode

The above code block,

  • Initializes a Large Language Model (LLM) using OpenAI's GPT-4 Turbo model.
  • Creates an instance of the Composio toolset, which provides access to the Google service integrations.
  • Fetches the actions to find free slots and create events from the Composio toolset related to Google Calendar.
  • Fetches the action to create a draft email in Gmail.
  • Creates a tool node with all the actions to be used in LangGraph.

Note: The Tool Node abstracts away the calling and handling tool calls by the LLM.


Defining Prompts

Next, define the system prompt. This is crucial as it will give the LLM the necessary context to handle incoming mail.

# System prompt
scheduler_system_prompt = f"""
You are an AI assistant specialized in analyzing emails, creating calendar events, and drafting response emails. Your primary tasks are:

1. Analyze email content:
   a. Understand the email received from the sender. 
   b. Determine if an event should be created based on the email content.
   c. Extract relevant information such as proposed meeting times, topics, and participants.

2. Manage calendar events:
   a. If an event should be created, use the Google Calendar Find Free Slots action to identify available time slots.
   b. Once a suitable slot is found, use the Google Calendar Create Event action to schedule the event.
   c. Ensure to invite the email sender and any other relevant participants to the event.

3. Draft response emails:
   a. If an event was created, draft a confirmation email for the sender.
   b. The email should include:
      - A clear subject line (e.g., "Meeting Scheduled")
      - A brief description of the scheduled meeting's purpose
      - The date, time, and duration of the event
      - Any other relevant details or instructions for the participants

Remember:
- The current date and time is {DATE_TIME}.
- All conversations and scheduling occur in the IST timezone.
- Be courteous and professional in all communications.
- If you encounter any errors when making function calls, try passing an empty config ({{"config": {{}}}}).
- Always provide a FINAL ANSWER when you've completed all necessary tasks.

You aim to efficiently manage scheduling and communication, ensuring a smooth experience for all parties involved.
"""
Enter fullscreen mode Exit fullscreen mode

We defined a clear and step-by-step system prompt to steer the LLM.


Defining AgentState and Helper Functions

In LangGraph, the AgentState keeps track of the state of the agentic workflow at any given point.

# Define AgentState
class AgentState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], operator.add]
    sender: str

# Helper functions
def create_agent_node(agent, name):
    def agent_node(state):
        result = agent.invoke(state)
        if not isinstance(result, ToolMessage):
            result = AIMessage(**result.dict(exclude={"type", "name"}), name=name)
        return {"messages": [result], "sender": name}
    return agent_node

def create_agent(system_prompt, tools):
    prompt = ChatPromptTemplate.from_messages([
        ("system", system_prompt),
        MessagesPlaceholder(variable_name="messages"),
    ])
    return prompt | llm.bind_tools(tools)
Enter fullscreen mode Exit fullscreen mode

Here is what is going on,

  • AgentState(TypedDict): Defines a dictionary structure for an agent's state, containing a list of messages and the sender's name.
  • create_agent_node(agent, name): Creates a function that processes an agent's state, invokes the agent, and formats the result as a message with the agent's name as the sender.
  • create_agent(system_prompt, tools): This function constructs an agent by creating a prompt template from system instructions and messages and binding the LLM to the specified tools.

Create an agent using the system prompt, the tools, and the agent node using the scheduler_agent and the constant SCHEDULER_AGENT_NAME.

scheduler_agent = create_agent(scheduler_system_prompt, tools)
scheduler_node = create_agent_node(scheduler_agent, SCHEDULER_AGENT_NAME)
Enter fullscreen mode Exit fullscreen mode

This node handles everything related to scheduling Google Calendar events, from checking slots to confirming events.


Defining the Nodes, Edges, and the Workflow

As we know, LangGraph follows a directed graph structure where each node contains an executable (a code that can be executed) and edges that direct the flow of information.

# Create workflow
workflow = StateGraph(AgentState)

workflow.add_node(SCHEDULER_AGENT_NAME, scheduler_node)
workflow.add_node(TOOL_NODE_NAME, tool_node)

Enter fullscreen mode Exit fullscreen mode

In the above code block,

  • We defined the workflow graph using StateGraph(AgentState). The AgentState keeps track of state objects.
  • Two nodes, tool_node and scheduler_node, were added.

Now, define edges to direct the flow of information.

# Router function
def router(state) -> Literal["call_tool", "__end__", "continue"]:
    last_message = state["messages"][-1]
    if last_message.tool_calls:
        return "call_tool"
    if "FINAL ANSWER" in last_message.content:
        return "__end__"
    return "continue"

workflow.add_edge(START, SCHEDULER_AGENT_NAME)
workflow.add_edge(SCHEDULER_AGENT_NAME, END)

Enter fullscreen mode Exit fullscreen mode

This is the code for defining the edges of our workflow.

  • The router returns the Node name from the agent state. This helps in navigating the workflow. It can also transfer flow control from one node to another and end the workflow.
  • We defined two normal edges that move from START→SCHEDULER_AGENT_NAME→END.

Conditional Edges

The workflow includes two key conditional edges:

  • Conditional Edge from SCHEDULER_AGENT_NAME to Various Nodes:

    pythonCopy code
    workflow.add_conditional_edges(
        SCHEDULER_AGENT_NAME,
        router,
        {
            "continue": SCHEDULER_AGENT_NAME,
            "call_tool": TOOL_NODE_NAME,
        },
    )
    
    
    • Purpose: This edge determines the next step after the scheduler agent processes the current state.
    • Condition: The router function checks the content of the last message in the state.
    • Possible Outcomes:
      • "continue": If the router function returns "continue", the workflow stays in the SCHEDULER_AGENT_NAME node, allowing the agent to keep processing.
      • "call_tool": If the router function returns "call_tool", the workflow transitions to the TOOL_NODE_NAME node, where the relevant tool (e.g., find free slots or create an event) is invoked.
  • Conditional Edge from TOOL_NODE_NAME to SCHEDULER_AGENT_NAME:

    pythonCopy code
    workflow.add_conditional_edges(
        TOOL_NODE_NAME,
        lambda x: x["sender"],
        {SCHEDULER_AGENT_NAME: SCHEDULER_AGENT_NAME},
    )
    
    • Purpose: This edge determines where to route the workflow after invoking a tool (like Google Calendar or Gmail).
    • Condition: The lambda function lambda x: x["sender"] checks the sender of the last message.
    • Outcome: If the sender is the SCHEDULER_AGENT_NAME, the workflow returns to the SCHEDULER_AGENT_NAME node to continue processing.

Finally, compile the workflow.

app = workflow.compile()
Enter fullscreen mode Exit fullscreen mode

Setting Up the Event Listener

The next step is to define the event listener. This will be responsible for fetching mail when someone sends an email to your connected Gmail account.

def extract_sender_email(payload):
    if not any(header.get("name") == "Delivered-To" and header.get("value") for header in payload["headers"]):
        return None

    for header in payload["headers"]:
        if header["name"] == "From":
            match = re.search(r"[\w\.-]+@[\w\.-]+", header["value"])
            return match.group(0) if match else None
    return None

def process_email(email_sender, email_content, thread_id):
    final_state = app.invoke({
        "messages": [
            HumanMessage(content=f"""                              
Please process the email 
Email sender: {email_sender} 
Email content: {email_content}
Thread id: {thread_id}
""")
        ]
    })
    return final_state["messages"][-1].content

listener = composio_toolset.create_trigger_listener()

@listener.callback(filters={"trigger_name": "gmail_new_gmail_message"})
def callback_new_message(event: TriggerEventData) -> None:
    payload = event.payload
    thread_id = payload.get("threadId")
    email_content = payload.get("snippet")
    email_sender = extract_sender_email(payload["payload"])

    if email_sender is None:
        print("No sender email found")
        return

    print(f"Processing email from: {email_sender}")
    output = process_email(email_sender, email_content, thread_id)
    print("Final output:", output)

listener.listen()
Enter fullscreen mode Exit fullscreen mode

So, here is the explanation of the above code block

  • extract_sender_email(payload): Extracts the sender's email address from the email payload headers, returning it if found or None if not.
  • process_email(email_sender, email_content, thread_id): Invokes the workflow with the email details and returns the final processed message content.
  • Listener Setup (listener = composio_toolset.create_trigger_listener()): Creates a listener to monitor for specific events, like new Gmail messages.
  • @listener.callback(filters={"trigger_name": "gmail_new_gmail_message"}): Defines a callback function that triggers when a new Gmail message event is detected, processing the email through the workflow.
  • listener.listen(): This command starts the listener and makes it actively listen for and respond to new Gmail messages by invoking the callback function.

After completing all the steps, run the Python file to set up the event listener.

The next time you email the configured Gmail account, the bot will process it and determine whether to schedule an event or pass it along. If an event is scheduled, a draft email will be prepared for your review, allowing you to send it if desired.

You can find the full code here: Scheduling Agent


Thank you for reading! 🎉


Help me out!

If you feel this article helped you better understand AI-powered event automation, I would be super happy if you could give us a star! Let me know in the comments.

I need your help GIF

Star the Composio.dev repository ⭐

Top comments (2)

Collapse
 
codegoat profile image
Antonio Fuller

It would be way better if you made a YouTube tutorial on how to implement the AI bot. I have no idea what to do with the copied code snippets

Some comments may only be visible to logged-in visitors. Sign in to view all comments.