code: def to_device(
      model: PreTrainedModel,
      device: Union[str, torch.DeviceObjType],
      config_obj: "LLMModelConfig",  # noqa F821
      curr_device: torch.DeviceObjType,
  ) -> Tuple[PreTrainedModel, torch.DeviceObjType]:
      """Move an LLM to the requested device, accounting for sharding and adapters.
  
      Args:
          model: Pretrained model to put on device
          config_obj: LLM config
          curr_device: The current device that the model is on
  
      Returns:
          `model` moved to `device`
      """
      device = torch.device(device)
  
      if device.type == curr_device.type:
          log_once(f"Model already on device'{device}'.")
          return model, device
      else:
          log_once(f"Moving LLM from '{curr_device}' to '{device}'.")
  
      model_kwargs = {}
      num_gpus = torch.cuda.device_count()
      if device == torch.device("cuda") and num_gpus > 1:
          # TODO: make this configurable in the future. These parameters are from FastChat:
          # https://github.com/lm-sys/FastChat/blob/0e958b852a14f4bef5f0e9d7a5e7373477329cf2/fastchat/serve/inference.py#L90  # noqa
          # TODO: Wrap device_map="auto" in a try-except block since it may not be supported for all models (E.g. BertLMHead)  # noqa
          # We don't add quantization here (float16 or bfloat16) since we may not always want to quantize. We should
          # make quantization configurable in the future via the trainer config.
          model_kwargs.update(
              dict(
                  low_cpu_mem_usage=True,
                  max_memory={i: "13GiB" for i in range(num_gpus)},
              )
          )
  
          if (
              config_obj.base_model
              not in _MODELS_WITH_DEVICE_MAP_AUTO_EXCLUSION
          ):
              model_kwargs["device_map"] = "auto"
  
          if config_obj.quantization:
              model_kwargs[
                  "quantization_config"
              ] = config_obj.quantization.to_bitsandbytes()
  
          # we save and reload the weights to ensure that they can be sharded across the GPUs using `from_pretrained`
          with tempfile.TemporaryDirectory() as tmpdir:
              model.save_pretrained(tmpdir)
  
              if config_obj.adapter:
                  model = AutoModelForCausalLM.from_pretrained(
                      config_obj.base_model,
                      **model_kwargs,
                  )
  
                  # Leave this import inline to support a minimal install of Ludwig
                  from peft import PeftModel  # noqa
  
                  model = PeftModel.from_pretrained(
                      model,
                      tmpdir,
                      torch_dtype=torch.float16,
                  )
              else:
                  model = AutoModelForCausalLM.from_pretrained(
                      tmpdir,
                      **model_kwargs,
                  )
      else:
          model = model.to(device)
  
      return model, device
  
imports: import copy
  import logging
  import tempfile
  from typing import Dict, Optional, Tuple, TYPE_CHECKING, Union
  import torch
  import torch.nn.functional
  import transformers
  from bitsandbytes.nn.modules import Embedding
  from packaging import version
  from transformers import AutoConfig, AutoModelForCausalLM, PreTrainedModel, PreTrainedTokenizer, TextStreamer
  from ludwig.constants import IGNORE_INDEX_TOKEN_ID, LOGITS, PREDICTIONS, PROBABILITIES
  from ludwig.schema.trainer import LLMTrainerConfig
  from ludwig.utils.error_handling_utils import default_retry
  from ludwig.utils.logging_utils import log_once
  from ludwig.utils.model_utils import find_embedding_layer_with_path
label_desc: Use image node_id 2 for calling a global function with example usage: to_device(model, device, config_obj, curr_device) and returns: model,  device, model,  device