Beispiel #1
0
 def _validate_against_domain(self, domain: Domain) -> None:
     if self._fallback_action_name not in domain.action_names_or_texts:
         raise InvalidDomain(
             f"The fallback action '{self._fallback_action_name}' which was "
             f"configured for the {RulePolicy.__name__} must be present in the "
             f"domain."
         )
Beispiel #2
0
    def extract_requested_slot(
        self,
        tracker: "DialogueStateTracker",
        domain: Domain,
        slot_to_fill: Text,
    ) -> Dict[Text, Any]:
        """Extract the value of requested slot from a user input else return `None`.

        Args:
            tracker: a DialogueStateTracker instance
            domain: the current domain
            slot_to_fill: the name of the slot to fill

        Returns:
            a dictionary with one key being the name of the slot to fill
            and its value being the slot value, or an empty dictionary
            if no slot value was found.
        """
        logger.debug(f"Trying to extract requested slot '{slot_to_fill}' ...")

        # get mapping for requested slot
        requested_slot_mappings = self.get_mappings_for_slot(
            slot_to_fill, domain)

        for requested_slot_mapping in requested_slot_mappings:
            logger.debug(f"Got mapping '{requested_slot_mapping}'")

            if self.intent_is_desired(requested_slot_mapping, tracker):
                mapping_type = requested_slot_mapping["type"]

                if mapping_type == str(SlotMapping.FROM_ENTITY):
                    value = self.get_entity_value(
                        requested_slot_mapping.get("entity"),
                        tracker,
                        requested_slot_mapping.get("role"),
                        requested_slot_mapping.get("group"),
                    )
                elif mapping_type == str(SlotMapping.FROM_INTENT):
                    value = requested_slot_mapping.get("value")
                elif mapping_type == str(SlotMapping.FROM_TRIGGER_INTENT):
                    # from_trigger_intent is only used on form activation
                    continue
                elif mapping_type == str(SlotMapping.FROM_TEXT):
                    value = tracker.latest_message.text
                else:
                    raise InvalidDomain(
                        "Provided slot mapping type is not supported")

                if value is not None:
                    logger.debug(
                        f"Successfully extracted '{value}' for requested slot "
                        f"'{slot_to_fill}'")
                    return {slot_to_fill: value}

        logger.debug(f"Failed to extract requested slot '{slot_to_fill}'")
        return {}
Beispiel #3
0
    def validate(mapping: Dict[Text, Any], slot_name: Text) -> None:
        """Validates a slot mapping.

        Args:
            mapping: The mapping which is validated.
            slot_name: The name of the slot which is mapped by this mapping.

        Raises:
            InvalidDomain: In case the slot mapping is not valid.
        """
        from rasa.shared.core.domain import InvalidDomain

        if not isinstance(mapping, dict):
            raise InvalidDomain(
                f"Please make sure that the slot mappings for slot '{slot_name}' in "
                f"your domain are valid dictionaries. Please see "
                f"{DOCS_URL_SLOTS} for more information.")

        validations = {
            str(SlotMapping.FROM_ENTITY): ["entity"],
            str(SlotMapping.FROM_INTENT): ["value"],
            str(SlotMapping.FROM_TRIGGER_INTENT): ["value"],
            str(SlotMapping.FROM_TEXT): [],
            str(SlotMapping.CUSTOM): [],
        }

        mapping_type = mapping.get("type")
        required_keys = validations.get(mapping_type)

        if required_keys is None:
            raise InvalidDomain(
                f"Your domain uses an invalid slot mapping of type "
                f"'{mapping_type}' for slot '{slot_name}'. Please see "
                f"{DOCS_URL_SLOTS} for more information.")

        for required_key in required_keys:
            if mapping.get(required_key) is None:
                raise InvalidDomain(
                    f"You need to specify a value for the key "
                    f"'{required_key}' in the slot mapping of type '{mapping_type}' "
                    f"for slot '{slot_name}'. Please see "
                    f"{DOCS_URL_SLOTS} for more information.")
