def save_model(
    name: Tag | str,
    model: onnx.ModelProto,
    *,
    signatures: dict[str, ModelSignatureDict] | dict[str, ModelSignature] | 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,
) -> bentoml.Model:
    """Save a onnx model instance to the BentoML model store.

    Args:
        name (``str``):
            The name to give to the model in the BentoML store. This must be a valid
            :obj:`~bentoml.Tag` name.
        model (:obj:`~onnx.ModelProto`):
            The ONNX model to be saved.
        signatures (``dict[str, ModelSignatureDict]``, optional):
            Signatures of predict methods to be used. If not provided, the signatures default to
            ``{"run": {"batchable": False}}``. See :obj:`~bentoml.types.ModelSignature` for more details.
            ``bentoml.onnx`` internally use ``onnxruntime.InferenceSession`` to run inference.
            When the original model is converted to ONNX format and loaded by
            ``onnxruntime.InferenceSession``, the inference method of the original model is
            converted to the ``run`` method of the ``onnxruntime.InferenceSession``.
            ``signatures`` here refers to the predict method of
            ``onnxruntime.InferenceSession``, hence the only allowed method name in ``signatures``
            is ``run``.
        labels (``dict[str, str]``, optional):
            A default set of management labels to be associated with the model. An example is
            ``{"training-set": "data-1"}``.
        custom_objects (``dict[str, Any]``, optional):
            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 (:code:`List[ModuleType]`, `optional`, default to :code:`None`):
            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 (``dict[str, Any]``, optional):
            Metadata to be associated with the model. An example is ``{"bias": 4}``.

            Metadata is intended for display in a model management UI and therefore must be a
            default Python type, such as ``str`` or ``int``.

    Returns:

        :obj:`~bentoml.Model`: A BentoML model containing the saved
        ONNX model instance.  store.

    Example:

    .. code-block:: python

        import bentoml

        import torch
        import torch.nn as nn

        class ExtendedModel(nn.Module):
            def __init__(self, D_in, H, D_out):
                # In the constructor we instantiate two nn.Linear modules and assign them as
                #  member variables.
                super(ExtendedModel, self).__init__()
                self.linear1 = nn.Linear(D_in, H)
                self.linear2 = nn.Linear(H, D_out)

            def forward(self, x, bias):
                # In the forward function we accept a Tensor of input data and an optional bias
                h_relu = self.linear1(x).clamp(min=0)
                y_pred = self.linear2(h_relu)
                return y_pred + bias


        N, D_in, H, D_out = 64, 1000, 100, 1
        x = torch.randn(N, D_in)
        model = ExtendedModel(D_in, H, D_out)

        input_names = ["x", "bias"]
        output_names = ["output1"]

        tmpdir = "/tmp/model"
        model_path = os.path.join(tmpdir, "test_torch.onnx")
        torch.onnx.export(
            model,
            (x, torch.Tensor([1.0])),
            model_path,
            input_names=input_names,
            output_names=output_names,
        )

        bento_model = bentoml.onnx.save_model("onnx_model", model_path, signatures={"run": {"batchable": True}})

    """

    # prefer "onnxruntime-gpu" over "onnxruntime" for framework versioning
    _onnxruntime_version = None
    _onnxruntime_pkg = None
    _PACKAGE = ["onnxruntime-gpu", "onnxruntime", "onnxruntime-silicon"]
    for p in _PACKAGE:
        try:
            _onnxruntime_version = get_pkg_version(p)
            _onnxruntime_pkg = p
            break
        except PackageNotFoundError:
            pass
    assert _onnxruntime_pkg is not None and _onnxruntime_version is not None, (
        "Failed to find onnxruntime package version."
    )

    assert _onnxruntime_version is not None, "onnxruntime is not installed"
    if not isinstance(model, onnx.ModelProto):
        raise TypeError(f"Given model ({model}) is not a onnx.ModelProto.")

    context = ModelContext(
        framework_name="onnx",
        framework_versions={
            "onnx": get_pkg_version("onnx"),
            _onnxruntime_pkg: _onnxruntime_version,
        },
    )

    if signatures is None:
        signatures = {
            "run": {"batchable": False},
        }
        logger.info(
            'Using the default model signature for ONNX (%s) for model "%s".',
            signatures,
            name,
        )
    else:
        provided_methods = list(signatures.keys())
        if provided_methods != ["run"]:
            raise BentoMLException(
                f"Provided method names {[m for m in provided_methods if m != 'run']} are invalid. 'bentoml.onnx' will load ONNX model into an 'onnxruntime.InferenceSession' for inference, so the only supported method name is 'run'."
            )

    run_input_specs = [MessageToDict(inp) for inp in model.graph.input]
    run_output_specs = [MessageToDict(out) for out in model.graph.output]
    input_specs = {"run": run_input_specs}
    output_specs = {"run": run_output_specs}

    options = ModelOptions(input_specs=input_specs, output_specs=output_specs)

    with bentoml.models._create(  # type: ignore
        name,
        module=MODULE_NAME,
        api_version=API_VERSION,
        signatures=signatures,
        labels=labels,
        options=options,
        custom_objects=custom_objects,
        external_modules=external_modules,
        metadata=metadata,
        context=context,
    ) as bento_model:
        onnx.save(model, bento_model.path_of(MODEL_FILENAME))

        return bento_model
