Exemplo n.º 1
0
    def can_handle_language(cls, language: Hashable) -> bool:
        """Check if component supports a specific language.

        This method can be overwritten when needed. (e.g. dynamically
        determine which language is supported.)

        Args:
            language: The language to check.

        Returns:
            `True` if component can handle specific language, `False` otherwise.
        """

        # If both `supported_language_list` and `not_supported_language_list` are set
        # to `None`,
        # it means: support all languages
        if language is None or (
            cls.supported_language_list is None
            and cls.not_supported_language_list is None
        ):
            return True

        # check language supporting settings
        if cls.supported_language_list and cls.not_supported_language_list:
            # When user set both language supporting settings to not None, it will lead
            # to ambiguity.
            raise RasaException(
                "Only one of `supported_language_list` and"
                "`not_supported_language_list` can be set to not None"
            )

        # convert to `list` for membership test
        supported_language_list = (
            cls.supported_language_list
            if cls.supported_language_list is not None
            else []
        )
        not_supported_language_list = (
            cls.not_supported_language_list
            if cls.not_supported_language_list is not None
            else []
        )

        # check if user provided a valid setting
        if not supported_language_list and not not_supported_language_list:
            # One of language settings must be valid (not None and not a empty list),
            # There are three combinations of settings are not valid:
            # (None, []), ([], None) and ([], [])
            raise RasaException(
                "Empty lists for both "
                "`supported_language_list` and `not_supported language_list` "
                "is not a valid setting. If you meant to allow all languages "
                "for the component use `None` for both of them."
            )

        if supported_language_list:
            return language in supported_language_list
        else:
            return language not in not_supported_language_list
Exemplo n.º 2
0
    def __init__(
        self,
        tracker_store: TrackerStore,
        strategy: str,
        count: int = None,
        seed: Any = None,
    ) -> None:
        """Creates a MarkerTrackerLoader.

        Args:
            tracker_store: The underlying tracker store to access.
            strategy: The strategy to use for selecting trackers,
                      can be 'all', 'sample_n', or 'first_n'.
            count: Number of trackers to return, can only be None if strategy is 'all'.
            seed: Optional seed to set up random number generator,
                  only useful if strategy is 'sample_n'.
        """
        self.tracker_store = tracker_store

        if strategy not in MarkerTrackerLoader._STRATEGY_MAP:
            raise RasaException(
                f"Invalid strategy for loading markers - '{strategy}' was given, \
                options 'all', 'sample_n', or 'first_n' exist.")

        self.strategy = MarkerTrackerLoader._STRATEGY_MAP[strategy]

        if strategy != STRATEGY_ALL:
            if not count:
                raise RasaException(
                    f"Desired tracker count must be given for strategy '{strategy}'."
                )

            if count < 1:
                # If count is ever < 1, user has an error, so issue exception
                raise RasaException(
                    "Parameter 'count' must be greater than 0.")

        self.count = count

        if count and strategy == STRATEGY_ALL:
            rasa.shared.utils.io.raise_warning(
                "Parameter 'count' is ignored by strategy 'all'.")
            self.count = None

        if seed:
            if strategy == STRATEGY_SAMPLE_N:
                random.seed(seed)
            else:
                rasa.shared.utils.io.raise_warning(
                    f"Parameter 'seed' is ignored by strategy '{strategy}'.")
Exemplo n.º 3
0
    def __init__(
            self,
            configuration_values: Optional[Dict[Text, Any]] = None) -> None:
        """Create a model configuration, optionally overriding
        defaults with a dictionary ``configuration_values``.
        """
        if not configuration_values:
            configuration_values = {}

        self.language = "en"
        self.pipeline = []
        self.data = None

        self.override(configuration_values)

        if self.__dict__["pipeline"] is None:
            # replaces None with empty list
            self.__dict__["pipeline"] = []
        elif isinstance(self.__dict__["pipeline"], str):
            # DEPRECATION EXCEPTION - remove in 2.1
            raise RasaException(
                f"You are using a pipeline template. All pipelines templates "
                f"have been removed in 2.0. Please add "
                f"the components you want to use directly to your configuration "
                f"file. {DOCS_URL_MIGRATION_GUIDE}")

        for key, value in self.items():
            setattr(self, key, value)
