示例#1
0
    def test_get_entity_by_wiki_name(self):
        """Test the get_entity_by_wiki_name function"""
        entity = get_entity_by_wiki_name('Chicago (2002 film)')
        self.assertEqual(entity.doc_id, 201534)  # Chicago the movie

        entity = get_entity_by_wiki_name('Chicago (2002 filmm)')
        self.assertIsNone(entity)  # there is no wikipedia article by this name
示例#2
0
    def get_response(self, state: State,
                     state_manager) -> ResponseGeneratorResult:
        """Ask the first unasked question for state.cur_category_name"""

        category_name = state.cur_category_name
        question = state.get_first_category_response(
            category_name, state_manager)  # CategoryQuestion or None
        if question:
            question_str = None
            if question.statement is None:
                question_str = question.question
            elif question.question is None:
                question_str = question.statement
            else:
                question_str = ' '.join(
                    (question.statement, question.question))
            response = "{} {}".format(choice(ACKNOWLEDGEMENTS), question_str)
            priority = ResponsePriority.CAN_START if category_name == HistoryCategory.__name__ else ResponsePriority.FORCE_START
            cur_entity = get_entity_by_wiki_name(question.cur_entity_wiki_name,
                                                 state_manager.current_state)
            conditional_state = ConditionalState(HandleAnswerTreelet.__name__,
                                                 category_name,
                                                 question.statement,
                                                 question.question, False)
            return ResponseGeneratorResult(
                text=response,
                priority=priority,
                needs_prompt=False,
                state=state,
                cur_entity=cur_entity,
                expected_type=question.expected_type,
                conditional_state=conditional_state)
        else:
            return emptyResult(state)
    def get_response(self, state: dict) -> ResponseGeneratorResult:
        utterance = self.state_manager.current_state.text.lower()
        nav_intent_output = self.state_manager.current_state.navigational_intent

        if self.talk_about_george_floyd(state, utterance):
            blm_entity = get_entity_by_wiki_name("Black Lives Matter")
            return ResponseGeneratorResult(text=RESPONSE_TO_QUESTION_ONE_GEORGE_FLOYD, 
                                        priority=ResponsePriority.FORCE_START,
                                        needs_prompt=True, state=state,
                                        cur_entity=blm_entity, conditional_state={"talked_about_blm": True},
                                        smooth_handoff=SmoothHandoff.ONE_TURN_TO_WIKI_GF)

        # Check for chatty phrases in utterance
        slots = ChattyTemplate().execute(utterance)
        my_name_slots = MyNameIsNonContextualTemplate().execute(utterance)
        not_my_name_slots = MyNameIsNotTemplate().execute(utterance)
        if slots is not None:
            chatty_phrase = slots["chatty_phrase"]
            logger.primary_info('Detected chatty phrase intent with slots={}'.format(slots))

            # Step 3: Get response from dictionary of hand-written responses
            response, needs_prompt = one_turn_responses[chatty_phrase]
            logger.primary_info('Chatty RG returned user_response={}'.format(response))

        # Check for user hesitating while trying to navigate to a topic
        elif nav_intent_output.pos_intent and nav_intent_output.pos_topic_is_hesitate and "depends on" not in utterance:
            logger.primary_info('User has PositiveNavigationalIntent with topic=HESITATE, so asking them for topic again')
            response, needs_prompt = "I think I missed the last part of that sentence. Can you tell me one more time what you want to talk about?", False

        # Check for user giving general positive talking intent (e.g. "i want to chat")
        # If WIKI is supposed to handle the utterance and it contains tell, it typically means user is asking for more info (and hence doesn't really specify topic)
        elif nav_intent_output.pos_intent and nav_intent_output.pos_topic is None and not (self.state_manager.last_state_active_rg == 'WIKI' and contains_phrase(utterance, {'tell'})):
            logger.primary_info('User has PositiveNavigationalIntent with topic=None, so ONE_TURN_HACK is responding with "What would you like to talk about?"')
            response, needs_prompt = "Ok, I'd love to talk to you! What would you like to talk about?", False

        # Check for user correcting their name
        elif (my_name_slots and self.state_manager.last_state_active_rg and not self.state_manager.last_state_active_rg == 'LAUNCH') or not_my_name_slots:
            logger.primary_info('User is attempting to correct name.')
            response = "Oops, it sounds like I got your name wrong. I'm so sorry about that! I won't make that mistake again."
            needs_prompt = True
            setattr(self.state_manager.user_attributes, 'name', None)

        # Otherwise return empty
        else:
            return emptyResult(state)

        # Step 7: set priority
        priority = ResponsePriority.FORCE_START
        is_safe = True

        # Step 8: return result
        return ResponseGeneratorResult(text=response, priority=priority, needs_prompt=needs_prompt, state=state,
                                       cur_entity=None, conditional_state=state)
 def introduce_entity(self, state: State, is_blm=False):
     latest_untalked_entity = None
     text = None
     if is_blm:
         latest_untalked_entity = get_entity_by_wiki_name("Black Lives Matter")
         text = "I am proud to support the Black Lives Matter movement. Would you like to learn more about Black Lives Matter?"
     else:
         latest_untalked_entity = self.rg.get_untalked_entity(state)
         text = self.rg.state_manager.current_state.choose_least_repetitive(ENTITY_INTRODUCTION_TEMPLATES).format(entity_name=latest_untalked_entity.common_name)
     conditional_state = ConditionalState(
                 cur_doc_title=latest_untalked_entity.name,
                 responding_treelet=self.__repr__(),
                 prompt_handler=self.__repr__())
     return latest_untalked_entity, text, conditional_state
    def get_prompt(self, state: State) -> PromptResult:
        """This method is the same as get_response. Only difference is that 

        1. we do not get a list of reasons (because there is not enough time)
        2. we only chose to solicit an opinion if user didn't have an opinion on it before, or 
        3. we will solicit a reason if user told us before that they like or dislike a phrase
        
        :param state: the current state
        :type state: State
        :return: a result chirpy can use
        :rtype: PromptResult
        """
        self.initialize_turn()
        if state.last_turn_prompt:
            return emptyPrompt(state)
        phrase, priority, transition_phrase = self.select_phrase_for_prompt(state)
        if phrase is None:
            return emptyPrompt(state)
        utterance = self.state_manager.current_state.text
        additional_features = self.populate_features(state, utterance)
        additional_features.detected_phrases = tuple([phrase for phrase in additional_features.detected_phrases if phrase not in state.phrases_done])

        # Then need to advance the state using the utterance
        state_p = state_actions.next_state(state, utterance, additional_features)
        if state_p is None:
            # Special handling for prompt: Add phrase and sentiment
            state_p = state.reset_state()
            state_p.cur_phrase = phrase
            user_sentiments_history = dict(state_p.user_sentiment_history)
            state_p.cur_sentiment = user_sentiments_history[phrase] if phrase in user_sentiments_history else 2
        if state_p.cur_sentiment != 2:
            action = Action(solicit_reason=True)
        else:
            action = Action(solicit_opinion=True)
        # Then need to utterancify the action
        text, phrase, reason = fancy_utterancify_prompt(state_p, action, [], [], [], state_p.cur_phrase not in state.detected_opinionated_phrases, 
                                                        self.state_manager.current_state.choose_least_repetitive) # type: ignore
        # Then need to fill the rest of the fields of state_p (through mutation)
        state_p = state_actions.fill_state_on_action(state_p, action, text, phrase, additional_features, reason, 
            self.opinionable_phrases, self.opinionable_entities)

        state_p.last_turn_prompt, state_p.last_turn_select = True, False
        wiki_entity = None
        if self.opinionable_phrases[phrase].good_for_wiki:
            wiki_entity = get_entity_by_wiki_name(self.opinionable_phrases[phrase].wiki_entity_name)
        state.last_turn_prompt, state.last_turn_select = False, False

        text = ' '.join((transition_phrase, text))
            
        return PromptResult(text, priority, state, wiki_entity, conditional_state=state_p)
