Beispiel #1
0
async def test_remote_action_runs(default_dispatcher_collecting, default_domain):
    tracker = DialogueStateTracker("default", default_domain.slots)

    endpoint = EndpointConfig("https://example.com/webhooks/actions")
    remote_action = action.RemoteAction("my_action", endpoint)

    with aioresponses() as mocked:
        mocked.post(
            "https://example.com/webhooks/actions",
            payload={"events": [], "responses": []},
        )

        await remote_action.run(default_dispatcher_collecting, tracker, default_domain)

        r = latest_request(mocked, "post", "https://example.com/webhooks/actions")

        assert r

        assert json_of_latest_request(r) == {
            "domain": default_domain.as_dict(),
            "next_action": "my_action",
            "sender_id": "default",
            "version": rasa.__version__,
            "tracker": {
                "latest_message": {"entities": [], "intent": {}, "text": None},
                "active_form": {},
                "latest_action_name": None,
                "sender_id": "default",
                "paused": False,
                "latest_event_time": None,
                "followup_action": "action_listen",
                "slots": {"name": None},
                "events": [],
                "latest_input_channel": None,
            },
        }
Beispiel #2
0
    async def replace_events(request: Request, conversation_id: Text):
        """Use a list of events to set a conversations tracker to a state."""
        validate_request_body(
            request,
            "You must provide events in the request body to set the sate of the "
            "conversation tracker.",
        )

        verbosity = event_verbosity_parameter(request,
                                              EventVerbosity.AFTER_RESTART)

        try:
            async with app.agent.lock_store.lock(conversation_id):
                tracker = DialogueStateTracker.from_dict(
                    conversation_id, request.json, app.agent.domain.slots)

                # will override an existing tracker with the same id!
                app.agent.tracker_store.save(tracker)

            return response.json(tracker.current_state(verbosity))
        except Exception as e:
            logger.debug(traceback.format_exc())
            raise ErrorResponse(500, "ConversationError",
                                f"An unexpected error occurred. Error: {e}")
Beispiel #3
0
def test_form_wins_over_everything_else(ensemble: SimplePolicyEnsemble):
    form_name = "test-form"
    domain = f"""
    forms:
    - {form_name}
    """
    domain = Domain.from_yaml(domain)

    events = [
        Form("test-form"),
        ActionExecuted(ACTION_LISTEN_NAME),
        utilities.user_uttered("test", 1),
    ]
    tracker = DialogueStateTracker.from_events("test", events, [])
    result, best_policy = ensemble.probabilities_using_best_policy(
        tracker, domain, RegexInterpreter()
    )

    max_confidence_index = result.index(max(result))
    next_action = domain.action_for_index(max_confidence_index, None)

    index_of_form_policy = 0
    assert best_policy == f"policy_{index_of_form_policy}_{FormPolicy.__name__}"
    assert next_action.name() == form_name
Beispiel #4
0
    async def tracker_predict(request: Request):
        """ Given a list of events, predicts the next action"""

        sender_id = UserMessage.DEFAULT_SENDER_ID
        request_params = request.json
        verbosity = event_verbosity_parameter(request, EventVerbosity.AFTER_RESTART)

        try:
            tracker = DialogueStateTracker.from_dict(
                sender_id, request_params, app.agent.domain.slots
            )
        except Exception as e:
            raise ErrorResponse(
                400,
                "InvalidParameter",
                "Supplied events are not valid. {}".format(e),
                {"parameter": "", "in": "body"},
            )

        policy_ensemble = app.agent.policy_ensemble
        probabilities, policy = policy_ensemble.probabilities_using_best_policy(
            tracker, app.agent.domain
        )

        scores = [
            {"action": a, "score": p}
            for a, p in zip(app.agent.domain.action_names, probabilities)
        ]

        return response.json(
            {
                "scores": scores,
                "policy": policy,
                "tracker": tracker.current_state(verbosity),
            }
        )
Beispiel #5
0
    async def _update_tracker_session(self, tracker: DialogueStateTracker,
                                      output_channel: OutputChannel) -> 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:
            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,
            )
