code: def clip_gradients(
      gradients_dict: "ParamDict",
      *,
      grad_clip: Optional[float] = None,
      grad_clip_by: str = "value",
  ) -> Optional[float]:
      """Performs gradient clipping on a grad-dict based on a clip value and clip mode.
  
      Changes the provided gradient dict in place.
  
      Args:
          gradients_dict: The gradients dict, mapping str to gradient tensors.
          grad_clip: The value to clip with. The way gradients are clipped is defined
              by the `grad_clip_by` arg (see below).
          grad_clip_by: One of 'value', 'norm', or 'global_norm'.
  
      Returns:
          If `grad_clip_by`="global_norm" and `grad_clip` is not None, returns the global
          norm of all tensors, otherwise returns None.
      """
      # No clipping, return.
      if grad_clip is None:
          return
  
      # Clip by value (each gradient individually).
      if grad_clip_by == "value":
          for k, v in gradients_dict.copy().items():
              gradients_dict[k] = (
                  None
                  if v is None
                  else torch.clip(v, -grad_clip, grad_clip)
              )
  
      # Clip by L2-norm (per gradient tensor).
      elif grad_clip_by == "norm":
          for k, v in gradients_dict.copy().items():
              if v is not None:
                  # Compute the L2-norm of the gradient tensor.
                  norm = v.norm(2)
                  # Clip all the gradients.
                  if norm > grad_clip:
                      v.mul_(grad_clip / norm)
  
      # Clip by global L2-norm (across all gradient tensors).
      else:
          assert (
              grad_clip_by == "global_norm"
          ), f"`grad_clip_by` ({grad_clip_by}) must be one of [value|norm|global_norm]!"
  
          grads = [g for g in gradients_dict.values() if g is not None]
          norm_type = 2.0
          if len(grads) == 0:
              return torch.tensor(0.0)
          device = grads[0].device
  
          total_norm = torch.norm(
              torch.stack(
                  [
                      torch.norm(g.detach(), norm_type).to(device)
                      for g in grads
                  ]
              ),
              norm_type,
          )
          if torch.logical_or(total_norm.isnan(), total_norm.isinf()):
              raise RuntimeError(
                  f"The total norm of order {norm_type} for gradients from "
                  "`parameters` is non-finite, so it cannot be clipped. "
              )
          clip_coef = grad_clip / (total_norm + 1e-6)
          # Note: multiplying by the clamped coef is redundant when the coef is clamped to
          # 1, but doing so avoids a `if clip_coef < 1:` conditional which can require a
          # CPU <=> device synchronization when the gradients do not reside in CPU memory.
          clip_coef_clamped = torch.clamp(clip_coef, max=1.0)
          for g in grads:
              g.detach().mul_(clip_coef_clamped.to(g.device))
          return total_norm
  
imports: import logging
  import os
  import warnings
  from typing import Dict, List, Optional, TYPE_CHECKING, Union
  import gymnasium
  import numpy
  import tree
  from gymnasium.spaces import Discrete, MultiDiscrete
  from packaging import version
  import ray
  from ray.rllib.models.repeated_values import RepeatedValues
  from ray.rllib.utils.annotations import Deprecated, PublicAPI, DeveloperAPI
  from ray.rllib.utils.framework import try_import_torch
  from ray.rllib.utils.numpy import SMALL_NUMBER
  from ray.rllib.utils.typing import LocalOptimizer, SpaceStruct, TensorStructType, TensorType
label_desc: Use image node_id 3 for calling a global function with example usage: clip_gradients(gradients_dict) and returns: total_norm, torch