autogpt 0.4.1

🦀 A Pure Rust Framework For Building AGIs.
Documentation
// Copyright 2026 Mahmoud Harmouch.
//
// Licensed under the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! # `MailerGPT` agent.
//!
//! This module provides functionality for utilizing emails to generate text-based
//! content based on prompts using Nylas and Gemini APIs. The `MailerGPT` agent
//! understands email contents and produces textual responses tailored to user requirements.

use crate::agents::agent::AgentGPT;
#[cfg(feature = "net")]
use crate::collaboration::Collaborator;
use crate::common::utils::{
    Capability, ClientType, ContextManager, Knowledge, Message, Persona, Planner, Reflection,
    Status, Task, TaskScheduler, Tool,
};
use crate::traits::agent::Agent;
use crate::traits::functions::{AsyncFunctions, Executor, Functions, ReqResponse};
use anyhow::{Result, anyhow};
use async_trait::async_trait;
use auto_derive::Auto;
use colored::*;
use nylas::client::Nylas;
use nylas::messages::Message as NylasMessage;
use std::borrow::Cow;
use std::env::var;
use tracing::{debug, info};

#[cfg(feature = "mem")]
use {
    crate::common::memory::load_long_term_memory, crate::common::memory::long_term_memory_context,
    crate::common::memory::save_long_term_memory,
};

#[cfg(feature = "hf")]
use crate::prelude::hf_model_from_str;

/// Struct representing a `MailerGPT`, which manages email processing and text generation using Nylas and Gemini API.
#[derive(Auto, Default)]
#[allow(unused)]
pub struct MailerGPT {
    /// Represents the GPT agent responsible for handling email processing and text generation.
    agent: AgentGPT,
    /// Represents the task to be executed by the agent.
    task: Task,
    /// Represents the Nylas client for interacting with email services.
    nylas_client: Option<Nylas>,
    /// Represents an OpenAI or Gemini client for interacting with their API.
    client: ClientType,
}

impl std::fmt::Debug for MailerGPT {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("MailerGPT")
            .field("agent", &self.agent)
            .field("task", &self.task)
            .field("client", &self.client)
            .finish()
    }
}

impl Clone for MailerGPT {
    fn clone(&self) -> Self {
        Self {
            agent: self.agent.clone(),
            task: self.task.clone(),
            nylas_client: None,
            client: self.client.clone(),
        }
    }
}

impl MailerGPT {
    /// Constructor function to create a new instance of MailerGPT.
    ///
    /// # Arguments
    ///
    /// * `persona` - persona for MailerGPT.
    /// * `behavior` - behavior description for MailerGPT.
    ///
    /// # Returns
    ///
    /// (`MailerGPT`): A new instance of MailerGPT.
    ///
    /// # Business Logic
    ///
    /// - Initializes the GPT agent with the given persona and behavior.
    /// - Creates a Nylas client for interacting with email services.
    /// - Creates a Gemini client for interacting with Gemini API.
    ///
    pub async fn new(persona: &'static str, behavior: &'static str) -> Self {
        let mut agent: AgentGPT = AgentGPT::new_borrowed(persona, behavior);
        agent.id = agent.persona().to_string().into();
        let client_id = var("NYLAS_CLIENT_ID").unwrap_or_default().to_owned();
        let client_secret = var("NYLAS_CLIENT_SECRET").unwrap_or_default().to_owned();
        let access_token = var("NYLAS_ACCESS_TOKEN").unwrap_or_default().to_owned();

        let nylas_client = Nylas::new(&client_id, &client_secret, Some(&access_token))
            .await
            .unwrap();

        let client = ClientType::from_env();

        info!(
            "{}",
            format!("[*] {:?}: 🛠️  Getting ready!", agent.persona(),)
                .bright_white()
                .bold()
        );

        Self {
            agent,
            task: Task::default(),
            nylas_client: Some(nylas_client),
            client,
        }
    }