Beispiel #4
0
    def validate(mapping: Dict[Text, Any], slot_name: Text) -> None:
        """Validates a slot mapping.

        Args:
            mapping: The mapping which is validated.
            slot_name: The name of the slot which is mapped by this mapping.

        Raises:
            InvalidDomain: In case the slot mapping is not valid.
        """
        from rasa.shared.core.domain import InvalidDomain

        if not isinstance(mapping, dict):
            raise InvalidDomain(
                f"Please make sure that the slot mappings for slot '{slot_name}' in "
                f"your domain are valid dictionaries. Please see "
                f"{DOCS_URL_SLOTS} for more information.")

        try:
            mapping_type = SlotMappingType(mapping.get(MAPPING_TYPE))
        except ValueError:
            raise InvalidDomain(
                f"Your domain uses an invalid slot mapping of type "
                f"'{mapping.get(MAPPING_TYPE)}' for slot '{slot_name}'. Please see "
                f"{DOCS_URL_SLOTS} for more information.")

        validations: Dict[SlotMappingType, List[Text]] = {
            SlotMappingType.FROM_ENTITY: ["entity"],
            SlotMappingType.FROM_INTENT: ["value"],
            SlotMappingType.FROM_TRIGGER_INTENT: ["value"],
            SlotMappingType.FROM_TEXT: [],
            SlotMappingType.CUSTOM: [],
        }

        required_keys = validations[mapping_type]
        for required_key in required_keys:
            if mapping.get(required_key) is None:
                raise InvalidDomain(
                    f"You need to specify a value for the key "
                    f"'{required_key}' in the slot mapping of type '{mapping_type}' "
                    f"for slot '{slot_name}'. Please see "
                    f"{DOCS_URL_SLOTS} for more information.")
Beispiel #5
0
def _check_policy_for_forms_available(
        domain: Domain, ensemble: Optional["PolicyEnsemble"]) -> None:
    if not ensemble:
        return

    has_policy_for_forms = ensemble is not None and any(
        isinstance(policy, RulePolicy) for policy in ensemble.policies)

    if domain.form_names and not has_policy_for_forms:
        raise InvalidDomain(
            "You have defined a form action, but have not added the "
            f"'{RulePolicy.__name__}' to your policy ensemble. Either "
            f"remove all forms from your domain or add the '{RulePolicy.__name__}' "
            f"to your policy configuration.")
    def validate_against_domain(cls, ensemble: Optional["PolicyEnsemble"],
                                domain: Optional[Domain]) -> None:
        if ensemble is None:
            return

        for p in ensemble.policies:
            if not isinstance(p, TwoStageFallbackPolicy):
                continue
            if domain is None or p.deny_suggestion_intent_name not in domain.intents:
                raise InvalidDomain(
                    "The intent '{0}' must be present in the "
                    "domain file to use TwoStageFallbackPolicy. "
                    "Either include the intent '{0}' in your domain "
                    "or exclude the TwoStageFallbackPolicy from your "
                    "policy configuration".format(
                        p.deny_suggestion_intent_name))
Beispiel #7
0
    def validate_against_domain(cls, ensemble: Optional["PolicyEnsemble"],
                                domain: Optional[Domain]) -> None:
        if ensemble is None:
            return

        rule_policy = next(
            (p for p in ensemble.policies if isinstance(p, RulePolicy)), None)
        if not rule_policy or not rule_policy._enable_fallback_prediction:
            return

        if (domain is None or rule_policy._fallback_action_name
                not in domain.action_names_or_texts):
            raise InvalidDomain(
                f"The fallback action '{rule_policy._fallback_action_name}' which was "
                f"configured for the {RulePolicy.__name__} must be present in the "
                f"domain.")
Beispiel #8
0
    def validate_against_domain(cls, ensemble: Optional["PolicyEnsemble"],
                                domain: Optional[Domain]) -> None:
        if not domain:
            return

        has_mapping_policy = ensemble is not None and any(
            isinstance(p, cls) for p in ensemble.policies)
        has_triggers_in_domain = any([
            "triggers" in properties
            for intent, properties in domain.intent_properties.items()
        ])
        if has_triggers_in_domain and not has_mapping_policy:
            raise InvalidDomain(
                "You have defined triggers in your domain, but haven't "
                "added the MappingPolicy to your policy ensemble. "
                "Either remove the triggers from your domain or "
                "include the MappingPolicy in your policy configuration.")
    def _raise_if_domain_contains_form_names_but_no_rule_policy_given(
            self, domain: Domain) -> None:
        """Validates that there exists a rule policy if forms are defined.

        Raises:
            `InvalidConfigException` if domain and rule policies do not match
        """
        contains_rule_policy = any(
            schema_node for schema_node in self._graph_schema.nodes.values()
            if schema_node.uses == RulePolicy)

        if domain.form_names and not contains_rule_policy:
            raise InvalidDomain(
                "You have defined a form action, but have not added the "
                f"'{RulePolicy.__name__}' to your policy ensemble. "
                f"Either remove all forms from your domain or add the "
                f"'{RulePolicy.__name__}' to your policy configuration.")
