class_docstr: A CritiqueClient that creates tasks for workers on Surge AI.
  
  Surge AI concepts:
  
  - A **project** contains **instructions** and **questions**, which are templates that can contain
    placeholders.
  - A **task** is created in a project and contains **fields** that are interpolated into the
    placeholders in the project's instructions and questions templates to instantiate the actual instructions
    and questions in the task.
  - A **task response** is a response to a task by a single worker and contains answers to all the questions
    in the task.
  
  Mapping of HELM concepts to Surge AI concepts:
  
  - A `CritiqueTaskTemplate` maps to a **project**.
  - A `CritiqueQuestionTemplate` maps to a **question** template in a project.
  - A `CritiqueRequest` maps to a **task**
      - `CritiqueRequest.template` indicates which **project** the task should be created in.
      - `CritiqueRequest.fields` provides the fields that are interpolated into the placeholders in the
        projects' instructions and questions templates.
  - A `CritiqueResponse` maps to a **task response**.
  - A `CritiqueRequestResult` maps to a list of **task responses** across multiple workers for a task.
docstr: Get or create a task on Surge AI and return the Surge AI project ID.
  
  Attempt to find a Surge AI task inside this project for the fields from the cache.
  If one exists, reuse that task. Otherwise, create a new task inside the project using the fields.
  and save it to the cache. Return the Surge AI task ID.
docstr_tok: ["Get","or","create","a","task","on","Surge","AI","and","return","the","Surge","AI","project","ID",".","Attempt","to","find","a","Surge","AI","task","inside","this","project","for","the","fields","from","the","cache",".","If","one","exists",",","reuse","that","task",".","Otherwise",",","create","a","new","task","inside","the","project","using","the","fields",".","and","save","it","to","the","cache",".","Return","the","Surge","AI","task","ID","."]
code: def _get_or_create_task(
      self, project_id: str, fields: Dict[str, str]
  ) -> str:
      """Get or create a task on Surge AI and return the Surge AI project ID.
  
      Attempt to find a Surge AI task inside this project for the fields from the cache.
      If one exists, reuse that task. Otherwise, create a new task inside the project using the fields.
      and save it to the cache. Return the Surge AI task ID."""
      project = surge.Project.retrieve(project_id)
  
      def create_surge_task():
          tasks = project.create_tasks(
              [fields], launch=False
          )  # TODO: Make launch parameter configurable
          if len(tasks) != 1:
              return RuntimeError(
                  f"Expected one task in Surge response, but got {len(tasks)} tasks"
              )
          task = tasks[0]
          return {"id": task.id}
  
      with _surge_cache_lock:
          # Example cache key:
          # {
          #   "project_id": "17e323f1-f7e4-427c-a2d5-456743aba8",
          #   "fields": {
          #     "some_field": "some_value"
          #   }
          # }
          #
          # Example cache value:
          # {"id": "17e323f1-f7e4-427c-a2d5-456743aba8"}
          #
          # Note:
          # We do not cache the additional fields returned by surge.Project.create()
          # because they are mutable server-side, and server-side mutations may cause
          # stale cache issues.
          task_response, is_cached = self._cache.get(
              {"project_id": project_id, "fields": fields},
              create_surge_task,
          )
      task_id = task_response["id"]
      if is_cached:
          hlog(f"Reusing existing Surge AI task: {task_id}")
      else:
          hlog(f"Creating new Surge AI task: {task_id}")
      return task_id
  
imports: from abc import ABC, abstractmethod
  import random
  import threading
  from typing import Dict, List, Union
  from cattrs import unstructure
  import surge
  from surge import questions
  from helm.common.hierarchical_logger import hlog
  from helm.common.cache import Cache, CacheConfig
  from helm.common.critique_request import CritiqueQuestionTemplate, CritiqueRequest, CritiqueRequestResult, CritiqueTaskTemplate, CritiqueResponse, QuestionType
label_desc: Use image node_id 4 for calling the SurgeAICritiqueClient obj's underlying member method code with example usage: obj._get_or_create_task(project_id, fields) and returns: task_id, dict, RuntimeError