Beispiel #6
0
async def replay_events(tracker: DialogueStateTracker, agent: "Agent") -> None:
    """Take a tracker and replay the logged user utterances against an agent.

    During replaying of the user utterances, the executed actions and events
    created by the agent are compared to the logged ones of the tracker that
    is getting replayed. If they differ, a warning is logged.

    At the end, the tracker stored in the agent's tracker store for the
    same sender id will have quite the same state as the one
    that got replayed."""

    actions_between_utterances = []
    last_prediction = [ACTION_LISTEN_NAME]

    for i, event in enumerate(tracker.events_after_latest_restart()):
        if isinstance(event, UserUttered):
            _check_prediction_aligns_with_story(last_prediction,
                                                actions_between_utterances)

            actions_between_utterances = []
            cliutils.print_success(event.text)
            out = CollectingOutputChannel()
            await agent.handle_text(event.text,
                                    sender_id=tracker.sender_id,
                                    output_channel=out)
            for m in out.messages:
                console.print_bot_output(m)

            tracker = agent.tracker_store.retrieve(tracker.sender_id)
            last_prediction = actions_since_last_utterance(tracker)

        elif isinstance(event, ActionExecuted):
            actions_between_utterances.append(event.action_name)

    _check_prediction_aligns_with_story(last_prediction,
                                        actions_between_utterances)
Beispiel #7
0
def test_temporary_tracker():
    extra_slot = "some_slot"
    sender_id = "test"
    domain = Domain.from_yaml(
        f"""        slots:
          {extra_slot}:
            type: unfeaturized
        """
    )

    previous_events = [ActionExecuted(ACTION_LISTEN_NAME)]
    old_tracker = DialogueStateTracker.from_events(
        sender_id, previous_events, slots=domain.slots
    )
    new_events = [Restarted()]
    form_action = FormAction("some name", None)
    temp_tracker = form_action._temporary_tracker(old_tracker, new_events, domain)

    assert extra_slot in temp_tracker.slots.keys()
    assert list(temp_tracker.events) == [
        *previous_events,
        ActionExecuted(form_action.name()),
        *new_events,
    ]
Beispiel #8
0
def test_policy_priority():
    domain = Domain.load("data/test_domains/default.yml")
    tracker = DialogueStateTracker.from_events("test", [UserUttered("hi")], [])

    priority_1 = ConstantPolicy(priority=1, predict_index=0)
    priority_2 = ConstantPolicy(priority=2, predict_index=1)

    policy_ensemble_0 = SimplePolicyEnsemble([priority_1, priority_2])
    policy_ensemble_1 = SimplePolicyEnsemble([priority_2, priority_1])

    priority_2_result = priority_2.predict_action_probabilities(
        tracker, domain)

    i = 1  # index of priority_2 in ensemble_0
    result, best_policy = policy_ensemble_0.probabilities_using_best_policy(
        tracker, domain, RegexInterpreter())
    assert best_policy == "policy_{}_{}".format(i, type(priority_2).__name__)
    assert result == priority_2_result

    i = 0  # index of priority_2 in ensemble_1
    result, best_policy = policy_ensemble_1.probabilities_using_best_policy(
        tracker, domain, RegexInterpreter())
    assert best_policy == "policy_{}_{}".format(i, type(priority_2).__name__)
    assert result == priority_2_result
def test_yaml_writer_dumps_user_messages():
    events = [
        UserUttered("Hello", {"name": "greet"}),
        ActionExecuted("utter_greet"),
    ]
    tracker = DialogueStateTracker.from_events("default", events)
    dump = YAMLStoryWriter().dumps(tracker.as_story().story_steps)

    assert (
        dump.strip()
        == textwrap.dedent(
            """
        version: "2.0"
        stories:
        - story: default
          steps:
          - intent: greet
            user: |-
              Hello
          - action: utter_greet

    """
        ).strip()
    )
Beispiel #10
0
    async def generate(
        self,
        template_name: Text,
        tracker: DialogueStateTracker,
        output_channel: Text,
        **kwargs: Any,
    ) -> Optional[Dict[Text, Any]]:
        """Generate a response for the requested template."""

        filled_slots = tracker.current_slot_values()

        fallback_language_slot = tracker.slots.get("fallback_language")
        fallback_language = fallback_language_slot.initial_value if fallback_language_slot else None
        language = tracker.latest_message.metadata.get(
            "language") or fallback_language

        return self.generate_from_slots(
            template_name,
            filled_slots,
            output_channel,
            **kwargs,
            language=language,
            fallback_language=fallback_language,
        )
