Gmail Daily Digest

Summarize recent emails into a structured daily digest grouped by category and tagged with priority.

Group recent emails by category and record a priority for each message in a structured daily digest.

daily_digest.py
"""
Gmail Daily Digest
==================
Summarizes recent emails into a structured daily digest grouped by priority.

The agent fetches today's emails, classifies each by category and urgency,
and returns a structured report.

Key concepts:
- output_schema: Forces structured JSON output matching DailyDigest model
- add_datetime_to_context: Agent knows today's date for time-aware queries


Setup:
1. Create OAuth credentials at https://console.cloud.google.com (enable Gmail API)
2. Export GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_PROJECT_ID env vars
3. pip install openai google-api-python-client google-auth-httplib2 google-auth-oauthlib
4. First run opens browser for OAuth consent, saves token.json for reuse
"""

from typing import List, Literal

from agno.agent import Agent
from agno.models.openai import OpenAIResponses
from agno.tools.google.gmail import GmailTools
from pydantic import BaseModel, Field

# ---------------------------------------------------------------------------
# Output Schema
# ---------------------------------------------------------------------------


class EmailDigestItem(BaseModel):
    subject: str = Field(..., description="Email subject line")
    sender: str = Field(..., description="Sender name or email")
    category: Literal["action_required", "fyi", "newsletter", "personal", "other"] = (
        Field(..., description="Email category based on content")
    )
    summary: str = Field(..., description="One-sentence summary of the email")
    priority: Literal["high", "medium", "low"] = Field(
        ..., description="Priority level based on urgency and importance"
    )


class DailyDigest(BaseModel):
    date: str = Field(..., description="Digest date in YYYY-MM-DD format")
    total_emails: int = Field(..., description="Total number of emails processed")
    action_required: List[EmailDigestItem] = Field(
        default_factory=list, description="Emails requiring action"
    )
    fyi: List[EmailDigestItem] = Field(
        default_factory=list, description="Informational emails"
    )
    newsletters: List[EmailDigestItem] = Field(
        default_factory=list, description="Newsletter and subscription emails"
    )
    personal: List[EmailDigestItem] = Field(
        default_factory=list, description="Personal emails"
    )


# ---------------------------------------------------------------------------
# Create Agent
# ---------------------------------------------------------------------------

agent = Agent(
    name="Daily Digest Agent",
    model=OpenAIResponses(id="gpt-5.5"),
    tools=[GmailTools()],
    instructions=[
        "Categorize each email as action_required, fyi, newsletter, personal, or other.",
        "Assign priority: high for urgent/time-sensitive, medium for important, low for routine.",
        "Write a one-sentence summary for each email capturing the key point.",
        "Group results by category in the output schema.",
    ],
    output_schema=DailyDigest,
    add_datetime_to_context=True,
    markdown=True,
)

# ---------------------------------------------------------------------------
# Run Demo
# ---------------------------------------------------------------------------

if __name__ == "__main__":
    agent.print_response(
        "Give me a digest of today's emails, categorized by urgency",
        stream=True,
    )

For this reading task, replace the toolkit with GmailTools(include_tools=["search_emails", "search_threads", "get_thread", "get_message"]) (retain max_results=10 for the action-item example if desired). GmailTools() otherwise also enables sending, drafting, and label changes. Search returns a page of results; follow nextPageToken for a complete digest or a broader thread search.

The item model allows category other, but DailyDigest has no field for those messages. Add other: List[EmailDigestItem] = Field(default_factory=list) to DailyDigest so uncategorized messages have a place in the structured result.

Run the Example

Set up your virtual environment

uv venv --python 3.12
source .venv/bin/activate

Install dependencies

uv pip install -U agno google-api-python-client google-auth google-auth-httplib2 google-auth-oauthlib openai

Configure Google OAuth

Enable the Google API used by this example, configure the consent screen, and create a Desktop OAuth client in your Cloud project. Export its GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, and GOOGLE_PROJECT_ID, or place the downloaded client JSON at credentials.json in the directory where you run Python. Google's Python quickstart shows the Desktop client setup.

The first tool call opens a browser for consent and caches credentials in token.json. Use a separate token_path when switching accounts. Passing an Agno user_id does not switch the authenticated Google account.

Export environment variables

export GOOGLE_CLIENT_ID="your_google_client_id_here"
export GOOGLE_CLIENT_SECRET="your_google_client_secret_here"
export GOOGLE_PROJECT_ID="your_google_project_id_here"
export OPENAI_API_KEY="your_openai_api_key_here"

Run the example

Save the code above as daily_digest.py, then run:

python daily_digest.py

Full source: cookbook/91_tools/google/gmail/daily_digest.py