Skip to main content

rlx_cli/
format.rs

1// RLX — versatile ML compiler + runtime.
2// Copyright (C) 2026 Eugene Hauptmann, Nataliya Kosmyna.
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, version 3.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program. If not, see <https://www.gnu.org/licenses/>.
15
16use anyhow::{Result, anyhow};
17use rlx_core::gguf_support::list_gguf_files_in_dir;
18use std::path::Path;
19
20/// What the model file is. Used by runners to pick the right loader.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum WeightFormat {
23    Safetensors,
24    Gguf,
25}
26
27impl WeightFormat {
28    /// Infer format from a file extension, or from directory contents.
29    pub fn detect(path: &Path) -> Result<Self> {
30        if path.is_dir() {
31            if !list_gguf_files_in_dir(path)?.is_empty() {
32                return Ok(Self::Gguf);
33            }
34            if path.join("model.safetensors").is_file() {
35                return Ok(Self::Safetensors);
36            }
37            return Err(anyhow!(
38                "directory {path:?} has no .gguf files and no model.safetensors; \
39                 pass a concrete file or run `rlx-inspect {path:?}`"
40            ));
41        }
42        Self::from_path(path)
43    }
44
45    pub fn from_path(path: &Path) -> Result<Self> {
46        match path.extension().and_then(|s| s.to_str()) {
47            Some("safetensors") => Ok(Self::Safetensors),
48            Some("gguf") => Ok(Self::Gguf),
49            other => {
50                let hint = rlx_core::registered_extensions_hint();
51                Err(anyhow!(
52                    "cannot autodetect weight format from extension {:?} on {path:?}\n\
53                     Registered extensions: .{hint}\n\
54                     Pass --format safetensors|gguf, or register a custom extension via rlx_core::weights",
55                    other
56                ))
57            }
58        }
59    }
60
61    /// Parse CLI `--format` values (`safetensors` | `gguf`).
62    pub fn parse(s: &str) -> Result<Self> {
63        match s {
64            "safetensors" => Ok(Self::Safetensors),
65            "gguf" => Ok(Self::Gguf),
66            other => Err(anyhow!("expected safetensors|gguf, got {other}")),
67        }
68    }
69
70    /// Use an explicit override when set; otherwise infer from the file extension.
71    pub fn resolve(path: &Path, override_fmt: Option<Self>) -> Result<Self> {
72        match override_fmt {
73            Some(f) => Ok(f),
74            None => Self::detect(path),
75        }
76    }
77}