Beispiel #10
0
    def raise_if_incompatible_with_domain(cls, config: Dict[Text, Any],
                                          domain: Domain) -> None:
        """Checks whether the domains action names match the configured fallback.

        Args:
            config: configuration of a `RulePolicy`
            domain: a domain
        Raises:
            `InvalidDomain` if this policy is incompatible with the domain
        """
        fallback_action_name = config.get("core_fallback_action_name", None)
        if (fallback_action_name
                and fallback_action_name not in domain.action_names_or_texts):
            raise InvalidDomain(
                f"The fallback action '{fallback_action_name}' which was "
                f"configured for the {RulePolicy.__name__} must be "
                f"present in the domain.")
Beispiel #11
0
    def extract_requested_slot(self, tracker: "DialogueStateTracker",
                               domain: Domain) -> Dict[Text, Any]:
        """Extract the value of requested slot from a user input
        else return `None`.
        """
        slot_to_fill = tracker.get_slot(REQUESTED_SLOT)
        logger.debug(f"Trying to extract requested slot '{slot_to_fill}' ...")

        # get mapping for requested slot
        requested_slot_mappings = self.get_mappings_for_slot(
            slot_to_fill, domain)

        for requested_slot_mapping in requested_slot_mappings:
            logger.debug(f"Got mapping '{requested_slot_mapping}'")

            if self.intent_is_desired(requested_slot_mapping, tracker):
                mapping_type = requested_slot_mapping["type"]

                if mapping_type == str(SlotMapping.FROM_ENTITY):
                    value = self.get_entity_value(
                        requested_slot_mapping.get("entity"),
                        tracker,
                        requested_slot_mapping.get("role"),
                        requested_slot_mapping.get("group"),
                    )
                elif mapping_type == str(SlotMapping.FROM_INTENT):
                    value = requested_slot_mapping.get("value")
                elif mapping_type == str(SlotMapping.FROM_TRIGGER_INTENT):
                    # from_trigger_intent is only used on form activation
                    continue
                elif mapping_type == str(SlotMapping.FROM_TEXT):
                    value = tracker.latest_message.text
                else:
                    raise InvalidDomain(
                        "Provided slot mapping type is not supported")

                if value is not None:
                    logger.debug(
                        f"Successfully extracted '{value}' for requested slot "
                        f"'{slot_to_fill}'")
                    return {slot_to_fill: value}

        logger.debug(f"Failed to extract requested slot '{slot_to_fill}'")
        return {}
Beispiel #12
0
def _check_policy_for_forms_available(
        domain: Domain, ensemble: Optional["PolicyEnsemble"]) -> None:
    if not ensemble:
        return

    from rasa.core.policies.form_policy import FormPolicy

    suited_policies_for_forms = (FormPolicy, RulePolicy)

    has_policy_for_forms = ensemble is not None and any(
        isinstance(policy, suited_policies_for_forms)
        for policy in ensemble.policies)

    if domain.form_names and not has_policy_for_forms:
        raise InvalidDomain(
            "You have defined a form action, but have neither added the "
            f"'{RulePolicy.__name__}' nor the '{FormPolicy.__name__}' (deprecated) to "
            f"your policy ensemble. Either remove all forms from your domain or add "
            f"the missing policy to your policy configuration.")
Beispiel #13
0
def _check_policy_for_forms_available(
        domain: Domain, ensemble: Optional["PolicyEnsemble"]) -> None:
    if not ensemble:
        return

    from rasa.core.policies.form_policy import FormPolicy

    suited_policies_for_forms = (FormPolicy, RulePolicy)

    has_policy_for_forms = ensemble is not None and any(
        isinstance(policy, suited_policies_for_forms)
        for policy in ensemble.policies)

    if domain.form_names and not has_policy_for_forms:
        raise InvalidDomain(
            "You have defined a form action, but haven't added the "
            "FormPolicy to your policy ensemble. Either remove all "
            "forms from your domain or exclude the FormPolicy from your "
            "policy configuration.")
Beispiel #14
0
async def test_pull_model_with_invalid_domain(
    model_server: TestClient, monkeypatch: MonkeyPatch, caplog: LogCaptureFixture
):
    # mock `Domain.load()` as if the domain contains invalid YAML
    error_message = "domain is invalid"
    mock_load = Mock(side_effect=InvalidDomain(error_message))

    monkeypatch.setattr(Domain, "load", mock_load)
    model_endpoint_config = EndpointConfig.from_dict(
        {"url": model_server.make_url("/model"), "wait_time_between_pulls": None}
    )

    agent = Agent()
    await rasa.core.agent.load_from_server(agent, model_server=model_endpoint_config)

    # `Domain.load()` was called
    mock_load.assert_called_once()

    # error was logged
    assert error_message in caplog.text