def parley(self): print( f'{self.__class__.__name__}:{self.tag}: is at turn {self.task_turn_idx}, with {self.num_turns} pairs of turns needed...' ) if self.task_turn_idx == 0: self._run_initial_turn() self.task_turn_idx += 1 return """Otherwise, we proceed accordingly""" print( f'{self.__class__.__name__}:{self.tag}: About to act with task turn idx: {self.task_turn_idx}' ) acts = [None, None] for idx, agent in enumerate([self.agent, self.bot]): if not self.chat_done: acts[idx] = agent.act(timeout=self.max_resp_time) if (agent == self.bot and hasattr(self.bot, 'agent_id') and self.bot.agent_id): # Set speaker name as self.bot_agent_id otherwise, at frontend bot name such as "TransformerGenerator" would appear Compatibility.backward_compatible_force_set( acts[idx], 'id', self.bot.agent_id) acts[idx] = Message(Compatibility.maybe_fix_act( acts[idx])).json_safe_payload() print( f'Got act for agent idx {idx}, act was: {acts[idx]} and self.task_turn_idx: {self.task_turn_idx}.' ) if acts[idx].get('task_data', {}).get('final_rating') is not None: self.chat_done = True # agent ends chat after exceeding minimum number of turns # Human has just responded. Any problem data received now will be # regarding the bot's prior utterance turn_idx = -1 # Attach the problem data and final rating to the last utterance, since # the human hasn't said anything since then p = acts[idx]['task_data'].get( 'problem_data_for_prior_message') if p is not None: self.__add_problem_data_to_utterance(p, turn_idx=turn_idx) self.dialog[turn_idx]['final_rating'] = acts[idx]['task_data'][ 'final_rating'] # Save the final chat data date_folder = time.strftime('%Y_%m_%d') time_string = time.strftime('%Y%m%d_%H%M%S') chat_data_subfolder = os.path.join( self.opt['chat_data_folder'], date_folder) os.makedirs(chat_data_subfolder, exist_ok=True) chat_data_path = os.path.join( chat_data_subfolder, f'{time_string}_{np.random.randint(0, 1000)}_{self.task_type}.json', ) final_chat_data = self.get_final_chat_data() self.agent.mephisto_agent.state.messages.append( {'final_chat_data': final_chat_data}) # Append the chat data directly to the agent state's message list in # order to prevent the worker from seeing a new text response in the UI with open(chat_data_path, 'w+') as f_json: data_str = json.dumps(final_chat_data) f_json.write(data_str) print(f'{self.__class__.__name__}:{self.tag}: Data saved at ' f'{chat_data_path} for model: {self.bot.worker_id}.') # Soft-block the worker if there were acceptability violations acceptability_violations = final_chat_data[ 'acceptability_violations'][0] if (acceptability_violations is not None and acceptability_violations != ''): print( f'**NOTE** Acceptability violations detected: {acceptability_violations}' ) # Grant the failed qualification self.agent.mephisto_agent.get_worker().grant_qualification( self.block_qualification, 1) return else: utterance_data = { 'agent_idx': idx, # Get rid of annotations HTML if it's the bot response 'text': acts[idx]['text'].split('<br>')[0], 'id': acts[idx].get('id', 'NULL_ID'), # In case model doesn't set id } self.dialog.append(utterance_data) if idx == 0: # Human has just responded. Any problem data received now will be # regarding the bot's prior utterance p = acts[idx]['task_data'].get( 'problem_data_for_prior_message') if p is not None: turn_idx = -2 # Attach the problem data to the second-to-last utterance, since # the last utterance is what the human just said self.__add_problem_data_to_utterance(p, turn_idx=turn_idx) self._postprocess_acts(acts=acts, agent_idx=idx) for other_agent in [self.agent, self.bot]: if other_agent != agent: other_agent.observe(validate(acts[idx])) print( f'[agent {idx}] self.task_turn_idx: {self.task_turn_idx}, self.dialog is: {self.dialog}' ) self.task_turn_idx += 1
def _run_initial_turn(self) -> None: """ Run the initial turn for both the human and the bot. Optionally show the bot its persona. If we are in BST conversation mode, show 2 previous BST utterances to both the human and the bot; if we are in Meena-like conversation mode, show "Hi!" to the human and the bot and let the bot respond accordingly. """ control_msg = {"episode_done": False} if self.opt['include_persona']: # The Bot agent # We add the personas and 1/3 of the time WoW topic as the # first utterance in the history. # Previously for BST task, we also had a big first utterance # that gave instructions. Removing that for this task. persona_strings = [s.strip() for s in self.personas[1]] persona_utterance = self._get_persona_utterance( persona_strings=persona_strings, context_dataset=self.context_info['context_dataset'], additional_context=self.context_info['additional_context'], is_bot=True, ) message = control_msg.copy() message['text'] = persona_utterance # The bot seeing its persona does not count as a "turn" self.bot.observe(validate(message), increment_turn=False) if self.opt['conversation_start_mode'] == 'blended_skill_talk': print('[Displaying first utterances as per BST task.]') # Display the previous two utterances human_first_msg = { 'episode_done': False, 'id': self.agent.agent_id, 'text': self.context_info['person1_seed_utterance'], 'fake_start': True, 'agent_idx': 0, } for k, v in control_msg.items(): human_first_msg[k] = v bot_first_msg = { 'episode_done': False, 'id': self.bot.agent_id, 'text': self.context_info['person2_seed_utterance'], 'fake_start': True, 'agent_idx': 1, } print( f'human_first_msg: {human_first_msg}, bot_first_msg: {bot_first_msg}' ) self.dialog.append(human_first_msg) self.dialog.append(bot_first_msg) for observer in [self.agent, self.bot]: observer.observe(validate(human_first_msg)) observer.observe(validate(bot_first_msg)) elif self.opt['conversation_start_mode'] == 'hi': print('[Displaying "Hi!" only as per Meena task.]') if self.personas is not None: human_persona_strings = [s.strip() for s in self.personas[0]] else: human_persona_strings = ['', ''] human_first_msg = { 'episode_done': False, 'id': self.agent.agent_id, 'text': 'Hi!', 'fake_start': True, 'agent_idx': 0, 'task_data': { 'human_persona_string_1': human_persona_strings[0], 'human_persona_string_2': human_persona_strings[1], }, } for k, v in control_msg.items(): human_first_msg[k] = v self.dialog.append(human_first_msg) self.agent.observe(validate(human_first_msg)) self.bot.observe(validate(human_first_msg)) first_bot_act = self.bot.act() first_bot_act = Compatibility.backward_compatible_force_set( first_bot_act, 'id', self.bot.agent_id) self.agent.observe(validate(first_bot_act)) bot_utterance_data = { 'agent_idx': 1, 'text': first_bot_act['text'], 'id': first_bot_act['id'], } self.dialog.append(bot_utterance_data) else: raise ValueError( f"Conversation start mode {self.opt['conversation_start_mode']} " f"not recognized!")