Beispiel #11
0
    def retrieve(self, sender_id: Text) -> Optional[DialogueStateTracker]:
        """Create a tracker from all previously stored events."""

        import sqlalchemy as sa
        from rasa.core.events import SessionStarted

        with self.session_scope() as session:

            serialised_events = self._event_query(session, sender_id).all()

            events = [json.loads(event.data) for event in serialised_events]

            if self.domain and len(events) > 0:
                logger.debug(f"Recreating tracker from sender id '{sender_id}'")
                return DialogueStateTracker.from_dict(
                    sender_id, events, self.domain.slots
                )
            else:
                logger.debug(
                    f"Can't retrieve tracker matching "
                    f"sender id '{sender_id}' from SQL storage. "
                    f"Returning `None` instead."
                )
                return None
Beispiel #12
0
async def test_give_it_up_after_low_confidence_after_affirm_request():
    tracker = DialogueStateTracker.from_events(
        "some-sender",
        evts=[
            # User sends message with low NLU confidence
            *_message_requiring_fallback(),
            ActiveLoop(ACTION_TWO_STAGE_FALLBACK_NAME),
            # Action asks user to affirm
            *_two_stage_clarification_request(),
            # User's affirms with low NLU confidence again
            *_message_requiring_fallback(),
        ],
    )
    domain = Domain.empty()
    action = TwoStageFallbackAction()

    events = await action.run(
        CollectingOutputChannel(),
        TemplatedNaturalLanguageGenerator(domain.templates),
        tracker,
        domain,
    )

    assert events == [ActiveLoop(None), UserUtteranceReverted()]
Beispiel #13
0
def test_tracker_entity_retrieval(default_domain: Domain):
    tracker = DialogueStateTracker("default", default_domain.slots)
    # the retrieved tracker should be empty
    assert len(tracker.events) == 0
    assert list(tracker.get_latest_entity_values("entity_name")) == []

    intent = {"name": "greet", "confidence": 1.0}
    tracker.update(
        UserUttered(
            "/greet",
            intent,
            [
                {
                    "start": 1,
                    "end": 5,
                    "value": "greet",
                    "entity": "entity_name",
                    "extractor": "manual",
                }
            ],
        )
    )
    assert list(tracker.get_latest_entity_values("entity_name")) == ["greet"]
    assert list(tracker.get_latest_entity_values("unknown")) == []
Beispiel #14
0
def test_get_latest_entity_values(
    entities: List[Dict[Text, Any]], expected_values: List[Text], default_domain: Domain
):
    entity_type = entities[0].get("entity")
    entity_role = entities[0].get("role")
    entity_group = entities[0].get("group")

    tracker = DialogueStateTracker("default", default_domain.slots)
    # the retrieved tracker should be empty
    assert len(tracker.events) == 0
    assert list(tracker.get_latest_entity_values(entity_type)) == []

    intent = {"name": "greet", "confidence": 1.0}
    tracker.update(UserUttered("/greet", intent, entities))

    assert (
        list(
            tracker.get_latest_entity_values(
                entity_type, entity_role=entity_role, entity_group=entity_group
            )
        )
        == expected_values
    )
    assert list(tracker.get_latest_entity_values("unknown")) == []
Beispiel #15
0
def test_tracker_duplicate():
    filename = "{}/data/test_dialogues/moodbot.json".format(PRJ_DIR)
    dialogue = read_dialogue_file(filename)
    tracker = DialogueStateTracker(dialogue.name, domain.slots)
    tracker.recreate_from_dialogue(dialogue)
    num_actions = len(
        [event for event in dialogue.events if isinstance(event, ActionExecuted)]
    )
    events = [event for event in dialogue.events if isinstance(event, ActionExecuted)]
    viz_events(dialogue.events)
    # print(type(events[0]).__name__)
    # exit()
    # viz_events(tracker.events)
    # viz_tracker(tracker, v_domain=True)
    # There is always one duplicated tracker more than we have actions,
    # as the tracker also gets duplicated for the
    # action that would be next (but isn't part of the operations)
    assert len(list(tracker.generate_all_prior_trackers())) == num_actions + 1

    # print(list(tracker.generate_all_prior_trackers()))
    viz_trackers(list(tracker.generate_all_prior_trackers()))
