#![allow(unreachable_code)]
use crate::agents::agent::AgentGPT;
#[cfg(feature = "net")]
use crate::collaboration::Collaborator;
#[cfg(feature = "cli")]
use crate::common::utils::spinner;
#[allow(unused_imports)]
use crate::common::utils::{
Capability, ClientType, Communication, ContextManager, GenerationOutput, Goal, Knowledge,
OutputKind, Persona, Planner, Reflection, Route, Scope, Status, Task, TaskScheduler, Tool,
extract_array, strip_code_blocks,
};
use crate::prompts::backend::{
API_ENDPOINTS_PROMPT, FIX_CODE_PROMPT, IMPROVED_WEBSERVER_CODE_PROMPT, WEBSERVER_CODE_PROMPT,
};
use crate::traits::agent::Agent;
use crate::traits::functions::{AsyncFunctions, Executor, Functions};
use auto_derive::Auto;
use std::path::Path;
use std::process::Stdio;
use anyhow::{Result, anyhow};
use async_trait::async_trait;
use colored::*;
use reqwest::Client as ReqClient;
use std::borrow::Cow;
use std::env::var;
use std::time::Duration;
use tokio::fs;
use tokio::io::AsyncReadExt;
use tokio::process::Child;
use tokio::process::Command;
use tracing::{debug, error, info, warn};
use webbrowser::{Browser, BrowserOptions, open_browser_with_options};
#[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 = "oai")]
use {openai_dive::v1::models::FlagshipModel, openai_dive::v1::resources::chat::*};
#[cfg(feature = "cld")]
use anthropic_ai_sdk::types::message::{
ContentBlock, CreateMessageParams, Message as AnthMessage, MessageClient,
RequiredMessageParams, Role,
};
#[cfg(feature = "gem")]
use gems::{
chat::ChatBuilder,
imagen::ImageGenBuilder,
messages::{Content, Message},
models::Model,
stream::StreamBuilder,
traits::CTrait,
};
#[cfg(any(feature = "oai", feature = "gem", feature = "cld", feature = "xai"))]
use crate::traits::functions::ReqResponse;
#[cfg(feature = "xai")]
use x_ai::{
chat_compl::{ChatCompletionsRequestBuilder, Message as XaiMessage},
traits::ChatCompletionsFetcher,
};
#[derive(Debug, Clone, Default, Auto)]
#[allow(dead_code)]
pub struct BackendGPT {
workspace: Cow<'static, str>,
agent: AgentGPT,
client: ClientType,
req_client: ReqClient,
bugs: Option<Cow<'static, str>>,
language: &'static str,
nb_bugs: u64,
}
impl BackendGPT {
#[allow(unused)]
pub async fn new(
objective: &'static str,
position: &'static str,
language: &'static str,
) -> Self {
let base_workspace = var("AUTOGPT_WORKSPACE").unwrap_or_else(|_| "workspace".to_string());
let workspace = format!("{base_workspace}/backend");
if !fs::try_exists(&workspace).await.unwrap_or(false) {
match fs::create_dir_all(&workspace).await {
Ok(_) => debug!("Directory '{}' created successfully!", workspace),
Err(e) => error!("Error creating directory '{}': {}", workspace, e),
}
} else {
debug!("Workspace directory '{}' already exists.", workspace);
}
info!(
"{}",
format!("[*] {position:?}: 🛠️ Getting ready!")
.bright_white()
.bold()
);
match language {
"rust" => {
if !Path::new(&format!("{workspace}/Cargo.toml")).exists() {
let cargo_new = Command::new("cargo").arg("init").arg(&workspace).spawn();
match cargo_new {
Ok(_) => debug!("Cargo project initialized successfully."),
Err(e) => error!("Error initializing Cargo project: {}", e),
}
}
let template_path = format!("{workspace}/src/template.rs");
if !Path::new(&template_path).exists() {
if let Err(e) = fs::write(&template_path, "").await {
error!("Error creating file '{}': {}", template_path, e);
} else {
debug!("File '{}' created successfully.", template_path);
}
}
}
"python" => {
let files = ["main.py", "template.py"];
for file in files.iter() {
let full_path = format!("{workspace}/{file}");
if !Path::new(&full_path).exists() {
if let Err(e) = fs::write(&full_path, "").await {
error!("Error creating file '{}': {}", full_path, e);
} else {
debug!("File '{}' created successfully.", full_path);
}
}
}
}
"javascript" => {
if !Path::new(&format!("{workspace}/package.json")).exists() {
let npx_install = Command::new("npx")
.arg("create-react-app")
.arg(&workspace)
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn();
match npx_install {
Ok(mut child) => match child.wait().await {
Ok(status) => {
if status.success() {
debug!("React JS project initialized successfully.");
} else {
error!("Failed to initialize React JS project.");
}
}
Err(e) => {
error!("Error waiting for process: {}", e);
}
},
Err(e) => {
error!("Error initializing React JS project: {}", e);
}
}
}
let template_path = format!("{workspace}/src/template.js");
if !Path::new(&template_path).exists() {
if let Err(e) = fs::write(&template_path, "").await {
error!("Error creating file '{}': {}", template_path, e);
} else {
debug!("File '{}' created successfully.", template_path);
}
}
}
_ => panic!("Unsupported language '{language}'. Consider opening an issue/PR.",),
}
let mut agent: AgentGPT = AgentGPT::new_borrowed(objective, position);
agent.id = agent.position().to_string().into();
let client = ClientType::from_env();
let req_client: ReqClient = ReqClient::builder()
.timeout(Duration::from_secs(3))
.build()
.unwrap();
Self {
workspace: workspace.into(),
agent,
client,
req_client,
bugs: None,
language,
nb_bugs: 0,
}
}
pub async fn build_request(
&mut self,
prompt: &str,
tasks: &mut Task,
output_type: OutputKind,
) -> Result<GenerationOutput> {
#[cfg(feature = "mem")]
{
self.agent.memory = self.get_ltm().await?;
}
let request: String = format!(
"{}\n\nTask Description: {}\nPrevious Conversation: {:?}",
prompt,
tasks.description,
self.agent.memory(),
);
self.agent.add_communication(Communication {
role: Cow::Borrowed("user"),
content: Cow::Owned(request.clone()),
});
#[cfg(feature = "mem")]
{
let _ = self
.save_ltm(Communication {
role: Cow::Borrowed("user"),
content: Cow::Owned(request.clone()),
})
.await;
}
#[allow(unused)]
let mut response_text = String::new();
#[cfg(any(feature = "oai", feature = "gem", feature = "cld", feature = "xai"))]
{
response_text = self.generate(&request).await?;
}
self.agent.add_communication(Communication {
role: Cow::Borrowed("assistant"),
content: Cow::Owned(response_text.clone()),
});
#[cfg(feature = "mem")]
{
let _ = self
.save_ltm(Communication {
role: Cow::Borrowed("assistant"),
content: Cow::Owned(response_text.clone()),
})
.await;
}
debug!("[*] {:?}: {:?}", self.agent.position(), self.agent);
match output_type {
OutputKind::Text => Ok(GenerationOutput::Text(strip_code_blocks(&response_text))),
OutputKind::UrlList => {
let urls: Vec<Cow<'static, str>> =
serde_json::from_str(&extract_array(&response_text).unwrap_or_default())?;
tasks.urls = Some(urls.clone());
self.agent.update(Status::InUnitTesting);
Ok(GenerationOutput::UrlList(urls))
}
OutputKind::Scope => {
let scope: Scope = serde_json::from_str(&strip_code_blocks(&response_text))?;
Ok(GenerationOutput::Scope(scope))
}
}
}
pub async fn generate_backend_code(&mut self, tasks: &mut Task) -> Result<String> {
let path = self.workspace.clone();
let backend_path = match self.language {
"rust" => format!("{}/{}", path, "src/main.rs"),
"python" => format!("{}/{}", path, "main.py"),
"javascript" => format!("{}/{}", path, "src/index.js"),
_ => panic!("Unsupported language, consider opening an Issue/PR"),
};
let template = fs::read_to_string(&backend_path).await?;
let prompt = format!(
"{}\n\nCode Template: {}\nProject Description: {}",
WEBSERVER_CODE_PROMPT, template, tasks.description
);
let output = self.build_request(&prompt, tasks, OutputKind::Text).await?;
let code = match output {
GenerationOutput::Text(code) => code,
_ => {
return Err(anyhow!("Expected text output for backend code generation"));
}
};
fs::write(&backend_path, &code).await?;
tasks.backend_code = Some(code.clone().into());
self.agent.update(Status::Completed);
debug!("[*] {:?}: {:?}", self.agent.position(), self.agent);
Ok(code)
}
pub async fn improve_backend_code(&mut self, tasks: &mut Task) -> Result<String> {
#[cfg(feature = "mem")]
{
self.agent.memory = self.get_ltm().await?;
}
let code_template = tasks.backend_code.clone().unwrap_or_default();
let request = format!(
"{}\n\nCode Template: {}\nProject Description: {}",
IMPROVED_WEBSERVER_CODE_PROMPT, code_template, tasks.description
);
self.agent.add_communication(Communication {
role: Cow::Borrowed("user"),
content: Cow::Owned(request.clone()),
});
#[cfg(feature = "mem")]
{
let _ = self
.save_ltm(Communication {
role: Cow::Borrowed("user"),
content: Cow::Owned(request.clone()),
})
.await;
}
#[allow(unused)]
let mut response_text = String::new();
#[cfg(any(feature = "oai", feature = "gem", feature = "cld", feature = "xai"))]
{
response_text = self.generate(&request).await?;
}
self.agent.add_communication(Communication {
role: Cow::Borrowed("assistant"),
content: Cow::Owned(response_text.clone()),
});
#[cfg(feature = "mem")]
{
let _ = self
.save_ltm(Communication {
role: Cow::Borrowed("assistant"),
content: Cow::Owned(response_text.clone()),
})
.await;
}
let cleaned_code = strip_code_blocks(&response_text);
let backend_path = match self.language {
"rust" => format!("{}/src/main.rs", self.workspace),
"python" => format!("{}/main.py", self.workspace),
"javascript" => format!("{}/src/index.js", self.workspace),
_ => return Err(anyhow!("Unsupported language")),
};
debug!(
"[*] {:?}: Writing to {}",
self.agent.position(),
backend_path
);
fs::write(&backend_path, &cleaned_code).await?;
tasks.backend_code = Some(cleaned_code.clone().into());
self.agent.update(Status::Completed);
debug!("[*] {:?}: {:?}", self.agent.position(), self.agent);
Ok(cleaned_code)
}
pub async fn fix_code_bugs(&mut self, tasks: &mut Task) -> Result<String> {
#[cfg(feature = "mem")]
{
self.agent.memory = self.get_ltm().await?;
}
let buggy_code = tasks.backend_code.clone().unwrap_or_default();
let bugs = self.bugs.clone().unwrap_or_default();
let request =
format!("{FIX_CODE_PROMPT}\n\nBuggy Code: {buggy_code}\nBugs: {bugs}\n\nFix all bugs.");
self.agent.add_communication(Communication {
role: Cow::Borrowed("user"),
content: Cow::Owned(request.clone()),
});
#[cfg(feature = "mem")]
{
let _ = self
.save_ltm(Communication {
role: Cow::Borrowed("user"),
content: Cow::Owned(request.clone()),
})
.await;
}
#[allow(unused)]
let mut response_text = String::new();
#[cfg(any(feature = "oai", feature = "gem", feature = "cld", feature = "xai"))]
{
response_text = self.generate(&request).await?;
}
self.agent.add_communication(Communication {
role: Cow::Borrowed("assistant"),
content: Cow::Owned(response_text.clone()),
});
#[cfg(feature = "mem")]
{
let _ = self
.save_ltm(Communication {
role: Cow::Borrowed("assistant"),
content: Cow::Owned(response_text.clone()),
})
.await;
}
let cleaned_code = strip_code_blocks(&response_text);
let workspace = &self.workspace;
let backend_path = match self.language {
"rust" => format!("{workspace}/src/main.rs"),
"python" => format!("{workspace}/main.py"),
"javascript" => format!("{workspace}/src/index.js"),
_ => return Err(anyhow!("Unsupported language")),
};
debug!(
"[*] {:?}: Writing to {}",
self.agent.position(),
backend_path
);
fs::write(&backend_path, &cleaned_code).await?;
tasks.backend_code = Some(cleaned_code.clone().into());
self.agent.update(Status::Completed);
debug!("[*] {:?}: {:?}", self.agent.position(), self.agent);
Ok(cleaned_code)
}
pub async fn get_routes_json(&mut self) -> Result<String> {
#[cfg(feature = "mem")]
{
self.agent.memory = self.get_ltm().await?;
}
let path = self.workspace.clone();
let full_path = match self.language {
"rust" => format!("{path}/src/main.rs"),
"python" => format!("{path}/main.py"),
"javascript" => format!("{path}/src/index.js"),
_ => return Err(anyhow!("Unsupported language")),
};
debug!(
"[*] {:?}: Reading from {}",
self.agent.position(),
full_path
);
let backend_code = fs::read_to_string(full_path).await?;
let request = format!(
"{API_ENDPOINTS_PROMPT}\n\nHere is the backend code with all routes:{backend_code}"
);
self.agent.add_communication(Communication {
role: Cow::Borrowed("user"),
content: Cow::Owned(request.clone()),
});
#[cfg(feature = "mem")]
{
let _ = self
.save_ltm(Communication {
role: Cow::Borrowed("user"),
content: Cow::Owned(request.clone()),
})
.await;
}
#[allow(unused)]
let mut response_text = String::new();
#[cfg(any(feature = "oai", feature = "gem", feature = "cld", feature = "xai"))]
{
response_text = self.generate(&request).await?;
}
self.agent.add_communication(Communication {
role: Cow::Borrowed("assistant"),
content: Cow::Owned(response_text.clone()),
});
#[cfg(feature = "mem")]
{
let _ = self
.save_ltm(Communication {
role: Cow::Borrowed("assistant"),
content: Cow::Owned(response_text.clone()),
})
.await;
}
self.agent.update(Status::Completed);
debug!("[*] {:?}: {:?}", self.agent.position(), self.agent);
Ok(strip_code_blocks(&response_text))
}
pub fn think(&self) -> String {
let objective = self.agent.objective();
format!("How to build and test backend for '{objective}'")
}
pub fn plan(&mut self, _context: String) -> Goal {
let mut goals = vec![
Goal {
description: "Generate backend code".into(),
priority: 1,
completed: false,
},
Goal {
description: "Fix code bugs if any".into(),
priority: 2,
completed: false,
},
Goal {
description: "Run unit tests and backend server".into(),
priority: 3,
completed: false,
},
];
goals.sort_by_key(|g| g.priority);
if let Some(planner) = self.agent.planner_mut() {
if planner.current_plan.is_empty() {
for g in goals.into_iter().rev() {
planner.current_plan.push(g);
}
}
if let Some(next_goal) = planner.current_plan.iter().rev().find(|g| !g.completed) {
return next_goal.clone();
}
}
Goal {
description: "Default backend task".into(),
priority: 1,
completed: false,
}
}
pub async fn act(
&mut self,
goal: Goal,
tasks: &mut Task,
execute: bool,
max_tries: u64,
) -> Result<()> {
info!(
"{}",
format!(
"[*] {:?}: Executing goal: {}",
self.agent.position(),
goal.description
)
.cyan()
.bold()
);
match goal.description.as_str() {
"Generate backend code" => {
self.generate_or_improve_code(tasks).await?;
self.agent.update(Status::Active);
}
"Fix code bugs if any" => {
if self.nb_bugs > 0 {
self.fix_code_bugs(tasks).await?;
} else {
self.improve_backend_code(tasks).await?;
}
self.agent.update(Status::InUnitTesting);
}
"Run unit tests and backend server" => {
self.unit_test_and_run_backend(tasks, execute, max_tries)
.await?;
self.agent.update(Status::Completed);
}
_ => {
warn!(
"{}",
format!(
"[*] {:?}: Unknown goal: {}",
self.agent.position(),
goal.description
)
.yellow()
);
}
}
Ok(())
}
pub fn reflect(&mut self) {
let entry = format!(
"Reflection on backend task for '{}'",
self.agent.objective()
);
self.agent.memory_mut().push(Communication {
role: Cow::Borrowed("assistant"),
content: entry.clone().into(),
});
self.agent
.context_mut()
.recent_messages
.push(Communication {
role: Cow::Borrowed("assistant"),
content: entry.into(),
});
if let Some(reflection) = self.agent.reflection() {
let feedback = (reflection.evaluation_fn)(&self.agent);
info!(
"{}",
format!(
"[*] {:?}: Self Reflection: {}",
self.agent.position(),
feedback
)
.blue()
);
}
}
pub fn has_completed_objective(&self) -> bool {
if let Some(planner) = self.planner() {
planner.current_plan.iter().all(|g| g.completed)
} else {
false
}
}
pub fn mark_goal_complete(&mut self, goal: Goal) {
if let Some(planner) = self.planner_mut() {
for g in &mut planner.current_plan {
if g.description == goal.description {
g.completed = true;
}
}
}
}
fn display_task_info(&self, tasks: &Task) {
for task in tasks.clone().description.clone().split("- ") {
if !task.trim().is_empty() {
info!("{} {}", "•".bright_white().bold(), task.trim().cyan());
}
}
}
async fn open_docs_in_browser(&self) {
let _ = open_browser_with_options(
Browser::Default,
"http://127.0.0.1:8000/docs",
BrowserOptions::new().with_suppress_output(false),
);
}
async fn generate_or_improve_code(&mut self, tasks: &mut Task) -> Result<()> {
if self.nb_bugs == 0 {
self.generate_backend_code(tasks).await?;
} else {
self.improve_backend_code(tasks).await?;
}
Ok(())
}
async fn unit_test_and_run_backend(
&mut self,
tasks: &mut Task,
execute: bool,
max_tries: u64,
) -> Result<()> {
info!(
"{}",
format!(
"[*] {:?}: Backend Code Unit Testing...",
self.agent.position()
)
.bright_white()
.bold()
);
if !execute {
warn!(
"{}",
format!(
"[*] {:?}: Code not safe to proceed, skipping execution...",
self.agent.position()
)
.bright_yellow()
.bold()
);
return Ok(());
}
let path = &self.workspace.to_string();
let result = self.build_and_run_backend(path).await?;
if let Some(mut child) = result {
let mut stderr_output = String::new();
if let Some(mut stderr) = child.stderr.take() {
stderr.read_to_string(&mut stderr_output).await?;
}
if !stderr_output.trim().is_empty() {
self.nb_bugs += 1;
self.bugs = Some(stderr_output.into());
if self.nb_bugs > max_tries {
error!(
"{}",
format!(
"[*] {:?}: Too many bugs detected. Please debug manually.",
self.agent.position()
)
.bright_red()
.bold()
);
return Ok(());
}
self.agent.update(Status::Active);
return Ok(());
} else {
self.nb_bugs = 0;
info!(
"{}",
format!(
"[*] {:?}: Backend server build successful...",
self.agent.position()
)
.bright_white()
.bold()
);
}
let endpoints = self.get_routes_json().await?;
let api_endpoints: Vec<Route> =
serde_json::from_str(&endpoints).expect("Failed to decode API Endpoints");
let filtered_endpoints: Vec<Route> = api_endpoints
.iter()
.filter(|&route| route.method == "get" && route.dynamic == "false")
.cloned()
.collect();
tasks.api_schema = Some(filtered_endpoints.clone());
info!(
"{}",
format!(
"[*] {:?}: Starting web server to test endpoints...",
self.agent.position()
)
.bright_white()
.bold()
);
for endpoint in filtered_endpoints {
info!(
"{}",
format!(
"[*] {:?}: Testing endpoint: {}",
self.agent.position(),
endpoint.path
)
.bright_white()
.bold()
);
let url = format!("http://127.0.0.1:8080{}", endpoint.path);
let status_code = self.req_client.get(url).send().await?.status();
if status_code != 200 {
info!(
"{}",
format!(
"[*] {:?}: Endpoint failed: {}. Needs further investigation.",
self.agent.position(),
endpoint.path
)
.bright_white()
.bold()
);
}
}
let _ = child.kill().await;
let backend_path = format!("{path}/api.json");
fs::write(&backend_path, endpoints).await?;
info!(
"{}",
format!(
"[*] {:?}: Backend testing complete. Results saved to api.json",
self.agent.position()
)
.bright_white()
.bold()
);
} else {
error!(
"{}",
format!(
"[*] {:?}: Failed to build or run backend project.",
self.agent.position()
)
.bright_red()
.bold()
);
}
Ok(())
}
async fn build_and_run_backend(&self, path: &str) -> Result<Option<Child>> {
match self.language {
"rust" => self.build_and_run_rust_backend(path).await,
"python" => self.build_and_run_python_backend(path).await,
"javascript" => self.build_and_run_js_backend(path).await,
_ => Ok(None),
}
}
async fn build_and_run_rust_backend(&self, path: &str) -> Result<Option<Child>> {
let build_output = Command::new("cargo")
.arg("build")
.arg("--release")
.arg("--verbose")
.current_dir(path)
.output()
.await
.expect("Failed to build backend");
if build_output.status.success() {
let child = Command::new("timeout")
.arg("10s")
.arg("cargo")
.arg("run")
.arg("--release")
.arg("--verbose")
.current_dir(path)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("Failed to run backend");
Ok(Some(child))
} else {
Ok(None)
}
}
async fn build_and_run_python_backend(&self, path: &str) -> Result<Option<Child>> {
let venv_path = format!("{path}/.venv");
let pip_path = format!("{venv_path}/bin/pip");
let venv_exists = Path::new(&venv_path).exists();
if !venv_exists {
let create_venv = Command::new("python3")
.arg("-m")
.arg("venv")
.arg(&venv_path)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
if let Ok(status) = create_venv.await {
if status.success() {
let main_py_path = format!("{path}/main.py");
let main_py_content = fs::read_to_string(&main_py_path)
.await
.expect("Failed to read main.py");
let mut packages = vec![];
for line in main_py_content.lines() {
if line.starts_with("from ") || line.starts_with("import ") {
let parts: Vec<&str> = line.split_whitespace().collect();
if let Some(pkg) = parts.get(1) {
let root_pkg = pkg.split('.').next().unwrap_or(pkg);
if !packages.contains(&root_pkg) {
packages.push(root_pkg);
}
}
}
}
if !packages.is_empty() {
if !packages.contains(&"uvicorn") {
packages.push("uvicorn");
}
if !packages.contains(&"httpx") {
packages.push("httpx");
}
for pkg in &packages {
let install_status = Command::new(&pip_path)
.arg("install")
.arg(pkg)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
match install_status.await {
Ok(status) if status.success() => {
info!(
"{}",
format!(
"[*] {:?}: Successfully installed Python package '{}'",
self.agent.position(),
pkg
)
.bright_white()
.bold()
);
}
Err(e) => {
error!(
"{}",
format!(
"[*] {:?}: Failed to install Python package '{}': {}",
self.agent.position(),
pkg,
e
)
.bright_red()
.bold()
);
}
_ => {
error!(
"{}",
format!(
"[*] {:?}: Installation of package '{}' exited with an error",
self.agent.position(),
pkg
)
.bright_red()
.bold()
);
}
}
}
}
}
}
}
let run_output = Command::new("sh")
.arg("-c")
.arg(format!(
"timeout {} '.venv/bin/python' -m uvicorn main:app --host 0.0.0.0 --port 8000",
10
))
.current_dir(path)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("Failed to run the backend application");
Ok(Some(run_output))
}
async fn build_and_run_js_backend(&self, path: &str) -> Result<Option<Child>> {
let child = Command::new("timeout")
.arg("10s")
.arg("node")
.arg("app.js")
.current_dir(path)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("Failed to run js backend");
Ok(Some(child))
}
pub fn update_bugs(&mut self, bugs: Option<Cow<'static, str>>) {
self.bugs = bugs;
}
}
#[async_trait]
impl Executor for BackendGPT {
async fn execute<'a>(
&'a mut self,
tasks: &'a mut Task,
execute: bool,
browse: bool,
max_tries: u64,
) -> Result<()> {
self.agent.update(Status::Idle);
info!(
"{}",
format!("[*] {:?}: Executing task:", self.agent.position())
.bright_white()
.bold()
);
self.display_task_info(tasks);
if browse {
#[cfg(feature = "cli")]
let pb = spinner("Opening documentation in browser...");
self.open_docs_in_browser().await;
#[cfg(feature = "cli")]
pb.finish_with_message("Documentation opened.");
}
while self.agent.status() != &Status::Completed {
#[cfg(feature = "cli")]
let pb = spinner("Thinking...");
let context = self.think();
#[cfg(feature = "cli")]
pb.finish_with_message("Thinking complete!");
#[cfg(feature = "cli")]
let pb = spinner("Planning...");
let goal = self.plan(context);
#[cfg(feature = "cli")]
pb.finish_with_message("Planning complete!");
#[cfg(feature = "cli")]
let pb = spinner("Acting on goal...");
self.act(goal.clone(), tasks, execute, max_tries).await?;
#[cfg(feature = "cli")]
pb.finish_with_message("Action complete!");
#[cfg(feature = "cli")]
let pb = spinner("Marking goal complete...");
self.mark_goal_complete(goal);
#[cfg(feature = "cli")]
pb.finish_with_message("Goal marked complete!");
#[cfg(feature = "cli")]
let pb = spinner("Reflecting...");
self.reflect();
#[cfg(feature = "cli")]
pb.finish_with_message("Reflection complete!");
if self.has_completed_objective() {
info!(
"{}",
format!("[*] {:?}: Objective complete!", self.agent.position())
.green()
.bold()
);
self.agent.update(Status::Completed);
break;
}
}
self.agent.update(Status::Idle);
Ok(())
}
}