Ejemplo n.º 1
0
    def _back_to_the_future(
            tracker: DialogueStateTracker,
            again: bool = False) -> Optional[DialogueStateTracker]:
        """Send Marty to the past to get
        the new featurization for the future"""

        idx_of_first_action = None
        idx_of_second_action = None

        # we need to find second executed action
        for e_i, event in enumerate(tracker.applied_events()):
            # find second ActionExecuted
            if isinstance(event, ActionExecuted):
                if idx_of_first_action is None:
                    idx_of_first_action = e_i
                else:
                    idx_of_second_action = e_i
                    break

        # use first action, if we went first time and second action, if we went again
        idx_to_use = idx_of_second_action if again else idx_of_first_action
        if idx_to_use is None:
            return

        # make second ActionExecuted the first one
        events = tracker.applied_events()[idx_to_use:]
        if not events:
            return

        mcfly_tracker = tracker.init_copy()
        for e in events:
            mcfly_tracker.update(e)

        return mcfly_tracker
Ejemplo n.º 2
0
    def _should_skip_prediction(tracker: DialogueStateTracker, domain: Domain,) -> bool:
        """Checks if the policy should skip making a prediction.

        A prediction can be skipped if:
            1. There is no event of type `UserUttered` in the tracker.
            2. If the `UserUttered` event's intent is new and not in domain
                (a new intent can be created from rasa interactive and not placed in
                domain yet)
            3. There is an event of type `ActionExecuted` after the last
                `UserUttered` event. This is to prevent the dialogue manager
                from getting stuck in a prediction loop.
                For example, if the last `ActionExecuted` event
                contained `action_unlikely_intent` predicted by
                `UnexpecTEDIntentPolicy` and
                if `UnexpecTEDIntentPolicy` runs inference
                on the same tracker, it will predict `action_unlikely_intent`
                again which would make the dialogue manager get stuck in a
                prediction loop.

        Returns:
            Whether prediction should be skipped.
        """
        applied_events = tracker.applied_events()

        for event in reversed(applied_events):
            if isinstance(event, ActionExecuted):
                return True
            elif isinstance(event, UserUttered):
                if event.intent_name not in domain.intents:
                    return True
                return False
        # No event of type `ActionExecuted` and `UserUttered` means
        # that there is nothing for `UnexpecTEDIntentPolicy` to predict on.
        return True
Ejemplo n.º 3
0
    async def _update_tracker_session(
        self,
        tracker: DialogueStateTracker,
        output_channel: OutputChannel,
        metadata: Optional[Dict] = None,
    ) -> None:
        """Check the current session in `tracker` and update it if expired.

        An 'action_session_start' is run if the latest tracker session has expired,
        or if the tracker does not yet contain any events (only those after the last
        restart are considered).

        Args:
            metadata: Data sent from client associated with the incoming user message.
            tracker: Tracker to inspect.
            output_channel: Output channel for potential utterances in a custom
                `ActionSessionStart`.
        """
        if not tracker.applied_events() or self._has_session_expired(tracker):
            logger.debug(
                f"Starting a new session for conversation ID '{tracker.sender_id}'."
            )

            await self._run_action(
                action=self._get_action(ACTION_SESSION_START_NAME),
                tracker=tracker,
                output_channel=output_channel,
                nlg=self.nlg,
                metadata=metadata,
                prediction=PolicyPrediction.for_action_name(
                    self.domain, ACTION_SESSION_START_NAME),
            )
Ejemplo n.º 4
0
def _get_max_applied_events_for_max_history(
    tracker: DialogueStateTracker,
    max_history: Optional[int],
) -> Optional[int]:
    """Computes the number of events in the tracker that correspond to max_history.

    Args:
        tracker: Some tracker holding the events
        max_history: The number of actions to count

    Returns:
        The number of actions, as counted from the end of the event list, that should
        be taken into accout according to the `max_history` setting. If all events
        should be taken into account, the return value is `None`.
    """
    if not max_history:
        return None
    num_events = 0
    num_actions = 0
    for event in reversed(tracker.applied_events()):
        num_events += 1
        if isinstance(event, ActionExecuted):
            num_actions += 1
        if num_actions > max_history:
            return num_events
    return None
