code: def _ragged_substr(text_input, begin, size):
      if not (
          isinstance(text_input, ragged_tensor.RaggedTensor)
          or isinstance(begin, ragged_tensor.RaggedTensor)
          or isinstance(size, ragged_tensor.RaggedTensor)
      ):
          return string_ops.substr_v2(text_input, begin, size)
  
      # TODO(edloper) Update this to use ragged_tensor_shape.broadcast_dynamic_shape
      # once it's been updated to handle uniform_row_lengths correctly.
      if ragged_tensor.is_ragged(text_input):
          if text_input.ragged_rank != 1 or text_input.shape.rank != 2:
              return None  # Test only works for `shape=[N, None]`
          text_input_flat = text_input.flat_values
      else:
          text_input_flat = array_ops.reshape(text_input, [-1])
      broadcasted_text = array_ops.gather_v2(
          text_input_flat, begin.nested_value_rowids()[-1]
      )
      new_tokens = string_ops.substr_v2(
          broadcasted_text, begin.flat_values, size.flat_values
      )
      return begin.with_flat_values(new_tokens)
  
imports: from absl.testing import parameterized
  import tensorflow
  from tensorflow.python.framework import constant_op
  from tensorflow.python.framework import errors_impl
  from tensorflow.python.framework import test_util
  from tensorflow.python.ops import array_ops
  from tensorflow.python.ops import string_ops
  from tensorflow.python.ops.ragged import ragged_factory_ops
  from tensorflow.python.ops.ragged import ragged_tensor
  from tensorflow_text.python.ops import regex_split_ops
label_desc: Use image node_id 2 for calling a global function with example usage: _ragged_substr(text_input, begin, size) and returns: begin, string_ops, None