async def extract_story_graph( resource_name: Text, domain: "Domain", interpreter: Optional["NaturalLanguageInterpreter"] = None, use_e2e: bool = False, exclusion_percentage: int = None, ) -> "StoryGraph": from rasa.core.interpreter import RegexInterpreter from rasa.core.training.dsl import StoryFileReader from rasa.core.training.structures import StoryGraph if not interpreter: interpreter = RegexInterpreter() story_steps = await StoryFileReader.read_from_folder( resource_name, domain, interpreter, use_e2e=use_e2e, exclusion_percentage=exclusion_percentage, ) return StoryGraph(story_steps)
async def extract_story_graph( resource_name: Text, domain: "Domain", interpreter: Optional["NaturalLanguageInterpreter"] = None, use_e2e: bool = False, exclusion_percentage: Optional[int] = None, ) -> "StoryGraph": from rasa.core.interpreter import RegexInterpreter from rasa.core.training.structures import StoryGraph import rasa.core.training.loading as core_loading if not interpreter: interpreter = RegexInterpreter() story_steps = await core_loading.load_data_from_resource( resource_name, domain, interpreter, use_e2e=use_e2e, exclusion_percentage=exclusion_percentage, ) return StoryGraph(story_steps)
async def test_read_rules_with_stories(default_domain: Domain): yaml_file = "data/test_yaml_stories/stories_and_rules.yml" steps = await loading.load_data_from_files([yaml_file], default_domain, RegexInterpreter()) ml_steps = [s for s in steps if not s.is_rule] rule_steps = [s for s in steps if s.is_rule] # this file contains three rules and three ML stories assert len(ml_steps) == 3 assert len(rule_steps) == 3 assert rule_steps[0].block_name == "rule 1" assert rule_steps[1].block_name == "rule 2" assert rule_steps[2].block_name == "rule 3" assert ml_steps[0].block_name == "simple_story_without_checkpoint" assert ml_steps[1].block_name == "simple_story_with_only_start" assert ml_steps[2].block_name == "simple_story_with_only_end"
def test_channel_inheritance(): with mock.patch.object(sanic.Sanic, "run", fake_sanic_run): from rasa.core.channels import RestInput from rasa.core.channels import RasaChatInput from rasa.core.agent import Agent from rasa.core.interpreter import RegexInterpreter # load your trained agent agent = Agent.load(MODEL_PATH, interpreter=RegexInterpreter()) rasa_input = RasaChatInput("https://example.com") s = agent.handle_channels([RestInput(), rasa_input], 5004) routes_list = utils.list_routes(s) assert routes_list.get("custom_webhook_RasaChatInput.health").startswith( "/webhooks/rasa" ) assert routes_list.get("custom_webhook_RasaChatInput.receive").startswith( "/webhooks/rasa/webhook" )
async def read_from_file(filename, domain, interpreter=RegexInterpreter(), template_variables=None, use_e2e=False): """Given a md file reads the contained stories.""" try: with open(filename, "r", encoding="utf-8") as f: lines = f.readlines() reader = StoryFileReader(domain, interpreter, template_variables, use_e2e) return await reader.process_lines(lines) except ValueError as err: file_info = ("Invalid story file format. Failed to parse " "'{}'".format(os.path.abspath(filename))) logger.exception(file_info) if not err.args: err.args = ('', ) err.args = err.args + (file_info, ) raise
async def test_form_unhappy_path(): form_name = "some_form" domain = Domain.from_yaml(f""" intents: - {GREET_INTENT_NAME} actions: - {UTTER_GREET_ACTION} - some-action slots: {REQUESTED_SLOT}: type: unfeaturized forms: - {form_name} """) policy = RulePolicy() policy.train([GREET_RULE], domain, RegexInterpreter()) unhappy_form_conversation = DialogueStateTracker.from_events( "in a form", evts=[ # We are in an active form ActionExecuted(form_name), Form(form_name), SlotSet(REQUESTED_SLOT, "some value"), # User responds to slot request ActionExecuted(ACTION_LISTEN_NAME), UserUttered("haha", {"name": GREET_INTENT_NAME}), # Form isn't happy with the answer and rejects execution ActionExecutionRejected(form_name), ], slots=domain.slots, ) # RulePolicy doesn't trigger form but FAQ action_probabilities = policy.predict_action_probabilities( unhappy_form_conversation, domain) assert_predicted_action(action_probabilities, domain, UTTER_GREET_ACTION)
def predict_action_probabilities( self, tracker: DialogueStateTracker, domain: Domain, interpreter: NaturalLanguageInterpreter = RegexInterpreter(), **kwargs: Any, ) -> List[float]: """Predicts the next action the bot should take after seeing the tracker. Returns the list of probabilities for the next actions. If memorized action was found returns 1 for its index, else returns 0 for all actions. """ result = self._default_predictions(domain) if not self.is_enabled: return result tracker_as_states = self.featurizer.prediction_states([tracker], domain) states = tracker_as_states[0] logger.debug(f"Current tracker state {states}") recalled = self.recall(states, tracker, domain) if recalled is not None: logger.debug( f"There is a memorised next action '{domain.action_names[recalled]}'" ) if self.USE_NLU_CONFIDENCE_AS_SCORE: # the memoization will use the confidence of NLU on the latest # user message to set the confidence of the action score = tracker.latest_message.intent.get("confidence", 1.0) else: score = 1.0 result[recalled] = score else: logger.debug("There is no memorised next action") return result
async def load_data_from_files( story_files: List[Text], domain: Domain, interpreter: NaturalLanguageInterpreter = RegexInterpreter(), template_variables: Optional[Dict] = None, use_e2e: bool = False, exclusion_percentage: Optional[int] = None, ) -> List["StoryStep"]: """Loads core training data from the specified files. Args: story_files: List of files with training data in it. domain: Domain object. interpreter: Interpreter to be used for parsing user's utterances. template_variables: Variables that have to be replaced in the training data. use_e2e: Identifies whether the e2e reader should be used. exclusion_percentage: Identifies the percentage of training data that should be excluded from the training. Returns: Story steps from the training data. """ story_steps = [] for story_file in story_files: reader = _get_reader(story_file, domain, interpreter, template_variables, use_e2e) steps = await reader.read_from_file(story_file) story_steps.extend(steps) if exclusion_percentage and exclusion_percentage != 100: import random idx = int(round(exclusion_percentage / 100.0 * len(story_steps))) random.shuffle(story_steps) story_steps = story_steps[:-idx] return story_steps
async def test_predict_form_action_if_in_form(): form_name = "some_form" domain = Domain.from_yaml( f""" intents: - {GREET_INTENT_NAME} actions: - {UTTER_GREET_ACTION} - some-action slots: {REQUESTED_SLOT}: type: unfeaturized forms: - {form_name} """ ) policy = RulePolicy() policy.train([GREET_RULE], domain, RegexInterpreter()) form_conversation = DialogueStateTracker.from_events( "in a form", evts=[ # We are in an activate form ActionExecuted(form_name), Form(form_name), SlotSet(REQUESTED_SLOT, "some value"), ActionExecuted(ACTION_LISTEN_NAME), # User sends message as response to a requested slot UserUttered("haha", {"name": GREET_INTENT_NAME}), ], slots=domain.slots, ) # RulePolicy triggers form again action_probabilities = policy.predict_action_probabilities( form_conversation, domain ) assert_predicted_action(action_probabilities, domain, form_name)
def __init__( self, domain: Union[Text, Domain, None] = None, policies: Union[PolicyEnsemble, List[Policy], None] = None, interpreter: Optional[Dict[Text, NaturalLanguageInterpreter]] = None, generator: Union[EndpointConfig, NaturalLanguageGenerator, None] = None, tracker_store: Optional[TrackerStore] = None, lock_store: Optional[LockStore] = None, action_endpoint: Optional[EndpointConfig] = None, fingerprint: Optional[Text] = None, model_directory: Optional[Text] = None, model_server: Optional[EndpointConfig] = None, remote_storage: Optional[Text] = None, path_to_model_archive: Optional[Text] = None, ): # Initializing variables with the passed parameters. self.domain = self._create_domain(domain) self.policy_ensemble = self._create_ensemble(policies) if self.domain is not None: self.domain.add_requested_slot() self.domain.add_knowledge_base_slots() self.domain.add_categorical_slot_default_value() PolicyEnsemble.check_domain_ensemble_compatibility( self.policy_ensemble, self.domain ) self.interpreter = interpreter or RegexInterpreter() self.nlg = NaturalLanguageGenerator.create(generator, self.domain) self.tracker_store = self.create_tracker_store(tracker_store, self.domain) self.lock_store = self._create_lock_store(lock_store) self.action_endpoint = action_endpoint self._set_fingerprint(fingerprint) self.model_directory = model_directory self.model_server = model_server self.remote_storage = remote_storage self.path_to_model_archive = path_to_model_archive
async def get_stories( self, interpreter: "NaturalLanguageInterpreter" = RegexInterpreter(), template_variables: Optional[Dict] = None, use_e2e: bool = False, exclusion_percentage: Optional[int] = None, ) -> StoryGraph: """Retrieves the stories that should be used for training. Args: interpreter: Interpreter that should be used to parse end to end learning annotations. template_variables: Values of templates that should be replaced while reading the story files. use_e2e: Specifies whether to parse end to end learning annotations. exclusion_percentage: Amount of training data that should be excluded. Returns: ``StoryGraph`` containing all loaded stories. """ raise NotImplementedError()
def test_callback_channel(): # START DOC INCLUDE from rasa.core.channels.callback import CallbackInput from rasa.core.agent import Agent from rasa.core.interpreter import RegexInterpreter # load your trained agent agent = Agent.load(MODEL_PATH, interpreter=RegexInterpreter()) input_channel = CallbackInput( # URL Core will call to send the bot responses endpoint=EndpointConfig("http://localhost:5004")) s = agent.handle_channels([input_channel], 5004) # END DOC INCLUDE # the above marker marks the end of the code snipped included # in the docs routes_list = utils.list_routes(s) assert routes_list.get("callback_webhook.health").startswith( "/webhooks/callback") assert routes_list.get("callback_webhook.webhook").startswith( "/webhooks/callback/webhook")
def test_default_actions(intent_name: Text, expected_action_name: Text): domain = Domain.from_yaml(f""" intents: - {GREET_INTENT_NAME} actions: - {UTTER_GREET_ACTION} """) policy = RulePolicy() policy.train([GREET_RULE], domain, RegexInterpreter()) new_conversation = DialogueStateTracker.from_events( "bla2", evts=[ ActionExecuted(ACTION_LISTEN_NAME), UserUttered("haha", {"name": GREET_INTENT_NAME}), ActionExecuted(ACTION_LISTEN_NAME), UserUttered("haha", {"name": intent_name}), ], ) action_probabilities = policy.predict_action_probabilities( new_conversation, domain) assert_predicted_action(action_probabilities, domain, expected_action_name)
def test_telegram_channel(): # telegram channel will try to set a webhook, so we need to mock the api with mock.patch.object(sanic.Sanic, "run", fake_sanic_run): httpretty.register_uri( httpretty.POST, "https://api.telegram.org/bot123:YOUR_ACCESS_TOKEN/setWebhook", body='{"ok": true, "result": {}}', ) httpretty.enable() # START DOC INCLUDE from rasa.core.channels.telegram import TelegramInput from rasa.core.agent import Agent from rasa.core.interpreter import RegexInterpreter # load your trained agent agent = Agent.load(MODEL_PATH, interpreter=RegexInterpreter()) input_channel = TelegramInput( # you get this when setting up a bot access_token="123:YOUR_ACCESS_TOKEN", # this is your bots username verify="YOUR_TELEGRAM_BOT", # the url your bot should listen for messages webhook_url="YOUR_WEBHOOK_URL", ) s = agent.handle_channels([input_channel], 5004) # END DOC INCLUDE # the above marker marks the end of the code snipped included # in the docs routes_list = utils.list_routes(s) assert routes_list.get("telegram_webhook.health").startswith( "/webhooks/telegram" ) assert routes_list.get("telegram_webhook.message").startswith( "/webhooks/telegram/webhook" ) httpretty.disable()
async def test_generate_training_data_with_cycles( stories_file: Text, default_domain: Domain ): featurizer = MaxHistoryTrackerFeaturizer(SingleStateFeaturizer(), max_history=4) training_trackers = await training.load_data( stories_file, default_domain, augmentation_factor=0 ) training_data, label_ids = featurizer.featurize_trackers( training_trackers, default_domain, interpreter=RegexInterpreter() ) # how many there are depends on the graph which is not created in a # deterministic way but should always be 3 or 4 assert len(training_trackers) == 3 or len(training_trackers) == 4 # if we have 4 trackers, there is going to be one example more for label 10 num_tens = len(training_trackers) - 1 # if new default actions are added the keys of the actions will be changed all_label_ids = [id for ids in label_ids for id in ids] assert Counter(all_label_ids) == {0: 6, 12: num_tens, 14: 1, 1: 2, 13: 3}
def test_missing_classes_filled_correctly( self, default_domain, trackers, tracker, featurizer, priority ): # Pretend that a couple of classes are missing and check that # those classes are predicted as 0, while the other class # probabilities are predicted normally. policy = self.create_policy(featurizer=featurizer, priority=priority, cv=None) classes = [1, 3] new_trackers = [] for tr in trackers: new_tracker = DialogueStateTracker( UserMessage.DEFAULT_SENDER_ID, default_domain.slots ) for e in tr.applied_events(): if isinstance(e, ActionExecuted): new_action = default_domain.action_for_index( np.random.choice(classes), action_endpoint=None ).name() new_tracker.update(ActionExecuted(new_action)) else: new_tracker.update(e) new_trackers.append(new_tracker) policy.train( new_trackers, domain=default_domain, interpreter=RegexInterpreter() ) predicted_probabilities = policy.predict_action_probabilities( tracker, default_domain ) assert len(predicted_probabilities) == default_domain.num_actions assert np.allclose(sum(predicted_probabilities), 1.0) for i, prob in enumerate(predicted_probabilities): if i in classes: assert prob >= 0.0 else: assert prob == 0.0
def test_faq_rule(): domain = Domain.from_yaml(f""" intents: - {GREET_INTENT_NAME} actions: - {UTTER_GREET_ACTION} """) policy = RulePolicy() policy.train([GREET_RULE], domain, RegexInterpreter()) # remove first ... action and utter_greet and last action_listen from greet rule new_conversation = DialogueStateTracker.from_events( "simple greet", evts=[ ActionExecuted(ACTION_LISTEN_NAME), UserUttered("haha", {"name": GREET_INTENT_NAME}), ], ) action_probabilities = policy.predict_action_probabilities( new_conversation, domain) assert_predicted_action(action_probabilities, domain, UTTER_GREET_ACTION)
async def test_form_unhappy_path_without_rule(): form_name = "some_form" other_intent = "bye" domain = Domain.from_yaml(f""" intents: - {GREET_INTENT_NAME} - {other_intent} actions: - {UTTER_GREET_ACTION} - some-action slots: {REQUESTED_SLOT}: type: unfeaturized forms: - {form_name} """) policy = RulePolicy() policy.train([GREET_RULE], domain, RegexInterpreter()) conversation_events = [ ActionExecuted(form_name), Form(form_name), SlotSet(REQUESTED_SLOT, "some value"), ActionExecuted(ACTION_LISTEN_NAME), UserUttered("haha", {"name": other_intent}), Form(form_name), ActionExecutionRejected(form_name), ] # Unhappy path is not handled. No rule matches. Let's hope ML fixes our problems 🤞 action_probabilities = policy.predict_action_probabilities( DialogueStateTracker.from_events("casd", evts=conversation_events, slots=domain.slots), domain, ) assert max(action_probabilities) == 0
def predict_action_probabilities( self, tracker: DialogueStateTracker, domain: Domain, interpreter: NaturalLanguageInterpreter = RegexInterpreter(), **kwargs: Any, ) -> List[float]: """Predicts a fallback action. The fallback action is predicted if the NLU confidence is low or no other policy has a high-confidence prediction. """ nlu_data = tracker.latest_message.parse_data if (tracker.latest_action_name == self.fallback_action_name and tracker.latest_action_name != ACTION_LISTEN_NAME): logger.debug( "Predicted 'action_listen' after fallback action '{}'".format( self.fallback_action_name)) result = self._default_predictions(domain) idx = domain.index_for_action(ACTION_LISTEN_NAME) result[idx] = 1.0 elif self.should_nlu_fallback(nlu_data, tracker.latest_action_name): result = self.fallback_scores(domain) else: # NLU confidence threshold is met, so # predict fallback action with confidence `core_threshold` # if this is the highest confidence in the ensemble, # the fallback action will be executed. logger.debug("NLU confidence threshold met, confidence of " "fallback action set to core threshold ({}).".format( self.core_threshold)) result = self.fallback_scores(domain, self.core_threshold) return result
async def test_read_rules_without_stories(default_domain: Domain): yaml_file = "data/test_yaml_stories/rules_without_stories.yml" steps = await loading.load_data_from_files([yaml_file], default_domain, RegexInterpreter()) ml_steps = [s for s in steps if not s.is_rule] rule_steps = [s for s in steps if s.is_rule] # this file contains three rules and no ML stories assert len(ml_steps) == 0 assert len(rule_steps) == 3 assert rule_steps[0].block_name == "rule 1" assert rule_steps[1].block_name == "rule 2" assert rule_steps[2].block_name == "rule 3" # inspect the first rule and make sure all events were picked up correctly events = rule_steps[0].events assert len(events) == 5 assert events[0] == Form("loop_q_form") assert events[1] == SlotSet("requested_slot", "some_slot") assert events[2] == ActionExecuted("...") assert events[3] == UserUttered( "inform", { "name": "inform", "confidence": 1.0 }, [{ "entity": "some_slot", "value": "bla" }], ) assert events[4] == ActionExecuted("loop_q_form")
def test_fallback_mapping_restart(): domain = Domain.load("data/test_domains/default.yml") events = [ ActionExecuted(ACTION_DEFAULT_FALLBACK_NAME, timestamp=1), utilities.user_uttered(USER_INTENT_RESTART, 1, timestamp=2), ] tracker = DialogueStateTracker.from_events("test", events, []) two_stage_fallback_policy = TwoStageFallbackPolicy( priority=2, deny_suggestion_intent_name="deny") mapping_policy = MappingPolicy(priority=1) mapping_fallback_ensemble = SimplePolicyEnsemble( [two_stage_fallback_policy, mapping_policy]) result, best_policy = mapping_fallback_ensemble.probabilities_using_best_policy( tracker, domain, RegexInterpreter()) max_confidence_index = result.index(max(result)) index_of_mapping_policy = 1 next_action = domain.action_for_index(max_confidence_index, None) assert best_policy == f"policy_{index_of_mapping_policy}_{MappingPolicy.__name__}" assert next_action.name() == ACTION_RESTART_NAME
def test_mapping_wins_over_form(events: List[Event]): domain = """ forms: - test-form """ domain = Domain.from_yaml(domain) tracker = DialogueStateTracker.from_events("test", events, []) ensemble = SimplePolicyEnsemble([ MappingPolicy(), ConstantPolicy(priority=1, predict_index=0), FormPolicy(), FallbackPolicy(), ]) result, best_policy = ensemble.probabilities_using_best_policy( tracker, domain, RegexInterpreter()) max_confidence_index = result.index(max(result)) next_action = domain.action_for_index(max_confidence_index, None) index_of_mapping_policy = 0 assert best_policy == f"policy_{index_of_mapping_policy}_{MappingPolicy.__name__}" assert next_action.name() == ACTION_RESTART_NAME
def test_predict_nothing_if_fallback_disabled(): other_intent = "other" domain = Domain.from_yaml( f""" intents: - {GREET_INTENT_NAME} - {other_intent} actions: - {UTTER_GREET_ACTION} """ ) policy = RulePolicy(enable_fallback_prediction=False) policy.train([GREET_RULE], domain, RegexInterpreter()) new_conversation = DialogueStateTracker.from_events( "bla2", evts=[ ActionExecuted(ACTION_LISTEN_NAME), UserUttered("haha", {"name": other_intent}), ], ) action_probabilities = policy.predict_action_probabilities(new_conversation, domain) assert max(action_probabilities) == 0
def test_form_wins_over_everything_else(ensemble: SimplePolicyEnsemble): form_name = "test-form" domain = f""" forms: - {form_name} """ domain = Domain.from_yaml(domain) events = [ ActiveLoop("test-form"), ActionExecuted(ACTION_LISTEN_NAME), utilities.user_uttered("test", 1), ] tracker = DialogueStateTracker.from_events("test", events, []) result, best_policy = ensemble.probabilities_using_best_policy( tracker, domain, RegexInterpreter()) max_confidence_index = result.index(max(result)) next_action = domain.action_for_index(max_confidence_index, None) index_of_form_policy = 0 assert best_policy == f"policy_{index_of_form_policy}_{FormPolicy.__name__}" assert next_action.name() == form_name
def predict_action_probabilities( self, tracker: DialogueStateTracker, domain: Domain, interpreter: NaturalLanguageInterpreter = RegexInterpreter(), **kwargs: Any, ) -> List[float]: result = self._default_predictions(domain) tracker_as_states = self.featurizer.prediction_states([tracker], domain) states = tracker_as_states[0] logger.debug(f"Current tracker state {states}") predicted_action_name = self.recall(states, tracker, domain) if predicted_action_name is not None: logger.debug( f"There is a memorised next action '{predicted_action_name}'") result = self._prediction_result(predicted_action_name, tracker, domain) else: logger.debug("There is no memorised next action") return result
async def read_from_file( filename: Text, domain: Domain, interpreter: NaturalLanguageInterpreter = RegexInterpreter(), template_variables: Optional[Dict] = None, use_e2e: bool = False, ) -> List[StoryStep]: """Given a md file reads the contained stories.""" try: with open(filename, "r", encoding=io_utils.DEFAULT_ENCODING) as f: lines = f.readlines() reader = StoryFileReader(interpreter, domain, template_variables, use_e2e) return await reader.process_lines(lines) except ValueError as err: file_info = "Invalid story file format. Failed to parse '{}'".format( os.path.abspath(filename)) logger.exception(file_info) if not err.args: err.args = ("", ) err.args = err.args + (file_info, ) raise
async def test_form_activation_rule(): form_name = "some_form" other_intent = "bye" domain = Domain.from_yaml( f""" intents: - {GREET_INTENT_NAME} - {other_intent} actions: - {UTTER_GREET_ACTION} - some-action slots: {REQUESTED_SLOT}: type: unfeaturized forms: - {form_name} """ ) form_activation_rule = _form_activation_rule(domain, form_name, other_intent) policy = RulePolicy() policy.train([GREET_RULE, form_activation_rule], domain, RegexInterpreter()) conversation_events = [ ActionExecuted(ACTION_LISTEN_NAME), UserUttered("haha", {"name": other_intent}), ] # RulePolicy correctly predicts the form action action_probabilities = policy.predict_action_probabilities( DialogueStateTracker.from_events( "casd", evts=conversation_events, slots=domain.slots ), domain, ) assert_predicted_action(action_probabilities, domain, form_name)
def _load_and_set_updated_model( agent: "Agent", model_directory: Text, fingerprint: Text ): """Load the persisted model into memory and set the model on the agent.""" logger.debug(f"Found new model with fingerprint {fingerprint}. Loading...") core_path, nlu_path = get_model_subdirectories(model_directory) if nlu_path: from rasa.core.interpreter import RasaNLUInterpreter interpreter = RasaNLUInterpreter(model_directory=nlu_path) else: interpreter = ( agent.interpreter if agent.interpreter is not None else RegexInterpreter() ) domain = None if core_path: domain_path = os.path.join(os.path.abspath(core_path), DEFAULT_DOMAIN_PATH) domain = Domain.load(domain_path) try: policy_ensemble = None if core_path: policy_ensemble = PolicyEnsemble.load(core_path) agent.update_model( domain, policy_ensemble, fingerprint, interpreter, model_directory ) logger.debug("Finished updating agent to new model.") except Exception: logger.exception( "Failed to load policy and update agent. " "The previous model will stay loaded instead." )
async def read_from_files( files: Iterable[Text], domain: Domain, interpreter: NaturalLanguageInterpreter = RegexInterpreter(), template_variables: Optional[Dict] = None, use_e2e: bool = False, exclusion_percentage: Optional[int] = None, ) -> List[StoryStep]: story_steps = [] for f in files: steps = await StoryFileReader.read_from_file( f, domain, interpreter, template_variables, use_e2e) story_steps.extend(steps) # if exclusion percentage is not 100 if exclusion_percentage and exclusion_percentage != 100: import random idx = int(round(exclusion_percentage / 100.0 * len(story_steps))) random.shuffle(story_steps) story_steps = story_steps[:-idx] return story_steps
async def read_from_folder( resource_name: Text, domain: Domain, interpreter: NaturalLanguageInterpreter = RegexInterpreter(), template_variables: Optional[Dict] = None, use_e2e: bool = False, exclusion_percentage: Optional[int] = None, ) -> List[StoryStep]: """Given a path reads all contained story files.""" if not os.path.exists(resource_name): raise ValueError("Story file or folder could not be found. Make " "sure '{}' exists and points to a story folder " "or file.".format(os.path.abspath(resource_name))) files = io_utils.list_files(resource_name) return await StoryFileReader.read_from_files( files, domain, interpreter, template_variables, use_e2e, exclusion_percentage, )