Skip to content

Commit f448195

Browse files
committed
fix(pull): handle repos without config.json (FLUX/SD image models)
The pull command now: - Makes config.json optional (image models don't have it) - Lists repo files via HF API when no standard model layout found - Downloads all root-level .safetensors files automatically - Fixes pull for Comfy-Org/flux1-dev and similar repos
1 parent cb206f3 commit f448195

1 file changed

Lines changed: 37 additions & 13 deletions

File tree

cake-core/src/utils/hf.rs

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -135,15 +135,17 @@ pub fn ensure_model_downloaded(repo_id: &str) -> Result<PathBuf> {
135135
let api = builder.build()?;
136136
let repo = api.model(repo_id.to_string());
137137

138-
// Download config.json firstvalidates repo access (fails fast on auth errors).
138+
// Download config.json (optionalimage models like FLUX don't have it).
139139
log::info!("downloading config.json ...");
140-
repo.download("config.json")
141-
.map_err(|e| anyhow!("failed to download config.json from '{}': {}", repo_id, e))?;
140+
match repo.download("config.json") {
141+
Ok(_) => log::info!("config.json downloaded"),
142+
Err(_) => log::info!("no config.json (image/diffusion model)"),
143+
}
142144

143145
// Download tokenizer (optional — some models like VibeVoice use external tokenizers).
144146
log::info!("downloading tokenizer.json ...");
145-
if let Err(e) = repo.download("tokenizer.json") {
146-
log::warn!("tokenizer.json not available for '{}': {}", repo_id, e);
147+
if repo.download("tokenizer.json").is_err() {
148+
log::info!("no tokenizer.json");
147149
}
148150

149151
// Try sharded model first (model.safetensors.index.json), fall back to single file.
@@ -178,16 +180,38 @@ pub fn ensure_model_downloaded(repo_id: &str) -> Result<PathBuf> {
178180
}
179181

180182
index_path.parent().unwrap().to_path_buf()
181-
} else {
183+
} else if let Ok(model_path) = repo.download("model.safetensors") {
182184
log::info!("downloading model.safetensors ...");
183-
let model_path = repo.download("model.safetensors").map_err(|e| {
184-
anyhow!(
185-
"failed to download model from '{}': no index.json and no model.safetensors found: {}",
186-
repo_id,
187-
e
188-
)
189-
})?;
190185
model_path.parent().unwrap().to_path_buf()
186+
} else {
187+
// No standard model layout — list repo files and download all .safetensors
188+
// (e.g. FLUX repos use flux1-dev-fp8.safetensors at root)
189+
log::info!("no standard model layout, listing repo files...");
190+
let repo_info = repo.info().map_err(|e| {
191+
anyhow!("failed to get repo info for '{}': {}", repo_id, e)
192+
})?;
193+
let safetensor_files: Vec<&str> = repo_info.siblings.iter()
194+
.map(|s| s.rfilename.as_str())
195+
.filter(|f| f.ends_with(".safetensors") && !f.contains('/'))
196+
.collect();
197+
if safetensor_files.is_empty() {
198+
anyhow::bail!(
199+
"no .safetensors files found in '{}' — cannot download model",
200+
repo_id
201+
);
202+
}
203+
log::info!("found {} safetensor files to download", safetensor_files.len());
204+
let mut snapshot_dir = None;
205+
for (i, file) in safetensor_files.iter().enumerate() {
206+
log::info!("[{}/{}] downloading {} ...", i + 1, safetensor_files.len(), file);
207+
let path = repo.download(file).map_err(|e| {
208+
anyhow!("failed to download '{}' from '{}': {}", file, repo_id, e)
209+
})?;
210+
if snapshot_dir.is_none() {
211+
snapshot_dir = Some(path.parent().unwrap().to_path_buf());
212+
}
213+
}
214+
snapshot_dir.unwrap()
191215
};
192216

193217
log::info!("model files ready at {}", snapshot_dir.display());

0 commit comments

Comments
 (0)