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 project on Surge AI and return the Surge AI project ID.
  
  Attempt to find a Surge AI project for the template from the cache. If one exists, reuse that project.
  Otherwise, create a new project using the template and save it to the cache. Return the Surge AI project ID.
docstr_tok: ["Get","or","create","a","project","on","Surge","AI","and","return","the","Surge","AI","project","ID",".","Attempt","to","find","a","Surge","AI","project","for","the","template","from","the","cache",".","If","one","exists",",","reuse","that","project",".","Otherwise",",","create","a","new","project","using","the","template","and","save","it","to","the","cache",".","Return","the","Surge","AI","project","ID","."]
code: def _get_or_create_surge_project(
      self, template: CritiqueTaskTemplate
  ) -> str:
      """Get or create a project on Surge AI and return the Surge AI project ID.
  
      Attempt to find a Surge AI project for the template from the cache. If one exists, reuse that project.
      Otherwise, create a new project using the template and save it to the cache. Return the Surge AI project ID.
      """
  
      def create_surge_project():
          project = surge.Project.create(
              name=template.name,
              instructions=template.instructions,
              questions=[
                  self._to_surge_question(question)
                  for question in template.questions
              ],
              num_workers_per_task=template.num_respondents,
          )
          return {"id": project.id}
  
      with _surge_cache_lock:
          # Example cache key:
          # {
          #   "template": {
          #     # See CritiqueQuestionTemplate for complete schema
          #     "name": "some_name",
          #     "instructions": "some_instructions",
          #     "num_respondents": 1,
          #     "questions": []
          #   }
          # }
          #
          # 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.
          project_response, is_cached = self._cache.get(
              {"template": unstructure(template)}, create_surge_project
          )
      project_id = project_response["id"]
      if is_cached:
          hlog(f"Reusing existing Surge AI project: {project_id}")
      else:
          hlog(f"Creating new Surge AI project: {project_id}")
      return project_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 3 for calling the SurgeAICritiqueClient obj's underlying member method code with example usage: obj._get_or_create_surge_project(template) and returns: project_id, dict