1use std::path::Path;
4
5use anyhow::Result;
6use clap::ValueEnum;
7use zsfm_checkpoint::{load_checkpoint, LoadOptions};
8use zsfm_gguf::GGMLType;
9
10#[derive(Clone, Copy, Debug, ValueEnum)]
15pub enum DtypeArg {
16 F32,
17 F16,
18 Q8,
19}
20
21impl DtypeArg {
22 pub fn name(self) -> &'static str {
23 match self {
24 DtypeArg::F32 => "f32",
25 DtypeArg::F16 => "f16",
26 DtypeArg::Q8 => "q8",
27 }
28 }
29}
30
31impl From<DtypeArg> for GGMLType {
32 fn from(d: DtypeArg) -> Self {
33 match d {
34 DtypeArg::F32 => GGMLType::F32,
35 DtypeArg::F16 => GGMLType::F16,
36 DtypeArg::Q8 => GGMLType::Q8_0,
37 }
38 }
39}
40
41pub fn try_recast_from_cache(
48 canonical: &Path,
49 output: &Path,
50 dtype: GGMLType,
51 redownload: bool,
52) -> Result<bool> {
53 zsfm_hub::try_recast_from_cache(canonical, output, dtype, redownload)
54}
55
56pub fn median_index(quantile_levels: &[f32]) -> usize {
58 quantile_levels
59 .iter()
60 .position(|&q| (q - 0.5).abs() < 1e-6)
61 .unwrap_or(quantile_levels.len() / 2)
62}
63
64pub fn inspect_tensors(path: &Path) -> Result<()> {
70 let ckpt = load_checkpoint(&[path.to_path_buf()], &LoadOptions::default())?;
71 eprintln!("Tensors in {}:", path.display());
72 let mut tensors: Vec<_> = ckpt.tensors.iter().collect();
73 tensors.sort_by(|a, b| a.name.cmp(&b.name));
74 for t in tensors {
75 println!(" {:80} {} {:?}", t.name, t.dtype.name(), t.shape);
76 }
77 Ok(())
78}
79
80pub fn delete_cached_model(canonical: &Path, output: Option<&Path>) -> Result<()> {
84 zsfm_hub::delete_cached_model(canonical, output)
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn dtype_names() {
93 assert_eq!(DtypeArg::F32.name(), "f32");
94 assert_eq!(DtypeArg::F16.name(), "f16");
95 assert_eq!(DtypeArg::Q8.name(), "q8");
96 assert_eq!(GGMLType::from(DtypeArg::F32), GGMLType::F32);
97 assert_eq!(GGMLType::from(DtypeArg::Q8), GGMLType::Q8_0);
98 }
99
100 #[test]
101 fn median_prefers_q05() {
102 let levels = vec![0.1, 0.5, 0.9];
103 assert_eq!(median_index(&levels), 1);
104 let no_median = vec![0.1, 0.9];
105 assert_eq!(median_index(&no_median), 1);
106 let empty: Vec<f32> = vec![];
107 assert_eq!(median_index(&empty), 0);
108 }
109
110 #[test]
111 fn cache_miss_returns_false() {
112 let dir = std::env::temp_dir().join(format!("zsfm-common-{}", std::process::id()));
113 let canonical = dir.join("owner__repo").join("model-f32.gguf");
114 let out = dir.join("out.gguf");
115 assert!(!try_recast_from_cache(&canonical, &out, GGMLType::F16, false).unwrap());
117 assert!(!try_recast_from_cache(&canonical, &out, GGMLType::F16, true).unwrap());
119 }
120}