async def test_adding_e2e_actions_to_domain(project: Text): config_path = os.path.join(project, DEFAULT_CONFIG_PATH) domain_path = os.path.join(project, DEFAULT_DOMAIN_PATH) default_data_path = os.path.join(project, DEFAULT_DATA_PATH) existing = TrainingDataImporter.load_from_dict({}, config_path, domain_path, [default_data_path]) additional_actions = ["Hi Joey.", "it's sunny outside."] stories = StoryGraph([ StoryStep(events=[ UserUttered("greet_from_stories", {"name": "greet_from_stories"}), ActionExecuted("utter_greet_from_stories"), ]), StoryStep(events=[ UserUttered("how are you doing?", {"name": "greet_from_stories"}), ActionExecuted(additional_actions[0], action_text=additional_actions[0]), ActionExecuted(additional_actions[1], action_text=additional_actions[1]), ActionExecuted(additional_actions[1], action_text=additional_actions[1]), ]), ]) # Patch to return our test stories existing.get_stories = asyncio.coroutine(lambda *args: stories) importer = E2EImporter(existing) domain = await importer.get_domain() assert all(action_name in domain.action_names for action_name in additional_actions)
def _next_story_steps(self): start_checkpoints = self._prev_end_checkpoints() if not start_checkpoints: start_checkpoints = [Checkpoint(STORY_START)] current_turns = [StoryStep(block_name=self.name, start_checkpoints=start_checkpoints)] return current_turns
async def test_without_additional_e2e_examples(tmp_path: Path): domain_path = tmp_path / "domain.yml" domain_path.write_text(Domain.empty().as_yaml()) config_path = tmp_path / "config.yml" config_path.touch() existing = TrainingDataImporter.load_from_dict({}, str(config_path), str(domain_path), []) stories = StoryGraph([ StoryStep(events=[ UserUttered("greet_from_stories", {"name": "greet_from_stories"}), ActionExecuted("utter_greet_from_stories"), ]) ]) # Patch to return our test stories existing.get_stories = asyncio.coroutine(lambda *args: stories) importer = E2EImporter(existing) training_data = await importer.get_nlu_data() assert training_data.training_examples assert training_data.is_empty() assert not training_data.without_empty_e2e_examples().training_examples
def _process_step( self, step: StoryStep, incoming_trackers: List[TrackerWithCachedStates] ) -> TrackersTuple: """Processes a steps events with all trackers. The trackers that reached the steps starting checkpoint will be used to process the events. Collects and returns training data while processing the story step.""" events = step.explicit_events(self.domain) trackers = [] if events: # small optimization # need to copy the tracker as multiple story steps # might start with the same checkpoint and all of them # will use the same set of incoming trackers for tracker in incoming_trackers: # sender id is used to be able for a human to see where the # messages and events for this tracker came from - to do this # we concatenate the story block names of the blocks that # contribute to the trackers events if tracker.sender_id: if step.block_name not in tracker.sender_id.split(" > "): new_sender = tracker.sender_id + " > " + step.block_name else: new_sender = tracker.sender_id else: new_sender = step.block_name trackers.append(tracker.copy(new_sender, step.source_name)) end_trackers = [] for event in events: for tracker in trackers: if isinstance( event, (ActionReverted, UserUtteranceReverted, Restarted) ): end_trackers.append(tracker.copy(tracker.sender_id)) if step.is_rule: # The rules can specify that a form or a slot shouldn't be set, # therefore we need to distinguish between not set # and explicitly set to None if isinstance(event, ActiveLoop) and event.name is None: event.name = SHOULD_NOT_BE_SET if isinstance(event, SlotSet) and event.value is None: event.value = SHOULD_NOT_BE_SET tracker.update(event) # end trackers should be returned separately # to avoid using them for augmentation return trackers, end_trackers
def _next_story_steps(self) -> List[StoryStep]: start_checkpoints = self._prev_end_checkpoints() if not start_checkpoints: start_checkpoints = [Checkpoint(STORY_START)] current_turns = [ StoryStep( block_name=self.name, start_checkpoints=start_checkpoints, source_name=self.source_name, is_rule=self.is_rule, ) ] return current_turns
def _process_step( self, step: StoryStep, incoming_trackers: List[TrackerWithCachedStates]) -> TrackersTuple: """Processes a steps events with all trackers. The trackers that reached the steps starting checkpoint will be used to process the events. Collects and returns training data while processing the story step.""" events = step.explicit_events(self.domain) trackers = [] if events: # small optimization # need to copy the tracker as multiple story steps # might start with the same checkpoint and all of them # will use the same set of incoming trackers for tracker in incoming_trackers: # sender id is used to be able for a human to see where the # messages and events for this tracker came from - to do this # we concatenate the story block names of the blocks that # contribute to the trackers events if tracker.sender_id: if step.block_name not in tracker.sender_id.split(" > "): new_sender = tracker.sender_id + " > " + step.block_name else: new_sender = tracker.sender_id else: new_sender = step.block_name trackers.append( tracker.copy(new_sender, tracker.request_id, tracker.user_id)) end_trackers = [] for event in events: for tracker in trackers: if isinstance( event, (ActionReverted, UserUtteranceReverted, Restarted)): end_trackers.append( tracker.copy(tracker.sender_id, tracker.request_id, tracker.user_id)) tracker.update(event) # end trackers should be returned separately # to avoid using them for augmentation return trackers, end_trackers
def __prepare_training_story_step(self, bot: Text): for story in Stories.objects(bot=bot, status=True): story_events = list( self.__prepare_training_story_events( story.events, datetime.now().timestamp())) yield StoryStep( block_name=story.block_name, events=story_events, start_checkpoints=[ Checkpoint(start_checkpoint) for start_checkpoint in story.start_checkpoints ], end_checkpoints=[ Checkpoint(end_checkpoints) for end_checkpoints in story.end_checkpoints ], )
async def test_import_nlu_training_data_from_e2e_stories(project: Text): config_path = os.path.join(project, DEFAULT_CONFIG_PATH) domain_path = os.path.join(project, DEFAULT_DOMAIN_PATH) default_data_path = os.path.join(project, DEFAULT_DATA_PATH) importer = TrainingDataImporter.load_from_dict({}, config_path, domain_path, [default_data_path]) # The `E2EImporter` correctly wraps the underlying `CombinedDataImporter` assert isinstance(importer, E2EImporter) importer_without_e2e = importer.importer stories = StoryGraph([ StoryStep(events=[ SlotSet("some slot", "doesn't matter"), UserUttered("greet_from_stories", {"name": "greet_from_stories"}), ActionExecuted("utter_greet_from_stories"), ]), StoryStep(events=[ UserUttered("how are you doing?"), ActionExecuted("utter_greet_from_stories", action_text="Hi Joey."), ]), ]) # Patch to return our test stories importer_without_e2e.get_stories = asyncio.coroutine(lambda *args: stories) # The wrapping `E2EImporter` simply forwards these method calls assert (await importer_without_e2e.get_stories()).as_story_string() == ( await importer.get_stories()).as_story_string() assert (await importer_without_e2e.get_config()) == (await importer.get_config()) # Check additional NLU training data from stories was added nlu_data = await importer.get_nlu_data() # The `E2EImporter` adds NLU training data based on our training stories assert len(nlu_data.training_examples) > len( (await importer_without_e2e.get_nlu_data()).training_examples) # Check if the NLU training data was added correctly from the story training data expected_additional_messages = [ Message(data={ TEXT: "greet_from_stories", INTENT_NAME: "greet_from_stories" }), Message(data={ ACTION_NAME: "utter_greet_from_stories", ACTION_TEXT: "" }), Message(data={ TEXT: "how are you doing?", INTENT_NAME: None }), Message(data={ ACTION_NAME: "utter_greet_from_stories", ACTION_TEXT: "Hi Joey." }), ] assert all(m in nlu_data.training_examples for m in expected_additional_messages)