Save a onnx model instance to the BentoML model store.

Args:
name (``str``):
The name to give to the model in the BentoML store. This must be a valid
:obj:`~bentoml.Tag` name.
model (:obj:`~onnx.ModelProto`):
The ONNX model to be saved.
signatures (``dict[str, ModelSignatureDict]``, optional):
Signatures of predict methods to be used. If not provided, the signatures default to
``{"run": {"batchable": False}}``. See :obj:`~bentoml.types.ModelSignature` for more details.
``bentoml.onnx`` internally use ``onnxruntime.InferenceSession`` to run inference.
When the original model is converted to ONNX format and loaded by
``onnxruntime.InferenceSession``, the inference method of the original model is
converted to the ``run`` method of the ``onnxruntime.InferenceSession``.
``signatures`` here refers to the predict method of
``onnxruntime.InferenceSession``, hence the only allowed method name in ``signatures``
is ``run``.
labels (``dict[str, str]``, optional):
A default set of management labels to be associated with the model. An example is
``{"training-set": "data-1"}``.
custom_objects (``dict[str, Any]``, optional):
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 (:code:`List[ModuleType]`, `optional`, default to :code:`None`):
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 (``dict[str, Any]``, optional):
Metadata to be associated with the model. An example is ``{"bias": 4}``.

Metadata is intended for display in a model management UI and therefore must be a
default Python type, such as ``str`` or ``int``.

Returns:

:obj:`~bentoml.Model`: A BentoML model containing the saved
ONNX model instance.  store.

Example:

.. code-block:: python

import bentoml

import torch
import torch.nn as nn

class ExtendedModel(nn.Module):
def __init__(self, D_in, H, D_out):
# In the constructor we instantiate two nn.Linear modules and assign them as
#  member variables.
super(ExtendedModel, self).__init__()
self.linear1 = nn.Linear(D_in, H)
self.linear2 = nn.Linear(H, D_out)

def forward(self, x, bias):
# In the forward function we accept a Tensor of input data and an optional bias
h_relu = self.linear1(x).clamp(min=0)
y_pred = self.linear2(h_relu)
return y_pred + bias


N, D_in, H, D_out = 64, 1000, 100, 1
x = torch.randn(N, D_in)
model = ExtendedModel(D_in, H, D_out)

input_names = ["x", "bias"]
output_names = ["output1"]

tmpdir = "/tmp/model"
model_path = os.path.join(tmpdir, "test_torch.onnx")
torch.onnx.export(
model,
(x, torch.Tensor([1.0])),
model_path,
input_names=input_names,
output_names=output_names,
)

bento_model = bentoml.onnx.save_model("onnx_model", model_path, signatures={"run": {"batchable": True}})
src/bentoml/_internal/frameworks/onnx.py