use clap::{Parser, Subcommand};
use std::process;
mod commands;
mod error;
mod output;
use commands::{analyze, bench, compare, config, dashboard, eval, optimize, test};
#[derive(Parser)]
#[command(name = "llm-test-bench")]
#[command(author = "LLM Test Bench Contributors")]
#[command(version)]
#[command(about = "A production-grade CLI for testing and benchmarking LLM applications", long_about = None)]
#[command(propagate_version = true)]
#[command(arg_required_else_help = true)]
struct Cli {
#[command(subcommand)]
command: Commands,
#[arg(short, long, global = true)]
verbose: bool,
#[arg(long, global = true)]
no_color: bool,
}
#[derive(Subcommand)]
enum Commands {
#[command(visible_alias = "t")]
Test(test::TestArgs),
#[command(visible_alias = "b")]
Bench(bench::BenchArgs),
#[command(visible_alias = "e")]
Eval(eval::EvalArgs),
#[command(visible_alias = "c")]
Compare(compare::CompareArgs),
#[command(visible_alias = "d")]
Dashboard(dashboard::DashboardArgs),
#[command(visible_alias = "a")]
Analyze(analyze::AnalyzeArgs),
#[command(visible_alias = "o")]
Optimize(optimize::OptimizeArgs),
#[command(subcommand)]
Config(config::ConfigCommands),
Completions {
#[arg(value_enum)]
shell: clap_complete::Shell,
},
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.init();
let cli = Cli::parse();
if cli.no_color {
colored::control::set_override(false);
}
let result = match cli.command {
Commands::Test(args) => test::execute(args, cli.verbose).await,
Commands::Bench(args) => bench::execute(args, cli.verbose).await,
Commands::Eval(args) => eval::execute(args, cli.verbose).await,
Commands::Compare(args) => compare::execute(args, cli.verbose).await,
Commands::Dashboard(args) => dashboard::execute(args, cli.verbose).await,
Commands::Analyze(args) => analyze::execute(args, cli.verbose).await,
Commands::Optimize(args) => optimize::execute(args, cli.verbose).await,
Commands::Config(cmd) => config::execute(cmd, cli.verbose).await,
Commands::Completions { shell } => {
generate_completions(shell);
Ok(())
}
};
if let Err(e) = result {
eprintln!("Error: {}", e);
if cli.verbose {
eprintln!("\nCaused by:");
for cause in e.chain().skip(1) {
eprintln!(" {}", cause);
}
}
process::exit(1);
}
}
fn generate_completions(shell: clap_complete::Shell) {
use clap::CommandFactory;
use clap_complete::generate;
use std::io;
let mut cmd = Cli::command();
let bin_name = cmd.get_name().to_string();
generate(shell, &mut cmd, bin_name, &mut io::stdout());
}