code: def save_model(
      name: Tag | str,
      pretrained_or_pipeline: TransformersPreTrained
      | transformers.Pipeline
      | PreTrainedProtocol
      | None = None,
      pipeline: transformers.Pipeline | None = None,
      task_name: str | None = None,
      task_definition: dict[str, t.Any] | TaskDefinition | None = None,
      *,
      signatures: ModelSignaturesType | None = None,
      labels: dict[str, str] | None = None,
      custom_objects: dict[str, t.Any] | None = None,
      external_modules: t.List[ModuleType] | None = None,
      metadata: dict[str, t.Any] | None = None,
      **save_kwargs: t.Any,
  ) -> bentoml.Model:
      """
      Save a model instance to BentoML modelstore.
  
      Args:
          name: Name for given model instance. This should pass Python identifier check.
          pretrained_or_pipeline: Instance of the Transformers pipeline to be saved, or any instance that ``transformers`` supports.
                                  The object instance should have ``save_pretrained`` and ``from_pretrained`` (follows the protocol that is defined by `transformers`.)
          task_name: Name of pipeline task. If not provided, the task name will be derived from ``pipeline.task``. Only needed when the target is a custom pipeline.
          task_definition: Task definition for the Transformers custom pipeline. The definition is a dictionary
                           consisting of the following keys:
  
                          - ``impl`` (``type[transformers.Pipeline]``): The name of the pipeline implementation module. The name should be the same as the pipeline passed in the ``pipeline`` argument.
                          - ``tf`` (:code:`tuple[AnyType]`): The name of the Tensorflow auto model class. One of ``tf`` and ``pt`` auto model class argument is required.
                          - ``pt`` (:code:`tuple[AnyType]`): The name of the PyTorch auto model class. One of ``tf`` and ``pt`` auto model class argument is required.
                          - ``default`` (:code:`Dict[str, AnyType]`): The names of the default models, tokenizers, feature extractors, etc.
                          - ``type`` (:code:`str`): The type of the pipeline, e.g. ``text``, ``audio``, ``image``, ``multimodal``.
  
                          Example:
  
                          .. code-block:: python
  
                              task_definition = {
                                  "impl": Text2TextGenerationPipeline,
                                  "tf": (TFAutoModelForSeq2SeqLM,) if is_tf_available() else (),
                                  "pt": (AutoModelForSeq2SeqLM,) if is_torch_available() else (),
                                  "default": {"model": {"pt": "t5-base", "tf": "t5-base"}},
                                  "type": "text",
                              }
  
                          See module `src/transformers/pipelines/__init__.py <https://github.com/huggingface/transformers/blob/main/src/transformers/pipelines/__init__.py#L129>`_ for more details.
          signatures: Methods to expose for running inference on the target model. Signatures are used for creating :obj:`~bentoml.Runner` instances when serving model with :obj:`~bentoml.Service`
          labels: User-defined labels for managing models, e.g. ``team=nlp``, ``stage=dev``.
          custom_objects: Custom objects to be saved with the model. An example is ``{"my-normalizer": normalizer}``.
  
                          Custom objects are currently serialized with cloudpickle, but this implementation is subject to change.
          external_modules: user-defined additional python modules to be saved alongside the model or custom objects,
                            e.g. a tokenizer module, preprocessor module, model configuration module
          metadata: Custom metadata for given model.
          save_kwargs: Additional keyword arguments to be pass to ````
  
      .. note::
  
          Both arguments ``task_name`` and ``task_definition`` must be provided to set save a custom pipeline.
  
      Returns:
          :obj:`~bentoml.Tag`: A :obj:`tag` with a format ``name:version`` where ``name`` is the user-defined model's name, and a generated ``version``.
  
      Examples:
  
      .. code-block:: python
  
          import bentoml
  
          from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM
  
          tokenizer = AutoTokenizer.from_pretrained("distilgpt2")
          model = AutoModelForCausalLM.from_pretrained("distilgpt2")
          generator = pipeline(task="text-generation", model=model, tokenizer=tokenizer)
          bento_model = bentoml.transformers.save_model("text-generation-pipeline", generator)
      """  # noqa
      # backward compatibility
      if pipeline is not None:
          warnings.warn(
              f"The 'pipeline={pipeline}' argument is deprecated and will be removed in the future. Please use 'pretrained_or_pipeline' instead.",
              DeprecationWarning,
          )
          pretrained_or_pipeline = pipeline
  
      assert (
          pretrained_or_pipeline is not None
      ), "Please provide a pipeline or a pretrained object as a second argument."
  
      # The below API are introduced since 4.18
      if pkg_version_info("transformers")[:2] >= (4, 18):
          from transformers.utils import is_flax_available
          from transformers.utils import is_tf_available
          from transformers.utils import is_torch_available
      else:
          from .utils.transformers import is_flax_available
          from .utils.transformers import is_tf_available
          from .utils.transformers import is_torch_available
  
      framework_versions = {
          "transformers": get_pkg_version("transformers")
      }
      if is_torch_available():
          framework_versions["torch"] = get_pkg_version("torch")
      if is_tf_available():
          from .utils.tensorflow import get_tf_version
  
          framework_versions[
              "tensorflow-macos"
              if platform.system() == "Darwin"
              else "tensorflow"
          ] = get_tf_version()
      if is_flax_available():
          framework_versions.update(
              {
                  "flax": get_pkg_version("flax"),
                  "jax": get_pkg_version("jax"),
                  "jaxlib": get_pkg_version("jaxlib"),
              }
          )
      context = ModelContext(
          framework_name="transformers",
          framework_versions=framework_versions,
      )
  
      if signatures is None:
          signatures = make_default_signatures(
              pretrained_or_pipeline.__class__
          )
          # NOTE: ``make_default_signatures`` can return an empty dict, hence we will only
          # log when signatures are available.
          if signatures:
              logger.info(
                  'Using the default model signature for Transformers (%s) for model "%s".',
                  signatures,
                  name,
              )
  
      if LazyType("transformers.Pipeline").isinstance(
          pretrained_or_pipeline
      ):
          from transformers.pipelines import check_task
          from transformers.pipelines import get_supported_tasks
  
          # NOTE: safe casting to annotate task_definition types
          task_definition = (
              t.cast(TaskDefinition, task_definition)
              if task_definition is not None
              else task_definition
          )
  
          pipeline_ = t.cast(
              "transformers.Pipeline", pretrained_or_pipeline
          )
  
          if task_name is not None and task_definition is not None:
              logger.info(
                  "Arguments 'task_name' and 'task_definition' are provided. Saving model with pipeline task name '%s' and task definition '%s'.",
                  task_name,
                  task_definition,
              )
              if pipeline_.task != task_name:
                  raise BentoMLException(
                      f"Argument 'task_name' '{task_name}' does not match pipeline task name '{pipeline_.task}'."
                  )
  
              assert (
                  "impl" in task_definition
              ), "'task_definition' requires 'impl' key."
  
              impl = task_definition["impl"]
              if type(pipeline_) != impl:
                  raise BentoMLException(
                      f"Argument 'pipeline' is not an instance of {impl}. It is an instance of {type(pipeline_)}."
                  )
              options_args = (task_name, task_definition)
  
              if task_name not in get_supported_tasks():
                  register_pipeline(task_name, **task_definition)
                  logger.info(
                      "Task '%s' is a custom task and has been registered to the pipeline registry.",
                      task_name,
                  )
  
              assert (
                  task_name in get_supported_tasks()
              ), f"Task '{task_name}' failed to register into pipeline registry."
          else:
              assert (
                  task_definition is None
              ), "'task_definition' must be None if 'task_name' is not provided."
  
              # if task_name is None, then we derive the task from pipeline.task
              options_args = t.cast(
                  "tuple[str, TaskDefinition]",
                  check_task(
                      pipeline_.task if task_name is None else task_name
                  )[:2],
              )
  
          with bentoml.models.create(
              name,
              module=MODULE_NAME,
              api_version=API_VERSION,
              labels=labels,
              context=context,
              options=TransformersOptions.from_task(*options_args),
              signatures=signatures,
              custom_objects=custom_objects,
              external_modules=external_modules,
              metadata=metadata,
          ) as bento_model:
              pipeline_.save_pretrained(bento_model.path, **save_kwargs)
  
              # NOTE: we want to pickle the class so that tensorflow, flax pipeline will also work.
              # the weights is already save, so we only need to save the class.
              with open(
                  bento_model.path_of(PIPELINE_PICKLE_NAME), "wb"
              ) as f:
                  cloudpickle.dump(pipeline_.__class__, f)
              return bento_model
      else:
          pretrained = t.cast(
              "PreTrainedProtocol", pretrained_or_pipeline
          )
          assert all(
              hasattr(pretrained, defn)
              for defn in ("save_pretrained", "from_pretrained")
          ), f"'pretrained={pretrained}' is not a valid Transformers object. It must have 'save_pretrained' and 'from_pretrained' methods."
          if metadata is None:
              metadata = {}
  
          metadata.update(
              {
                  "_pretrained_class": pretrained.__class__.__name__,
              }
          )
          if hasattr(pretrained, "framework") and isinstance(
              pretrained,
              (
                  transformers.PreTrainedModel,
                  transformers.TFPreTrainedModel,
                  transformers.FlaxPreTrainedModel,
              ),
          ):
              # NOTE: Only PreTrainedModel and variants has this, not tokenizer.
              metadata["_framework"] = pretrained.framework
  
          with bentoml.models.create(
              name,
              module=MODULE_NAME,
              api_version=API_VERSION,
              labels=labels,
              context=context,
              options=TransformersOptions(),
              signatures=signatures,
              custom_objects=custom_objects,
              external_modules=external_modules,
              metadata=metadata,
          ) as bento_model:
              pretrained.save_pretrained(
                  bento_model.path, **save_kwargs
              )
  
              with open(
                  bento_model.path_of(PRETRAINED_PROTOCOL_NAME), "wb"
              ) as f:
                  cloudpickle.dump(pretrained.__class__, f)
              return bento_model
  
imports: from __future__ import annotations
  import inspect
  import logging
  import os
  import platform
  import shutil
  import typing
  import warnings
  from types import ModuleType
  import attr
  import bentoml
  from ...exceptions import BentoMLException
  from ...exceptions import MissingDependencyException
  from ...exceptions import NotFound
  from ..models.model import Model
  from ..models.model import ModelContext
  from ..models.model import ModelOptions
  from ..models.model import ModelSignature
  from ..tag import Tag
  from ..types import LazyType
  from ..utils import LazyLoader
  from ..utils.pkg import get_pkg_version
  from ..utils.pkg import pkg_version_info
  from .utils.transformers import extract_commit_hash
  from .utils.transformers import init_empty_weights
label_desc: Use image node_id 13 for calling a global function with example usage: save_model(name, pretrained_or_pipeline, pipeline, task_name, task_definition) and returns: bento_model, bento_model