Beispiel #16
0
    def test_missing_classes_filled_correctly(
        self, default_domain, trackers, tracker, featurizer, priority
    ):
        # Pretend that a couple of classes are missing and check that
        # those classes are predicted as 0, while the other class
        # probabilities are predicted normally.
        policy = self.create_policy(featurizer=featurizer, priority=priority, cv=None)

        classes = [1, 3]
        new_trackers = []
        for tr in trackers:
            new_tracker = DialogueStateTracker(
                UserMessage.DEFAULT_SENDER_ID, default_domain.slots
            )
            for e in tr.applied_events():
                if isinstance(e, ActionExecuted):
                    new_action = default_domain.action_for_index(
                        np.random.choice(classes), action_endpoint=None
                    ).name()
                    new_tracker.update(ActionExecuted(new_action))
                else:
                    new_tracker.update(e)

            new_trackers.append(new_tracker)

        policy.train(
            new_trackers, domain=default_domain, interpreter=RegexInterpreter()
        )
        predicted_probabilities = policy.predict_action_probabilities(
            tracker, default_domain
        )

        assert len(predicted_probabilities) == default_domain.num_actions
        assert np.allclose(sum(predicted_probabilities), 1.0)
        for i, prob in enumerate(predicted_probabilities):
            if i in classes:
                assert prob >= 0.0
            else:
                assert prob == 0.0
Beispiel #17
0
def default_tracker(default_domain):
    return DialogueStateTracker("my-sender", default_domain.slots)
Beispiel #18
0
 def _should_handle_message(tracker: DialogueStateTracker):
     return (not tracker.is_paused()
             or tracker.latest_message.intent.get("name")
             == USER_INTENT_RESTART)
Beispiel #19
0
async def test_restart(default_dispatcher_collecting, default_domain):
    tracker = DialogueStateTracker("default", default_domain.slots)
    events = await ActionRestart().run(default_dispatcher_collecting, tracker,
                                       default_domain)
    assert events == [Restarted()]
Beispiel #20
0
async def test_remote_action_logs_events(default_dispatcher_collecting,
                                         default_domain):
    tracker = DialogueStateTracker("default", default_domain.slots)

    endpoint = EndpointConfig("https://example.com/webhooks/actions")
    remote_action = action.RemoteAction("my_action", endpoint)

    response = {
        "events": [{
            "event": "slot",
            "value": "rasa",
            "name": "name"
        }],
        "responses": [
            {
                "text": "test text",
                "buttons": [{
                    "title": "cheap",
                    "payload": "cheap"
                }]
            },
            {
                "template": "utter_greet"
            },
        ],
    }

    with aioresponses() as mocked:
        mocked.post("https://example.com/webhooks/actions", payload=response)

        events = await remote_action.run(default_dispatcher_collecting,
                                         tracker, default_domain)

        r = latest_request(mocked, "post",
                           "https://example.com/webhooks/actions")
        assert r

        assert json_of_latest_request(r) == {
            "domain": default_domain.as_dict(),
            "next_action": "my_action",
            "sender_id": "default",
            "version": rasa.__version__,
            "tracker": {
                "latest_message": {
                    "entities": [],
                    "intent": {},
                    "text": None
                },
                "active_form": {},
                "latest_action_name": None,
                "sender_id": "default",
                "paused": False,
                "followup_action": "action_listen",
                "latest_event_time": None,
                "slots": {
                    "name": None
                },
                "events": [],
                "latest_input_channel": None,
            },
        }

    assert events == [SlotSet("name", "rasa")]

    channel = default_dispatcher_collecting.output_channel
    assert channel.messages == [
        {
            "text": "test text",
            "recipient_id": "my-sender",
            "buttons": [{
                "title": "cheap",
                "payload": "cheap"
            }],
        },
        {
            "text": "hey there None!",
            "recipient_id": "my-sender"
        },
    ]
Beispiel #21
0
def get_tracker(events: List[Event]) -> DialogueStateTracker:
    return DialogueStateTracker.from_events("sender", events, [], 20)
Beispiel #22
0
def has_user_rephrased(tracker: DialogueStateTracker) -> bool:
    return tracker.last_executed_action_has(ACTION_DEFAULT_ASK_REPHRASE_NAME)
Beispiel #23
0
 def _has_user_denied(self, last_intent: Text,
                      tracker: DialogueStateTracker) -> bool:
     return (tracker.last_executed_action_has(
         ACTION_DEFAULT_ASK_AFFIRMATION_NAME)
             and last_intent == self.deny_suggestion_intent_name)
