code: def has_pending_ancestor_jobs(
      dataset: str,
      processing_step_names: list[str],
      processing_graph: ProcessingGraph = processing_graph,
  ) -> bool:
      """
      Check if the processing steps, or one of their ancestors, have a pending job, ie. if artifacts could exist
          in the cache in the future. This method is used when a cache entry is missing in the API,
          to return a:
          - 404 error, saying that the artifact does not exist,
          - or a 500 error, saying that the artifact could be available soon (retry).
  
      It is implemented by checking if a job exists for the artifacts or one of their ancestors.
  
      Note that, if dataset-config-names' job is pending, we cannot know if the config is valid or not, so we
          consider that the artifact could exist.
  
      Args:
          dataset (str): The name of the dataset.
          processing_step_names (list[str]): The processing step names (artifacts) to check.
          processing_graph (ProcessingGraph, optional): The processing graph.
  
      Returns:
          bool: True if any of the artifact could exist, False otherwise.
  
      Raises:
          ProcessingStepDoesNotExist: If any of the processing step does not exist.
      """
      job_types: set[str] = set()
      for processing_step_name in processing_step_names:
          processing_step = processing_graph.get_processing_step(
              processing_step_name
          )
          ancestors = processing_graph.get_ancestors(
              processing_step_name
          )
          job_types.add(processing_step.job_type)
          job_types.update(ancestor.job_type for ancestor in ancestors)
      logging.debug(
          f"looking at ancestor jobs of {processing_step_names}: {list(job_types)}"
      )
      # check if a pending job exists for the artifact or one of its ancestors
      # note that we cannot know if the ancestor is really for the artifact (ie: ancestor is for config1,
      # while we look for config2,split1). Looking in this detail would be too complex, this approximation
      # is good enough.
      return Queue().has_pending_jobs(
          dataset=dataset, job_types=list(job_types)
      )
  
imports: import logging
  from abc import ABC, abstractmethod
  from dataclasses import dataclass, field
  from http import HTTPStatus
  from typing import Optional, Union
  import pandas
  from libcommon.constants import CONFIG_INFO_KINDS, CONFIG_SPLIT_NAMES_KINDS, DATASET_CONFIG_NAMES_KINDS, DEFAULT_DIFFICULTY_MAX, DIFFICULTY_BONUS_BY_FAILED_RUNS
  from libcommon.processing_graph import ProcessingGraph, ProcessingStep, ProcessingStepDoesNotExist, processing_graph
  from libcommon.prometheus import StepProfiler
  from libcommon.queue import Queue
  from libcommon.simple_cache import CacheEntryDoesNotExistError, delete_dataset_responses, fetch_names, get_best_response, get_cache_entries_df, get_response_metadata, upsert_response_params
  from libcommon.state import ArtifactState, DatasetState, FirstStepsDatasetState
  from libcommon.storage_client import StorageClient
  from libcommon.utils import JobInfo, JobResult, Priority
label_desc: Use image node_id 5 for calling a global function with example usage: has_pending_ancestor_jobs(dataset, processing_step_names, processing_graph) and returns: Queue