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
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)
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()
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)
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)
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
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
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
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
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
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)
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"
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")
async def test_no_target_or_overlay(self): result = ObjectPath.assign(None, None, Options) assert result