@@ -224,30 +224,99 @@ static handle_model_result common_params_handle_model(
224224 if (model.hf_file .empty ()) {
225225 if (model.path .empty ()) {
226226 auto auto_detected = common_get_hf_file (model.hf_repo , bearer_token, offline);
227- if (auto_detected.repo .empty () || auto_detected. ggufFile . empty () ) {
227+ if (auto_detected.repo .empty ()) {
228228 exit (1 ); // built without CURL, error message already printed
229229 }
230+
230231 model.hf_repo = auto_detected.repo ;
231- model.hf_file = auto_detected.ggufFile ;
232- if (!auto_detected.mmprojFile .empty ()) {
233- result.found_mmproj = true ;
234- result.mmproj .hf_repo = model.hf_repo ;
235- result.mmproj .hf_file = auto_detected.mmprojFile ;
232+
233+ // Handle safetensors format
234+ if (auto_detected.is_safetensors ) {
235+ LOG_INF (" %s: detected safetensors format for %s\n " , __func__, model.hf_repo .c_str ());
236+
237+ // Create a directory for the safetensors files
238+ std::string dir_name = model.hf_repo ;
239+ string_replace_all (dir_name, " /" , " _" );
240+ model.path = fs_get_cache_directory () + " /" + dir_name;
241+
242+ // Create directory if it doesn't exist
243+ std::filesystem::create_directories (model.path );
244+
245+ // Download required files: config.json, tokenizer.json, tokenizer_config.json, and .safetensors files
246+ std::string model_endpoint = get_model_endpoint ();
247+ std::vector<std::pair<std::string, std::string>> files_to_download;
248+
249+ // Required config files
250+ files_to_download.push_back ({
251+ model_endpoint + model.hf_repo + " /resolve/main/config.json" ,
252+ model.path + " /config.json"
253+ });
254+ files_to_download.push_back ({
255+ model_endpoint + model.hf_repo + " /resolve/main/tokenizer.json" ,
256+ model.path + " /tokenizer.json"
257+ });
258+ files_to_download.push_back ({
259+ model_endpoint + model.hf_repo + " /resolve/main/tokenizer_config.json" ,
260+ model.path + " /tokenizer_config.json"
261+ });
262+
263+ // Safetensors files
264+ for (const auto & st_file : auto_detected.safetensors_files ) {
265+ files_to_download.push_back ({
266+ model_endpoint + model.hf_repo + " /resolve/main/" + st_file,
267+ model.path + " /" + st_file
268+ });
269+ }
270+
271+ // Download all files
272+ LOG_INF (" %s: downloading %zu files for safetensors model...\n " , __func__, files_to_download.size ());
273+ for (const auto & [url, path] : files_to_download) {
274+ bool ok = common_download_file_single (url, path, bearer_token, offline);
275+ if (!ok) {
276+ LOG_ERR (" error: failed to download file from %s\n " , url.c_str ());
277+ exit (1 );
278+ }
279+ }
280+
281+ LOG_INF (" %s: safetensors model downloaded to %s\n " , __func__, model.path .c_str ());
282+ } else {
283+ // Handle GGUF format (existing logic)
284+ if (auto_detected.ggufFile .empty ()) {
285+ exit (1 ); // no GGUF file found
286+ }
287+ model.hf_file = auto_detected.ggufFile ;
288+ if (!auto_detected.mmprojFile .empty ()) {
289+ result.found_mmproj = true ;
290+ result.mmproj .hf_repo = model.hf_repo ;
291+ result.mmproj .hf_file = auto_detected.mmprojFile ;
292+ }
293+
294+ std::string model_endpoint = get_model_endpoint ();
295+ model.url = model_endpoint + model.hf_repo + " /resolve/main/" + model.hf_file ;
296+ // make sure model path is present (for caching purposes)
297+ if (model.path .empty ()) {
298+ // this is to avoid different repo having same file name, or same file name in different subdirs
299+ std::string filename = model.hf_repo + " _" + model.hf_file ;
300+ // to make sure we don't have any slashes in the filename
301+ string_replace_all (filename, " /" , " _" );
302+ model.path = fs_get_cache_file (filename);
303+ }
236304 }
237305 } else {
238306 model.hf_file = model.path ;
239307 }
240- }
241-
242- std::string model_endpoint = get_model_endpoint ();
243- model.url = model_endpoint + model.hf_repo + " /resolve/main/" + model.hf_file ;
244- // make sure model path is present (for caching purposes)
245- if (model.path .empty ()) {
246- // this is to avoid different repo having same file name, or same file name in different subdirs
247- std::string filename = model.hf_repo + " _" + model.hf_file ;
248- // to make sure we don't have any slashes in the filename
249- string_replace_all (filename, " /" , " _" );
250- model.path = fs_get_cache_file (filename);
308+ } else {
309+ // User specified hf_file explicitly - use GGUF download path
310+ std::string model_endpoint = get_model_endpoint ();
311+ model.url = model_endpoint + model.hf_repo + " /resolve/main/" + model.hf_file ;
312+ // make sure model path is present (for caching purposes)
313+ if (model.path .empty ()) {
314+ // this is to avoid different repo having same file name, or same file name in different subdirs
315+ std::string filename = model.hf_repo + " _" + model.hf_file ;
316+ // to make sure we don't have any slashes in the filename
317+ string_replace_all (filename, " /" , " _" );
318+ model.path = fs_get_cache_file (filename);
319+ }
251320 }
252321
253322 } else if (!model.url .empty ()) {
0 commit comments