use std::{path::PathBuf, time::Duration};
use clap::{Args, Parser, Subcommand};
use lmcpp::*;
#[derive(Debug, Parser)]
#[command(name = "lmcpp-server-cli", version)]
struct Cli {
#[command(flatten)]
recipe: RecipeSpec,
#[command(flatten)]
model: ModelSource,
#[arg(long)]
http: bool,
#[arg(long)]
webui: bool,
#[command(flatten)]
timing: Budgets,
#[command(subcommand)]
cmd: Option<Cmd>,
}
#[derive(Debug, Subcommand)]
enum Cmd {
Pids,
KillAll,
}
#[derive(Debug, Args)]
struct ModelSource {
#[arg(long, short = 'm', value_name = "PATH", conflicts_with = "model_url")]
local_model_path: Option<PathBuf>,
#[arg(
long,
short = 'u',
value_name = "URL",
conflicts_with = "local_model_path"
)]
model_url: Option<url::Url>,
}
#[derive(Debug, Args)]
struct Budgets {
#[arg(long, default_value_t = 120)]
load_budget_secs: u32,
#[arg(long, default_value_t = 300)]
download_budget_secs: u32,
}
fn main() -> LmcppResult<()> {
let cli = Cli::parse();
match cli.cmd {
Some(Cmd::Pids) => {
let pids = get_all_server_pids(LMCPP_SERVER_EXECUTABLE);
if pids.is_empty() {
println!("No running `{}` processes.", LMCPP_SERVER_EXECUTABLE);
} else {
println!("Running `{}` instances: {pids:?}", LMCPP_SERVER_EXECUTABLE);
}
return Ok(());
}
Some(Cmd::KillAll) => {
kill_all_servers(LMCPP_SERVER_EXECUTABLE)?;
println!(
"Sent termination signal to all `{}` processes.",
LMCPP_SERVER_EXECUTABLE
);
return Ok(());
}
None => { }
}
let toolchain = LmcppToolChain::builder()
.compute_backend(cli.recipe.backend)
.maybe_repo_tag(cli.recipe.repo_tag)
.build_install_mode(cli.recipe.mode)
.build_args(cli.recipe.build_args)
.build()?;
let builder = LmcppServerLauncher::builder()
.toolchain(toolchain)
.webui(cli.webui)
.http(cli.http)
.load_budget(LoadBudget(Duration::from_secs(
cli.timing.load_budget_secs.into(),
)))
.download_budget(DownloadBudget(Duration::from_secs(
cli.timing.download_budget_secs.into(),
)));
let server = if let Some(p) = cli.model.local_model_path {
builder
.server_args(ServerArgs::builder().model(p)?.build())
.load()?
} else if let Some(url) = cli.model.model_url {
builder
.server_args(ServerArgs::builder().model_url(url)?.build())
.load()?
} else {
builder.server_args(ServerArgs::default()).load()?
};
println!("✅ server running: {server}");
println!("Press Ctrl-C to stop.");
let (tx, rx) = std::sync::mpsc::channel();
ctrlc::set_handler(move || {
let _ = tx.send(());
})
.map_err(|e| LmcppError::Internal(format!("Failed to set Ctrl-C handler: {e}")))?;
let _ = rx.recv();
println!("🔻 server stopped");
Ok(())
}