Ejemplo n.º 5
0
def _get_max_applied_events_for_max_history(
        tracker: DialogueStateTracker,
        max_history: Optional[int]) -> Optional[int]:
    """Computes the number of events in the tracker that correspond to max_history.

    To ensure that the last user utterance is correctly included in the prediction
    states, return the index of the most recent `action_listen` event occuring
    before the tracker would be truncated according to the value of `max_history`.

    Args:
        tracker: Some tracker holding the events
        max_history: The number of actions to count

    Returns:
        The number of events, as counted from the end of the event list, that should
        be taken into accout according to the `max_history` setting. If all events
        should be taken into account, the return value is `None`.
    """
    if not max_history:
        return None
    num_events = 0
    num_actions = 0
    for event in reversed(tracker.applied_events()):
        num_events += 1
        if isinstance(event, ActionExecuted):
            num_actions += 1
            if num_actions > max_history and event.action_name == ACTION_LISTEN_NAME:
                return num_events
    return None
Ejemplo n.º 6
0
    def _is_reminder_still_valid(tracker: DialogueStateTracker,
                                 reminder_event: ReminderScheduled) -> bool:
        """Check if the conversation has been restarted after reminder."""

        for e in reversed(tracker.applied_events()):
            if MessageProcessor._is_reminder(e, reminder_event.name):
                return True
        return False  # not found in applied events --> has been restarted
Ejemplo n.º 7
0
    def _extract_examples(
        self,
        tracker: DialogueStateTracker,
        domain: Domain,
        omit_unset_slots: bool = False,
        ignore_action_unlikely_intent: bool = False,
    ) -> Iterator[Tuple[List[State], List[Text], List[Dict[Text, Any]]]]:
        """Creates an iterator over training examples from a tracker.

        Args:
            trackers: The tracker from which to extract training examples.
            domain: The domain of the training data.
            omit_unset_slots: If `True` do not include the initial values of slots.
            ignore_action_unlikely_intent: Whether to remove `action_unlikely_intent`
                from training states.

        Returns:
            An iterator over example states, labels, and entity data.
        """
        tracker_states = self._create_states(
            tracker, domain, omit_unset_slots=omit_unset_slots
        )
        events = tracker.applied_events()

        if ignore_action_unlikely_intent:
            tracker_states = self._remove_action_unlikely_intent_from_states(
                tracker_states
            )
            events = self._remove_action_unlikely_intent_from_events(events)

        label_index = 0
        entity_data = {}
        for event in events:
            if isinstance(event, UserUttered):
                entity_data = self._entity_data(event)

            elif isinstance(event, ActionExecuted):

                label_index += 1

                # use only actions which can be predicted at a stories start
                if event.unpredictable:
                    continue

                sliced_states = self.slice_state_history(
                    tracker_states[:label_index], self.max_history
                )
                label = [event.action_name or event.action_text]
                entities = [entity_data]

                yield sliced_states, label, entities

                # reset entity_data for the the next turn
                entity_data = {}
Ejemplo n.º 8
0
    async def _update_tracker_session(
        self,
        tracker: DialogueStateTracker,
        output_channel: OutputChannel,
        metadata: Optional[Dict] = None,
    ) -> None:
        """Check the current session in `tracker` and update it if expired.

        An 'action_session_start' is run if the latest tracker session has expired,
        or if the tracker does not yet contain any events (only those after the last
        restart are considered).

        Args:
            metadata: Data sent from client associated with the incoming user message.
            tracker: Tracker to inspect.
            output_channel: Output channel for potential utterances in a custom
                `ActionSessionStart`.
        """
        if not tracker.applied_events() or self._has_session_expired(tracker):
            logger.debug(
                f"Starting a new session for conversation ID '{tracker.sender_id}'."
            )

            action_session_start = self._get_action(ACTION_SESSION_START_NAME)
            # TODO: Remove in 3.0.0 and describe migration to `session_start_metadata`
            # slot in migration guide.
            if isinstance(
                action_session_start, rasa.core.actions.action.ActionSessionStart
            ):
                # Here we set optional metadata to the ActionSessionStart, which will
                # then be passed to the SessionStart event.
                action_session_start.metadata = metadata

            if metadata:
                tracker.update(
                    SlotSet(SESSION_START_METADATA_SLOT, metadata), self.domain
                )

            await self._run_action(
                action=action_session_start,
                tracker=tracker,
                output_channel=output_channel,
                nlg=self.nlg,
                prediction=PolicyPrediction.for_action_name(
                    self.domain, ACTION_SESSION_START_NAME
                ),
            )
