code: def infer_and_cast(value: Any):
      
      # pylint: disable=too-many-return-statements
      if isinstance(value, (int, float, bool)):
          # Already one of our desired types, so leave as is.
          return value
      elif isinstance(value, list):
          # Recursively call on each list element.
          return [infer_and_cast(item) for item in value]
      elif isinstance(value, dict):
          # Recursively call on each dict value.
          return {key: infer_and_cast(item) for key, item in value.items()}
      elif isinstance(value, str):
          # If it looks like a bool, make it a bool.
          if value.lower() == "true":
              return True
          elif value.lower() == "false":
              return False
          else:
              # See if it could be an int.
              try:
                  return int(value)
              except ValueError:
                  pass
              # See if it could be a float.
              try:
                  return float(value)
              except ValueError:
                  # Just return it as a string.
                  return value
      else:
          raise ValueError(f"cannot infer type of {value}")
docs: In some cases we'll be feeding params dicts to functions we don't own;
      for example, PyTorch optimizers. In that case we can't use ``pop_int``
      or similar to force casts (which means you can't specify ``int`` parameters
      using environment variables). This function takes something that looks JSON-like
      and recursively casts things that look like (bool, int, float) to (bool, int, float).