示例#6
0
    def get_prompt(self, state: State, state_manager) -> PromptResult:
        """Ask the first unasked question for state.cur_category_name"""

        category_name = state.cur_category_name
        question = state.get_first_unasked_question(
            category_name)  # CategoryQuestion or None
        if question:
            question_str = question.question
            response = "{} {}".format(choice(BRIDGES), question_str)
            cur_entity = get_entity_by_wiki_name(question.cur_entity_wiki_name,
                                                 state_manager.current_state)
            conditional_state = ConditionalState(HandleAnswerTreelet.__name__,
                                                 category_name, None,
                                                 question_str, False)
            return PromptResult(text=response,
                                prompt_type=PromptType.CURRENT_TOPIC,
                                state=state,
                                cur_entity=cur_entity,
                                expected_type=question.expected_type,
                                conditional_state=conditional_state)
        else:
            return emptyPrompt(state)
示例#7
0
 def get_by_name(cls, name):
     try:
         wiki_entity = get_entity_by_wiki_name(name)
     except Exception:
         wiki_entity = None
     return wiki_entity
示例#8
0
    def get_prompt(self, state: State) -> PromptResult:
        """
        Randomly choose an undiscussed generic category and ask its first question.
        """

        # If this is a smooth transition from movies, get a prompt for tv category from the Introductory treelet
        '''
        if self.state_manager.current_state.smooth_handoff == SmoothHandoff.MOVIES_TO_CATEGORIES:
            logger.primary_info('Getting prompt from tv IntroductoryTreelet.')
            state.cur_category_name = 'TVCategory'
            question = state.get_first_unasked_question(state.cur_category_name)
            if question is None:
                logger.warning('Unable to complete smooth handoff from movies to categories because we have no unasked tv questions left')
            else:
                new_entity = get_entity_by_wiki_name(question.cur_entity_wiki_name, self.state_manager.current_state)
                conditional_state = ConditionalState(HandleAnswerTreelet.__name__, state.cur_category_name, None, question.question, False)
                result = PromptResult(question.question, PromptType.FORCE_START, state, cur_entity=new_entity,
                                      expected_type=question.expected_type, conditional_state=conditional_state)
                return result
        '''
        # If the user is requesting a category, get a prompt for that category from the Introductory treelet
        utterance = self.state_manager.current_state.text.lower()
        user_initiated_category, user_has_posnav = get_user_initiated_category(
            utterance, self.state_manager.current_state)
        if user_initiated_category is not None:
            state.cur_category_name = user_initiated_category
            logger.primary_info(
                f'Getting prompt from {IntroductoryTreelet.__name__}')
            prompt_result = self.treeletname2treelet[
                IntroductoryTreelet.__name__].get_prompt(
                    state, self.state_manager)
            if prompt_result.text is not None and not user_has_posnav:
                logger.primary_info(
                    "Setting prompt type to CONTEXTUAL as the user does not have posnav"
                )
                prompt_result.type = PromptType.CONTEXTUAL
            if prompt_result:
                return prompt_result

        # Ask any remaining unasked generic category questions
        category_name, question = state.get_random_generic_undiscussed_category_question(
        )
        if category_name and question:
            logger.primary_info(
                f"Randomly chose an undiscussed generic category {category_name}. Asking its first question as a generic prompt."
            )
            new_entity = get_entity_by_wiki_name(
                question.cur_entity_wiki_name,
                self.state_manager.current_state)
            conditional_state = ConditionalState(HandleAnswerTreelet.__name__,
                                                 category_name, None,
                                                 question.question, False)
            transition = self.state_manager.current_state.choose_least_repetitive(
                QUESTION_CONNECTORS)
            result = PromptResult(transition + question.question,
                                  PromptType.GENERIC,
                                  state,
                                  cur_entity=new_entity,
                                  expected_type=question.expected_type,
                                  conditional_state=conditional_state)

            return result

        # Otherwise return nothing
        return emptyPrompt(state)
    def get_response(self, state : State) -> ResponseGeneratorResult:
        """This function defines the stages that we go through to generate the result. The procedure is

        1. First populate the "additional_features"
        2. Incorporate unconditional information such as user's likes and dislikes, phrases that were detected
        3. Advance the state to the next state depending on the user's utterance and additional features
        4. Define the action space for the policy
        5. Select an action using a policy
        6. Utterancify the action chosen using additional information like lists of reasons and alternatives
        7. Post process the state conditioned on the action
        
        :param state: the current state
        :type state: State
        :return: a result that can be used for chirpy
        :rtype: ResponseGeneratorResult
        """
        self.initialize_turn()
        neg_intent = self.state_manager.current_state.navigational_intent.neg_intent  # type: ignore
        if neg_intent and self.state_manager.last_state_active_rg == 'OPINION': # type: ignore
            return self.respond_neg_nav(state, None)
        utterance = self.state_manager.current_state.text
        additional_features = self.populate_features(state, utterance)
        high_prec = self.state_manager.current_state.entity_linker.high_prec # type: ignore
        # should_evaluate = len(state.action_history) > 4 and not state.evaluated \
        #     and not state.first_episode and state.cur_policy != '' and state.cur_policy != repr(OneTurnAgreePolicy())
        should_evaluate = False # Turning off evaluation question. 
        if self.state_manager.current_state.entity_linker.high_prec and state.cur_phrase != '': # type: ignore
            cur_entity_name = self.opinionable_phrases[state.cur_phrase].wiki_entity_name
            if (cur_entity_name is None and state.cur_phrase not in [linked_span.span for linked_span in high_prec]) \
                    or (cur_entity_name is not None and cur_entity_name not in [linked_span.top_ent.name for linked_span in high_prec]):
                # If the above condition passes, it means that the linked entity is not the currently opinionating phrase.
                if len(additional_features.detected_phrases) == 0:
                    # User no longer want to talk about an opinionable phrase
                    return self.respond_neg_nav(state, random.choice(self.state_manager.current_state.entity_linker.high_prec).top_ent) # type: ignore
        if state.last_turn_prompt or state.last_turn_select:
            priority = ResponsePriority.STRONG_CONTINUE
        elif len(high_prec) > 0 and \
                not any(linked_span.span in self.opinionable_phrases \
                        or linked_span.top_ent.name in self.opinionable_entities for linked_span in high_prec): # type: ignore
            self.logger.primary_info(f'Opinion realized that there is a high precision entity, will not CAN_START our conversation') # type: ignore
            priority = ResponsePriority.NO
        # if WhatsYourOpinion().execute(utterance) is not None:
        #     self.logger.primary_info(f"Opinion detected user is asking for our opinion, raising priority to FORCE_START") # type: ignore
        #     priority = ResponsePriority.FORCE_START
        else:
            priority = ResponsePriority.CAN_START
        if len(state.action_history) > 0 and state.action_history[-1].exit:
            self.logger.primary_info(f'Opinion detected our previous action is to exit and we were not selected, will reset the state before this turn starts') # type: ignore
            state = state.reset_state()
            priority = ResponsePriority.CAN_START # Drop the priority to CAN_START because we already ended a convo before
        # First need to incorporate the unconditional information learned from this turn
        state.detected_opinionated_phrases += additional_features.detected_phrases
        if len(additional_features.detected_phrases) > 0:
            # Here we only use regex since sentiment analysis may not always do well
            if utils.is_like(utterance)[0]:
                state.user_sentiment_history += tuple((phrase, 4) for phrase in additional_features.detected_phrases)
            elif utils.is_not_like(utterance)[0]:
                state.user_sentiment_history += tuple((phrase, 0) for phrase in additional_features.detected_phrases)
        additional_features.detected_phrases = tuple([phrase for phrase in additional_features.detected_phrases if phrase not in state.phrases_done])

        # Then need to advance the state using the utterance
        state_p = state_actions.next_state(state, utterance, additional_features)
        if state_p is None or state_p.cur_phrase is None:
            return emptyResult(state.reset_state())
        reasons_used = dict(state.reasons_used)
        phrase_reasons_used = set(reasons_used[state_p.cur_phrase]) if state_p.cur_phrase in reasons_used else []
        pos_reasons, neg_reasons = utils.get_reasons(state_p.cur_phrase)
        pos_reasons = [reason for reason in pos_reasons if reason not in phrase_reasons_used]
        neg_reasons = [reason for reason in neg_reasons if reason not in phrase_reasons_used]
        related_entities = [phrase.text for phrase in self.opinionable_phrases.values() \
            if phrase.category is not None and  phrase.category == self.opinionable_phrases[state_p.cur_phrase].category \
                and phrase.wiki_entity_name != self.opinionable_phrases[state_p.cur_phrase].wiki_entity_name]
        related_entities = [e for e in related_entities if e not in state_p.phrases_done]
    
        # Then need to define the action space
        action_space = self.get_action_space(state_p, pos_reasons, neg_reasons, related_entities)
        # Then need to select a policy if we don't have one
        ab_test_policy = self.state_manager.current_state.experiments.look_up_experiment_value('opinion_policy') # type: ignore
        if state_p.cur_policy != '':
            self.logger.primary_info(f'OPINION is using current policy {state_p.cur_policy} to respond to the user') # type: ignore
        elif ab_test_policy != 'random' or ab_test_policy == 'not_defined': 
            state_p.cur_policy = ab_test_policy 
            self.logger.primary_info(f'Opinion detected a/b test policy is {ab_test_policy}, will set current episode accordingly ') # type: ignore
        elif state_p.num_turns_since_long_policy < 20:
            policies, weights = zip(*self.short_policy_rates)
            state_p.cur_policy = random.choices(policies, weights, k=1)[0] # type: ignore
            self.logger.primary_info(f'Opinion had a long conversation {state_p.num_turns_since_long_policy} < 20 turns ago. Will use policy {state_p.cur_policy}') # type: ignore
        else:
            if state_p.last_policy in set([p for p, _ in self.disagree_policy_rates]):
                policies, weights = zip(*self.agree_policies_rates)
            elif state_p.last_policy in set([p for p, _ in self.agree_policies_rates]):
                policies, weights = zip(*self.agree_policies_rates)
            else:
                policies, weights = zip(*self.policy_rates)
            state_p.cur_policy = random.choices(policies, weights, k=1)[0] # type: ignore
            self.logger.primary_info(f'OPINION have no current policy, randomly picked {state_p.cur_policy} to respond to the user, resetting turn count') # type: ignore
        state_p.last_policy = state_p.cur_policy
        policy = self.policies[state_p.cur_policy] # type: ignore
        # Then need to get the action from a policy
        action = policy.get_action(state_p, action_space, additional_features)
        self.logger.primary_info(f'OPINION\'s strategy chose action {action}') # type: ignore
        action_space = self.get_action_space(state_p, pos_reasons, neg_reasons, related_entities) # Redefine action space for checks in case cur_phrase changed
        if action not in action_space:
            self.logger.error(f'OPINION policy {repr(policy)} generated an action {action} that is not in the action space {action_space}. Check policy implementation.')
            new_state = state.reset_state()
            return emptyResult(new_state)
        # Then need to utterancify the action
        text, phrase, reason = fancy_utterancify(state_p, action, pos_reasons, neg_reasons, 
                                                related_entities, should_evaluate, self.state_manager.current_state.choose_least_repetitive) # type: ignore
        # Then need to fill the rest of the fields of state_p (through mutation)
        state_p = state_actions.fill_state_on_action(state_p, action, text, phrase, additional_features, reason, 
            self.opinionable_phrases, self.opinionable_entities)

        state_p.last_turn_select = True
        state_p.last_turn_prompt = False
        user_sentiment_history_dict = dict(state.user_sentiment_history)
        wiki_entity = None
        if phrase != '' and phrase in user_sentiment_history_dict and user_sentiment_history_dict[phrase] > 2 \
                and self.opinionable_phrases[phrase].good_for_wiki:
            wiki_entity = get_entity_by_wiki_name(self.opinionable_phrases[phrase].wiki_entity_name)
        state.last_turn_prompt, state.last_turn_select = False, False
        needs_prompt = False
        if action.exit:
            if len(state_p.action_history) > 6:
                self.logger.primary_info(f"Opinion had a conversation of length {len(state_p.action_history)}, will reset long_policy count") # type: ignore
                state_p.num_turns_since_long_policy = 0
            if not should_evaluate:
                needs_prompt = True
            if len(state_p.action_history) < 4:
                self.logger.primary_info(f"Opinion only had 4 turns. Will WEAK_CONTINUE the conversation") # type: ignore
                priority = ResponsePriority.WEAK_CONTINUE
            state_p.first_episode = False
        return ResponseGeneratorResult(text, priority, needs_prompt, state, wiki_entity, conditional_state=state_p)
示例#10
0
                            user=user,
                            password=password)
    cur = conn.cursor()
    cur.execute(sql_statement)
    conn.commit()
    cur.close()
    return


def get_ent_group(entity):
    for ent_group_name, ent_group in ENTITY_GROUPS_FOR_CLASSIFICATION.ordered_items:
        if ent_group.matches(entity):
            return ent_group_name
    return None


if __name__ == "__main__":
    results = fetch_sql(
        "select * from labeled_phrases_cat where generic = true")

    id_to_wiki_cat = {
        row[0]: get_ent_group(get_entity_by_wiki_name(row[3]))
        if row[3] is not None else None
        for row in results
    }

    for phrase_id, wiki_cat in id_to_wiki_cat.items():
        if wiki_cat is not None:
            execute_sql(
                f"update labeled_phrases_cat set wiki_category = '{wiki_cat}' where id = {phrase_id}"
            )