Get things done with a Todoist agent
We all dream to have our own AI personal assistant someday. While the perfect AI assistant might still be far away, we can build simple AI agents that work with Todoist today.
Scenario
For this demo, we will show how to build an AI agent that can get tasks from Todoist, complete them, and close them in Todoist.
In my Todoist, I have a simple demo task, "Email email@example.com the top 3 HN posts".
Our AI agent will:
- Get the task from Todoist
- Get the top 3 HN posts
- Send them to the recipient via Gmail
- Mark the task as done in Todoist
To do this, our AI agent is equipped with tools to:
- Get, update, and close tasks in Todoist
- Fetch the top stories on Hacker News
- Send plain-text email via Gmail
For a simpler demo for Todoist, check out Getting tasks from Todoist.
Setup
To get started, we first set the following environment variables in a .env
file:
TODOIST_API_TOKEN
: The API key of your Todoist account (see below)GMAIL_ADDRESS
: The sender's emailGMAIL_PASSWORD
: The sender's app password, not account password<COMPANY>_API_KEY
: The API key of the model you want to use- The Hacker News tools do not require an API key.
Todoist setup
To get your Todoist API key:
- Click on your profile icon in the top-right corner
- Select "Settings"
- Click on "Integrations"
- Click on "Developer"
- Copy the API token and save it as an environment variable
Scripts
Some APIs and frameworks (e.g. Gemini, LangGraph, and LlamaIndex agent) automatically execute tool calls, which make the code much simpler. For the rest, we will need to add a while
loop so that the agent will keep working on the next step until the task is completed.
import os
import anthropic
from dotenv import load_dotenv
import stores
# Load environment variables
load_dotenv()
# Load tools and set the required environment variables
index = stores.Index(
["silanthro/todoist", "silanthro/hackernews", "silanthro/send-gmail"],
env_var={
"silanthro/todoist": {
"TODOIST_API_TOKEN": os.environ["TODOIST_API_TOKEN"],
},
"silanthro/send-gmail": {
"GMAIL_ADDRESS": os.environ["GMAIL_ADDRESS"],
"GMAIL_PASSWORD": os.environ["GMAIL_PASSWORD"],
},
},
)
# Initialize Anthropic client and messages
client = anthropic.Anthropic()
messages = [
{
"role": "user",
"content": "What tasks are due today? Use your tools to complete them for me. Don't ask questions.",
}
]
# Run agent loop
while True:
# Get the response from the model
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=messages,
# Pass tools
tools=index.format_tools("anthropic"),
)
# Check if all blocks contain only text, which indicates task completion for this example
blocks = response.content
if all(block.type == "text" for block in blocks):
print(f"Assistant response: {blocks[0].text}\n")
break # End the agent loop
# Otherwise, process the response, which could include both text and tool use
for block in blocks:
if block.type == "text" and block.text:
print(f"Assistant response: {block.text}\n")
# Append the assistant's response as context
messages.append({"role": "assistant", "content": block.text})
elif block.type == "tool_use":
name = block.name
args = block.input
# Execute tool call
print(f"Executing tool call: {name}({args})\n")
output = index.execute(name, args)
print(f"Tool output: {output}\n")
# Append the assistant's tool call as context
messages.append(
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": block.id,
"name": block.name,
"input": block.input,
}
],
}
)
# Append the tool call result as context
messages.append(
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": block.id,
"content": str(output),
}
],
}
)
In the folder where you have this script, you can run the AI agent with the command:
python complete-task.py
The AI agent will get the task, complete it, and mark it as done in Todoist.
If you have any questions, let us know on GitHub.