Beispiel #1
0
    def create_skill_definition(skill: SkillDefinition):
        if skill.group.lower() == ("echo"):
            skill_definition = ObjectPath.assign(EchoSkill(), skill)

        elif skill.group.lower() == ("waterfall"):
            skill_definition = ObjectPath.assign(WaterfallSkill(), skill)

        elif skill.group.lower() == ("teams"):
            skill_definition = ObjectPath.assign(TeamsSkill(), skill)

        else:
            raise Exception(f"Unable to find definition class for {skill.id}.")

        return skill_definition
Beispiel #2
0
    async def __call_train(self, step_context: WaterfallStepContext):
        dialog_options: QnAMakerDialogOptions = ObjectPath.get_path_value(
            step_context.active_dialog.state, QnAMakerDialog.KEY_OPTIONS)
        train_responses: [
            QueryResult
        ] = step_context.values[QnAMakerDialog.PROPERTY_QNA_DATA]
        current_query = step_context.values[
            QnAMakerDialog.PROPERTY_CURRENT_QUERY]

        reply = step_context.context.activity.text

        if len(train_responses) > 1:
            qna_results = [
                result for result in train_responses
                if result.questions[0] == reply
            ]

            if qna_results:
                qna_result = qna_results[0]
                step_context.values[QnAMakerDialog.PROPERTY_QNA_DATA] = [
                    qna_result
                ]

                feedback_records = [
                    FeedbackRecord(
                        user_id=step_context.context.activity.id,
                        user_question=current_query,
                        qna_id=qna_result.id,
                    )
                ]

                # Call Active Learning Train API
                qna_client = self._get_qnamaker_client(step_context)
                await qna_client.call_train(feedback_records)
                await qna_client.close()

                return await step_context.next([qna_result])

            if (reply.lower() == dialog_options.response_options.
                    card_no_match_text.lower()):
                activity = dialog_options.response_options.card_no_match_response
                if not activity:
                    await step_context.context.send_activity(
                        QnAMakerDialog.DEFAULT_CARD_NO_MATCH_RESPONSE)
                else:
                    await step_context.context.send_activity(activity)

                return await step_context.end_dialog()

            return await super().run_step(step_context,
                                          index=0,
                                          reason=DialogReason.BeginCalled,
                                          result=None)

        return await step_context.next(step_context.result)
Beispiel #3
0
    async def __display_qna_result(self, step_context: WaterfallStepContext):
        dialog_options: QnAMakerDialogOptions = ObjectPath.get_path_value(
            step_context.active_dialog.state, QnAMakerDialog.KEY_OPTIONS
        )

        reply = step_context.context.activity.text
        if reply.lower() == dialog_options.response_options.card_no_match_text.lower():
            activity = dialog_options.response_options.card_no_match_response
            if not activity:
                await step_context.context.send_activity(
                    QnAMakerDialog.DEFAULT_CARD_NO_MATCH_RESPONSE
                )
            else:
                await step_context.context.send_activity(activity)

            return await step_context.end_dialog()

        # If previous QnAId is present, replace the dialog
        previous_qna_id = ObjectPath.get_path_value(
            step_context.active_dialog.state, QnAMakerDialog.KEY_PREVIOUS_QNA_ID, 0
        )
        if previous_qna_id > 0:
            return await super().run_step(
                step_context, index=0, reason=DialogReason.BeginCalled, result=None
            )

        # If response is present then show that response, else default answer.
        response = step_context.result
        if response and isinstance(response, List):
            await step_context.context.send_activity(response[0].answer)
        else:
            activity = dialog_options.response_options.no_answer
            if not activity:
                await step_context.context.send_activity(
                    QnAMakerDialog.DEFAULT_NO_ANSWER
                )
            else:
                await step_context.context.send_activity(activity)

        return await step_context.end_dialog()
