async def validate( self, output_channel: "OutputChannel", nlg: "NaturalLanguageGenerator", tracker: "DialogueStateTracker", domain: "Domain", ) -> List[Event]: # extract other slots that were not requested # but set by corresponding entity or trigger intent mapping slot_values = self.extract_other_slots(output_channel, nlg, tracker, domain) # extract requested slot slot_to_fill = tracker.get_slot(REQUESTED_SLOT) if slot_to_fill: slot_values.update( self.extract_requested_slot(output_channel, nlg, tracker, domain)) if not slot_values: # reject to execute the form action # if some slot was requested but nothing was extracted # it will allow other policies to predict another action raise ActionExecutionRejection( self.name(), f"Failed to extract slot {slot_to_fill} with action {self.name()}", ) logger.debug(f"Validating extracted slots: {slot_values}") return await self.validate_slots(slot_values, output_channel, nlg, tracker, domain)
async def validate( self, tracker: "DialogueStateTracker", domain: Domain, output_channel: OutputChannel, nlg: NaturalLanguageGenerator, ) -> List[Event]: """Extract and validate value of requested slot. If nothing was extracted reject execution of the form action. Subclass this method to add custom validation and rejection logic """ # extract other slots that were not requested # but set by corresponding entity or trigger intent mapping slot_values = self.extract_other_slots(tracker, domain) # extract requested slot slot_to_fill = self.get_slot_to_fill(tracker) if slot_to_fill: slot_values.update( self.extract_requested_slot(tracker, domain, slot_to_fill) ) validation_events = await self.validate_slots( slot_values, tracker, domain, output_channel, nlg ) some_slots_were_validated = any( isinstance(event, SlotSet) for event in validation_events # Ignore `SlotSet`s for `REQUESTED_SLOT` as that's not a slot which needs # to be filled by the user. if isinstance(event, SlotSet) and not event.key == REQUESTED_SLOT ) if ( slot_to_fill and not some_slots_were_validated and not self._user_rejected_manually(validation_events) ): # reject to execute the form action # if some slot was requested but nothing was extracted # it will allow other policies to predict another action # # don't raise it here if the user rejected manually, to allow slots other # than the requested slot to be filled. # raise ActionExecutionRejection( self.name(), f"Failed to extract slot {slot_to_fill} with action {self.name()}", ) return validation_events
async def validate( self, tracker: "DialogueStateTracker", domain: Domain, output_channel: OutputChannel, nlg: NaturalLanguageGenerator, ) -> List[Union[SlotSet, Event]]: """Extract and validate value of requested slot and other slots. Returns: The new validation events created by the custom form validation action Raises: ActionExecutionRejection exception to reject execution of form action if nothing was extracted. Subclass this method to add custom validation and rejection logic. """ extracted_slot_values = self._get_slot_extractions(tracker, domain) validation_events = await self.validate_slots(extracted_slot_values, tracker, domain, output_channel, nlg) some_slots_were_validated = any( isinstance(event, SlotSet) and not event.key == REQUESTED_SLOT for event in validation_events # Ignore `SlotSet`s for `REQUESTED_SLOT` as that's not a slot which needs # to be filled by the user. ) # extract requested slot slot_to_fill = self.get_slot_to_fill(tracker) if (slot_to_fill and not extracted_slot_values and not some_slots_were_validated and not self._user_rejected_manually(validation_events)): # reject to execute the form action # if some slot was requested but nothing was extracted # it will allow other policies to predict another action # # don't raise it here if the user rejected manually, to allow slots other # than the requested slot to be filled. # raise ActionExecutionRejection( self.name(), f"Failed to extract slot {slot_to_fill} with action {self.name()}", ) return validation_events
assert action_received_events tracker = default_processor.get_tracker(conversation_id) # The action was logged on the tracker as well expected_events.append(ActionExecuted(ACTION_LISTEN_NAME)) for event, expected in zip(tracker.events, expected_events): assert event == expected # noinspection PyTypeChecker @pytest.mark.parametrize( "reject_fn", [ lambda: [ActionExecutionRejected(ACTION_LISTEN_NAME)], lambda: (_ for _ in ()).throw(ActionExecutionRejection(ACTION_LISTEN_NAME)), ], ) async def test_policy_events_not_applied_if_rejected( default_processor: MessageProcessor, monkeypatch: MonkeyPatch, reject_fn: Callable[[], List[Event]], ): expected_action = ACTION_LISTEN_NAME expected_events = [LoopInterrupted(True)] conversation_id = "test_policy_events_are_applied_to_tracker" user_message = "/greet" class ConstantEnsemble(PolicyEnsemble): def probabilities_using_best_policy( self,