code: def tf_action_exploration(self, action, exploration, action_spec):
          
          action_shape = tf.shape(input=action)
          exploration_value = exploration.tf_explore(
              episode=self.global_episode,
              timestep=self.global_timestep,
              shape=action_spec['shape']
          )
          exploration_value = tf.expand_dims(input=exploration_value, axis=0)
  
          if action_spec['type'] == 'bool':
              action = tf.where(
                  condition=(tf.random_uniform(shape=action_shape) < exploration_value),
                  x=(tf.random_uniform(shape=action_shape) < 0.5),
                  y=action
              )
  
          elif action_spec['type'] == 'int':
              action = tf.where(
                  condition=(tf.random_uniform(shape=action_shape) < exploration_value),
                  x=tf.random_uniform(shape=action_shape, maxval=action_spec['num_actions'], dtype=util.tf_dtype('int')),
                  y=action
              )
  
          elif action_spec['type'] == 'float':
              noise = tf.random_normal(shape=action_shape, dtype=util.tf_dtype('float'))
              action += noise * exploration_value
              if 'min_value' in action_spec:
                  action = tf.clip_by_value(
                      t=action,
                      clip_value_min=action_spec['min_value'],
                      clip_value_max=action_spec['max_value']
                  )
  
          return action
docs: Applies optional exploration to the action (post-processor for action outputs).
  
          Args:
               action (tf.Tensor): The original output action tensor (to be post-processed).
               exploration (Exploration): The Exploration object to use.
               action_spec (dict): Dict specifying the action space.
          Returns:
              The post-processed action output tensor.