def get_runnable(bento_model: bentoml.Model) -> t.Type[bentoml.Runnable]:
    """
    Private API: use :obj:`~bentoml.Model.to_runnable` instead.
    """

    # backward compatibility for v1, load raw model to infer
    # input_specs/output_specs for onnx model
    if bento_model.info.api_version == "v1":
        raw_model: onnx.ModelProto | None = None
        options = t.cast(ModelOptions, bento_model.info.options)

        if not options.input_specs:
            raw_model = _load_raw_model(bento_model)
            run_input_specs = [MessageToDict(inp) for inp in raw_model.graph.input]
            input_specs = {"run": run_input_specs}
            bento_model = bento_model.with_options(input_specs=input_specs)

        if not options.output_specs:
            raw_model = raw_model or _load_raw_model(bento_model)
            run_output_specs = [MessageToDict(out) for out in raw_model.graph.output]
            output_specs = {"run": run_output_specs}
            bento_model = bento_model.with_options(output_specs=output_specs)

    class ONNXRunnable(bentoml.Runnable):
        SUPPORTED_RESOURCES = ("nvidia.com/gpu", "cpu")
        SUPPORTS_CPU_MULTI_THREADING = True

        def __init__(self):
            super().__init__()

            session_options = (
                bento_model.info.options.session_options or ort.SessionOptions()
            )

            # check for resources
            available_gpus = os.getenv("CUDA_VISIBLE_DEVICES")
            if available_gpus is not None and available_gpus not in ("", "-1"):
                # assign GPU resources
                providers = bento_model.info.options.providers or [
                    "CUDAExecutionProvider",
                    "CPUExecutionProvider",
                ]

            else:
                # assign CPU resources

                # If onnxruntime-gpu is installed,
                # CUDAExecutionProvider etc. will be available even no
                # GPU is presented in system, which may result some
                # error when initializing ort.InferenceSession
                providers = bento_model.info.options.providers or [
                    "CPUExecutionProvider"
                ]

                # set CPUExecutionProvider parallelization options
                # TODO @larme: follow onnxruntime issue 11668 and
                # 10330 to decide best cpu parallelization strategy
                thread_count = int(os.getenv("BENTOML_NUM_THREAD", 1))
                session_options.execution_mode = ort.ExecutionMode.ORT_PARALLEL
                if session_options.intra_op_num_threads != 0:
                    logger.warning(
                        "Overriding specified 'session_options.intra_op_num_threads'."
                    )
                session_options.intra_op_num_threads = thread_count
                if session_options.inter_op_num_threads != 0:
                    logger.warning(
                        "Overriding specified 'session_options.inter_op_num_threads'."
                    )
                session_options.inter_op_num_threads = thread_count

            self.model = load_model(
                bento_model, session_options=session_options, providers=providers
            )

            self.predict_fns: dict[str, t.Callable[..., t.Any]] = {}
            for method_name in bento_model.info.signatures:
                self.predict_fns[method_name] = getattr(self.model, method_name)

    def add_runnable_method(
        method_name: str,
        signatures: ModelSignature,
        input_specs: list[dict[str, t.Any]],
        output_specs: list[dict[str, t.Any]],
    ):
        casting_funcs = [gen_input_casting_func(spec) for spec in input_specs]

        if len(output_specs) > 1:

            def _process_output(outs):
                return tuple(outs)

        else:

            def _process_output(outs):
                return outs[0]

        def _run(self: ONNXRunnable, *args: t.Any) -> t.Any:
            casted_args = [
                casting_funcs[idx](args[idx]) for idx in range(len(casting_funcs))
            ]

            input_names: dict[str, ONNXArgCastedType] = {
                i.name: val for i, val in zip(self.model.get_inputs(), casted_args)
            }
            output_names: list[str] = [o.name for o in self.model.get_outputs()]
            raw_outs = self.predict_fns[method_name](output_names, input_names)
            return _process_output(raw_outs)

        ONNXRunnable.add_method(
            _run,
            name=method_name,
            batchable=signatures.batchable,
            batch_dim=signatures.batch_dim,
            input_spec=signatures.input_spec,
            output_spec=signatures.output_spec,
        )

    for method_name, signatures in bento_model.info.signatures.items():
        options = t.cast(ModelOptions, bento_model.info.options)
        input_specs = options.input_specs[method_name]
        output_specs = options.output_specs[method_name]
        add_runnable_method(method_name, signatures, input_specs, output_specs)

    return ONNXRunnable
Private API: use :obj:`~bentoml.Model.to_runnable` instead.
src/bentoml/_internal/frameworks/onnx.py