code: def create_variables(
      module: typing.Union[Network, tf.keras.layers.Layer],
      input_spec: typing.Optional[types.NestedTensorSpec] = None,
      **kwargs: typing.Any,
  ) -> types.NestedTensorSpec:
      """Create variables in `module` given `input_spec`; return `output_spec`.
  
      Here `module` can be a `tf_agents.networks.Network` or `Keras` layer.
  
      Args:
        module: The instance we would like to create layers on.
        input_spec: The input spec (excluding batch dimensions).
        **kwargs: Extra arguments to `module.__call__`, e.g. `training=True`.
  
      Returns:
        Output specs, a nested `tf.TypeSpec` describing the output signature.
        If `module` returns a `tfp.Distribution`, then the associated nested
        object is a `tf_agents.specs.DistributionSpecV2` (which is not a true
        `tf.TypeSpec` but contains enough information to create a nested
        `tf.TypeSpec` using `tf_agents.distributions.utils.parameters_to_dict`).
  
      Raises:
        ValueError: If `module` is a generic Keras layer but `input_spec is None`.
        TypeError: If `module` is a `tf.keras.layers.{RNN,LSTM,GRU,...}`.  These
          must be wrapped in `tf_agents.keras_layers.RNNWrapper`.
      """
      # NOTE(ebrevdo): As a side effect, for generic keras Layers (not Networks)
      # this method stores new hidden properties in `module`:
      # `_network_output_spec`, `_network_state_spec`,
      # - which internal TF-Agents libraries make use of.
      if isinstance(module, Network):
          return module.create_variables(input_spec, **kwargs)
  
      # Generic keras layer
      if input_spec is None:
          raise ValueError(
              "Module is a Keras layer; an input_spec is required but saw "
              "None: {}".format(module)
          )
  
      if isinstance(module, tf.keras.layers.RNN):
          raise TypeError(
              "Keras RNN layers (non-cell layers) must be wrapped in "
              "tf_agents.keras_layers.RNNWrapper.  Layer: {}".format(
                  module
              )
          )
  
      maybe_spec = getattr(module, "_network_output_spec", None)
      if maybe_spec is not None:
          return maybe_spec
  
      # Has state outputs.
      recurrent_layer = (
          getattr(module, "get_initial_state", None) is not None
      )
  
      # Required input rank
      outer_ndim = _get_input_outer_ndim(module, input_spec)
  
      random_input = tensor_spec.sample_spec_nest(
          input_spec, outer_dims=(1,) * outer_ndim
      )
  
      if recurrent_layer:
          state = module.get_initial_state(random_input)
  
          def remove_singleton_batch_spec_dim(t):
              # Convert tensor to its type-spec, and remove the batch dimension
              # from the spec.
              spec = tf.type_spec_from_value(t)
              return nest_utils.remove_singleton_batch_spec_dim(
                  spec, outer_ndim=1
              )
  
          state_spec = tf.nest.map_structure(
              remove_singleton_batch_spec_dim, state
          )
  
          outputs = module(random_input, state, **kwargs)
          # tf.keras.layers.{LSTMCell, ...} return (output, [state1, state2,...]).
          output = outputs[0]
      else:
          output = module(random_input, **kwargs)
          state_spec = ()
  
      def _calc_unbatched_spec(x):
          if isinstance(x, tfp.distributions.Distribution):
              parameters = distribution_utils.get_parameters(x)
              parameter_specs = (
                  _convert_to_spec_and_remove_singleton_batch_dim(
                      parameters, outer_ndim=outer_ndim
                  )
              )
              return distribution_utils.DistributionSpecV2(
                  event_shape=x.event_shape,
                  dtype=x.dtype,
                  parameters=parameter_specs,
              )
          else:
              return tensor_spec.remove_outer_dims_nest(
                  tf.type_spec_from_value(x), num_outer_dims=outer_ndim
              )
  
      # pylint: disable=protected-access
      module._network_output_spec = tf.nest.map_structure(
          _calc_unbatched_spec, output
      )
      module._network_state_spec = state_spec
  
      return module._network_output_spec
  
imports: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  import abc
  import typing
  import six
  import tensorflow
  import tensorflow_probability
  from tf_agents.distributions import utils
  from tf_agents.networks import layer_utils
  from tf_agents.specs import tensor_spec
  from tf_agents.trajectories import time_step
  from tf_agents.typing import types
  from tf_agents.utils import common
  from tf_agents.utils import nest_utils
  from tf_agents.utils import object_identity
  from tensorflow.python.trackable import base
  from tensorflow.python.util import tf_decorator
  from tensorflow.python.util import tf_inspect
label_desc: Use image node_id 4 for calling a global function with example usage: create_variables(module, input_spec) and returns: module, module, maybe_spec, nest_utils, distribution_utils, tensor_spec