Beispiel #24
0
    def predict_action_probabilities(self, tracker: DialogueStateTracker,
                                     domain: Domain) -> List[float]:
        """Predicts the assigned action.

        If the current intent is assigned to an action that action will be
        predicted with the highest probability of all policies. If it is not
        the policy will predict zero for every action."""

        result = self._default_predictions(domain)

        intent = tracker.latest_message.intent.get("name")
        if intent == USER_INTENT_RESTART:
            action = ACTION_RESTART_NAME
        elif intent == USER_INTENT_BACK:
            action = ACTION_BACK_NAME
        elif intent == USER_INTENT_SESSION_START:
            action = ACTION_SESSION_START_NAME
        else:
            action = domain.intent_properties.get(intent, {}).get("triggers")

        if tracker.latest_action_name == ACTION_LISTEN_NAME:
            # predict mapped action
            if action:
                idx = domain.index_for_action(action)
                if idx is None:
                    raise_warning(
                        f"MappingPolicy tried to predict unknown "
                        f"action '{action}'. Make sure all mapped actions are "
                        f"listed in the domain.",
                        docs=DOCS_URL_POLICIES + "#mapping-policy",
                    )
                else:
                    result[idx] = 1

            if any(result):
                logger.debug("The predicted intent '{}' is mapped to "
                             " action '{}' in the domain."
                             "".format(intent, action))
        elif tracker.latest_action_name == action and action is not None:
            # predict next action_listen after mapped action
            latest_action = tracker.get_last_event_for(ActionExecuted)
            assert latest_action.action_name == action
            if latest_action.policy and latest_action.policy.endswith(
                    type(self).__name__):
                # this ensures that we only predict listen,
                # if we predicted the mapped action
                logger.debug(
                    "The mapped action, '{}', for this intent, '{}', was "
                    "executed last so MappingPolicy is returning to "
                    "action_listen.".format(action, intent))

                idx = domain.index_for_action(ACTION_LISTEN_NAME)
                result[idx] = 1
            else:
                logger.debug(
                    "The mapped action, '{}', for the intent, '{}', was "
                    "executed last, but it was predicted by another policy, '{}', "
                    "so MappingPolicy is not predicting any action.".format(
                        action, intent, latest_action.policy))
        elif action == ACTION_RESTART_NAME:
            logger.debug("Restarting the conversation with action_restart.")
            idx = domain.index_for_action(ACTION_RESTART_NAME)
            result[idx] = 1
        else:
            logger.debug("There is no mapped action for the predicted intent, "
                         "'{}'.".format(intent))
        return result
Beispiel #25
0
    def serialise_tracker(tracker: DialogueStateTracker) -> Text:
        """Serializes the tracker, returns representation of the tracker."""
        dialogue = tracker.as_dialogue()

        return json.dumps(dialogue.as_dict())
Beispiel #26
0
 def _have_options_been_suggested(self,
                                  tracker: DialogueStateTracker) -> bool:
     return tracker.last_executed_action_has(self.disambiguation_action)
Beispiel #27
0
def test_traveling_back_in_time(default_domain):
    tracker = DialogueStateTracker("default", default_domain.slots)
    # the retrieved tracker should be empty
    assert len(tracker.events) == 0

    intent = {"name": "greet", "confidence": 1.0}
    tracker.update(ActionExecuted(ACTION_LISTEN_NAME))
    tracker.update(UserUttered("/greet", intent, []))

    import time

    time.sleep(1)
    time_for_timemachine = time.time()
    time.sleep(1)

    tracker.update(ActionExecuted("my_action"))
    tracker.update(ActionExecuted(ACTION_LISTEN_NAME))

    # Expecting count of 4:
    #   +3 executed actions
    #   +1 final state
    assert tracker.latest_action_name == ACTION_LISTEN_NAME
    assert len(tracker.events) == 4
    assert len(list(tracker.generate_all_prior_trackers())) == 4

    tracker = tracker.travel_back_in_time(time_for_timemachine)

    # Expecting count of 2:
    #   +1 executed actions
    #   +1 final state
    assert tracker.latest_action_name == ACTION_LISTEN_NAME
    assert len(tracker.events) == 2
    assert len(list(tracker.generate_all_prior_trackers())) == 2
