1use std::path::PathBuf;
2
3use crate::common::DtypeArg;
4use anyhow::Context;
5use clap::Subcommand;
6
7use zsfm_core::ForecastOutput;
8use zsfm_gguf::GGMLType;
9use zsfm_lag_llama::config::LagLlamaConfig;
10use zsfm_lag_llama::convert::{convert, ConvertOptions};
11
12#[derive(Subcommand)]
13pub enum Command {
14 Upload {
16 #[arg(short, long, default_value = "amaye15/lag-llama-gguf")]
17 repo: String,
18 #[arg(long, env = "HF_TOKEN")]
19 token: String,
20 #[arg(long, default_value = ".")]
22 root: PathBuf,
23 },
24 Convert {
35 #[arg(short, long, default_value = "time-series-foundation-models/Lag-Llama")]
36 model: String,
37 #[arg(short, long, default_value = "gguf/lag_llama-f32.gguf")]
38 output: PathBuf,
39 #[arg(long, default_value = "f32")]
40 dtype: DtypeArg,
41 #[arg(long, default_value = "models")]
42 model_dir: PathBuf,
43 #[arg(long, env = "HF_TOKEN")]
44 token: Option<String>,
45 #[arg(long)]
47 redownload: bool,
48 },
49 InspectTensors { path: PathBuf },
52 Infer {
59 #[arg(short, long, default_value = "gguf/lag_llama-f32.gguf")]
60 gguf: PathBuf,
61 },
62
63 Delete {
70 #[arg(short, long, default_value = "time-series-foundation-models/Lag-Llama")]
72 model: String,
73 #[arg(long, default_value = "models")]
75 model_dir: PathBuf,
76 #[arg(short, long)]
78 output: Option<PathBuf>,
79 },
80}
81
82pub async fn run(command: Command) -> anyhow::Result<()> {
83 match command {
84 Command::Delete {
85 model,
86 model_dir,
87 output,
88 } => {
89 let canonical = zsfm_hub::canonical_gguf_path(&model_dir, &model);
90 crate::common::delete_cached_model(&canonical, output.as_deref())?;
91 }
92 Command::Upload { repo, token, root } => {
93 zsfm_hub::upload_repo(&repo, &token, &root).await?;
94 }
95
96 Command::Convert {
97 model,
98 output,
99 dtype,
100 model_dir,
101 token,
102 redownload,
103 } => {
104 let canonical = zsfm_hub::canonical_gguf_path(&model_dir, &model);
105 if crate::common::try_recast_from_cache(&canonical, &output, dtype.into(), redownload)?
106 {
107 return Ok(());
108 }
109 if let Some(parent) = canonical.parent() {
113 std::fs::create_dir_all(parent).context("create canonical GGUF dir")?;
114 }
115
116 eprintln!("Downloading {model} into {} …", model_dir.display());
117 let ckpt_path =
118 zsfm_hub::download_file(&model, "lag-llama.ckpt", token.as_deref(), &model_dir)
119 .await
120 .context("download failed")?;
121
122 let config = LagLlamaConfig::default_from_ckpt();
123 let f32_opts = ConvertOptions {
124 output_dtype: GGMLType::F32,
125 };
126 convert(&ckpt_path, &config, &f32_opts, &canonical)?;
127 eprintln!("Wrote canonical F32 GGUF to {} …", canonical.display());
128 let _ = std::fs::remove_file(&ckpt_path);
129
130 zsfm_checkpoint::recast(&canonical, &output, dtype.into())?;
131 eprintln!("Wrote {}", output.display());
132 }
133
134 Command::InspectTensors { path } => crate::common::inspect_tensors(&path)?,
135
136 Command::Infer { gguf } => {
137 run_infer_burn(&gguf).await?;
138 }
139 }
140
141 Ok(())
142}
143
144async fn run_infer_burn(gguf: &std::path::PathBuf) -> anyhow::Result<()> {
145 use zsfm_lag_llama::infer::BurnLagLlamaModel;
146
147 let buf = zsfm_core::read_stdin_limited()?;
148 let req: serde_json::Value = serde_json::from_str(&buf).context("parse JSON input")?;
149 let contexts = zsfm_core::parse_mv_contexts(req["context"].clone())?;
150 let horizon = zsfm_core::parse_horizon(&req)?;
151 let config = LagLlamaConfig::default_from_ckpt();
152
153 eprintln!("Loading Burn model from {} …", gguf.display());
154 let model = BurnLagLlamaModel::load(gguf, config).context("load model")?;
155
156 let mut fc_outputs = Vec::new();
157 let mut total_ctx = 0usize;
158 for raw_variates in &contexts {
159 anyhow::ensure!(
160 raw_variates.len() == 1,
161 "Lag-Llama only supports univariate forecasting (1 variate per context)"
162 );
163 let ctx = &raw_variates[0];
164 anyhow::ensure!(!ctx.is_empty(), "context series must not be empty");
165 total_ctx += ctx.len();
166 let point = model.forecast(ctx, horizon).context("forecast")?;
167 fc_outputs.push(ForecastOutput::Univariate {
168 point,
169 quantiles: Default::default(),
170 });
171 }
172 println!(
173 "{}",
174 zsfm_core::forecast_response_json("lag-llama", total_ctx, horizon, fc_outputs)?
175 );
176 Ok(())
177}