Execution working

This commit is contained in:
Dhanji Prasanna
2025-09-05 13:13:41 +10:00
parent 57626042a9
commit 80e5178a1f
2 changed files with 143 additions and 82 deletions

View File

@@ -1,10 +1,11 @@
use g3_providers::{ProviderRegistry, CompletionRequest, Message, MessageRole}; use anyhow::Result;
use g3_config::Config; use g3_config::Config;
use g3_execution::CodeExecutor; use g3_execution::CodeExecutor;
use anyhow::Result; use g3_providers::{CompletionRequest, Message, MessageRole, ProviderRegistry};
use serde::{Serialize, Deserialize}; use serde::{Deserialize, Serialize};
use std::path::Path; use std::path::Path;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use tracing::field::debug;
use tracing::info; use tracing::info;
pub struct Agent { pub struct Agent {
@@ -52,7 +53,7 @@ pub struct CodeMetrics {
impl Agent { impl Agent {
pub async fn new(config: Config) -> Result<Self> { pub async fn new(config: Config) -> Result<Self> {
let mut providers = ProviderRegistry::new(); let mut providers = ProviderRegistry::new();
// Register providers based on configuration // Register providers based on configuration
if let Some(openai_config) = &config.providers.openai { if let Some(openai_config) = &config.providers.openai {
let openai_provider = crate::providers::openai::OpenAIProvider::new( let openai_provider = crate::providers::openai::OpenAIProvider::new(
@@ -62,7 +63,7 @@ impl Agent {
)?; )?;
providers.register(openai_provider); providers.register(openai_provider);
} }
if let Some(anthropic_config) = &config.providers.anthropic { if let Some(anthropic_config) = &config.providers.anthropic {
let anthropic_provider = crate::providers::anthropic::AnthropicProvider::new( let anthropic_provider = crate::providers::anthropic::AnthropicProvider::new(
anthropic_config.api_key.clone(), anthropic_config.api_key.clone(),
@@ -70,19 +71,19 @@ impl Agent {
)?; )?;
providers.register(anthropic_provider); providers.register(anthropic_provider);
} }
// Set default provider // Set default provider
providers.set_default(&config.providers.default_provider)?; providers.set_default(&config.providers.default_provider)?;
Ok(Self { providers, config }) Ok(Self { providers, config })
} }
pub async fn analyze(&self, path: &str) -> Result<AnalysisResult> { pub async fn analyze(&self, path: &str) -> Result<AnalysisResult> {
info!("Analyzing path: {}", path); info!("Analyzing path: {}", path);
let content = self.read_file_or_directory(path)?; let content = self.read_file_or_directory(path)?;
let provider = self.providers.get(None)?; let provider = self.providers.get(None)?;
let messages = vec![ let messages = vec![
Message { Message {
role: MessageRole::System, role: MessageRole::System,
@@ -93,16 +94,16 @@ impl Agent {
content: format!("Please analyze this code:\n\n{}", content), content: format!("Please analyze this code:\n\n{}", content),
}, },
]; ];
let request = CompletionRequest { let request = CompletionRequest {
messages, messages,
max_tokens: Some(2048), max_tokens: Some(2048),
temperature: Some(0.1), temperature: Some(0.1),
stream: false, stream: false,
}; };
let response = provider.complete(request).await?; let response = provider.complete(request).await?;
// For now, return a simplified analysis // For now, return a simplified analysis
// In a real implementation, we'd parse the LLM response into structured data // In a real implementation, we'd parse the LLM response into structured data
Ok(AnalysisResult { Ok(AnalysisResult {
@@ -116,12 +117,12 @@ impl Agent {
}, },
}) })
} }
pub async fn generate(&self, description: &str) -> Result<String> { pub async fn generate(&self, description: &str) -> Result<String> {
info!("Generating code for: {}", description); info!("Generating code for: {}", description);
let provider = self.providers.get(None)?; let provider = self.providers.get(None)?;
let messages = vec![ let messages = vec![
Message { Message {
role: MessageRole::System, role: MessageRole::System,
@@ -132,24 +133,24 @@ impl Agent {
content: description.to_string(), content: description.to_string(),
}, },
]; ];
let request = CompletionRequest { let request = CompletionRequest {
messages, messages,
max_tokens: Some(2048), max_tokens: Some(2048),
temperature: Some(0.2), temperature: Some(0.2),
stream: false, stream: false,
}; };
let response = provider.complete(request).await?; let response = provider.complete(request).await?;
Ok(response.content) Ok(response.content)
} }
pub async fn review(&self, path: &str) -> Result<String> { pub async fn review(&self, path: &str) -> Result<String> {
info!("Reviewing path: {}", path); info!("Reviewing path: {}", path);
let content = self.read_file_or_directory(path)?; let content = self.read_file_or_directory(path)?;
let provider = self.providers.get(None)?; let provider = self.providers.get(None)?;
let messages = vec![ let messages = vec![
Message { Message {
role: MessageRole::System, role: MessageRole::System,
@@ -160,33 +161,62 @@ impl Agent {
content: format!("Please review this code:\n\n{}", content), content: format!("Please review this code:\n\n{}", content),
}, },
]; ];
let request = CompletionRequest { let request = CompletionRequest {
messages, messages,
max_tokens: Some(2048), max_tokens: Some(2048),
temperature: Some(0.1), temperature: Some(0.1),
stream: false, stream: false,
}; };
let response = provider.complete(request).await?; let response = provider.complete(request).await?;
Ok(response.content) Ok(response.content)
} }
pub async fn execute_task(&self, description: &str, language: Option<&str>, _auto_execute: bool) -> Result<String> { pub async fn execute_task(
self.execute_task_with_options(description, language, false, false, false).await &self,
description: &str,
language: Option<&str>,
_auto_execute: bool,
) -> Result<String> {
self.execute_task_with_options(description, language, false, false, false)
.await
} }
pub async fn execute_task_with_options(&self, description: &str, language: Option<&str>, _auto_execute: bool, show_prompt: bool, show_code: bool) -> Result<String> { pub async fn execute_task_with_options(
self.execute_task_with_timing(description, language, _auto_execute, show_prompt, show_code, false).await &self,
description: &str,
language: Option<&str>,
_auto_execute: bool,
show_prompt: bool,
show_code: bool,
) -> Result<String> {
self.execute_task_with_timing(
description,
language,
_auto_execute,
show_prompt,
show_code,
false,
)
.await
} }
pub async fn execute_task_with_timing(&self, description: &str, language: Option<&str>, _auto_execute: bool, show_prompt: bool, show_code: bool, show_timing: bool) -> Result<String> { pub async fn execute_task_with_timing(
&self,
description: &str,
language: Option<&str>,
_auto_execute: bool,
show_prompt: bool,
show_code: bool,
show_timing: bool,
) -> Result<String> {
info!("Executing task: {}", description); info!("Executing task: {}", description);
let total_start = Instant::now(); let total_start = Instant::now();
let provider = self.providers.get(None)?; let provider = self.providers.get(None)?;
let system_prompt = format!( let system_prompt = format!(
"You are G3, a code-first AI agent. Your goal is to solve problems by writing and executing code autonomously. "You are G3, a code-first AI agent. Your goal is to solve problems by writing and executing code autonomously.
@@ -210,13 +240,13 @@ Format your response as:
[code] [code]
``` ```
Then execute it and show the output.", Then execute it and show the output.",
if let Some(lang) = language { if let Some(lang) = language {
format!(" (prefer {})", lang) format!(" (prefer {})", lang)
} else { } else {
" based on the task type".to_string() " based on the task type".to_string()
} }
); );
if show_prompt { if show_prompt {
println!("🔍 System Prompt:"); println!("🔍 System Prompt:");
println!("================"); println!("================");
@@ -224,7 +254,7 @@ Then execute it and show the output.",
println!("================"); println!("================");
println!(); println!();
} }
let messages = vec![ let messages = vec![
Message { Message {
role: MessageRole::System, role: MessageRole::System,
@@ -235,27 +265,36 @@ Then execute it and show the output.",
content: format!("Task: {}", description), content: format!("Task: {}", description),
}, },
]; ];
let request = CompletionRequest { let request = CompletionRequest {
messages, messages,
max_tokens: Some(2048), max_tokens: Some(2048),
temperature: Some(0.2), temperature: Some(0.2),
stream: false, stream: false,
}; };
// Time the LLM call // Time the LLM call
let llm_start = Instant::now(); let llm_start = Instant::now();
let response = provider.complete(request).await?; let response = provider.complete(request).await?;
let llm_duration = llm_start.elapsed(); let llm_duration = llm_start.elapsed();
// Log the LLM response before passing to CodeExecutor
// debug!(
// "LLM Response received ({} chars): {}\n=== END ===",
// response.content.len(),
// response.content
// );
// Time the code execution // Time the code execution
let exec_start = Instant::now(); let exec_start = Instant::now();
let executor = CodeExecutor::new(); let executor = CodeExecutor::new();
let result = executor.execute_from_response_with_options(&response.content, show_code).await?; let result = executor
.execute_from_response_with_options(&response.content, show_code)
.await?;
let exec_duration = exec_start.elapsed(); let exec_duration = exec_start.elapsed();
let total_duration = total_start.elapsed(); let total_duration = total_start.elapsed();
if show_timing { if show_timing {
let timing_summary = format!( let timing_summary = format!(
"\n⏱️ Task Summary:\n LLM call: {}\n Code execution: {}\n Total time: {}", "\n⏱️ Task Summary:\n LLM call: {}\n Code execution: {}\n Total time: {}",
@@ -268,10 +307,10 @@ Then execute it and show the output.",
Ok(result) Ok(result)
} }
} }
fn format_duration(duration: Duration) -> String { fn format_duration(duration: Duration) -> String {
let total_ms = duration.as_millis(); let total_ms = duration.as_millis();
if total_ms < 1000 { if total_ms < 1000 {
format!("{}ms", total_ms) format!("{}ms", total_ms)
} else if total_ms < 60_000 { } else if total_ms < 60_000 {
@@ -283,12 +322,12 @@ Then execute it and show the output.",
format!("{}m {:.1}s", minutes, remaining_seconds) format!("{}m {:.1}s", minutes, remaining_seconds)
} }
} }
pub async fn create_automation(&self, workflow: &str) -> Result<String> { pub async fn create_automation(&self, workflow: &str) -> Result<String> {
info!("Creating automation for: {}", workflow); info!("Creating automation for: {}", workflow);
let provider = self.providers.get(None)?; let provider = self.providers.get(None)?;
let messages = vec![ let messages = vec![
Message { Message {
role: MessageRole::System, role: MessageRole::System,
@@ -299,29 +338,29 @@ Then execute it and show the output.",
content: format!("Create an automation script for: {}", workflow), content: format!("Create an automation script for: {}", workflow),
}, },
]; ];
let request = CompletionRequest { let request = CompletionRequest {
messages, messages,
max_tokens: Some(2048), max_tokens: Some(2048),
temperature: Some(0.1), temperature: Some(0.1),
stream: false, stream: false,
}; };
let response = provider.complete(request).await?; let response = provider.complete(request).await?;
Ok(response.content) Ok(response.content)
} }
pub async fn process_data(&self, operation: &str, input_file: Option<&str>) -> Result<String> { pub async fn process_data(&self, operation: &str, input_file: Option<&str>) -> Result<String> {
info!("Processing data: {}", operation); info!("Processing data: {}", operation);
let provider = self.providers.get(None)?; let provider = self.providers.get(None)?;
let context = if let Some(file) = input_file { let context = if let Some(file) = input_file {
format!("Operation: {}\nInput file: {}", operation, file) format!("Operation: {}\nInput file: {}", operation, file)
} else { } else {
format!("Operation: {}", operation) format!("Operation: {}", operation)
}; };
let messages = vec![ let messages = vec![
Message { Message {
role: MessageRole::System, role: MessageRole::System,
@@ -332,29 +371,29 @@ Then execute it and show the output.",
content: context, content: context,
}, },
]; ];
let request = CompletionRequest { let request = CompletionRequest {
messages, messages,
max_tokens: Some(2048), max_tokens: Some(2048),
temperature: Some(0.1), temperature: Some(0.1),
stream: false, stream: false,
}; };
let response = provider.complete(request).await?; let response = provider.complete(request).await?;
Ok(response.content) Ok(response.content)
} }
pub async fn execute_web_task(&self, task: &str, url: Option<&str>) -> Result<String> { pub async fn execute_web_task(&self, task: &str, url: Option<&str>) -> Result<String> {
info!("Executing web task: {}", task); info!("Executing web task: {}", task);
let provider = self.providers.get(None)?; let provider = self.providers.get(None)?;
let context = if let Some(url) = url { let context = if let Some(url) = url {
format!("Task: {}\nURL: {}", task, url) format!("Task: {}\nURL: {}", task, url)
} else { } else {
format!("Task: {}", task) format!("Task: {}", task)
}; };
let messages = vec![ let messages = vec![
Message { Message {
role: MessageRole::System, role: MessageRole::System,
@@ -365,29 +404,33 @@ Then execute it and show the output.",
content: context, content: context,
}, },
]; ];
let request = CompletionRequest { let request = CompletionRequest {
messages, messages,
max_tokens: Some(2048), max_tokens: Some(2048),
temperature: Some(0.2), temperature: Some(0.2),
stream: false, stream: false,
}; };
let response = provider.complete(request).await?; let response = provider.complete(request).await?;
Ok(response.content) Ok(response.content)
} }
pub async fn execute_file_operation(&self, operation: &str, path: Option<&str>) -> Result<String> { pub async fn execute_file_operation(
&self,
operation: &str,
path: Option<&str>,
) -> Result<String> {
info!("Executing file operation: {}", operation); info!("Executing file operation: {}", operation);
let provider = self.providers.get(None)?; let provider = self.providers.get(None)?;
let context = if let Some(path) = path { let context = if let Some(path) = path {
format!("Operation: {}\nPath: {}", operation, path) format!("Operation: {}\nPath: {}", operation, path)
} else { } else {
format!("Operation: {}", operation) format!("Operation: {}", operation)
}; };
let messages = vec![ let messages = vec![
Message { Message {
role: MessageRole::System, role: MessageRole::System,
@@ -398,21 +441,21 @@ Then execute it and show the output.",
content: context, content: context,
}, },
]; ];
let request = CompletionRequest { let request = CompletionRequest {
messages, messages,
max_tokens: Some(2048), max_tokens: Some(2048),
temperature: Some(0.1), temperature: Some(0.1),
stream: false, stream: false,
}; };
let response = provider.complete(request).await?; let response = provider.complete(request).await?;
Ok(response.content) Ok(response.content)
} }
fn read_file_or_directory(&self, path: &str) -> Result<String> { fn read_file_or_directory(&self, path: &str) -> Result<String> {
let path = Path::new(path); let path = Path::new(path);
if path.is_file() { if path.is_file() {
Ok(std::fs::read_to_string(path)?) Ok(std::fs::read_to_string(path)?)
} else if path.is_dir() { } else if path.is_dir() {
@@ -424,23 +467,27 @@ Then execute it and show the output.",
anyhow::bail!("Path does not exist: {}", path.display()) anyhow::bail!("Path does not exist: {}", path.display())
} }
} }
fn read_directory_recursive(&self, dir: &Path, content: &mut String) -> Result<()> { fn read_directory_recursive(&self, dir: &Path, content: &mut String) -> Result<()> {
for entry in std::fs::read_dir(dir)? { for entry in std::fs::read_dir(dir)? {
let entry = entry?; let entry = entry?;
let path = entry.path(); let path = entry.path();
if path.is_file() { if path.is_file() {
if let Some(ext) = path.extension() { if let Some(ext) = path.extension() {
// Only read common code files // Only read common code files
if matches!(ext.to_str(), Some("rs" | "py" | "js" | "ts" | "go" | "java" | "cpp" | "c" | "h")) { if matches!(
ext.to_str(),
Some("rs" | "py" | "js" | "ts" | "go" | "java" | "cpp" | "c" | "h")
) {
content.push_str(&format!("\n--- {} ---\n", path.display())); content.push_str(&format!("\n--- {} ---\n", path.display()));
if let Ok(file_content) = std::fs::read_to_string(&path) { if let Ok(file_content) = std::fs::read_to_string(&path) {
content.push_str(&file_content); content.push_str(&file_content);
} }
} }
} }
} else if path.is_dir() && !path.file_name().unwrap().to_str().unwrap().starts_with('.') { } else if path.is_dir() && !path.file_name().unwrap().to_str().unwrap().starts_with('.')
{
self.read_directory_recursive(&path, content)?; self.read_directory_recursive(&path, content)?;
} }
} }
@@ -458,13 +505,21 @@ impl std::fmt::Display for AnalysisResult {
writeln!(f)?; writeln!(f)?;
writeln!(f, "Metrics:")?; writeln!(f, "Metrics:")?;
writeln!(f, "- Lines of Code: {}", self.metrics.lines_of_code)?; writeln!(f, "- Lines of Code: {}", self.metrics.lines_of_code)?;
writeln!(f, "- Complexity Score: {:.2}", self.metrics.complexity_score)?; writeln!(
writeln!(f, "- Maintainability Index: {:.2}", self.metrics.maintainability_index)?; f,
"- Complexity Score: {:.2}",
self.metrics.complexity_score
)?;
writeln!(
f,
"- Maintainability Index: {:.2}",
self.metrics.maintainability_index
)?;
Ok(()) Ok(())
} }
} }
pub mod providers { pub mod providers {
pub mod openai;
pub mod anthropic; pub mod anthropic;
pub mod openai;
} }

View File

@@ -29,6 +29,7 @@ impl CodeExecutor {
/// Extract code blocks from LLM response and execute them with UI options /// Extract code blocks from LLM response and execute them with UI options
pub async fn execute_from_response_with_options(&self, response: &str, show_code: bool) -> Result<String> { pub async fn execute_from_response_with_options(&self, response: &str, show_code: bool) -> Result<String> {
debug!("CodeExecutor received response ({} chars): {}", response.len(), response);
let code_blocks = self.extract_code_blocks(response)?; let code_blocks = self.extract_code_blocks(response)?;
if code_blocks.is_empty() { if code_blocks.is_empty() {
@@ -89,20 +90,25 @@ impl CodeExecutor {
/// Extract code blocks from markdown-formatted text /// Extract code blocks from markdown-formatted text
fn extract_code_blocks(&self, text: &str) -> Result<Vec<(String, String)>> { fn extract_code_blocks(&self, text: &str) -> Result<Vec<(String, String)>> {
let re = Regex::new(r"```(\w+)?\n(.*?)```")?; let re = Regex::new(r"(?s)```(\w+)?\n(.*?)```")?;
let mut blocks = Vec::new(); let mut blocks = Vec::new();
debug!("Extracting code blocks from text: {}", text);
for cap in re.captures_iter(text) { for cap in re.captures_iter(text) {
let language = cap.get(1) let language = cap.get(1)
.map(|m| m.as_str().to_lowercase()) .map(|m| m.as_str().to_lowercase())
.unwrap_or_else(|| "bash".to_string()); // Default to bash .unwrap_or_else(|| "bash".to_string()); // Default to bash
let code = cap.get(2).map(|m| m.as_str()).unwrap_or("").trim(); let code = cap.get(2).map(|m| m.as_str()).unwrap_or("").trim();
debug!("Found code block - language: '{}', code: '{}'", language, code);
if !code.is_empty() { if !code.is_empty() {
blocks.push((language, code.to_string())); blocks.push((language, code.to_string()));
} }
} }
debug!("Total code blocks found: {}", blocks.len());
Ok(blocks) Ok(blocks)
} }