Beispiel #4
0
    async def begin_dialog(
        self, dialog_context: DialogContext, options: object = None
    ) -> DialogTurnResult:
        """
        Called when the dialog is started and pushed onto the dialog stack.

        .. remarks:
          If the task is successful, the result indicates whether the dialog is still
          active after the turn has been processed by the dialog.

        :param dialog_context: The :class:'botbuilder.dialogs.DialogContext' for the current turn of conversation.
        :param options: Optional, initial information to pass to the dialog.
        """

        if not dialog_context:
            raise TypeError("DialogContext is required")

        if (
            dialog_context.context
            and dialog_context.context.activity
            and dialog_context.context.activity.type != ActivityTypes.message
        ):
            return Dialog.end_of_turn

        dialog_options = QnAMakerDialogOptions(
            options=self._get_qnamaker_options(dialog_context),
            response_options=self._get_qna_response_options(dialog_context),
        )

        if options:
            dialog_options = ObjectPath.assign(dialog_options, options)

        ObjectPath.set_path_value(
            dialog_context.active_dialog.state,
            QnAMakerDialog.KEY_OPTIONS,
            dialog_options,
        )

        return await super().begin_dialog(dialog_context, dialog_options)
Beispiel #5
0
    async def __check_for_multiturn_prompt(self, step_context: WaterfallStepContext):
        dialog_options: QnAMakerDialogOptions = ObjectPath.get_path_value(
            step_context.active_dialog.state, QnAMakerDialog.KEY_OPTIONS
        )

        response = step_context.result
        if response and isinstance(response, List):
            answer = response[0]
            if answer.context and answer.context.prompts:
                previous_context_data = ObjectPath.get_path_value(
                    step_context.active_dialog.state,
                    QnAMakerDialog.KEY_QNA_CONTEXT_DATA,
                    {},
                )
                for prompt in answer.context.prompts:
                    previous_context_data[prompt.display_text] = prompt.qna_id

                ObjectPath.set_path_value(
                    step_context.active_dialog.state,
                    QnAMakerDialog.KEY_QNA_CONTEXT_DATA,
                    previous_context_data,
                )
                ObjectPath.set_path_value(
                    step_context.active_dialog.state,
                    QnAMakerDialog.KEY_PREVIOUS_QNA_ID,
                    answer.id,
                )
                ObjectPath.set_path_value(
                    step_context.active_dialog.state,
                    QnAMakerDialog.KEY_OPTIONS,
                    dialog_options,
                )

                # Get multi-turn prompts card activity.
                message = QnACardBuilder.get_qna_prompts_card(
                    answer, dialog_options.response_options.card_no_match_text
                )
                await step_context.context.send_activity(message)

                return DialogTurnResult(DialogTurnStatus.Waiting)

        return await step_context.next(step_context.result)
Beispiel #6
0
    async def test_typed_no_overlay(self):
        default_options = Options(
            last_name="Smith",
            first_name="Fred",
            age=22,
            location=Location(
                lat=1.2312312,
                long=3.234234,
            ),
        )

        result = ObjectPath.assign(default_options, None)
        assert result.last_name == default_options.last_name
        assert result.first_name == default_options.first_name
        assert result.age == default_options.age
        assert result.boolean == default_options.boolean
        assert result.location.lat == default_options.location.lat
        assert result.location.long == default_options.location.long
Beispiel #7
0
    async def test_typed_no_target(self):
        overlay = Options(
            last_name="Smith",
            first_name="Fred",
            age=22,
            location=Location(
                lat=1.2312312,
                long=3.234234,
            ),
        )

        result = ObjectPath.assign(None, overlay)
        assert result.last_name == overlay.last_name
        assert result.first_name == overlay.first_name
        assert result.age == overlay.age
        assert result.boolean == overlay.boolean
        assert result.location.lat == overlay.location.lat
        assert result.location.long == overlay.location.long
    def _try_get_first_nested_value(remaining_path: str,
                                    memory: object) -> Tuple[bool, object]:
        # These modules are imported at static level to avoid circular dependency problems
        # pylint: disable=import-outside-toplevel

        from botbuilder.dialogs import ObjectPath

        array = ObjectPath.try_get_path_value(memory, remaining_path)
        if array:
            if isinstance(array[0], list):
                first = array[0]
                if first:
                    second = first[0]
                    return True, second

                return False, None

            return True, array[0]

        return False, None
