Example #1
0
def test_remote_action_validate_all_event_subclasses(event_class: Type[Event]):

    if event_class.type_name == "slot":
        response = {
            "events": [{
                "event": "slot",
                "name": "test",
                "value": "example"
            }],
            "responses": [],
        }
    elif event_class.type_name == "entities":
        response = {
            "events": [{
                "event": "entities",
                "entities": []
            }],
            "responses": []
        }
    else:
        response = {
            "events": [{
                "event": event_class.type_name
            }],
            "responses": []
        }

    # ignore the below events since these are not sent or received outside Rasa
    if event_class.type_name not in [
            "wrong_utterance",
            "wrong_action",
            "warning_predicted",
    ]:
        validate(response, RemoteAction.action_response_format_spec())
Example #2
0
    async def validate_slots(
        self,
        slot_candidates: Dict[Text, Any],
        tracker: "DialogueStateTracker",
        domain: Domain,
        output_channel: OutputChannel,
        nlg: NaturalLanguageGenerator,
    ) -> List[Union[SlotSet, Event]]:
        """Validate the extracted slots.

        If a custom action is available for validating the slots, we call it to validate
        them. Otherwise there is no validation.

        Args:
            slot_candidates: Extracted slots which are candidates to fill the slots
                required by the form.
            tracker: The current conversation tracker.
            domain: The current model domain.
            output_channel: The output channel which can be used to send messages
                to the user.
            nlg:  `NaturalLanguageGenerator` to use for response generation.

        Returns:
            The validation events including potential bot messages and `SlotSet` events
            for the validated slots.
        """
        logger.debug(f"Validating extracted slots: {slot_candidates}")
        events: List[Union[SlotSet, Event]] = [
            SlotSet(slot_name, value)
            for slot_name, value in slot_candidates.items()
        ]

        validate_name = f"validate_{self.name()}"

        if validate_name not in domain.action_names_or_texts:
            return events

        _tracker = self._temporary_tracker(tracker, events, domain)
        _action = RemoteAction(validate_name, self.action_endpoint)
        validate_events = await _action.run(output_channel, nlg, _tracker,
                                            domain)

        validated_slot_names = [
            event.key for event in validate_events
            if isinstance(event, SlotSet)
        ]

        # If the custom action doesn't return a SlotSet event for an extracted slot
        # candidate we assume that it was valid. The custom action has to return a
        # SlotSet(slot_name, None) event to mark a Slot as invalid.
        return validate_events + [
            event for event in events if event.key not in validated_slot_names
        ]
Example #3
0
    async def validate_slots(
        self,
        slot_candidates: Dict[Text, Any],
        tracker: "DialogueStateTracker",
        domain: Domain,
        output_channel: OutputChannel,
        nlg: NaturalLanguageGenerator,
    ) -> List[Union[SlotSet, Event]]:
        """Validate the extracted slots.

        If a custom action is available for validating the slots, we call it to validate
        them. Otherwise there is no validation.

        Args:
            slot_candidates: Extracted slots which are candidates to fill the slots
                required by the form.
            tracker: The current conversation tracker.
            domain: The current model domain.
            output_channel: The output channel which can be used to send messages
                to the user.
            nlg:  `NaturalLanguageGenerator` to use for response generation.

        Returns:
            The validation events including potential bot messages and `SlotSet` events
            for the validated slots, if the custom form validation action is present in
            domain actions.
            Otherwise, returns empty list since the extracted slots already have
            corresponding `SlotSet` events in the tracker.
        """
        logger.debug(f"Validating extracted slots: {slot_candidates}")
        events: List[Union[SlotSet, Event]] = [
            SlotSet(slot_name, value)
            for slot_name, value in slot_candidates.items()
        ]

        validate_name = f"validate_{self.name()}"

        if validate_name not in domain.action_names_or_texts:
            return []

        # create temporary tracker with only the SlotSet events added
        # since last user utterance
        _tracker = self._temporary_tracker(tracker, events, domain)

        _action = RemoteAction(validate_name, self.action_endpoint)
        validate_events = await _action.run(output_channel, nlg, _tracker,
                                            domain)

        # Only return the validated SlotSet events by the custom form validation action
        # to avoid adding duplicate SlotSet events for slots that are already valid.
        return validate_events
    async def validate_slots(
        self,
        slot_dict: Dict[Text, Any],
        output_channel: "OutputChannel",
        nlg: "NaturalLanguageGenerator",
        tracker: "DialogueStateTracker",
        domain: "Domain",
    ) -> List[Event]:
        events = []
        for slot, value in list(slot_dict.items()):
            validation_rule = self.get_field_for_slot(slot, "validation")

            if validation_rule is None:
                events += [SlotSet(slot, value)]
                validated = True
            else:
                operator, comparatum = (
                    validation_rule.get("operator"),
                    validation_rule.get("comparatum"),
                )

                if operator == 'contains' and comparatum == 'custom':
                    validate_name = f"bf_{self.name()}"

                    if validate_name in domain.action_names_or_texts:
                        _tracker = self._temporary_tracker(
                            tracker, events, domain)
                        _action = RemoteAction(validate_name,
                                               self.action_endpoint)
                        validate_events = await _action.run(
                            output_channel, nlg, _tracker, domain)

                        validated_slot_names = [
                            event.key for event in validate_events
                            if isinstance(event, SlotSet)
                        ]

                        if slot not in validated_slot_names:
                            validate_events += [SlotSet(slot, None)]
                        events += validate_events

                        tracker.update_with_events(validate_events, domain)
                        validated = True
                    else:
                        logger.info(f"Action not found: {validate_name}")
                        validated = False
                else:
                    validated = validate_with_rule(value, validation_rule)
                    events += [SlotSet(slot, value if validated else None)]

            # is it changing during this conversational turn? if it did, then:
            # either tracker value is different, or if tracker was updated
            # through regular entity recognition, then this happened in latest event
            # (will not work if multiple entities were extracted in user utterance)
            if tracker.get_slot(slot) != value or (
                    isinstance(tracker.events[-1], SlotSet)
                    and tracker.events[-1].key == slot
                    and tracker.events[-1].value == value):
                events += await self.utter_post_validation(
                    slot, value, validated, output_channel, nlg, tracker,
                    domain)

        return events