Ejemplo n.º 9
0
    def _extract_examples(
        self,
        tracker: DialogueStateTracker,
        domain: Domain,
        omit_unset_slots: bool = False,
        ignore_action_unlikely_intent: bool = False,
    ) -> Iterator[Tuple[List[State], List[Text], List[Dict[Text, Any]]]]:
        """Creates an iterator over training examples from a tracker.

        Args:
            tracker: The tracker from which to extract training examples.
            domain: The domain of the training data.
            omit_unset_slots: If `True` do not include the initial values of slots.
            ignore_action_unlikely_intent: Whether to remove `action_unlikely_intent`
                from training states.

        Returns:
            An iterator over example states, labels, and entity data.
        """
        tracker_states = self._create_states(tracker,
                                             domain,
                                             omit_unset_slots=omit_unset_slots)
        events = tracker.applied_events()

        if ignore_action_unlikely_intent:
            tracker_states = self._remove_action_unlikely_intent_from_states(
                tracker_states)
            events = self._remove_action_unlikely_intent_from_events(events)

        label_index = 0
        for event in events:

            if isinstance(event, ActionExecuted):
                label_index += 1

            elif isinstance(event, UserUttered):

                sliced_states = self.slice_state_history(
                    tracker_states[:label_index], self.max_history)
                label = cast(List[Text], [event.intent_name or event.text])
                entities: List[Dict[Text, Any]] = [{}]

                yield sliced_states, label, entities
Ejemplo n.º 10
0
    def _strip_leading_events_until_action_executed(
            tracker: DialogueStateTracker,
            again: bool = False) -> Optional[DialogueStateTracker]:
        """Truncates the tracker to begin at the next `ActionExecuted` event.

        Args:
            tracker: The tracker to truncate.
            again: When true, truncate tracker at the second action.
                Otherwise truncate to the first action.

        Returns:
            The truncated tracker if there were actions present.
            If none are found, returns `None`.
        """
        idx_of_first_action = None
        idx_of_second_action = None

        applied_events = tracker.applied_events()

        # we need to find second executed action
        for e_i, event in enumerate(applied_events):
            if isinstance(event, ActionExecuted):
                if idx_of_first_action is None:
                    idx_of_first_action = e_i
                else:
                    idx_of_second_action = e_i
                    break

        # use first action, if we went first time and second action, if we went again
        idx_to_use = idx_of_second_action if again else idx_of_first_action
        if idx_to_use is None:
            return None

        # make second ActionExecuted the first one
        events = applied_events[idx_to_use:]
        if not events:
            return None

        truncated_tracker = tracker.init_copy()
        for e in events:
            truncated_tracker.update(e)

        return truncated_tracker
Ejemplo n.º 11
0
    async def _revert_fallback_events(
        self,
        output_channel: OutputChannel,
        nlg: NaturalLanguageGenerator,
        tracker: DialogueStateTracker,
        domain: Domain,
        events_so_far: List[Event],
    ) -> List[Event]:
        revert_events = [UserUtteranceReverted(), UserUtteranceReverted()]

        temp_tracker = DialogueStateTracker.from_events(
            tracker.sender_id,
            tracker.applied_events() + events_so_far + revert_events)

        while temp_tracker.latest_message and not await self.is_done(
                output_channel, nlg, temp_tracker, domain, []):
            temp_tracker.update(revert_events[-1])
            revert_events.append(UserUtteranceReverted())

        return revert_events
Ejemplo n.º 12
0
def _trim_tracker_by_max_history(
    tracker: DialogueStateTracker, max_history: Optional[int],
) -> DialogueStateTracker:
    """Removes events from the tracker until it has `max_history` actions.

    Args:
        tracker: Some tracker.
        max_history: Number of actions to keep.

    Returns:
        A new tracker with up to `max_history` actions, or the same tracker if
        `max_history` is `None`.
    """
    max_applied_events = _get_max_applied_events_for_max_history(tracker, max_history)
    if not max_applied_events:
        return tracker

    applied_events = tracker.applied_events()[-max_applied_events:]
    new_tracker = tracker.init_copy()
    for event in applied_events:
        new_tracker.update(event)
    return new_tracker