Beispiel #9
0
    async def test_dict_partial_overlay(self):
        default_options = {
            "last_name": "Smith",
            "first_name": "Fred",
            "age": 22,
            "location": Location(
                lat=1.2312312,
                long=3.234234,
            ),
        }

        overlay = {
            "last_name": "Grant",
        }

        result = ObjectPath.assign(default_options, overlay)
        assert result["last_name"] == overlay["last_name"]
        assert result["first_name"] == default_options["first_name"]
        assert result["age"] == default_options["age"]
        assert result["location"].lat == default_options["location"].lat
        assert result["location"].long == default_options["location"].long
Beispiel #10
0
    async def test_typed_full_overlay(self):
        default_options = Options(
            last_name="Smith",
            first_name="Fred",
            age=22,
            location=Location(
                lat=1.2312312,
                long=3.234234,
            ),
            dictionary={
                "one": 1,
                "two": 2
            },
        )

        overlay = Options(
            last_name="Grant",
            first_name="Eddit",
            age=32,
            location=Location(
                lat=2.2312312,
                long=2.234234,
            ),
            dictionary={
                "one": 99,
                "three": 3
            },
        )

        result = ObjectPath.assign(default_options, overlay)
        assert result.last_name == overlay.last_name
        assert result.first_name == overlay.first_name
        assert result.age == overlay.age
        assert result.boolean == overlay.boolean
        assert result.location.lat == overlay.location.lat
        assert result.location.long == overlay.location.long
        assert "one" in result.dictionary
        assert result.dictionary["one"] == 99
        assert "two" in result.dictionary
        assert "three" in result.dictionary
Beispiel #11
0
    async def test_dict_to_typed_overlay(self):
        default_options = Options(
            last_name="Smith",
            first_name="Fred",
            age=22,
            location=Location(
                lat=1.2312312,
                long=3.234234,
            ),
        )

        overlay = {
            "last_name": "Grant",
        }

        result = ObjectPath.assign(default_options, overlay)
        assert result.last_name == overlay["last_name"]
        assert result.first_name == default_options.first_name
        assert result.age == default_options.age
        assert result.boolean == default_options.boolean
        assert result.location.lat == default_options.location.lat
        assert result.location.long == default_options.location.long
