Source code for nemo.core.classes.mixins.hf_io_mixin

# Copyright (c) 2025, NVIDIA CORPORATION.  All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from abc import ABC
from pathlib import Path
from typing import Any, Dict, Iterable, List, Optional, Union

from huggingface_hub import HfApi, ModelCard, ModelCardData
from huggingface_hub import get_token as get_hf_token
from huggingface_hub.hf_api import ModelInfo
from huggingface_hub.utils import SoftTemporaryDirectory


[docs] class HuggingFaceFileIO(ABC): """ Mixin that provides Hugging Face file IO functionality for NeMo models. It is usually implemented as a mixin to `ModelPT`. This mixin provides the following functionality: - `search_huggingface_models()`: Search the hub programmatically via some model filter. - `push_to_hf_hub()`: Push a model to the hub. """
[docs] @classmethod def get_hf_model_filter(cls) -> Dict[str, Any]: """ Generates a filter for HuggingFace models. Additionaly includes default values of some metadata about results returned by the Hub. Metadata: resolve_card_info: Bool flag, if set, returns the model card metadata. Default: False. limit_results: Optional int, limits the number of results returned. Returns: A dict representing the arguments passable to huggingface list_models(). """ model_filter = dict( author=None, filter=['nemo'], model_name=None, limit=None, full=None, cardData=False, ) return model_filter
[docs] @classmethod def search_huggingface_models(cls, model_filter: Optional[Dict[str, Any]] = None) -> Iterable['ModelInfo']: """ Should list all pre-trained models available via Hugging Face Hub. The following metadata can be passed via the `model_filter` for additional results. Metadata: resolve_card_info: Bool flag, if set, returns the model card metadata. Default: False. limit_results: Optional int, limits the number of results returned. .. code-block:: python # You can replace <DomainSubclass> with any subclass of ModelPT. from nemo.core import ModelPT # Get default filter dict filt = <DomainSubclass>.get_hf_model_filter() # Make any modifications to the filter as necessary filt['filter'].append('en') # Add language filter filt['filter'].append('automatic-speech-recognition') # Add task filter # Add any metadata to the filter as needed (kwargs to list_models) filt['limit'] = 5 # Obtain model info model_infos = <DomainSubclass>.search_huggingface_models(model_filter=filt) # Browse through cards and select an appropriate one card = model_infos[0] # Restore model using `modelId` of the card. model = ModelPT.from_pretrained(card.modelId) Args: model_filter: Optional Dictionary (for Hugging Face Hub kwargs) that filters the returned list of compatible model cards, and selects all results from each filter. Users can then use `model_card.modelId` in `from_pretrained()` to restore a NeMo Model. Returns: A list of ModelInfo entries. """ # Resolve model filter if not provided as argument if model_filter is None: model_filter = cls.get_hf_model_filter() # Check if api token exists, use if it does hf_token = get_hf_token() # Search for all valid models after filtering api = HfApi() results = api.list_models(token=hf_token, sort="lastModified", **model_filter) # type: Iterable[ModelInfo] return results
[docs] def push_to_hf_hub( self, repo_id: str, *, pack_nemo_file: bool = True, model_card: Optional['ModelCard'] | object | str = None, commit_message: str = "Push model using huggingface_hub.", private: bool = False, api_endpoint: Optional[str] = None, token: Optional[str] = None, branch: Optional[str] = None, allow_patterns: Optional[Union[List[str], str]] = None, ignore_patterns: Optional[Union[List[str], str]] = None, delete_patterns: Optional[Union[List[str], str]] = None, ): """ Upload model checkpoint to the Hub. Use `allow_patterns` and `ignore_patterns` to precisely filter which files should be pushed to the hub. Use `delete_patterns` to delete existing remote files in the same commit. See [`upload_folder`] reference for more details. Args: repo_id (`str`): ID of the repository to push to (example: `"username/my-model"`). pack_nemo_file (`bool`, *optional*, defaults to `True`): Whether to pack the model checkpoint and configuration into a single `.nemo` file. If set to false, uploads the contents of the directory containing the model checkpoint and configuration plus additional artifacts. model_card (`ModelCard`, *optional*): Model card to upload with the model. If None, will use the model card template provided by the class itself via `generate_model_card()`. Any object that implements str(obj) can be passed here. Two keyword replacements are passed to `generate_model_card()`: `model_name` and `repo_id`. If the model card generates a string, and it contains `{model_name}` or `{repo_id}`, they will be replaced with the actual values. commit_message (`str`, *optional*): Message to commit while pushing. private (`bool`, *optional*, defaults to `False`): Whether the repository created should be private. api_endpoint (`str`, *optional*): The API endpoint to use when pushing the model to the hub. token (`str`, *optional*): The token to use as HTTP bearer authorization for remote files. By default, it will use the token cached when running `huggingface-cli login`. branch (`str`, *optional*): The git branch on which to push the model. This defaults to `"main"`. allow_patterns (`List[str]` or `str`, *optional*): If provided, only files matching at least one pattern are pushed. ignore_patterns (`List[str]` or `str`, *optional*): If provided, files matching any of the patterns are not pushed. delete_patterns (`List[str]` or `str`, *optional*): If provided, remote files matching any of the patterns will be deleted from the repo. Returns: The url of the uploaded HF repo. """ if "/" not in repo_id or len(repo_id.split("/")) != 2: raise ValueError("Invalid repo_id provided. Please provide a repo_id of the form `username/repo-name`.") domain_name, model_name = repo_id.split("/") if token is None: token = get_hf_token() api = HfApi(endpoint=api_endpoint, token=token) repo_id = api.create_repo(repo_id=repo_id, private=private, exist_ok=True).repo_id # Push the files to the repo in a single commit with SoftTemporaryDirectory() as tmp: saved_path = Path(tmp) / repo_id saved_path.mkdir(parents=True, exist_ok=True) # Save nemo file in temp dir # Get SaveRestoreConnector from subclass implementation if not hasattr(self, '_save_restore_connector'): raise NotImplementedError( "Model must implement a `_save_restore_connector` property to push to the HuggingFace Hub." ) # We want to save a NeMo file, but not pack its contents into a tarfile by default save_restore_connector = self._save_restore_connector save_restore_connector.pack_nemo_file = pack_nemo_file nemo_filepath = saved_path / f"{model_name}.nemo" self.save_to(nemo_filepath) # Save model card in temp dir if model_card is None: card_model_name = model_name.replace("_", " ").split(" ") card_model_name = " ".join([word.capitalize() for word in card_model_name]) template_kwargs = { 'model_name': card_model_name, 'repo_id': repo_id, } # Generate model card from subclass that implements this method model_card = self.generate_model_card(type='hf', template_kwargs=template_kwargs) # Convert model card to str model_card = str(model_card) # Write model card to temp dir model_card_filepath = saved_path / "README.md" model_card_filepath.write_text(str(model_card), encoding='utf-8', errors='ignore') api.upload_folder( repo_id=repo_id, repo_type="model", folder_path=saved_path, commit_message=commit_message, revision=branch, allow_patterns=allow_patterns, ignore_patterns=ignore_patterns, delete_patterns=delete_patterns, ) if branch is None: branch = "main" return f"https://huggingface.co/{repo_id}/tree/{branch}"
def _get_hf_model_card(self, template: str, template_kwargs: Optional[Dict[str, str]] = None): """ Generate a HuggingFace ModelCard from a str template. The template may have markers with `{key}` that will be populated by values from `template_kwargs` if provided. Args: template: Str template for the model card. template_kwargs (optional): Dict of key-value pairs to populate the template with. Returns: A HuggingFace ModelCard object that can be converted to a model card string. """ card_data = ModelCardData( library_name='nemo', tags=['pytorch', 'NeMo'], license='cc-by-4.0', ignore_metadata_errors=True, ) if 'card_data' not in template_kwargs: template_kwargs['card_data'] = card_data.to_yaml() # Update template with kwargs # We need to do a manual replace because not all keys may be provided in the kwargs for key, val in template_kwargs.items(): template = template.replace("{" + key.strip() + "}", val) hf_model_card = ModelCard(template) return hf_model_card