Exemplo n.º 4
0
    def _check_if_sparse_feature_sizes_decreased(
        new_sparse_feature_sizes: Dict[Text, Dict[Text, List[int]]],
        old_sparse_feature_sizes: Dict[Text, Dict[Text, List[int]]],
    ) -> None:
        """Checks if the sizes of sparse features have decreased during fine-tuning.

        Sparse feature sizes might decrease after changing the training data.
        This can happen for example with `LexicalSyntacticFeaturizer`.
        We don't support this behaviour and we raise an exception if this happens.

        Args:
            new_sparse_feature_sizes: sizes of current sparse features.
            old_sparse_feature_sizes: sizes of sparse features the model was
                                      previously trained on.

        Raises:
            RasaException: When any of the sparse feature sizes decrease
                           from the last time training was run.
        """
        for attribute, new_feature_sizes in new_sparse_feature_sizes.items():
            old_feature_sizes = old_sparse_feature_sizes[attribute]
            for feature_type, new_sizes in new_feature_sizes.items():
                old_sizes = old_feature_sizes[feature_type]
                for new_size, old_size in zip(new_sizes, old_sizes):
                    if new_size < old_size:
                        raise RasaException(
                            "Sparse feature sizes have decreased from the last time "
                            "training was run. The training data was changed in a way "
                            "that resulted in some features not being present in the "
                            "data anymore. This can happen if you had "
                            "`LexicalSyntacticFeaturizer` in your pipeline. "
                            "The pipeline cannot support incremental training "
                            "in this setting. We recommend you to retrain "
                            "the model from scratch."
                        )
Exemplo n.º 5
0
    def create_component(
        self, component_config: Dict[Text, Any], cfg: RasaNLUModelConfig
    ) -> Component:
        """Creates a component.

        Tries to retrieve a component from the cache,
        calls `create` to create a new component.

        Args:
            component_config: The component configuration.
            cfg: The model configuration.

        Returns:
            The created component.
        """

        from rasa.nlu import registry
        from rasa.nlu.model import Metadata

        try:
            component, cache_key = self.__get_cached_component(
                component_config, Metadata(cfg.as_dict())
            )
            if component is None:
                component = registry.create_component_by_config(component_config, cfg)
                self.__add_to_cache(component, cache_key)
            return component
        except MissingArgumentError as e:  # pragma: no cover
            raise RasaException(
                f"Failed to create component '{component_config['name']}'. "
                f"Error: {e}"
            )
Exemplo n.º 6
0
def chat(
    model_path: Optional[Text] = None,
    endpoints: Optional[Text] = None,
    agent: Optional["Agent"] = None,
) -> None:
    """Chat to the bot within a Jupyter notebook.

    Args:
        model_path: Path to a combined Rasa model.
        endpoints: Path to a yaml with the action server is custom actions are defined.
        agent: Rasa Core agent (used if no Rasa model given).
    """
    if model_path:
        agent = asyncio.run(
            rasa.core.agent.load_agent(model_path=model_path,
                                       endpoints=endpoints))

    if agent is None:
        raise RasaException(
            "Either the provided model path could not load the agent "
            "or no core agent was provided.")

    print(
        "Your bot is ready to talk! Type your messages here or send '/stop'.")
    while True:
        message = input()
        if message == "/stop":
            break

        responses = asyncio.run(agent.handle_text(message))
        for response in responses:
            _display_bot_response(response)
Exemplo n.º 7
0
    def _merge_new_vocabulary_tokens(self, existing_vocabulary: Dict[Text,
                                                                     int],
                                     vocabulary: Set[Text]) -> None:
        """Merges new vocabulary tokens with the existing vocabulary.

        New vocabulary items should always be added to the end of the existing
        vocabulary and the order of the existing vocabulary should not be disturbed.

        Args:
            existing_vocabulary: existing vocabulary
            vocabulary: set of new tokens

        Raises:
            RasaException: if `use_shared_vocab` is set to True and there are new
                           vocabulary items added during incremental training.
        """
        for token in vocabulary:
            if token not in existing_vocabulary:
                if self.use_shared_vocab:
                    raise RasaException(
                        "Using a shared vocabulary in `CountVectorsFeaturizer` is not "
                        "supported during incremental training since it requires "
                        "dynamically adjusting layers that correspond to label "
                        f"attributes such as {INTENT_RESPONSE_KEY}, {INTENT}, etc. "
                        "This is currently not possible. In order to avoid this "
                        "exception we suggest to set `use_shared_vocab=False` or train"
                        " from scratch.")
                existing_vocabulary[token] = len(existing_vocabulary)
