class_docstr: Represents a stack of images to run through.
  
  Each element of the stack contains a list of the workers who have seen the given
  image for a given model. The stack ensures that no worker will see the same image
  twice.
class_docstr_tok: ["Represents","a","stack","of","images","to","run","through",".","Each","element","of","the","stack","contains","a","list","of","the","workers","who","have","seen","the","given","image","for","a","given","model",".","The","stack","ensures","that","no","worker","will","see","the","same","image","twice","."]
docstr: Returns the image name, persona strings, model name, etc. for the next HIT.
  
  Finds an image that we don't currently have enough conversations for, ensuring
  that the given worker will not have had a conversation employing this image
  before, with any model. Returns the index of the given image, the name of the
  model with which to have a conversation, and a flag indicating whether there are
  no more image pairs to show this worker.
docstr_tok: ["Returns","the","image","name",",","persona","strings",",","model","name",",","etc",".","for","the","next","HIT",".","Finds","an","image","that","we","do","n't","currently","have","enough","conversations","for",",","ensuring","that","the","given","worker","will","not","have","had","a","conversation","employing","this","image","before",",","with","any","model",".","Returns","the","index","of","the","given","image",",","the","name","of","the","model","with","which","to","have","a","conversation",",","and","a","flag","indicating","whether","there","are","no","more","image","pairs","to","show","this","worker","."]
code: def get_next_image(self, worker: str) -> Tuple[int, str, bool]:
      """
      Returns the image name, persona strings, model name, etc. for the next HIT.
  
      Finds an image that we don't currently have enough conversations for, ensuring
      that the given worker will not have had a conversation employing this image
      before, with any model. Returns the index of the given image, the name of the
      model with which to have a conversation, and a flag indicating whether there are
      no more image pairs to show this worker.
      """
      with self.next_image_lock:
          no_more_work = False
  
          # Find the next entry in the stack that needs more workers
          workers_by_model = self._get_stack_entry(self.pointer)
          while (
              workers_by_model is not None
              and not self._need_more_convos(workers_by_model)
          ):
              self.pointer += 1
              print(f"Pointer at {self.pointer}")
              workers_by_model = self._get_stack_entry(self.pointer)
  
          # Find the next entry in the stack that the worker hasn't completed before
          worker_pointer = self.pointer
          while workers_by_model is not None and (
              any(
                  worker in workers
                  for workers in workers_by_model.values()
              )
              or not self._need_more_convos(workers_by_model)
          ):
              print(f"Pointer for worker {worker} at {self.pointer}")
              worker_pointer += 1
              workers_by_model = self._get_stack_entry(worker_pointer)
  
          # Deal with the case in which no entry is suitable for the worker
          if workers_by_model is None:
              print(
                  f"WARNING: getting a random stack for worker {worker}."
              )
              worker_pointer = random.randrange(len(self.stack))
              workers_by_model = self.stack[worker_pointer]
              no_more_work = True
              # We'll want to assign this worker a qualification to prevent more work
  
          self.conditionally_save_stack()
  
          # Pick out a model for this worker, among the ones that we need more
          # conversations for
          available_models = [
              model
              for model, workers in workers_by_model.items()
              if len(workers) < self.evals_per_combo
          ]
          if len(available_models) == 0:
              print(
                  f"WARNING: no more convos needed for any model for "
                  f"{worker_pointer:d}. Picking a random model for worker "
                  f"{worker}."
              )
              available_models = list(workers_by_model.keys())
          print(f"Available models: " + ", ".join(available_models))
          chosen_model = random.choice(available_models)
          print(
              f"Retrieving stack {worker_pointer:d} for worker {worker} and test "
              f"case {chosen_model}."
          )
          workers_by_model[chosen_model].append(worker)
  
          return worker_pointer, chosen_model, no_more_work
  
imports: import base64
  import datetime
  import json
  import os
  import random
  import threading
  import time
  import unittest
  from io import BytesIO
  from typing import Any, Dict, Optional, Tuple
  from PIL import Image
  from parlai.core.message import Message
  from parlai.core.metrics import Metric
  from parlai.core.params import ParlaiParser
  from parlai.core.loader import load_task_module
  from parlai.crowdsourcing.utils.tests import AbstractParlAIChatTest
  from parlai.tasks.blended_skill_talk.agents import ContextGenerator
label_desc: Use image node_id 13 for calling the ImageStack obj's underlying member method code with example usage: obj.get_next_image(worker) and returns: worker_pointer,  chosen_model,  no_more_work