code: def output_steps(self):
      steps_info = {}
      graph_structure = []
  
      def node_to_type(node):
          if node.type in ["linear", "start", "end", "join"]:
              return node.type
          elif node.type == "split":
              return "split-static"
          elif node.type == "foreach":
              if node.parallel_foreach:
                  return "split-parallel"
              return "split-foreach"
          return "unknown"  # Should never happen
  
      def node_to_dict(name, node):
          d = {
              "name": name,
              "type": node_to_type(node),
              "line": node.func_lineno,
              "doc": node.doc,
              "decorators": [
                  {
                      "name": deco.name,
                      "attributes": deco.attributes,
                      "statically_defined": deco.statically_defined,
                  }
                  for deco in node.decorators
                  if not deco.name.startswith("_")
              ],
              "next": node.out_funcs,
          }
          if d["type"] == "split-foreach":
              d["foreach_artifact"] = node.foreach_param
          elif d["type"] == "split-parallel":
              d["num_parallel"] = node.num_parallel
          if node.matching_join:
              d["matching_join"] = node.matching_join
          return d
  
      def populate_block(start_name, end_name):
          cur_name = start_name
          resulting_list = []
          while cur_name != end_name:
              cur_node = self.nodes[cur_name]
              node_dict = node_to_dict(cur_name, cur_node)
  
              steps_info[cur_name] = node_dict
              resulting_list.append(cur_name)
  
              if cur_node.type not in ("start", "linear", "join"):
                  # We need to look at the different branches for this
                  resulting_list.append(
                      [
                          populate_block(s, cur_node.matching_join)
                          for s in cur_node.out_funcs
                      ]
                  )
                  cur_name = cur_node.matching_join
              else:
                  cur_name = cur_node.out_funcs[0]
          return resulting_list
  
      graph_structure = populate_block("start", "end")
  
      steps_info["end"] = node_to_dict("end", self.nodes["end"])
      graph_structure.append("end")
  
      return steps_info, graph_structure
  
imports: import inspect
  import ast
  import re
label_desc: Use image node_id 10 for calling the FlowGraph obj's underlying member method code with example usage: obj.output_steps() and returns: steps_info,  graph_structure, str, d, resulting_list, node, str, str, str