Exemplo n.º 8
0
 def raise_missing_credentials_exception(cls) -> NoReturn:
     raise RasaException(
         f"To use the {cls.name()} input channel, you need to "
         f"pass a credentials file using '--credentials'. "
         f"The argument should be a file path pointing to "
         f"a yml file containing the {cls.name()} authentication "
         f"information. Details in the docs: "
         f"{DOCS_BASE_URL}/messaging-and-voice-channels/")
Exemplo n.º 9
0
def _migrate_domain_files(domain_file: Path, backup_location: Path,
                          out_file: Path) -> Dict[Text, Any]:
    slots = {}
    forms = {}
    entities = []

    for file in domain_file.iterdir():
        if not Domain.is_domain_file(file):
            continue

        backup = backup_location / file.name
        original_content = _create_back_up(file, backup)

        if KEY_SLOTS not in original_content and KEY_FORMS not in original_content:
            if isinstance(original_content, dict):
                original_content.update({"version": '"3.0"'})

            # this is done so that the other domain files can be moved
            # in the migrated directory
            rasa.shared.utils.io.write_yaml(original_content,
                                            out_file / file.name, True)
        elif KEY_SLOTS in original_content and slots:
            raise RasaException(
                f"Domain files with multiple '{KEY_SLOTS}' "
                f"sections were provided. Please group these sections "
                f"in one file only to prevent content duplication across "
                f"multiple files. ")
        elif KEY_FORMS in original_content and forms:
            raise RasaException(
                f"Domain files with multiple '{KEY_FORMS}' "
                f"sections were provided. Please group these sections "
                f"in one file only to prevent content duplication across "
                f"multiple files. ")

        slots.update(original_content.get(KEY_SLOTS, {}))
        forms.update(original_content.get(KEY_FORMS, {}))
        entities.extend(original_content.get(KEY_ENTITIES, {}))

    if not slots or not forms:
        raise RasaException(
            f"The files you have provided in '{domain_file}' are missing slots "
            f"or forms. Please make sure to include these for a "
            f"successful migration.")

    return {KEY_SLOTS: slots, KEY_FORMS: forms, KEY_ENTITIES: entities}
Exemplo n.º 10
0
    def get_output_channel(self) -> TelegramOutput:
        """Loads the telegram channel."""
        channel = TelegramOutput(self.access_token)

        try:
            channel.set_webhook(url=self.webhook_url)
        except ApiTelegramException as error:
            raise RasaException("Failed to set channel webhook: " +
                                str(error)) from error

        return channel
Exemplo n.º 11
0
def remove_model(model_dir: Text) -> bool:
    """Removes a model directory and all its content."""
    import shutil

    if is_model_dir(model_dir):
        shutil.rmtree(model_dir)
        return True
    else:
        raise RasaException(
            f"Failed to remove {model_dir}, it seems it is not a model "
            f"directory. E.g. a directory which contains sub directories "
            f"is considered unsafe to remove.")
Exemplo n.º 12
0
def migrate_domain_format(domain_file: Union[Text, Path],
                          out_file: Union[Text, Path]) -> None:
    """Converts 2.0 domain to 3.0 format."""
    domain_file = Path(domain_file)
    out_file = Path(out_file)
    created_out_dir = False

    current_dir = domain_file.parent

    if domain_file.is_dir():
        backup_location = current_dir / "original_domain"
        backup_location.mkdir()

        if out_file.is_file() or not out_file.exists():
            out_file = current_dir / "new_domain"
            out_file.mkdir()
            created_out_dir = True
            rasa.shared.utils.io.raise_warning(
                f"The out path provided is not a directory, "
                f"creating a new directory '{str(out_file)}' "
                f"for migrated domain files.")

        try:
            original_domain = _migrate_domain_files(domain_file,
                                                    backup_location, out_file)
        except Exception as e:
            shutil.rmtree(backup_location)
            if out_file != domain_file:
                shutil.rmtree(out_file)
                if not created_out_dir:
                    # we recreate the deleted directory
                    # because it existed before
                    out_file.mkdir()
            raise e
    else:
        if not Domain.is_domain_file(domain_file):
            raise RasaException(
                f"The file '{domain_file}' could not be validated as a "
                f"domain file. Only domain yaml files can be migrated. ")

        backup_location = current_dir / "original_domain.yml"
        original_domain = _create_back_up(domain_file, backup_location)

    new_forms, updated_slots = _migrate_form_slots(original_domain)
    new_slots = _migrate_auto_fill_and_custom_slots(original_domain,
                                                    updated_slots)

    _write_final_domain(domain_file, new_forms, new_slots, out_file)

    rasa.shared.utils.cli.print_success(
        f"Your domain file '{str(domain_file)}' was successfully migrated! "
        f"The migrated version is now '{str(out_file)}'. "
        f"The original domain file is backed-up at '{str(backup_location)}'.")
