diff --git a/README.md b/README.md index 7d5d1812..68d26dfb 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,8 @@ wally run qwen3 "Hello" # one answer and exit wally serve qwen3 # OpenAI-compatible API on :8080 (macOS, Linux) ``` -`wally models list --all` shows everything you can pull. Any Hugging Face GGUF +`wally models list --local` shows everything you can pull; `--cloud` shows the +cloud models on your account, and `--all` shows both. Any Hugging Face GGUF works too, by its full path: ```bash @@ -120,7 +121,7 @@ has the full catalog. | Models | | |---|---| -| `wally models list --all` | models on this machine, or the whole catalog | +| `wally models list` | models on this machine; `--local` for the whole catalog, `--cloud` for your cloud models, `--all` for both | | `wally models pull` / `rm` | download or delete a model | | `wally models show` | size, context window, files | | `wally models default` | the model coding tools open with | diff --git a/docs/MODELS.md b/docs/MODELS.md index 1414274c..f70670d8 100644 --- a/docs/MODELS.md +++ b/docs/MODELS.md @@ -1,6 +1,6 @@ # Models -`wally models list --all` is the live list. +`wally models list --local` is the live list. `--cloud` lists the cloud models on your account, and `--all` shows both. This table groups models by publisher; the id you pull depends on the engine you want: diff --git a/src/account/console.rs b/src/account/console.rs index c1f208d9..c5a4253c 100644 --- a/src/account/console.rs +++ b/src/account/console.rs @@ -1784,6 +1784,17 @@ impl ConsoleClient { &self, console_url: &str, access_token: &str, + ) -> (IdentityResult, Vec, String) { + self.fetch_models_within(console_url, access_token, 0) + } + + /// `fetch_models` bounded to `timeout_ms` in all (0: the transport's + /// defaults), for a caller that has something else to show if it fails. + pub fn fetch_models_within( + &self, + console_url: &str, + access_token: &str, + timeout_ms: i32, ) -> (IdentityResult, Vec, String) { if !super::session_token_is_safe(access_token) { return ( @@ -1801,7 +1812,7 @@ impl ConsoleClient { url: format!("{origin}/v1/models"), body: String::new(), bearer_token: access_token.to_string(), - timeout_ms: 0, + timeout_ms, }; let response = match self.send(request) { Ok(response) => response, diff --git a/src/commands/cmd_list.rs b/src/commands/cmd_list.rs index bb5f7c48..c358f5e3 100644 --- a/src/commands/cmd_list.rs +++ b/src/commands/cmd_list.rs @@ -1,5 +1,7 @@ //! `wally models list` (alias `wally models ls`) — downloaded models by -//! default, the whole catalog with --all (port of src/commands/cmd_list.cpp). +//! default; the whole local catalog with --local; the signed-in account's +//! cloud models with --cloud; both with --all (port of +//! src/commands/cmd_list.cpp). //! //! The registry is refreshed with rescan_local so on-disk artifacts pulled by //! previous runs (or by the test rig / playground tooling) are linked before @@ -7,6 +9,7 @@ use std::collections::HashMap; +use crate::account::{ConsoleClient, ConsoleSession}; use crate::bootstrap::{bootstrap, GlobalOptions}; use crate::cli::App; use crate::commands::model_labels; @@ -209,7 +212,78 @@ fn settle_row_id_and_size(row: &mut GroupedRow) { } } -fn run_list(options: &GlobalOptions, show_all: bool) -> i32 { +// A list command must not sit on a dead network; the cached list is the +// fallback. +const CLOUD_LOOKUP_TIMEOUT_MS: i32 = 3_000; + +/// Where the cloud rows came from. +enum CloudModels { + Live(Vec), + /// The console could not be reached; these are the ids the last + /// successful lookup saved (`account::cached_model_ids`). + Cached(Vec), +} + +/// The hosted models the signed-in account can use, or why there are none to +/// show. Ordered as the console lists them. +fn cloud_models() -> Result { + let mut session = ConsoleSession::open(ConsoleClient::default())?; + match session + .call(|client, url, token| client.fetch_models_within(url, token, CLOUD_LOOKUP_TIMEOUT_MS)) + { + Ok(models) => Ok(CloudModels::Live( + models + .into_iter() + .map(|model| model.id) + .filter(|id| !id.is_empty()) + .collect(), + )), + Err(reason) => { + let cached = crate::account::cached_model_ids(); + if cached.is_empty() { + Err(reason) + } else { + Ok(CloudModels::Cached(cached)) + } + } + } +} + +/// The `[cloud]` tag, blue where stdout takes color. +fn cloud_tag(no_color: bool) -> String { + let color = !no_color + && crate::util::term::stdout_is_tty() + && crate::util::getenv("NO_COLOR").is_none(); + if color { + "\x1b[34m[cloud]\x1b[0m".to_string() + } else { + "[cloud]".to_string() + } +} + +/// Which rows `models list` shows. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Scope { + Downloaded, + Local, + Cloud, + All, +} + +impl Scope { + fn local_rows(self) -> bool { + self != Scope::Cloud + } + fn whole_catalog(self) -> bool { + matches!(self, Scope::Local | Scope::All) + } + fn cloud_rows(self) -> bool { + matches!(self, Scope::Cloud | Scope::All) + } +} + +fn run_list(options: &GlobalOptions, scope: Scope) -> i32 { + let show_all = scope.whole_catalog(); let Ok(_env) = bootstrap(options) else { return 1; }; @@ -259,7 +333,37 @@ fn run_list(options: &GlobalOptions, show_all: bool) -> i32 { } } - let (order, groups) = group_models(&all_models.models, &downloaded_ids, show_all); + let (order, groups) = if scope.local_rows() { + group_models(&all_models.models, &downloaded_ids, show_all) + } else { + (Vec::new(), HashMap::new()) + }; + + // Under --all a failure to reach the cloud never fails the local list; it + // is one line on stderr saying why they are missing. Under --cloud the + // cloud is all that was asked for, so the same failure is the error. + let mut cloud_ids: Vec = Vec::new(); + if scope.cloud_rows() { + match cloud_models() { + Ok(CloudModels::Live(ids)) => cloud_ids = ids, + Ok(CloudModels::Cached(ids)) => { + if !options.json { + out::status_line( + "could not reach Wally Cloud; cloud models are from the last time wally did", + ); + } + cloud_ids = ids; + } + Err(reason) if scope == Scope::Cloud => { + out::error_line(&format!("could not list cloud models: {reason}")); + return 1; + } + Err(reason) if !options.json => { + out::status_line(&format!("cloud models not shown: {reason}")); + } + Err(_) => {} + } + } if options.json { let mut json = out::JsonWriter::new(); @@ -278,6 +382,20 @@ fn run_list(options: &GlobalOptions, show_all: bool) -> i32 { // the group is downloaded (mirrors the pre-merge shape, which // callers already treat "" as "not downloaded"). .field_str("local_path", &row.local_path) + .field_bool("cloud", false) + .end_object(); + } + for id in &cloud_ids { + json.begin_array_object() + .field_str("id", id) + .field_str("name", id) + .field_str("modality", "llm") + .field_str("backend", "cloud") + .field_i64("size_bytes", 0) + .field_bool("downloaded", false) + .field_bool("harness_compatible", true) + .field_str("local_path", "") + .field_bool("cloud", true) .end_object(); } json.end_array().end_object(); @@ -285,7 +403,9 @@ fn run_list(options: &GlobalOptions, show_all: bool) -> i32 { return 0; } - print_pull_examples(); + if scope.local_rows() { + print_pull_examples(); + } let mut rows: Vec> = Vec::new(); for key in &order { @@ -309,11 +429,25 @@ fn run_list(options: &GlobalOptions, show_all: bool) -> i32 { ]); } + let tag = cloud_tag(options.no_color); + for id in &cloud_ids { + rows.push(vec![ + id.clone(), + "llm".to_string(), + "cloud".to_string(), + "-".to_string(), + "-".to_string(), + tag.clone(), + ]); + } + if rows.is_empty() { - out::result_line(if show_all { + out::result_line(if scope == Scope::Cloud { + "no cloud models on this account" + } else if show_all { "no models registered" } else { - "no models downloaded — try `wally models list --all` then `wally models pull `" + "no models downloaded — try `wally models list --local` then `wally models pull `" }); return 0; } @@ -324,12 +458,33 @@ fn run_list(options: &GlobalOptions, show_all: bool) -> i32 { 0 } +/// The scope the flags ask for, or why they contradict each other. +fn list_scope(all: bool, local: bool, cloud: bool) -> Result { + match (all, local, cloud) { + (false, false, false) => Ok(Scope::Downloaded), + (true, false, false) => Ok(Scope::All), + (false, true, false) => Ok(Scope::Local), + (false, false, true) => Ok(Scope::Cloud), + _ => Err("pick one of --all, --local or --cloud"), + } +} + pub fn configure_models_list(cmd: &mut App) { - cmd.add_flag("--all,-a", "Include catalog models not yet downloaded"); - cmd.callback(|p, g| { - let show_all = p.flag("--all"); - run_list(g, show_all) - }); + cmd.add_flag( + "--all,-a", + "Every local catalog model, plus your account's cloud models", + ); + cmd.add_flag("--local", "Every local catalog model, without cloud models"); + cmd.add_flag("--cloud", "Only your account's cloud models"); + cmd.callback( + |p, g| match list_scope(p.flag("--all"), p.flag("--local"), p.flag("--cloud")) { + Ok(scope) => run_list(g, scope), + Err(message) => { + out::error_line(message); + 2 + } + }, + ); } #[cfg(test)] @@ -467,4 +622,24 @@ mod tests { let (_, groups) = group_models(&models, &downloaded, true); assert_eq!(groups["qwen3-4b-instruct-2507"].size_bytes, 0); } + + #[test] + fn list_scope_maps_one_flag_to_one_scope() { + assert_eq!(list_scope(false, false, false), Ok(Scope::Downloaded)); + assert_eq!(list_scope(true, false, false), Ok(Scope::All)); + assert_eq!(list_scope(false, true, false), Ok(Scope::Local)); + assert_eq!(list_scope(false, false, true), Ok(Scope::Cloud)); + } + + #[test] + fn list_scope_refuses_two_flags_at_once() { + assert!(list_scope(true, true, false).is_err()); + assert!(list_scope(false, true, true).is_err()); + assert!(list_scope(true, false, true).is_err()); + } + + #[test] + fn cloud_tag_is_plain_when_color_is_off() { + assert_eq!(cloud_tag(true), "[cloud]"); + } } diff --git a/tests/golden/README.md b/tests/golden/README.md index 9fe14010..122bfad2 100644 --- a/tests/golden/README.md +++ b/tests/golden/README.md @@ -65,3 +65,9 @@ row's size from the build its id pulls (it had shown the MLX build's size while `pull ` fetched the GGUF), and started showing `mlx-ternary-bonsai-27b-2bit` for the one MLX-only row whose merge key names no registered model. Those sizes and that id are the only differences from the C++ capture. + +Fourteen `models list` cases were re-captured from the Rust binary when +`models list` gained `--cloud`, `--all` started adding the account's cloud +models, and `--local` took over the old meaning of `--all`. The differences are +the `--all`/`--local`/`--cloud` help lines, the empty-list hint (now `--local`), the "cloud models not shown" +line on a signed-out `--all`, and a `cloud` field on each `--json` row. diff --git a/tests/golden/expected/help__models__catalog__list.stdout b/tests/golden/expected/help__models__catalog__list.stdout index 7040f736..69714837 100644 --- a/tests/golden/expected/help__models__catalog__list.stdout +++ b/tests/golden/expected/help__models__catalog__list.stdout @@ -4,7 +4,10 @@ Usage: wally models list [OPTIONS] Options: -h, --help Show help - -a, --all Include catalog models not yet downloaded + -a, --all Every local catalog model, plus your account's + cloud models + --local Every local catalog model, without cloud models + --cloud Only your account's cloud models Examples: # Browse the whole catalog diff --git a/tests/golden/expected/help__models__list.stdout b/tests/golden/expected/help__models__list.stdout index 7040f736..69714837 100644 --- a/tests/golden/expected/help__models__list.stdout +++ b/tests/golden/expected/help__models__list.stdout @@ -4,7 +4,10 @@ Usage: wally models list [OPTIONS] Options: -h, --help Show help - -a, --all Include catalog models not yet downloaded + -a, --all Every local catalog model, plus your account's + cloud models + --local Every local catalog model, without cloud models + --cloud Only your account's cloud models Examples: # Browse the whole catalog diff --git a/tests/golden/expected/help__models__ls.stdout b/tests/golden/expected/help__models__ls.stdout index 7040f736..69714837 100644 --- a/tests/golden/expected/help__models__ls.stdout +++ b/tests/golden/expected/help__models__ls.stdout @@ -4,7 +4,10 @@ Usage: wally models list [OPTIONS] Options: -h, --help Show help - -a, --all Include catalog models not yet downloaded + -a, --all Every local catalog model, plus your account's + cloud models + --local Every local catalog model, without cloud models + --cloud Only your account's cloud models Examples: # Browse the whole catalog diff --git a/tests/golden/expected/leaf__models__list__bogus_flag.stderr b/tests/golden/expected/leaf__models__list__bogus_flag.stderr index 25d416fe..a9cc59a4 100644 --- a/tests/golden/expected/leaf__models__list__bogus_flag.stderr +++ b/tests/golden/expected/leaf__models__list__bogus_flag.stderr @@ -5,7 +5,10 @@ Usage: wally models list [OPTIONS] Options: -h, --help Show help - -a, --all Include catalog models not yet downloaded + -a, --all Every local catalog model, plus your account's + cloud models + --local Every local catalog model, without cloud models + --cloud Only your account's cloud models Examples: # Browse the whole catalog diff --git a/tests/golden/expected/leaf__models__list__extra_positional.stderr b/tests/golden/expected/leaf__models__list__extra_positional.stderr index 530ec774..e182f89a 100644 --- a/tests/golden/expected/leaf__models__list__extra_positional.stderr +++ b/tests/golden/expected/leaf__models__list__extra_positional.stderr @@ -5,7 +5,10 @@ Usage: wally models list [OPTIONS] Options: -h, --help Show help - -a, --all Include catalog models not yet downloaded + -a, --all Every local catalog model, plus your account's + cloud models + --local Every local catalog model, without cloud models + --cloud Only your account's cloud models Examples: # Browse the whole catalog diff --git a/tests/golden/expected/local__-q__models__list.stdout b/tests/golden/expected/local__-q__models__list.stdout index 2cf1d485..5a394238 100644 --- a/tests/golden/expected/local__-q__models__list.stdout +++ b/tests/golden/expected/local__-q__models__list.stdout @@ -2,4 +2,4 @@ Download a model with `wally models pull `: wally models pull qwen3-4b-instruct-2507 # llama.cpp wally models pull mlx-qwen3-4b-instruct-2507 # MLX (Apple GPU) -no models downloaded — try `wally models list --all` then `wally models pull ` +no models downloaded — try `wally models list --local` then `wally models pull ` diff --git a/tests/golden/expected/local__models__list.stdout b/tests/golden/expected/local__models__list.stdout index 2cf1d485..5a394238 100644 --- a/tests/golden/expected/local__models__list.stdout +++ b/tests/golden/expected/local__models__list.stdout @@ -2,4 +2,4 @@ Download a model with `wally models pull `: wally models pull qwen3-4b-instruct-2507 # llama.cpp wally models pull mlx-qwen3-4b-instruct-2507 # MLX (Apple GPU) -no models downloaded — try `wally models list --all` then `wally models pull ` +no models downloaded — try `wally models list --local` then `wally models pull ` diff --git a/tests/golden/expected/local__models__list__--all.stderr b/tests/golden/expected/local__models__list__--all.stderr index 998e352b..6c5af33d 100644 --- a/tests/golden/expected/local__models__list__--all.stderr +++ b/tests/golden/expected/local__models__list__--all.stderr @@ -1,3 +1,4 @@ telemetry POST /api/v2/sdk/telemetry/system -> rc=Network error (-151) http=0 body=(empty) telemetry POST /api/v2/sdk/telemetry/system -> rc=Network error (-151) http=0 body=(empty) telemetry ready | has_completed_http_setup=yes +cloud models not shown: not signed in — run `wally account login` diff --git a/tests/golden/expected/local__models__list__--all__--json.stdout b/tests/golden/expected/local__models__list__--all__--json.stdout index 21c380d4..a8c8869f 100644 --- a/tests/golden/expected/local__models__list__--all__--json.stdout +++ b/tests/golden/expected/local__models__list__--all__--json.stdout @@ -1 +1 @@ -{"models":[{"id":"bonsai-1.7b","name":"Bonsai 1.7B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":248302272,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"bonsai-27b","name":"Bonsai 27B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":3803452480,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"bonsai-4b","name":"Bonsai 4B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":572270624,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"bonsai-8b","name":"Bonsai 8B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":1158654496,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"gemma-4-12b","name":"Gemma 4 12B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":7121861440,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"gemma-4-26b-a4b","name":"Gemma 4 26B-A4B (MoE)","modality":"llm","backend":"mlx/llama.cpp","size_bytes":17010980576,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"gemma-4-31b","name":"Gemma 4 31B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":18323733440,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"gemma-4-e2b","name":"Gemma 4 E2B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":3106738272,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"gemma-4-e4b","name":"Gemma 4 E4B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":4977171584,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"granite-4.1-30b","name":"IBM Granite 4.1 30B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":17490241472,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"granite-4.1-3b","name":"IBM Granite 4.1 3B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":2099502400,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"granite-4.1-8b","name":"IBM Granite 4.1 8B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":5347915136,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"granite-4.2-30b","name":"IBM Granite 4.2 30B","modality":"llm","backend":"llama.cpp","size_bytes":18027118592,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"granite-4.2-8b","name":"IBM Granite 4.2 8B","modality":"llm","backend":"llama.cpp","size_bytes":5539627008,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"lfm2.5-1.2b","name":"LiquidAI LFM2.5 1.2B Instruct","modality":"llm","backend":"llama.cpp","size_bytes":730857472,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"lfm2.5-2.6b","name":"LiquidAI LFM2.5 2.6B","modality":"llm","backend":"llama.cpp","size_bytes":1674575872,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"lfm2.5-230m","name":"LiquidAI LFM2.5 230M","modality":"llm","backend":"llama.cpp","size_bytes":153406304,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"lfm2.5-350m","name":"LiquidAI LFM2.5 350M","modality":"llm","backend":"llama.cpp","size_bytes":229638144,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"llama-3.2-3b","name":"Llama 3.2 3B Instruct","modality":"llm","backend":"llama.cpp","size_bytes":2118123520,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"maple-preview","name":"DeepGrove Maple Preview","modality":"llm","backend":"mlx/llama.cpp","size_bytes":4984016416,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"mlx-nemotron-nano","name":"NVIDIA Llama 3.1 Nemotron Nano 8B","modality":"llm","backend":"mlx","size_bytes":4534806075,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"mlx-llama3.2","name":"Llama 3.2 1B Instruct","modality":"llm","backend":"mlx","size_bytes":712575975,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"mlx-nemotron-mini","name":"NVIDIA Nemotron Mini 4B Instruct","modality":"llm","backend":"mlx","size_bytes":2392679103,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"qwen3-0.6b","name":"Qwen3 0.6B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":670040064,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"qwen3-4b-instruct-2507","name":"Qwen3 4B Instruct 2507","modality":"llm","backend":"mlx/llama.cpp","size_bytes":4300000000,"downloaded":false,"harness_compatible":true,"local_path":""},{"id":"qwen3.8-27b","name":"Qwen3.8 27B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":17106775008,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"ternary-bonsai-1.7b","name":"Ternary-Bonsai 1.7B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":490163968,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"mlx-ternary-bonsai-27b-2bit","name":"Ternary-Bonsai 27B","modality":"llm","backend":"mlx","size_bytes":8490785104,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"ternary-bonsai-4b","name":"Ternary-Bonsai 4B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":1137806656,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"ternary-bonsai-8b","name":"Ternary-Bonsai 8B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":2310125920,"downloaded":false,"harness_compatible":false,"local_path":""},{"id":"smollm2-135m","name":"SmolLM2 135M","modality":"llm","backend":"llama.cpp","size_bytes":100663296,"downloaded":false,"harness_compatible":false,"local_path":""}]} +{"models":[{"id":"bonsai-1.7b","name":"Bonsai 1.7B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":248302272,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"bonsai-27b","name":"Bonsai 27B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":3803452480,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"bonsai-4b","name":"Bonsai 4B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":572270624,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"bonsai-8b","name":"Bonsai 8B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":1158654496,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"gemma-4-12b","name":"Gemma 4 12B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":7121861440,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"gemma-4-26b-a4b","name":"Gemma 4 26B-A4B (MoE)","modality":"llm","backend":"mlx/llama.cpp","size_bytes":17010980576,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"gemma-4-31b","name":"Gemma 4 31B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":18323733440,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"gemma-4-e2b","name":"Gemma 4 E2B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":3106738272,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"gemma-4-e4b","name":"Gemma 4 E4B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":4977171584,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"granite-4.1-30b","name":"IBM Granite 4.1 30B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":17490241472,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"granite-4.1-3b","name":"IBM Granite 4.1 3B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":2099502400,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"granite-4.1-8b","name":"IBM Granite 4.1 8B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":5347915136,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"granite-4.2-30b","name":"IBM Granite 4.2 30B","modality":"llm","backend":"llama.cpp","size_bytes":18027118592,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"granite-4.2-8b","name":"IBM Granite 4.2 8B","modality":"llm","backend":"llama.cpp","size_bytes":5539627008,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"lfm2.5-1.2b","name":"LiquidAI LFM2.5 1.2B Instruct","modality":"llm","backend":"llama.cpp","size_bytes":730857472,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"lfm2.5-2.6b","name":"LiquidAI LFM2.5 2.6B","modality":"llm","backend":"llama.cpp","size_bytes":1674575872,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"lfm2.5-230m","name":"LiquidAI LFM2.5 230M","modality":"llm","backend":"llama.cpp","size_bytes":153406304,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"lfm2.5-350m","name":"LiquidAI LFM2.5 350M","modality":"llm","backend":"llama.cpp","size_bytes":229638144,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"llama-3.2-3b","name":"Llama 3.2 3B Instruct","modality":"llm","backend":"llama.cpp","size_bytes":2118123520,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"maple-preview","name":"DeepGrove Maple Preview","modality":"llm","backend":"mlx/llama.cpp","size_bytes":4984016416,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"mlx-nemotron-nano","name":"NVIDIA Llama 3.1 Nemotron Nano 8B","modality":"llm","backend":"mlx","size_bytes":4534806075,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"mlx-llama3.2","name":"Llama 3.2 1B Instruct","modality":"llm","backend":"mlx","size_bytes":712575975,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"mlx-nemotron-mini","name":"NVIDIA Nemotron Mini 4B Instruct","modality":"llm","backend":"mlx","size_bytes":2392679103,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"qwen3-0.6b","name":"Qwen3 0.6B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":670040064,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"qwen3-4b-instruct-2507","name":"Qwen3 4B Instruct 2507","modality":"llm","backend":"mlx/llama.cpp","size_bytes":4300000000,"downloaded":false,"harness_compatible":true,"local_path":"","cloud":false},{"id":"qwen3.8-27b","name":"Qwen3.8 27B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":17106775008,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"ternary-bonsai-1.7b","name":"Ternary-Bonsai 1.7B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":490163968,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"mlx-ternary-bonsai-27b-2bit","name":"Ternary-Bonsai 27B","modality":"llm","backend":"mlx","size_bytes":8490785104,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"ternary-bonsai-4b","name":"Ternary-Bonsai 4B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":1137806656,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"ternary-bonsai-8b","name":"Ternary-Bonsai 8B","modality":"llm","backend":"mlx/llama.cpp","size_bytes":2310125920,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false},{"id":"smollm2-135m","name":"SmolLM2 135M","modality":"llm","backend":"llama.cpp","size_bytes":100663296,"downloaded":false,"harness_compatible":false,"local_path":"","cloud":false}]} diff --git a/tests/golden/expected/local__models__list__--home__HOMEelsewhere.stdout b/tests/golden/expected/local__models__list__--home__HOMEelsewhere.stdout index 2cf1d485..5a394238 100644 --- a/tests/golden/expected/local__models__list__--home__HOMEelsewhere.stdout +++ b/tests/golden/expected/local__models__list__--home__HOMEelsewhere.stdout @@ -2,4 +2,4 @@ Download a model with `wally models pull `: wally models pull qwen3-4b-instruct-2507 # llama.cpp wally models pull mlx-qwen3-4b-instruct-2507 # MLX (Apple GPU) -no models downloaded — try `wally models list --all` then `wally models pull ` +no models downloaded — try `wally models list --local` then `wally models pull ` diff --git a/tests/golden/expected/local__models__ls.stdout b/tests/golden/expected/local__models__ls.stdout index 2cf1d485..5a394238 100644 --- a/tests/golden/expected/local__models__ls.stdout +++ b/tests/golden/expected/local__models__ls.stdout @@ -2,4 +2,4 @@ Download a model with `wally models pull `: wally models pull qwen3-4b-instruct-2507 # llama.cpp wally models pull mlx-qwen3-4b-instruct-2507 # MLX (Apple GPU) -no models downloaded — try `wally models list --all` then `wally models pull ` +no models downloaded — try `wally models list --local` then `wally models pull ` diff --git a/tests/golden/expected/parse__--json__--bogus__models__list.stderr b/tests/golden/expected/parse__--json__--bogus__models__list.stderr index fbd6d671..3d5b215a 100644 --- a/tests/golden/expected/parse__--json__--bogus__models__list.stderr +++ b/tests/golden/expected/parse__--json__--bogus__models__list.stderr @@ -5,7 +5,10 @@ Usage: wally models list [OPTIONS] Options: -h, --help Show help - -a, --all Include catalog models not yet downloaded + -a, --all Every local catalog model, plus your account's + cloud models + --local Every local catalog model, without cloud models + --cloud Only your account's cloud models Examples: # Browse the whole catalog diff --git a/tests/golden/expected/parse__models__list__-u.stderr b/tests/golden/expected/parse__models__list__-u.stderr index 7a2a71cf..c124cbcc 100644 --- a/tests/golden/expected/parse__models__list__-u.stderr +++ b/tests/golden/expected/parse__models__list__-u.stderr @@ -5,7 +5,10 @@ Usage: wally models list [OPTIONS] Options: -h, --help Show help - -a, --all Include catalog models not yet downloaded + -a, --all Every local catalog model, plus your account's + cloud models + --local Every local catalog model, without cloud models + --cloud Only your account's cloud models Examples: # Browse the whole catalog