Beispiel #28
0
def test_revert_user_utterance_event(default_domain):
    tracker = DialogueStateTracker("default", default_domain.slots)
    # the retrieved tracker should be empty
    assert len(tracker.events) == 0

    intent1 = {"name": "greet", "confidence": 1.0}
    tracker.update(ActionExecuted(ACTION_LISTEN_NAME))
    tracker.update(UserUttered("/greet", intent1, []))
    tracker.update(ActionExecuted("my_action_1"))
    tracker.update(ActionExecuted(ACTION_LISTEN_NAME))

    intent2 = {"name": "goodbye", "confidence": 1.0}
    tracker.update(UserUttered("/goodbye", intent2, []))
    tracker.update(ActionExecuted("my_action_2"))
    tracker.update(ActionExecuted(ACTION_LISTEN_NAME))

    # Expecting count of 6:
    #   +5 executed actions
    #   +1 final state
    assert tracker.latest_action_name == ACTION_LISTEN_NAME
    assert len(list(tracker.generate_all_prior_trackers())) == 6

    tracker.update(UserUtteranceReverted())

    # Expecting count of 3:
    #   +5 executed actions
    #   +1 final state
    #   -2 rewound actions associated with the /goodbye
    #   -1 rewound action from the listen right before /goodbye
    assert tracker.latest_action_name == "my_action_1"
    assert len(list(tracker.generate_all_prior_trackers())) == 3

    dialogue = tracker.as_dialogue()

    recovered = DialogueStateTracker("default", default_domain.slots)
    recovered.recreate_from_dialogue(dialogue)

    assert recovered.current_state() == tracker.current_state()
    assert tracker.latest_action_name == "my_action_1"
    assert len(list(tracker.generate_all_prior_trackers())) == 3
Beispiel #29
0
def test_revert_action_event(default_domain):
    tracker = DialogueStateTracker("default", default_domain.slots)
    # the retrieved tracker should be empty
    assert len(tracker.events) == 0

    intent = {"name": "greet", "confidence": 1.0}
    tracker.update(ActionExecuted(ACTION_LISTEN_NAME))
    tracker.update(UserUttered("/greet", intent, []))
    tracker.update(ActionExecuted("my_action"))
    tracker.update(ActionExecuted(ACTION_LISTEN_NAME))

    # Expecting count of 4:
    #   +3 executed actions
    #   +1 final state
    assert tracker.latest_action_name == ACTION_LISTEN_NAME
    assert len(list(tracker.generate_all_prior_trackers())) == 4

    tracker.update(ActionReverted())

    # Expecting count of 3:
    #   +3 executed actions
    #   +1 final state
    #   -1 reverted action
    assert tracker.latest_action_name == "my_action"
    assert len(list(tracker.generate_all_prior_trackers())) == 3

    dialogue = tracker.as_dialogue()

    recovered = DialogueStateTracker("default", default_domain.slots)
    recovered.recreate_from_dialogue(dialogue)

    assert recovered.current_state() == tracker.current_state()
    assert tracker.latest_action_name == "my_action"
    assert len(list(tracker.generate_all_prior_trackers())) == 3
Beispiel #30
0
def test_restart_event(default_domain):
    tracker = DialogueStateTracker("default", default_domain.slots)
    # the retrieved tracker should be empty
    assert len(tracker.events) == 0

    intent = {"name": "greet", "confidence": 1.0}
    tracker.update(ActionExecuted(ACTION_LISTEN_NAME))
    tracker.update(UserUttered("/greet", intent, []))
    tracker.update(ActionExecuted("my_action"))
    tracker.update(ActionExecuted(ACTION_LISTEN_NAME))

    assert len(tracker.events) == 4
    assert tracker.latest_message.text == "/greet"
    assert len(list(tracker.generate_all_prior_trackers())) == 4

    tracker.update(Restarted())

    assert len(tracker.events) == 5
    assert tracker.followup_action is not None
    assert tracker.followup_action == ACTION_LISTEN_NAME
    assert tracker.latest_message.text is None
    assert len(list(tracker.generate_all_prior_trackers())) == 1

    dialogue = tracker.as_dialogue()

    recovered = DialogueStateTracker("default", default_domain.slots)
    recovered.recreate_from_dialogue(dialogue)

    assert recovered.current_state() == tracker.current_state()
    assert len(recovered.events) == 5
    assert recovered.latest_message.text is None
    assert len(list(recovered.generate_all_prior_trackers())) == 1