Exemplo n.º 13
0
 def env_var_constructor(loader, node):
     """Process environment variables found in the YAML."""
     value = loader.construct_scalar(node)
     expanded_vars = os.path.expandvars(value)
     not_expanded = [
         w for w in expanded_vars.split() if w.startswith("$") and w in value
     ]
     if not_expanded:
         raise RasaException(
             f"Error when trying to expand the environment variables in '{value}'. "
             f"Please make sure to also set these environment variables: '{not_expanded}'."
         )
     return expanded_vars
Exemplo n.º 14
0
    def separate_intent_response_key(
        original_intent: Text, ) -> Tuple[Text, Optional[Text]]:

        split_title = original_intent.split(RESPONSE_IDENTIFIER_DELIMITER)
        if len(split_title) == 2:
            return split_title[0], split_title[1]
        elif len(split_title) == 1:
            return split_title[0], None

        raise RasaException(
            f"Intent name '{original_intent}' is invalid, "
            f"it cannot contain more than one '{RESPONSE_IDENTIFIER_DELIMITER}'."
        )
Exemplo n.º 15
0
def validate_port(port: Any) -> Optional[int]:
    """Ensure that port can be converted to integer.

    Raises:
        RasaException if port cannot be cast to integer.
    """
    if port is not None and not isinstance(port, int):
        try:
            port = int(port)
        except ValueError as e:
            raise RasaException(f"The port '{port}' cannot be cast to integer.") from e

    return port
Exemplo n.º 16
0
    def predict_prob(self, X: np.ndarray) -> np.ndarray:
        """Given a bow vector of an input text, predict the intent label.

        Return probabilities for all labels.

        :param X: bow of input text
        :return: vector of probabilities containing one entry for each label.
        """
        if self.clf is None:
            raise RasaException(
                "Sklearn intent classifier has not been initialised and trained."
            )

        return self.clf.predict_proba(X)
Exemplo n.º 17
0
async def trained_response_selector_bot(trained_async: Callable) -> Path:
    zipped_model = await trained_async(
        domain="data/test_response_selector_bot/domain.yml",
        config="data/test_response_selector_bot/config.yml",
        training_files=[
            "data/test_response_selector_bot/data/rules.yml",
            "data/test_response_selector_bot/data/nlu.yml",
        ],
    )

    if not zipped_model:
        raise RasaException("Model training for responseselectorbot failed.")

    return Path(zipped_model)
Exemplo n.º 18
0
    def load_model_from_remote_storage(self, model_name: Text) -> None:
        """Loads an Agent from remote storage."""
        from rasa.nlu.persistor import get_persistor

        persistor = get_persistor(self.remote_storage)

        if persistor is not None:
            with tempfile.TemporaryDirectory() as temporary_directory:
                persistor.retrieve(model_name, temporary_directory)
                self.load_model(temporary_directory)

        else:
            raise RasaException(
                f"Persistor not found for remote storage: '{self.remote_storage}'."
            )
Exemplo n.º 19
0
async def e2e_bot(
    trained_async: Callable,
    e2e_bot_domain_file: Path,
    e2e_bot_config_file: Path,
    e2e_bot_training_files: List[Path],
) -> Path:
    zipped_model = await trained_async(
        domain=e2e_bot_domain_file,
        config=e2e_bot_config_file,
        training_files=e2e_bot_training_files,
    )

    if not zipped_model:
        raise RasaException("Model training for e2ebot failed.")

    return Path(zipped_model)
