mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-09 22:33:37 +00:00
Restructure CLI: nest runs under arc run, promote models to top-level
- `arc run <pipeline>` → `arc run start <pipeline>` - `arc runs list/prune` → `arc run list/prune` - `arc llm models list` → `arc models list` - Remove `arc llm models sync` - Add `arc models test [--provider, --model]` for connectivity testing - Clean up dead code (RunsArgs, RunsCommand, runs_command) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4a0fc63d5a
commit
b2b64dcc64
3 changed files with 97 additions and 81 deletions
|
|
@ -17,32 +17,43 @@ struct Cli {
|
|||
|
||||
#[derive(Subcommand)]
|
||||
enum Command {
|
||||
/// LLM prompt and model operations
|
||||
/// LLM prompt operations
|
||||
Llm {
|
||||
#[command(subcommand)]
|
||||
command: LlmCommand,
|
||||
},
|
||||
/// Run an agentic coding session
|
||||
Agent(arc_agent::cli::AgentArgs),
|
||||
/// Launch a pipeline
|
||||
Run(arc_workflows::cli::RunArgs),
|
||||
/// Launch and manage pipeline runs
|
||||
Run {
|
||||
#[command(subcommand)]
|
||||
command: RunCommand,
|
||||
},
|
||||
/// Validate a pipeline
|
||||
Validate(arc_workflows::cli::ValidateArgs),
|
||||
/// List and manage pipeline runs
|
||||
Runs(arc_workflows::cli::runs::RunsArgs),
|
||||
/// List and test LLM models
|
||||
Models {
|
||||
#[command(subcommand)]
|
||||
command: Option<arc_llm::cli::ModelsCommand>,
|
||||
},
|
||||
/// Start the HTTP API server
|
||||
Serve(arc_api::serve::ServeArgs),
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum RunCommand {
|
||||
/// Launch a pipeline from a .dot or .toml task file
|
||||
Start(arc_workflows::cli::RunArgs),
|
||||
/// List pipeline runs
|
||||
List(arc_workflows::cli::runs::RunsListArgs),
|
||||
/// Delete old pipeline runs
|
||||
Prune(arc_workflows::cli::runs::RunsPruneArgs),
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum LlmCommand {
|
||||
/// Execute a prompt
|
||||
Prompt(arc_llm::cli::PromptArgs),
|
||||
/// Manage models
|
||||
Models {
|
||||
#[command(subcommand)]
|
||||
command: Option<arc_llm::cli::ModelsCommand>,
|
||||
},
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
|
@ -59,9 +70,9 @@ async fn main() -> Result<()> {
|
|||
let command_name = match &cli.command {
|
||||
Command::Llm { .. } => "llm",
|
||||
Command::Agent(_) => "agent",
|
||||
Command::Run(_) => "run",
|
||||
Command::Run { .. } => "run",
|
||||
Command::Validate(_) => "validate",
|
||||
Command::Runs(_) => "runs",
|
||||
Command::Models { .. } => "models",
|
||||
Command::Serve(_) => "serve",
|
||||
};
|
||||
debug!(command = %command_name, "CLI command started");
|
||||
|
|
@ -69,21 +80,26 @@ async fn main() -> Result<()> {
|
|||
match cli.command {
|
||||
Command::Llm { command } => match command {
|
||||
LlmCommand::Prompt(args) => arc_llm::cli::run_prompt(args).await?,
|
||||
LlmCommand::Models { command } => arc_llm::cli::run_models(command).await?,
|
||||
},
|
||||
Command::Agent(args) => arc_agent::cli::run_with_args(args).await?,
|
||||
Command::Run(args) => {
|
||||
let styles: &'static arc_util::terminal::Styles =
|
||||
Box::leak(Box::new(arc_util::terminal::Styles::detect_stderr()));
|
||||
arc_workflows::cli::run::run_command(args, styles).await?;
|
||||
}
|
||||
Command::Run { command } => match command {
|
||||
RunCommand::Start(args) => {
|
||||
let styles: &'static arc_util::terminal::Styles =
|
||||
Box::leak(Box::new(arc_util::terminal::Styles::detect_stderr()));
|
||||
arc_workflows::cli::run::run_command(args, styles).await?;
|
||||
}
|
||||
RunCommand::List(args) => {
|
||||
arc_workflows::cli::runs::list_command(&args)?;
|
||||
}
|
||||
RunCommand::Prune(args) => {
|
||||
arc_workflows::cli::runs::prune_command(&args)?;
|
||||
}
|
||||
},
|
||||
Command::Validate(args) => {
|
||||
let styles = arc_util::terminal::Styles::detect_stderr();
|
||||
arc_workflows::cli::validate::validate_command(&args, &styles)?;
|
||||
}
|
||||
Command::Runs(args) => {
|
||||
arc_workflows::cli::runs::runs_command(args)?;
|
||||
}
|
||||
Command::Models { command } => arc_llm::cli::run_models(command).await?,
|
||||
Command::Serve(args) => {
|
||||
let styles: &'static arc_util::terminal::Styles =
|
||||
Box::leak(Box::new(arc_util::terminal::Styles::detect_stderr()));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use std::io::{self, IsTerminal, Read};
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use clap::{Args, Subcommand};
|
||||
|
|
@ -50,15 +51,15 @@ pub enum ModelsCommand {
|
|||
query: Option<String>,
|
||||
},
|
||||
|
||||
/// Download model metadata from OpenRouter
|
||||
Sync {
|
||||
/// URL to fetch models from
|
||||
#[arg(long, default_value = "https://openrouter.ai/api/v1/models")]
|
||||
url: String,
|
||||
/// Test model availability by sending a simple prompt
|
||||
Test {
|
||||
/// Filter by provider
|
||||
#[arg(short, long)]
|
||||
provider: Option<String>,
|
||||
|
||||
/// Output file path
|
||||
#[arg(short, long, default_value = "openrouter_models.json")]
|
||||
output: String,
|
||||
/// Test a specific model
|
||||
#[arg(short, long)]
|
||||
model: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -233,26 +234,6 @@ pub async fn run_prompt(args: PromptArgs) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn sync_models(url: &str, output: &str) -> Result<()> {
|
||||
let body = reqwest::get(url)
|
||||
.await
|
||||
.context("failed to connect to models endpoint")?
|
||||
.error_for_status()
|
||||
.context("models endpoint returned an error")?
|
||||
.text()
|
||||
.await
|
||||
.context("failed to read response body")?;
|
||||
|
||||
let json: serde_json::Value =
|
||||
serde_json::from_str(&body).context("response is not valid JSON")?;
|
||||
let pretty = serde_json::to_string_pretty(&json).context("failed to format JSON")?;
|
||||
|
||||
std::fs::write(output, &pretty).with_context(|| format!("failed to write {output}"))?;
|
||||
|
||||
eprintln!("Saved models to {output}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn run_models(command: Option<ModelsCommand>) -> Result<()> {
|
||||
let command = command.unwrap_or(ModelsCommand::List {
|
||||
provider: None,
|
||||
|
|
@ -276,10 +257,58 @@ pub async fn run_models(command: Option<ModelsCommand>) -> Result<()> {
|
|||
|
||||
print_models_table(&models);
|
||||
}
|
||||
ModelsCommand::Sync { url, output } => {
|
||||
sync_models(&url, &output).await?;
|
||||
ModelsCommand::Test { provider, model } => {
|
||||
test_models(provider.as_deref(), model.as_deref()).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_models(provider: Option<&str>, model: Option<&str>) -> Result<()> {
|
||||
let models_to_test = if let Some(model_id) = model {
|
||||
match catalog::get_model_info(model_id) {
|
||||
Some(info) => vec![info],
|
||||
None => bail!("Unknown model: {model_id}"),
|
||||
}
|
||||
} else {
|
||||
catalog::list_models(provider)
|
||||
};
|
||||
|
||||
if models_to_test.is_empty() {
|
||||
bail!("No models found");
|
||||
}
|
||||
|
||||
println!("{:<30} {:<12} RESULT", "MODEL", "PROVIDER");
|
||||
|
||||
let mut failures = 0u32;
|
||||
for info in &models_to_test {
|
||||
let params = GenerateParams::new(&info.id)
|
||||
.provider(&info.provider)
|
||||
.prompt("Say OK")
|
||||
.max_tokens(5);
|
||||
|
||||
let result =
|
||||
tokio::time::timeout(Duration::from_secs(10), generate::generate(params)).await;
|
||||
|
||||
let status = match result {
|
||||
Ok(Ok(_)) => "ok".to_string(),
|
||||
Ok(Err(e)) => {
|
||||
failures += 1;
|
||||
format!("error: {e}")
|
||||
}
|
||||
Err(_) => {
|
||||
failures += 1;
|
||||
"error: timeout (10s)".to_string()
|
||||
}
|
||||
};
|
||||
|
||||
println!("{:<30} {:<12} {status}", info.id, info.provider);
|
||||
}
|
||||
|
||||
if failures > 0 {
|
||||
bail!("{failures} model(s) failed");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,25 +2,10 @@ use std::collections::HashMap;
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Args, Subcommand};
|
||||
use clap::Args;
|
||||
use serde::Serialize;
|
||||
use tracing::{debug, info};
|
||||
|
||||
/// Arguments for the `arc runs` command.
|
||||
#[derive(Args)]
|
||||
pub struct RunsArgs {
|
||||
#[command(subcommand)]
|
||||
pub command: Option<RunsCommand>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum RunsCommand {
|
||||
/// List pipeline runs
|
||||
List(RunsListArgs),
|
||||
/// Delete old pipeline runs
|
||||
Prune(RunsPruneArgs),
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct RunsListArgs {
|
||||
/// Only show runs started before this date (YYYY-MM-DD prefix match)
|
||||
|
|
@ -336,20 +321,6 @@ pub fn prune_from(args: &RunsPruneArgs, base: &Path) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn runs_command(args: RunsArgs) -> Result<()> {
|
||||
match args.command {
|
||||
None => list_command(&RunsListArgs {
|
||||
before: None,
|
||||
pipeline: None,
|
||||
label: Vec::new(),
|
||||
orphans: false,
|
||||
json: false,
|
||||
}),
|
||||
Some(RunsCommand::List(args)) => list_command(&args),
|
||||
Some(RunsCommand::Prune(args)) => prune_command(&args),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue