class_docstr: A graph of processing steps.
  
  The processing steps can have multiple parents, and multiple children (next processing steps, found automatically
    by traversing the graph).
  The graph can have multiple roots.
  
  Args:
      specification (ProcessingGraphSpecification): The specification of the graph.
  
  Raises:
      ValueError: If the graph is not a DAG.
      ValueError: If a processing step provides dataset config names but its input type is not 'dataset', or if a
        processing step provides config split names but its input type is not 'config'.
      ValueError: If a root processing step (ie. a processing step with no parent) is not a dataset processing step.
class_docstr_tok: ["A","graph","of","processing","steps",".","The","processing","steps","can","have","multiple","parents",",","and","multiple","children","(","next","processing","steps",",","found","automatically","by","traversing","the","graph",")",".","The","graph","can","have","multiple","roots",".","Args",":","specification","(","ProcessingGraphSpecification",")",":","The","specification","of","the","graph",".","Raises",":","ValueError",":","If","the","graph","is","not","a","DAG",".","ValueError",":","If","a","processing","step","provides","dataset","config","names","but","its","input","type","is","not","'dataset","'",",","or","if","a","processing","step","provides","config","split","names","but","its","input","type","is","not","'config","'",".","ValueError",":","If","a","root","processing","step","(","ie",".","a","processing","step","with","no","parent",")","is","not","a","dataset","processing","step","."]
docstr: Get the first processing steps.
  
  The first processing steps are the ones that don't have a previous step. This means that they will be computed
  first when a dataset is updated. Their input type is always "dataset".
  
  The returned processing steps are copies of the original ones, so that they can be modified without affecting
  the original ones.
  
  Returns:
      list[ProcessingStep]: The list of first processing steps
docstr_tok: ["Get","the","first","processing","steps",".","The","first","processing","steps","are","the","ones","that","do","n't","have","a","previous","step",".","This","means","that","they","will","be","computed","first","when","a","dataset","is","updated",".","Their","input","type","is","always","``","dataset","''",".","The","returned","processing","steps","are","copies","of","the","original","ones",",","so","that","they","can","be","modified","without","affecting","the","original","ones",".","Returns",":","list","[","ProcessingStep","]",":","The","list","of","first","processing","steps"]
code: def get_first_processing_steps(self) -> list[ProcessingStep]:
      """
      Get the first processing steps.
  
      The first processing steps are the ones that don't have a previous step. This means that they will be computed
      first when a dataset is updated. Their input type is always "dataset".
  
      The returned processing steps are copies of the original ones, so that they can be modified without affecting
      the original ones.
  
      Returns:
          list[ProcessingStep]: The list of first processing steps
      """
      return copy_processing_steps_list(self._first_processing_steps)
  
imports: from __future__ import annotations
  from collections.abc import Mapping
  from dataclasses import dataclass, field
  from typing import Any, Literal, Optional, TypedDict, Union, get_args
  import networkx
  from libcommon.constants import DEFAULT_DIFFICULTY, DEFAULT_INPUT_TYPE, DEFAULT_JOB_RUNNER_VERSION, MIN_BYTES_FOR_BONUS_DIFFICULTY
  from libcommon.utils import inputs_to_string
label_desc: Use image node_id 7 for calling the ProcessingGraph obj's underlying member method code with example usage: obj.get_first_processing_steps() and returns: copy_processing_steps_list