Exemplo n.º 20
0
Arquivo: run.py Projeto: hitlmt/rasa
def _create_single_channel(channel: Text, credentials: Dict[Text, Any]) -> Any:
    from rasa.core.channels import BUILTIN_CHANNELS

    if channel in BUILTIN_CHANNELS:
        return BUILTIN_CHANNELS[channel].from_credentials(credentials)
    else:
        # try to load channel based on class name
        try:
            input_channel_class = rasa.shared.utils.common.class_from_module_path(
                channel)
            return input_channel_class.from_credentials(credentials)
        except (AttributeError, ImportError):
            raise RasaException(
                f"Failed to find input channel class for '{channel}'. Unknown "
                f"input channel. Check your credentials configuration to "
                f"make sure the mentioned channel is not misspelled. "
                f"If you are creating your own channel, make sure it "
                f"is a proper name of a class in a module.")
Exemplo n.º 21
0
    def load_component(
        self,
        component_meta: Dict[Text, Any],
        model_dir: Text,
        model_metadata: "Metadata",
        **context: Any,
    ) -> Component:
        """Loads a component.

        Tries to retrieve a component from the cache, else calls
        ``load`` to create a new component.

        Args:
            component_meta:
                The metadata of the component to load in the pipeline.
            model_dir:
                The directory to read the model from.
            model_metadata (Metadata):
                The model's :class:`rasa.nlu.model.Metadata`.

        Returns:
            The loaded component.
        """

        from rasa.nlu import registry

        try:
            cached_component, cache_key = self.__get_cached_component(
                component_meta, model_metadata
            )
            component = registry.load_component_by_meta(
                component_meta, model_dir, model_metadata, cached_component, **context
            )
            if not cached_component:
                # If the component wasn't in the cache,
                # let us add it if possible
                self.__add_to_cache(component, cache_key)
            return component
        except MissingArgumentError as e:  # pragma: no cover
            raise RasaException(
                f"Failed to load component from file '{component_meta.get('file')}'. "
                f"Error: {e}"
            )
Exemplo n.º 22
0
    def validate_response(content: Optional[Dict[Text, Any]]) -> bool:
        """Validate the NLG response. Raises exception on failure."""
        from jsonschema import validate
        from jsonschema import ValidationError

        try:
            if content is None or content == "":
                # means the endpoint did not want to respond with anything
                return True
            else:
                validate(content, nlg_response_format_spec())
                return True
        except ValidationError as e:
            raise RasaException(
                f"{e.message}. Failed to validate NLG response from API, make sure "
                f"your response from the NLG endpoint is valid. "
                f"For more information about the format please consult the "
                f"`nlg_response_format_spec` function from this same module: "
                f"https://github.com/RasaHQ/rasa/blob/main/rasa/core/nlg/callback.py"
            )
Exemplo n.º 23
0
    def separate_intent_response_key(
        original_intent: Text, ) -> Tuple[Text, Optional[Text]]:
        """Splits intent into main intent name and optional sub-intent name.

        For example, `"FAQ/how_to_contribute"` would be split into
        `("FAQ", "how_to_contribute")`. The response delimiter can
        take different values (not just `"/"`) and depends on the
        constant - `RESPONSE_IDENTIFIER_DELIMITER`.
        If there is no response delimiter in the intent, the second tuple
        item is `None`, e.g. `"FAQ"` would be mapped to `("FAQ", None)`.
        """
        split_title = original_intent.split(RESPONSE_IDENTIFIER_DELIMITER)
        if len(split_title) == 2:
            return split_title[0], split_title[1]
        elif len(split_title) == 1:
            return split_title[0], None

        raise RasaException(
            f"Intent name '{original_intent}' is invalid, "
            f"it cannot contain more than one '{RESPONSE_IDENTIFIER_DELIMITER}'."
        )
Exemplo n.º 24
0
def _validate_supported_languages(language: Optional[Text],
                                  node: SchemaNode) -> None:
    supported_languages = node.uses.supported_languages()
    not_supported_languages = node.uses.not_supported_languages()

    if supported_languages and not_supported_languages:
        raise RasaException(
            "Only one of `supported_languages` and "
            "`not_supported_languages` can return a value different from `None`."
        )

    if (language and supported_languages is not None
            and language not in supported_languages):
        raise GraphSchemaValidationException(
            f"The component '{node.uses.__name__}' does not support the currently "
            f"specified language '{language}'.")

    if (language and not_supported_languages is not None
            and language in not_supported_languages):
        raise GraphSchemaValidationException(
            f"The component '{node.uses.__name__}' does not support the currently "
            f"specified language '{language}'.")