Beispiel #12
0
    async def __call_generate_answer(self, step_context: WaterfallStepContext):
        dialog_options: QnAMakerDialogOptions = ObjectPath.get_path_value(
            step_context.active_dialog.state, QnAMakerDialog.KEY_OPTIONS
        )

        # Resetting context and QnAId
        dialog_options.options.qna_id = 0
        dialog_options.options.context = QnARequestContext()

        # Storing the context info
        step_context.values[
            QnAMakerDialog.PROPERTY_CURRENT_QUERY
        ] = step_context.context.activity.text

        # -Check if previous context is present, if yes then put it with the query
        # -Check for id if query is present in reverse index.
        previous_context_data = ObjectPath.get_path_value(
            step_context.active_dialog.state, QnAMakerDialog.KEY_QNA_CONTEXT_DATA, {}
        )
        previous_qna_id = ObjectPath.get_path_value(
            step_context.active_dialog.state, QnAMakerDialog.KEY_PREVIOUS_QNA_ID, 0
        )

        if previous_qna_id > 0:
            dialog_options.options.context = QnARequestContext(
                previous_qna_id=previous_qna_id
            )

            current_qna_id = previous_context_data.get(
                step_context.context.activity.text
            )
            if current_qna_id:
                dialog_options.options.qna_id = current_qna_id

        # Calling QnAMaker to get response.
        qna_client = self._get_qnamaker_client(step_context)
        response = await qna_client.get_answers_raw(
            step_context.context, dialog_options.options
        )

        is_active_learning_enabled = response.active_learning_enabled
        step_context.values[QnAMakerDialog.PROPERTY_QNA_DATA] = response.answers

        # Resetting previous query.
        previous_qna_id = -1
        ObjectPath.set_path_value(
            step_context.active_dialog.state,
            QnAMakerDialog.KEY_PREVIOUS_QNA_ID,
            previous_qna_id,
        )

        # Check if active learning is enabled and send card
        # maximum_score_for_low_score_variation is the score above which no need to check for feedback.
        if (
            response.answers
            and response.answers[0].score <= self.maximum_score_for_low_score_variation
        ):
            # Get filtered list of the response that support low score variation criteria.
            response.answers = qna_client.get_low_score_variation(response.answers)
            if len(response.answers) > 1 and is_active_learning_enabled:
                suggested_questions = [qna.questions[0] for qna in response.answers]
                message = QnACardBuilder.get_suggestions_card(
                    suggested_questions,
                    dialog_options.response_options.active_learning_card_title,
                    dialog_options.response_options.card_no_match_text,
                )
                await step_context.context.send_activity(message)

                ObjectPath.set_path_value(
                    step_context.active_dialog.state,
                    QnAMakerDialog.KEY_OPTIONS,
                    dialog_options,
                )

                await qna_client.close()

                return DialogTurnResult(DialogTurnStatus.Waiting)

        # If card is not shown, move to next step with top qna response.
        result = [response.answers[0]] if response.answers else []
        step_context.values[QnAMakerDialog.PROPERTY_QNA_DATA] = result
        ObjectPath.set_path_value(
            step_context.active_dialog.state, QnAMakerDialog.KEY_OPTIONS, dialog_options
        )

        await qna_client.close()

        return await step_context.next(result)
Beispiel #13
0
    async def test_remove_path_value(self):
        test = {}
        ObjectPath.set_path_value(test, "x.y.z", 15)
        ObjectPath.set_path_value(test, "x.p", "hello")
        ObjectPath.set_path_value(test, "foo", {"Bar": 15, "Blat": "yo"})
        ObjectPath.set_path_value(test, "x.a[1]", "yabba")
        ObjectPath.set_path_value(test, "x.a[0]", "dabba")

        ObjectPath.remove_path_value(test, "x.y.z")
        with self.assertRaises(KeyError):
            ObjectPath.get_path_value(test, "x.y.z")

        assert ObjectPath.get_path_value(test, "x.y.z", 99) == 99

        ObjectPath.remove_path_value(test, "x.a[1]")
        assert not ObjectPath.try_get_path_value(test, "x.a[1]")

        assert ObjectPath.try_get_path_value(test, "x.a[0]") == "dabba"
Beispiel #14
0
    async def test_set_value(self):
        test = {}
        ObjectPath.set_path_value(test, "x.y.z", 15)
        ObjectPath.set_path_value(test, "x.p", "hello")
        ObjectPath.set_path_value(test, "foo", {"Bar": 15, "Blat": "yo"})
        ObjectPath.set_path_value(test, "x.a[1]", "yabba")
        ObjectPath.set_path_value(test, "x.a[0]", "dabba")
        ObjectPath.set_path_value(test, "null", None)

        assert ObjectPath.get_path_value(test, "x.y.z") == 15
        assert ObjectPath.get_path_value(test, "x.p") == "hello"
        assert ObjectPath.get_path_value(test, "foo.bar") == 15

        assert not ObjectPath.try_get_path_value(test, "foo.Blatxxx")
        assert ObjectPath.try_get_path_value(test, "x.a[1]") == "yabba"
        assert ObjectPath.try_get_path_value(test, "x.a[0]") == "dabba"

        assert not ObjectPath.try_get_path_value(test, "null")
Beispiel #15
0
 async def test_no_target_or_overlay(self):
     result = ObjectPath.assign(None, None, Options)
     assert result