Context usage progress bar
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
use clap::Parser;
|
||||
use g3_core::Agent;
|
||||
use g3_config::Config;
|
||||
use anyhow::Result;
|
||||
use tracing::{info, error};
|
||||
use clap::Parser;
|
||||
use g3_config::Config;
|
||||
use g3_core::Agent;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::DefaultEditor;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{error, info};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "g3")]
|
||||
@@ -15,103 +16,107 @@ pub struct Cli {
|
||||
/// Enable verbose logging
|
||||
#[arg(short, long)]
|
||||
pub verbose: bool,
|
||||
|
||||
|
||||
/// Show the system prompt being sent to the LLM
|
||||
#[arg(long)]
|
||||
pub show_prompt: bool,
|
||||
|
||||
|
||||
/// Show the generated code before execution
|
||||
#[arg(long)]
|
||||
pub show_code: bool,
|
||||
|
||||
|
||||
/// Configuration file path
|
||||
#[arg(short, long)]
|
||||
pub config: Option<String>,
|
||||
|
||||
|
||||
/// Task to execute (if provided, runs in single-shot mode instead of interactive)
|
||||
pub task: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn run() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
|
||||
|
||||
// Initialize logging
|
||||
let level = if cli.verbose {
|
||||
tracing::Level::DEBUG
|
||||
} else {
|
||||
tracing::Level::INFO
|
||||
};
|
||||
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(level)
|
||||
.init();
|
||||
|
||||
|
||||
tracing_subscriber::fmt().with_max_level(level).init();
|
||||
|
||||
info!("Starting G3 AI Coding Agent");
|
||||
|
||||
|
||||
// Load configuration
|
||||
let config = Config::load(cli.config.as_deref())?;
|
||||
|
||||
|
||||
// Initialize agent
|
||||
let agent = Agent::new(config).await?;
|
||||
|
||||
let mut agent = Agent::new(config).await?;
|
||||
|
||||
// Execute task or start interactive mode
|
||||
if let Some(task) = cli.task {
|
||||
// Single-shot mode
|
||||
info!("Executing task: {}", task);
|
||||
let result = agent.execute_task_with_timing(&task, None, false, cli.show_prompt, cli.show_code, true).await?;
|
||||
let result = agent
|
||||
.execute_task_with_timing(&task, None, false, cli.show_prompt, cli.show_code, true)
|
||||
.await?;
|
||||
println!("{}", result);
|
||||
} else {
|
||||
// Interactive mode (default)
|
||||
info!("Starting interactive mode");
|
||||
run_interactive(agent, cli.show_prompt, cli.show_code).await?;
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_interactive(agent: Agent, show_prompt: bool, show_code: bool) -> Result<()> {
|
||||
async fn run_interactive(mut agent: Agent, show_prompt: bool, show_code: bool) -> Result<()> {
|
||||
println!("🤖 G3 AI Coding Agent - Interactive Mode");
|
||||
println!("I solve problems by writing and executing code. Tell me what you need to accomplish!");
|
||||
println!(
|
||||
"I solve problems by writing and executing code. Tell me what you need to accomplish!"
|
||||
);
|
||||
println!();
|
||||
println!("Type 'exit' or 'quit' to exit, use Up/Down arrows for command history");
|
||||
println!("Press ESC during operations to cancel the current request");
|
||||
println!();
|
||||
|
||||
|
||||
// Initialize rustyline editor with history
|
||||
let mut rl = DefaultEditor::new()?;
|
||||
|
||||
|
||||
// Try to load history from a file in the user's home directory
|
||||
let history_file = dirs::home_dir()
|
||||
.map(|mut path| {
|
||||
path.push(".g3_history");
|
||||
path
|
||||
});
|
||||
|
||||
let history_file = dirs::home_dir().map(|mut path| {
|
||||
path.push(".g3_history");
|
||||
path
|
||||
});
|
||||
|
||||
if let Some(ref history_path) = history_file {
|
||||
let _ = rl.load_history(history_path);
|
||||
}
|
||||
|
||||
|
||||
loop {
|
||||
// Display context window progress bar before each prompt
|
||||
display_context_progress(&agent);
|
||||
|
||||
let readline = rl.readline("g3> ");
|
||||
match readline {
|
||||
Ok(line) => {
|
||||
let input = line.trim();
|
||||
|
||||
|
||||
if input == "exit" || input == "quit" {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if input.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Add to history
|
||||
rl.add_history_entry(input)?;
|
||||
|
||||
|
||||
// Create cancellation token for this request
|
||||
let cancellation_token = CancellationToken::new();
|
||||
let cancel_token_clone = cancellation_token.clone();
|
||||
|
||||
|
||||
// Spawn a task to monitor for ESC key during execution
|
||||
let esc_monitor = tokio::spawn(async move {
|
||||
// This is a simplified approach - in a real implementation,
|
||||
@@ -119,7 +124,7 @@ async fn run_interactive(agent: Agent, show_prompt: bool, show_code: bool) -> Re
|
||||
// For now, we'll just provide the cancellation infrastructure
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(3600)).await;
|
||||
});
|
||||
|
||||
|
||||
// Execute task with cancellation support
|
||||
let execution_result = tokio::select! {
|
||||
result = agent.execute_task_with_timing_cancellable(
|
||||
@@ -135,7 +140,7 @@ async fn run_interactive(agent: Agent, show_prompt: bool, show_code: bool) -> Re
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
match execution_result {
|
||||
Ok(response) => println!("{}", response),
|
||||
Err(e) => {
|
||||
@@ -146,27 +151,47 @@ async fn run_interactive(agent: Agent, show_prompt: bool, show_code: bool) -> Re
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(ReadlineError::Interrupted) => {
|
||||
println!("CTRL-C");
|
||||
continue;
|
||||
},
|
||||
}
|
||||
Err(ReadlineError::Eof) => {
|
||||
println!("CTRL-D");
|
||||
break;
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Error: {:?}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Save history before exiting
|
||||
if let Some(ref history_path) = history_file {
|
||||
let _ = rl.save_history(history_path);
|
||||
}
|
||||
|
||||
|
||||
println!("👋 Goodbye!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_context_progress(agent: &Agent) {
|
||||
let context = agent.get_context_window();
|
||||
let percentage = context.percentage_used();
|
||||
|
||||
// Create a simple visual progress bar using the requested characters (10 dots max)
|
||||
let bar_width = 10;
|
||||
let filled_width = ((percentage / 100.0) * bar_width as f32) as usize;
|
||||
let empty_width = bar_width - filled_width;
|
||||
|
||||
let filled_chars = "●".repeat(filled_width);
|
||||
let empty_chars = "○".repeat(empty_width);
|
||||
let progress_bar = format!("{}{}", filled_chars, empty_chars);
|
||||
|
||||
// Print context info with visual progress bar
|
||||
println!(
|
||||
"Context: {} {:.1}% | {}/{} tokens",
|
||||
progress_bar, percentage, context.used_tokens, context.total_tokens
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user