Exemplo n.º 25
0
    async def generate(
        self,
        utter_action: Text,
        tracker: DialogueStateTracker,
        output_channel: Text,
        **kwargs: Any,
    ) -> Dict[Text, Any]:
        """Retrieve a named response from the domain using an endpoint."""
        body = nlg_request_format(utter_action, tracker, output_channel,
                                  **kwargs)

        logger.debug("Requesting NLG for {} from {}."
                     "".format(utter_action, self.nlg_endpoint.url))

        response = await self.nlg_endpoint.request(
            method="post", json=body, timeout=DEFAULT_REQUEST_TIMEOUT)

        if isinstance(response, dict) and self.validate_response(response):
            return response
        else:
            raise RasaException(
                "NLG web endpoint returned an invalid response.")
Exemplo n.º 26
0
def class_from_module_path(module_path: Text,
                           lookup_path: Optional[Text] = None) -> Type:
    """Given the module name and path of a class, tries to retrieve the class.

    The loaded class can be used to instantiate new objects.

    Args:
        module_path: either an absolute path to a Python class,
                     or the name of the class in the local / global scope.
        lookup_path: a path where to load the class from, if it cannot
                     be found in the local / global scope.

    Returns:
        a Python class

    Raises:
        ImportError, in case the Python class cannot be found.
        RasaException, in case the imported result is something other than a class
    """
    klass = None
    if "." in module_path:
        module_name, _, class_name = module_path.rpartition(".")
        m = importlib.import_module(module_name)
        klass = getattr(m, class_name, None)
    elif lookup_path:
        # try to import the class from the lookup path
        m = importlib.import_module(lookup_path)
        klass = getattr(m, module_path, None)

    if klass is None:
        raise ImportError(f"Cannot retrieve class from path {module_path}.")

    if not inspect.isclass(klass):
        raise RasaException(
            f"`class_from_module_path()` is expected to return a class, "
            f"but for {module_path} we got a {type(klass)}.")
    return klass
Exemplo n.º 27
0
def migrate_domain_format(
    domain_path: Union[Text, Path], out_path: Optional[Union[Text, Path]]
) -> None:
    """Converts 2.0 domain to 3.0 format."""
    domain_path = Path(domain_path)
    out_path = Path(out_path) if out_path else None

    domain_parent_dir = domain_path.parent
    migrate_file_only = domain_path.is_file()

    # Ensure the backup location does not exist yet
    # Note: We demand that file as well as folder with this name gets deleted before
    # the command is run to avoid confusion afterwards.
    suffix = f"{ORIGINAL_DOMAIN}{YML_SUFFIX}" if migrate_file_only else ORIGINAL_DOMAIN
    backup_location = domain_parent_dir / suffix
    if backup_location.exists():
        backup_location_str = "directory" if backup_location.is_dir() else "file"
        raise RasaException(
            f"The domain could not be migrated since the "
            f"{backup_location_str} '{backup_location}' already exists."
            f"Please make sure that there is no {backup_location_str} at "
            f"'{backup_location}'."
        )

    # Choose a default output location if nothing was specified
    if out_path is None:
        suffix = (
            f"{DEFAULT_NEW_DOMAIN}{YML_SUFFIX}"
            if migrate_file_only
            else DEFAULT_NEW_DOMAIN
        )
        out_path = domain_parent_dir / suffix

    # Ensure the output location is not already in-use
    if not migrate_file_only:
        if out_path.is_dir() and any(out_path.iterdir()):
            raise RasaException(
                f"The domain could not be migrated to "
                f"'{out_path}' because that folder is not empty."
                "Please remove the contents of the folder and try again."
            )
    else:
        if out_path.is_file():
            raise RasaException(
                f"The domain could not be migrated to "
                f"'{out_path}' because that file already exists."
                "Please remove the file and try again."
            )

    # Sanity Check: Assert the files to be migrated aren't in 3.0 format already
    # Note: we do not enforce that the version tag is 2.0 everywhere + validate that
    # migrate-able domain files are among these files later
    original_files = (
        [file for file in domain_path.iterdir() if Domain.is_domain_file(file)]
        if domain_path.is_dir()
        else [domain_path]
    )
    migrated_files = [
        file
        for file in original_files
        if rasa.shared.utils.io.read_yaml_file(file).get("version") == "3.0"
    ]
    if migrated_files:
        raise RasaException(
            f"Some of the given files ({[file for file in migrated_files]}) "
            f"have already been migrated to Rasa 3.0 format. Please remove these "
            f"migrated files (or replace them with files in 2.0 format) and try again."
        )

    # Validate given domain file(s) and migrate them
    try:
        created_out_dir = False
        if not migrate_file_only:
            if not out_path.is_dir():
                out_path.mkdir()
                created_out_dir = True
            backup_location.mkdir()
            original_domain = _migrate_domain_files(
                domain_path, backup_location, out_path
            )
        else:
            if not Domain.is_domain_file(domain_path):
                raise RasaException(
                    f"The file '{domain_path}' could not be validated as a "
                    f"domain file. Only domain yaml files can be migrated. "
                )
            original_domain = _create_back_up(domain_path, backup_location)

        new_forms, updated_slots = _migrate_form_slots(original_domain)
        new_slots = _migrate_auto_fill_and_custom_slots(original_domain, updated_slots)

        _write_final_domain(domain_path, new_forms, new_slots, out_path)

        rasa.shared.utils.cli.print_success(
            f"Your domain file '{str(domain_path)}' was successfully migrated! "
            f"The migrated version is now '{str(out_path)}'. "
            f"The original domain file is backed-up at '{str(backup_location)}'."
        )

    except Exception as e:
        # Remove the backups if migration couldn't be completed
        if backup_location.is_dir():
            shutil.rmtree(backup_location)
        if out_path.is_dir():
            if created_out_dir:
                shutil.rmtree(out_path)
            else:  # just remove contained files so we do not mess with access rights
                for f in out_path.glob("*"):
                    f.unlink()
        if backup_location.is_file():
            backup_location.unlink()
        raise e
Exemplo n.º 28
0
Arquivo: pika.py Projeto: spawn08/rasa
    def __init__(
        self,
        host: Text,
        username: Text,
        password: Text,
        port: Union[int, Text] = 5672,
        queues: Union[List[Text], Tuple[Text], Text, None] = None,
        should_keep_unpublished_messages: bool = True,
        raise_on_failure: bool = False,
        event_loop: Optional[AbstractEventLoop] = None,
        connection_attempts: int = 20,
        retry_delay_in_seconds: float = 5,
        exchange_name: Text = RABBITMQ_EXCHANGE,
        **kwargs: Any,
    ):
        """Initialise RabbitMQ event broker.

        Args:
            host: Pika host.
            username: Username for authentication with Pika host.
            password: Password for authentication with Pika host.
            port: port of the Pika host.
            queues: Pika queues to declare and publish to.
            should_keep_unpublished_messages: Whether or not the event broker should
                maintain a queue of unpublished messages to be published later in
                case of errors.
            raise_on_failure: Whether to raise an exception if publishing fails. If
                `False`, keep retrying.
            event_loop: The event loop which will be used to run `async` functions. If
                `None` `asyncio.get_event_loop()` is used to get a loop.
            connection_attempts: Number of attempts for connecting to RabbitMQ before
                an exception is thrown.
            retry_delay_in_seconds: Time in seconds between connection attempts.
            exchange_name: Exchange name to which the queues binds to.
                If nothing is mentioned then the default exchange name would be used.
        """
        super().__init__()

        self.host = host
        self.username = username
        self.password = password

        try:
            self.port = int(port)
        except ValueError as e:
            raise RasaException(
                "Port could not be converted to integer.") from e

        self.queues = self._get_queues_from_args(queues)
        self.raise_on_failure = raise_on_failure
        self._connection_attempts = connection_attempts
        self._retry_delay_in_seconds = retry_delay_in_seconds
        self.exchange_name = exchange_name

        # Unpublished messages which hopefully will be published later 🤞
        self._unpublished_events: Deque[Dict[Text, Any]] = deque()
        self.should_keep_unpublished_messages = should_keep_unpublished_messages

        self._loop = event_loop or asyncio.get_event_loop()

        self._connection: Optional[aio_pika.RobustConnection] = None
        self._exchange: Optional[aio_pika.RobustExchange] = None
