def check_bye_intent(self, utterance): # checking for intent = "bye" user_dacts = [] dact = DialogueAct(UserIntents.UNK, []) if any([ re.search(r'\b{0}\b'.format(pattern), utterance) for pattern in self.bye_pattern ]): dact.intent = UserIntents.BYE if dact.intent != UserIntents.UNK: user_dacts.append(dact) return user_dacts
def check_inquire_intent(self, utterance): # matching intents to "list", "Summarize", "Subset", "Compare" and "Similar" user_dacts = [] dact = DialogueAct(UserIntents.UNK, []) for slot, values in self.tag_words_user_inquire.items(): if any([value for value in values if value in utterance]): dact.intent = UserIntents.INQUIRE dact.params.append(ItemConstraint(slot, Operator.EQ, "")) if dact.intent == UserIntents.UNK: # and self._is_question(raw_utterance): for slot, values in self.tag_words_user_reveal_inquire.items(): if any([value for value in values if value in utterance]): dact.intent = UserIntents.INQUIRE dact.params.append(ItemConstraint(slot, Operator.EQ, "")) if dact.intent != UserIntents.UNK: user_dacts.append(dact) return user_dacts
def start_dialogue(self, new_user=False): """Starts the dialogue by generating a response from the agent :return: First agent response """ self.dialogue_state_tracker.dialogue_state.initialize() self.dialogue_state_tracker.dialogue_context.initialize() agent_dact = DialogueAct(AgentIntents.WELCOME, [ ItemConstraint('new_user', Operator.EQ, new_user), ItemConstraint('is_bot', Operator.EQ, self.isBot) ]) self.dialogue_state_tracker.update_state_agent([agent_dact]) return [agent_dact]
def check_reject_intent(self, utterance): # checking for intent = "reject" user_dacts = [] dact = DialogueAct(UserIntents.UNK, []) if any([ re.search(r'\b{0}\b'.format(pattern), utterance) for pattern in self.dontlike_movie_pattern ]): dact.intent = UserIntents.REJECT dact.params = [ItemConstraint('reason', Operator.EQ, 'dont_like')] elif any([ re.search(r'\b{0}\b'.format(pattern), utterance) for pattern in self.watched_pattern ]): dact.intent = UserIntents.REJECT dact.params = [ItemConstraint('reason', Operator.EQ, 'watched')] if dact.intent != UserIntents.UNK: user_dacts.append(dact) return user_dacts
def _user_options_remove_preference_CIN(self, CIN): """Generate options for user to select a parameter to remove :type CIN: dict :param CIN: The current information needs :return: a list of button options """ options = {} params = [] for slot, values in CIN.items(): if not values: continue if isinstance(values, list): params.extend([ ItemConstraint(slot, Operator.EQ, value) for value in set(values) if value not in Values.__dict__.values() and not value.startswith('.NOT.') ]) else: if values not in Values.__dict__.values( ) and not values.startswith('.NOT.'): params.append(ItemConstraint(slot, Operator.EQ, values)) for param in params: value = deepcopy(param.value) negative = False if value.startswith('.NOT.'): negative = True # TODO. Add changes here value = value.replace('.NOT.', '') _a_an = 'an' if value[0] in ['a', 'e', 'i', 'o', 'u'] else 'a' param_key = DialogueAct(UserIntents.REMOVE_PREFERENCE, [param]) if param.slot == Slots.GENRES.value: if negative: options[param_key] = [ random.choice([ f'I want {_a_an} "{value}" movie.', f'I would prefer {_a_an} "{value}" ' f'film.' ]) ] else: options[param_key] = [ random.choice([ f'Don\'t want {_a_an} "{value}" ' f'movie.', f'Won\'t prefer {_a_an} "{value}" film.' ]) ] elif param.slot == Slots.TITLE.value: if negative: options[param_key] = [ f'I want movies named like "{value}".' ] else: options[param_key] = [f'No movies named like "{value}".'] elif param.slot == Slots.KEYWORDS.value: if negative: options[param_key] = [f'I need movies based on "{value}".'] else: options[param_key] = [ random.choice([ f'Don\'t need movies based on ' f'"{value}".', f'No need of {_a_an} "{value}" film.' ]) ] elif param.slot == Slots.DIRECTORS.value: if negative: options[param_key] = [ random.choice([ f'I want the director "{value.title()}".', f'Should be directed by "{value.title()}".' ]) ] else: options[param_key] = [ random.choice([ f'Don\'t want the director "{value.title()}".', f'Shouldn\'t be directed by "{value.title()}".' ]) ] elif param.slot == Slots.ACTORS.value: if negative: options[param_key] = [ f'I want the actor "{value.title()}".' ] else: options[param_key] = [ random.choice([ f'Don\'t consider actor "{value.title()}".', f'Remove "{value.title()}" from the list of actors.' ]) ] elif param.slot == Slots.YEAR.value: if negative: options[param_key] = [ random.choice([ f'Release year should be the "' f'{self._summarize_title_year(value)}".', f'Need a movie from the "' f'{self._summarize_title_year(value)}".' ]) ] else: options[param_key] = [ random.choice([ f'Release year shouldn\'t be the "' f'{self._summarize_title_year(value)}".', f'Don\'t need a movie from the "' f'{self._summarize_title_year(value)}".' ]) ] options.update({'/restart': ['/restart']}) return options
def _user_options_remove_preference(self, dual_params): """Generate options for user to select in case of two parameters have same value :param dual_params: The current parameters with two slots :return: a list of button options """ options = {} for value, params in dual_params.items(): for param in params: negative = False if value.startswith('.NOT.'): negative = True # TODO. Add changes here value = value.replace('.NOT.', '') _a_an = 'an' if value[0] in ['a', 'e', 'i', 'o', 'u'] else 'a' param_key = DialogueAct(UserIntents.REMOVE_PREFERENCE, [param]) if param == Slots.GENRES.value: if negative: options[param_key] = [ random.choice([ f'I want {_a_an} "{value}" ' f'genre movie.', f'I would prefer {_a_an} "{value}" ' f'genre film.' ]) ] else: options[param_key] = [ random.choice([ f'Don\'t want {_a_an} "{value}" genre' f'movie.', f'Won\'t prefer {_a_an} "{value}" ' f'genre film.' ]) ] elif param == Slots.TITLE.value: if negative: options[param_key] = [ f'I want movies named like "{value}".' ] else: options[param_key] = [ f'No movies named like "{value}".' ] elif param == Slots.KEYWORDS.value: if negative: options[param_key] = [ f'I need movies based on "{value}".' ] else: options[param_key] = [ random.choice([ f'Don\'t need movies based on ' f'"{value}".', f'No need of {_a_an} "{value}" film.' ]) ] elif param == Slots.DIRECTORS.value: if negative: options[param_key] = [ random.choice([ f'I want the director "{value.title()}".', f'Should be directed by "{value.title()}".' ]) ] else: options[param_key] = [ random.choice([ f'Don\'t want the director "{value.title()}".', f'Shouldn\'t be directed by "{value.title()}".' ]) ] elif param == Slots.ACTORS.value: if negative: options[param_key] = [ f'I want the actor "{value.title()}".' ] else: options[param_key] = [ random.choice([ f'Don\'t consider actor "{value.title()}".', f'Remove "{value.title()}" from the list of actors.' ]) ] elif param == Slots.YEAR.value: if negative: options[param_key] = [ random.choice([ f'Release year should be the "' f'{self._summarize_title_year(value)}".', f'Need a movie from the "' f'{self._summarize_title_year(value)}".' ]) ] else: options[param_key] = [ random.choice([ f'Release year shouldn\'t be the "' f'{self._summarize_title_year(value)}".', f'Don\'t need a movie from the "' f'{self._summarize_title_year(value)}".' ]) ] options.update({'/restart': ['/restart']}) return options
def generate_dact(self, raw_utterance, options, dialogue_state=None, dialogue_context=None): """Processes the utterance according to dialogue state and context and generate a user dialogue act for Agent to understand. :type dialogue_state: DialogueState :type last_agent_dact: DialogueAct :type options: dict :param utterance: a string containing user input :param options: a list of options provided to the user to choose from :param dialogue_state: the current dialogue state, if available :param dialogue_context: the current dialogue context, if available :return: a list of dialogue acts """ # this is the top priority. The agent must check if user selected any option if options: for dact, value in options.items(): if (isinstance(value, list) and value[0] == raw_utterance) or value == \ raw_utterance: if dact.intent == UserIntents.CONTINUE_RECOMMENDATION: dact.params = self.intents_checker.generate_params_continue_recommendation( dialogue_state.item_in_focus) return [dact] user_dacts = [] # Define a list of dialogue acts for this specific utterance utterance = self.intents_checker._lemmatize_value( raw_utterance) # process the utterance for necessary self.dialogue_state = dialogue_state user_dacts.extend(self.intents_checker.check_bye_intent(utterance)) if len(user_dacts) > 0: return user_dacts if not self.dialogue_state.last_agent_dacts: user_dacts.extend(self.intents_checker.check_reveal_voluntary_intent(utterance, raw_utterance)) if len(user_dacts) == 0: user_dacts.extend(self.intents_checker.check_hi_intent(utterance)) if len(user_dacts) > 0: return user_dacts else: return None for last_agent_dact in self.dialogue_state.last_agent_dacts: if last_agent_dact.intent == AgentIntents.WELCOME: user_dacts.extend(self.intents_checker.check_reveal_voluntary_intent(utterance, raw_utterance)) if len(user_dacts) == 0: user_dacts.extend(self.intents_checker.check_acknowledge_intent(utterance)) if len(user_dacts) > 0: return user_dacts elif last_agent_dact.intent == AgentIntents.ELICIT: user_dacts.extend(self.intents_checker.check_reveal_intent(utterance, raw_utterance, last_agent_dact)) if len(user_dacts) == 0 or any([param.value in Values.__dict__.values() for dact in user_dacts for param in dact.params]): user_dacts.extend(self.intents_checker.check_reveal_voluntary_intent(utterance, raw_utterance)) if len(user_dacts) > 0: return user_dacts if dialogue_state.agent_made_offer: user_dacts.extend(self.intents_checker.check_reject_intent(utterance)) if len(user_dacts) == 0: user_dacts.extend(self.intents_checker.check_inquire_intent(utterance)) if len(user_dacts) == 0: user_dacts.extend(self.intents_checker.check_reveal_voluntary_intent(utterance, raw_utterance)) if len(user_dacts) == 0: deny_dact = self.intents_checker.check_deny_intent(utterance) if len(deny_dact) > 0: deny_dact[0].intent = UserIntents.INQUIRE user_dacts.extend(deny_dact) if len(user_dacts) > 0: return user_dacts if len(user_dacts) == 0: user_dacts.append(DialogueAct(UserIntents.UNK, [])) return user_dacts
def next_action(self, dialogue_state, dialogue_context=None, restart=False): """Decides the next action to be taken by the agent based on the current state and context. :type dialogue_state: DialogueState :param dialogue_state: current dialogue state :param dialogue_context: context of the dialogue :return: a list of Dialogue Acts """ agent_dacts = [] slots = deepcopy(dialogue_state.agent_requestable) if not dialogue_state.last_user_dacts and not restart: agent_dacts.append( DialogueAct(AgentIntents.WELCOME, [ ItemConstraint('new_user', Operator.EQ, self.new_user), ItemConstraint('is_bot', Operator.EQ, self.isBot) ])) return agent_dacts if not dialogue_state.last_agent_dacts and not restart: if not dialogue_state.last_agent_dacts: agent_dacts.append( DialogueAct(AgentIntents.WELCOME, [ ItemConstraint('new_user', Operator.EQ, self.new_user), ItemConstraint('is_bot', Operator.EQ, self.isBot) ])) if (not dialogue_state.last_user_dacts and restart) or \ (dialogue_state.last_user_dacts and UserIntents.RESTART in [dact.intent for dact in dialogue_state.last_user_dacts]): agent_dacts.append(DialogueAct(AgentIntents.RESTART, [])) agent_dacts.append( DialogueAct(AgentIntents.ELICIT, [ItemConstraint(slots[0], Operator.EQ, '')])) return agent_dacts for user_dact in dialogue_state.last_user_dacts: agent_dact = DialogueAct(AgentIntents.UNK, []) # generating intent = "bye" if user_dact.intent == UserIntents.BYE: agent_dact.intent = AgentIntents.BYE agent_dacts.append(deepcopy(agent_dact)) return agent_dacts # generating intent = "elicit" if user_dact.intent == UserIntents.ACKNOWLEDGE or user_dact.intent == \ UserIntents.UNK: if AgentIntents.WELCOME in [ dact.intent for dact in dialogue_state.last_agent_dacts ]: agent_dact.intent = AgentIntents.ELICIT agent_dact.params.append( ItemConstraint(slots[0], Operator.EQ, '')) agent_dacts.append(deepcopy(agent_dact)) return agent_dacts # deciding between intent "elicit" or "recommend" if dialogue_state.agent_made_partial_offer: # agent will inform about number of CIN_slots = [ key for key in dialogue_state.frame_CIN.keys() if not dialogue_state.frame_CIN[key] and key != Slots.TITLE.value ] if len( CIN_slots ) >= dialogue_state.slot_left_unasked: # if there is a scope of # further questioning # results and will ask next question agent_dact.intent = AgentIntents.COUNT_RESULTS agent_dact.params.append( ItemConstraint('count', Operator.EQ, len(dialogue_state.database_result))) agent_dacts.append(deepcopy(agent_dact)) # adding another dialogue act of ELICIT if dialogue_state.agent_req_filled: random.shuffle(CIN_slots) agent_dact = DialogueAct(AgentIntents.ELICIT, []) agent_dact.params.append( ItemConstraint(CIN_slots[0], Operator.EQ, "")) agent_dacts.append(deepcopy(agent_dact)) else: agent_dact = DialogueAct(AgentIntents.ELICIT, []) random.shuffle(slots) for slot in slots: if not dialogue_state.frame_CIN[slot]: agent_dact.params.append( ItemConstraint(slot, Operator.EQ, '')) break agent_dacts.append(deepcopy(agent_dact)) else: agent_dact = DialogueAct(AgentIntents.RECOMMEND, []) item_in_focus = dialogue_state.database_result[0] agent_dact.params.append( ItemConstraint(Slots.TITLE.value, Operator.EQ, item_in_focus[Slots.TITLE.value])) elif dialogue_state.agent_should_make_offer: agent_dact.intent = AgentIntents.RECOMMEND agent_dact.params.append( ItemConstraint( Slots.TITLE.value, Operator.EQ, dialogue_state.item_in_focus[Slots.TITLE.value])) agent_dacts.append(deepcopy(agent_dact)) elif dialogue_state.agent_offer_no_results: agent_dact.intent = AgentIntents.NO_RESULTS agent_dacts.append(deepcopy(agent_dact)) elif dialogue_state.agent_made_offer: if user_dact.intent == UserIntents.INQUIRE: agent_dact.intent = AgentIntents.INFORM for param in user_dact.params: if param.slot != Slots.MORE_INFO.value: agent_dact.params.append( ItemConstraint( param.slot, Operator.EQ, dialogue_state.item_in_focus[param.slot])) else: agent_dact.params.append( ItemConstraint( param.slot, Operator.EQ, dialogue_state.item_in_focus[ Slots.TITLE.value])) if len(agent_dact.params) == 0: agent_dact.params.append( ItemConstraint( 'deny', Operator.EQ, dialogue_state.item_in_focus[ Slots.TITLE.value])) agent_dacts.append(deepcopy(agent_dact)) # elif user_dact.intent == UserIntents.REVEAL and Slots.TITLE.value in [param.slot for # param in # user_dact.params]: # agent_dact.intent = AgentIntents.INFORM # agent_dact.params.append(ItemConstraint(Slots.MORE_INFO.value, Operator.EQ, # dialogue_state.item_in_focus[ # Slots.TITLE.value])) # agent_dacts.append(deepcopy(agent_dact)) elif user_dact.intent == UserIntents.ACCEPT: agent_dact.intent = AgentIntents.CONTINUE_RECOMMENDATION agent_dact.params.append( ItemConstraint( Slots.TITLE.value, Operator.EQ, dialogue_state.item_in_focus[Slots.TITLE.value])) agent_dacts.append(deepcopy(agent_dact)) if agent_dact.intent == AgentIntents.UNK: if not dialogue_state.agent_req_filled and user_dact.intent != UserIntents.HI: agent_dact.intent = AgentIntents.ELICIT # random.shuffle(slots) for slot in slots: if not dialogue_state.frame_CIN[slot]: agent_dact.params.append( ItemConstraint(slot, Operator.EQ, '')) break elif user_dact.intent == UserIntents.UNK: agent_dact.intent = AgentIntents.CANT_HELP if agent_dact.intent != AgentIntents.UNK: agent_dacts.append(deepcopy(agent_dact)) if len(agent_dacts) == 0: agent_dacts.append(DialogueAct(AgentIntents.CANT_HELP, [])) # Adding example: for agent_dact in agent_dacts: if agent_dact.intent == AgentIntents.ELICIT and \ agent_dact.params[0].slot not in [Slots.YEAR.value]: if dialogue_state.database_result: agent_dact.params[0].value = self._generate_examples( dialogue_state.database_result, agent_dact.params[0].slot) return agent_dacts