    /// Asynchronously retrieves the latest emails.
    ///
    /// # Returns
    ///
    /// (`Result<Vec<Message>>`): Result containing a vector of messages representing the latest emails.
    ///
    /// # Errors
    ///
    /// Returns an error if there's a failure in retrieving emails.
    ///
    /// # Business Logic
    ///
    /// - Retrieves the latest emails using the Nylas client.
    /// - Logs the number of messages read.
    /// - Returns a subset of the last 5 emails for processing.
    ///
    pub async fn get_latest_emails(&mut self) -> Result<Vec<NylasMessage>> {
        let nylas = self
            .nylas_client
            .as_mut()
            .ok_or_else(|| anyhow!("Nylas client not initialized"))?;
        let messages = nylas.messages().all().await.unwrap();

        info!(
            "[*] {:?}: Read {:?} Messages",
            self.agent.persona(),
            messages.len()
        );

        Ok(messages[95..].to_vec())
    }
    /// Asynchronously generates text from the latest emails.
    ///
    /// # Arguments
    ///
    /// * `prompt` - A prompt for generating text based on email content.
    ///
    /// # Returns
    ///
    /// (`Result<String>`): Result containing the generated text.
    ///
    /// # Errors
    ///
    /// Returns an error if there's a failure in generating text from emails.
    ///
    /// # Business Logic
    ///
    /// - Retrieves the latest emails.
    /// - Logs messages for user input and assistant response.
    /// - Constructs a request for generating text based on email content and the provided prompt.
    /// - Sends the request to the Gemini client to generate text.
    /// - Returns the generated text.
    pub async fn generate_text_from_emails(&mut self, prompt: &str) -> Result<String> {
        self.agent.add_message(Message {
            role: Cow::Borrowed("user"),
            content: Cow::Owned(format!(
                "Requested to generate text based on emails with prompt: '{prompt}'"
            )),
        });
        #[cfg(feature = "mem")]
        {
            let _ = self
                .save_ltm(Message {
                    role: Cow::Borrowed("user"),
                    content: Cow::Owned(format!(
                        "Requested to generate text based on emails with prompt: '{prompt}'"
                    )),
                })
                .await;
        }
        let emails = match self.get_latest_emails().await {
            Ok(e) => e,
            Err(err) => {
                let error_msg = format!("Failed to fetch latest emails: {err}");
                self.agent.add_message(Message {
                    role: Cow::Borrowed("system"),
                    content: Cow::Owned(error_msg.clone()),
                });
                #[cfg(feature = "mem")]
                {
                    let _ = self
                        .save_ltm(Message {
                            role: Cow::Borrowed("system"),
                            content: Cow::Owned(error_msg.clone()),
                        })
                        .await;
                }
                return Err(anyhow!(error_msg));
            }
        };

        self.agent.add_message(Message {
            role: Cow::Borrowed("assistant"),
            content: Cow::Owned(
                "Analyzing latest emails and generating text based on provided prompt..."
                    .to_string(),
            ),
        });
        #[cfg(feature = "mem")]
        {
            let _ = self
                .save_ltm(Message {
                    role: Cow::Borrowed("assistant"),
                    content: Cow::Owned(
                        "Analyzing latest emails and generating text based on provided prompt..."
                            .to_string(),
                    ),
                })
                .await;
        }

        let full_prompt = format!("User Request:{prompt}\n\nEmails:{emails:?}");
        let gemini_response = self.generate(&full_prompt).await?;

        self.agent.add_message(Message {
            role: Cow::Borrowed("assistant"),
            content: Cow::Owned(
                "Generated text from emails based on the given prompt.".to_string(),
            ),
        });

        #[cfg(feature = "mem")]
        {
            let _ = self
                .save_ltm(Message {
                    role: Cow::Borrowed("assistant"),
                    content: Cow::Owned(
                        "Generated text from emails based on the given prompt.".to_string(),
                    ),
                })
                .await;
        }

        info!(
            "[*] {:?}: Got Response: {:?}",
            self.agent.persona(),
            gemini_response
        );

        Ok(gemini_response)
    }
}

/// Implementation of the trait `AsyncFunctions` for MailerGPT.
/// Contains additional methods related to email processing and text generation.
///
/// This trait provides methods for:
///
/// - Retrieving the GPT agent associated with MailerGPT.
/// - Executing email processing and text generation tasks asynchronously.
///
/// # Business Logic
///
/// - Provides access to the GPT agent associated with the MailerGPT instance.
/// - Executes email processing and text generation tasks asynchronously based on the current status of the agent.
/// - Handles task execution including email retrieval and text generation.
/// - Manages retries and error handling during task execution.
#[async_trait]
impl Executor for MailerGPT {
    /// Asynchronously executes email processing and text generation tasks associated with MailerGPT.
    ///
    /// # Arguments
    ///
    /// * `task` - A mutable reference to tasks to be executed.
    /// * `execute` - A boolean indicating whether to execute the tasks (TODO).
    /// * `browse` - Whether to open a browser.
    /// * `max_tries` - Maximum number of attempts to execute tasks (TODO).
    ///
    /// # Returns
    ///
    /// (`Result<()>`): Result indicating success or failure of task execution.
    ///
    /// # Errors
    ///
    /// Returns an error if there's a failure in executing tasks.
    ///
    /// # Business Logic
    ///
    /// - Executes email processing and text generation tasks asynchronously based on the current status of the agent.
    /// - Handles task execution including email retrieval and text generation.
    /// - Manages retries and error handling during task execution.
    ///
    async fn execute<'a>(
        &'a mut self,
        task: &'a mut Task,
        _execute: bool,
        _browse: bool,
        _max_tries: u64,
    ) -> Result<()> {
        info!(
            "{}",
            format!("[*] {:?}: Executing task:", self.agent.persona(),)
                .bright_white()
                .bold()
        );
        for task in task.clone().description.clone().split("- ") {
            if !task.trim().is_empty() {
                info!("{} {}", "".bright_white().bold(), task.trim().cyan());
            }
        }
        let mut _count = 0;
        while self.agent.status() != &Status::Completed {
            match self.agent.status() {
                Status::Idle => {
                    debug!("[*] {:?}: Idle", self.agent.persona());

                    let _generated_text = self.generate_text_from_emails(&task.description).await?;

                    _count += 1;
                    self.agent.update(Status::Completed);
                }
                _ => {
                    self.agent.update(Status::Completed);
                }
            }
        }

        Ok(())
    }
}

// Copyright 2026 Mahmoud Harmouch.
//
// Licensed under the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.