66from pathlib import Path
77from typing import Dict , List , Optional
88
9+ try :
10+ from huggingface_hub import snapshot_download , list_repo_files
11+ HUGGINGFACE_HUB_AVAILABLE = True
12+ except ImportError :
13+ HUGGINGFACE_HUB_AVAILABLE = False
14+
915try :
1016 from fastembed import TextEmbedding
1117 FASTEMBED_AVAILABLE = True
@@ -557,64 +563,85 @@ def download_model(profile: str, progress_callback: Optional[callable] = None) -
557563
558564def download_custom_model (model_name : str , model_type : str = "embedding" , progress_callback : Optional [callable ] = None ) -> Dict [str , any ]:
559565 """Download a custom model by HuggingFace model name.
560-
561- This allows users to download any HuggingFace model that is compatible
562- with fastembed (TextEmbedding or TextCrossEncoder).
563-
566+
567+ This allows users to download any HuggingFace model directly from
568+ HuggingFace Hub. The model will be placed in the standard cache
569+ directory where it can be discovered by scan_discovered_models().
570+
571+ Note: Downloaded models may not be directly usable by FastEmbed unless
572+ they are in ONNX format. This function is primarily for downloading
573+ models that users want to use with other frameworks or custom code.
574+
564575 Args:
565- model_name: Full HuggingFace model name (e.g., "BAAI/bge -small-en-v1.5 ")
566- model_type: Type of model ("embedding" or "reranker")
576+ model_name: Full HuggingFace model name (e.g., "intfloat/e5 -small-v2 ")
577+ model_type: Type of model ("embedding" or "reranker") - for metadata only
567578 progress_callback: Optional callback function to report progress
568-
579+
569580 Returns:
570581 Result dictionary with success status
571582 """
572- if model_type == "embedding" :
573- if not FASTEMBED_AVAILABLE :
574- return {
575- "success" : False ,
576- "error" : "fastembed not installed. Install with: pip install codexlens[semantic]" ,
577- }
578- else :
579- if not RERANKER_AVAILABLE :
580- return {
581- "success" : False ,
582- "error" : "fastembed reranker not available. Install with: pip install fastembed>=0.4.0" ,
583- }
584-
583+ if not HUGGINGFACE_HUB_AVAILABLE :
584+ return {
585+ "success" : False ,
586+ "error" : "huggingface_hub not installed. Install with: pip install huggingface_hub" ,
587+ }
588+
585589 # Validate model name format (org/model-name)
586590 if not model_name or "/" not in model_name :
587591 return {
588592 "success" : False ,
589- "error" : "Invalid model name format. Expected: 'org/model-name' (e.g., 'BAAI/bge -small-en-v1.5 ')" ,
593+ "error" : "Invalid model name format. Expected: 'org/model-name' (e.g., 'intfloat/e5 -small-v2 ')" ,
590594 }
591-
595+
592596 try :
593597 cache_dir = get_cache_dir ()
594-
598+
595599 if progress_callback :
596- progress_callback (f"Downloading custom model { model_name } ..." )
597-
598- if model_type == "reranker" :
599- # Download reranker model
600- reranker = TextCrossEncoder (model_name = model_name , cache_dir = str (cache_dir ))
600+ progress_callback (f"Checking model format for { model_name } ..." )
601+
602+ # Check if model contains ONNX files before downloading
603+ try :
604+ files = list_repo_files (repo_id = model_name )
605+ has_onnx = any (
606+ f .endswith ('.onnx' ) or
607+ f .startswith ('onnx/' ) or
608+ '/onnx/' in f or
609+ f == 'model.onnx'
610+ for f in files
611+ )
612+
613+ if not has_onnx :
614+ return {
615+ "success" : False ,
616+ "error" : f"Model '{ model_name } ' does not contain ONNX files. "
617+ f"FastEmbed requires ONNX-format models. "
618+ f"Try Xenova/* versions or check the recommended models list." ,
619+ "files_found" : len (files ),
620+ "suggestion" : "Use models from the 'Recommended Models' list, or search for ONNX versions (e.g., Xenova/*)." ,
621+ }
622+
601623 if progress_callback :
602- progress_callback (f"Initializing reranker { model_name } ..." )
603- list (reranker .rerank ("test query" , ["test document" ]))
604- else :
605- # Download embedding model
606- embedder = TextEmbedding (model_name = model_name , cache_dir = str (cache_dir ))
624+ progress_callback (f"ONNX format detected. Downloading { model_name } ..." )
625+
626+ except Exception as check_err :
627+ # If we can't check, warn but allow download
607628 if progress_callback :
608- progress_callback (f"Initializing { model_name } ..." )
609- list (embedder .embed (["test" ]))
610-
629+ progress_callback (f"Could not verify format, proceeding with download..." )
630+
631+ # Use huggingface_hub to download the model
632+ # This downloads to the standard HuggingFace cache directory
633+ local_path = snapshot_download (
634+ repo_id = model_name ,
635+ cache_dir = str (cache_dir ),
636+ )
637+
611638 if progress_callback :
612- progress_callback (f"Custom model { model_name } downloaded successfully" )
613-
639+ progress_callback (f"Model { model_name } downloaded successfully" )
640+
614641 # Get cache info
615642 sanitized_name = f"models--{ model_name .replace ('/' , '--' )} "
616643 model_cache_path = cache_dir / sanitized_name
617-
644+
618645 cache_size = 0
619646 if model_cache_path .exists ():
620647 total_size = sum (
@@ -623,14 +650,16 @@ def download_custom_model(model_name: str, model_type: str = "embedding", progre
623650 if f .is_file ()
624651 )
625652 cache_size = round (total_size / (1024 * 1024 ), 1 )
626-
653+
627654 return {
628655 "success" : True ,
629656 "result" : {
630657 "model_name" : model_name ,
631658 "model_type" : model_type ,
632659 "cache_size_mb" : cache_size ,
633660 "cache_path" : str (model_cache_path ),
661+ "local_path" : local_path ,
662+ "note" : "Model downloaded. Note: Only ONNX-format models are compatible with FastEmbed." ,
634663 },
635664 }
636665
0 commit comments