Exemplo n.º 29
0
    async def run(
        self,
        output_channel: "OutputChannel",
        nlg: "NaturalLanguageGenerator",
        tracker: "DialogueStateTracker",
        domain: "Domain",
    ) -> List[Event]:
        """Runs action. Please see parent class for the full docstring."""
        json_body = self._action_call_format(tracker, domain)
        if not self.action_endpoint:
            raise RasaException(
                f"Failed to execute custom action '{self.name()}' "
                f"because no endpoint is configured to run this "
                f"custom action. Please take a look at "
                f"the docs and set an endpoint configuration via the "
                f"--endpoints flag. "
                f"{DOCS_BASE_URL}/custom-actions")

        try:
            logger.debug("Calling action endpoint to run action '{}'.".format(
                self.name()))
            response = await self.action_endpoint.request(
                json=json_body, method="post", timeout=DEFAULT_REQUEST_TIMEOUT)

            self._validate_action_result(response)

            events_json = response.get("events", [])
            responses = response.get("responses", [])
            bot_messages = await self._utter_responses(responses,
                                                       output_channel, nlg,
                                                       tracker)

            evts = events.deserialise_events(events_json)
            return bot_messages + evts

        except ClientResponseError as e:
            if e.status == 400:
                response_data = json.loads(e.text)
                exception = ActionExecutionRejection(
                    response_data["action_name"], response_data.get("error"))
                logger.error(exception.message)
                raise exception
            else:
                raise RasaException("Failed to execute custom action.") from e

        except aiohttp.ClientConnectionError as e:
            logger.error("Failed to run custom action '{}'. Couldn't connect "
                         "to the server at '{}'. Is the server running? "
                         "Error: {}".format(self.name(),
                                            self.action_endpoint.url, e))
            raise RasaException("Failed to execute custom action.")

        except aiohttp.ClientError as e:
            # not all errors have a status attribute, but
            # helpful to log if they got it

            # noinspection PyUnresolvedReferences
            status = getattr(e, "status", None)
            raise RasaException(
                "Failed to run custom action '{}'. Action server "
                "responded with a non 200 status code of {}. "
                "Make sure your action server properly runs actions "
                "and returns a 200 once the action is executed. "
                "Error: {}".format(self.name(), status, e))
Exemplo n.º 30
0
def _migrate_domain_files(
    domain_path: Path, backup_location: Path, out_path: Path
) -> Dict[Text, Any]:
    """Migrates files that only need a version update and collects the remaining info.

    Moreover, backups will be created from all domain files that can be found in the
    given domain directory.

    Args:
        domain_path: directory containing domain files
        backup_location: where to backup all domain files
        out_path: location where to store the migrated files
    """
    slots = {}
    forms = {}
    entities = []

    domain_files = [
        file for file in domain_path.iterdir() if Domain.is_domain_file(file)
    ]

    if not domain_files:
        raise RasaException(
            f"The domain directory '{domain_path}' does not contain any domain files. "
            f"Please make sure to include these for a successful migration."
        )

    for file in domain_files:

        backup = backup_location / file.name
        original_content = _create_back_up(file, backup)

        if KEY_SLOTS not in original_content and KEY_FORMS not in original_content:
            if isinstance(original_content, dict):
                original_content.update({"version": '"3.0"'})

            # this is done so that the other domain files can be moved
            # in the migrated directory
            rasa.shared.utils.io.write_yaml(
                original_content, out_path / file.name, True
            )
        elif KEY_SLOTS in original_content and slots:
            raise RasaException(
                f"Domain files with multiple '{KEY_SLOTS}' "
                f"sections were provided. Please group these sections "
                f"in one file only to prevent content duplication across "
                f"multiple files. "
            )
        elif KEY_FORMS in original_content and forms:
            raise RasaException(
                f"Domain files with multiple '{KEY_FORMS}' "
                f"sections were provided. Please group these sections "
                f"in one file only to prevent content duplication across "
                f"multiple files. "
            )

        slots.update(original_content.get(KEY_SLOTS, {}))
        forms.update(original_content.get(KEY_FORMS, {}))
        entities.extend(original_content.get(KEY_ENTITIES, {}))

    if not slots or not forms:
        raise RasaException(
            f"The files you have provided in '{domain_path}' are missing slots "
            f"or forms. Please make sure to include these for a "
            f"successful migration."
        )

    return {KEY_SLOTS: slots, KEY_FORMS: forms, KEY_ENTITIES: entities}