コード例 #1
0
    def as_story_string(self, flat=False, e2e=False):
        # if the result should be flattened, we
        # will exclude the caption and any checkpoints.

        for s in self.start_checkpoints:
            if s.name == STORY_START:
                # first story step in the story, so reset helper
                self.story_string_helper = StoryStringHelper()

        if flat:
            result = ""
        else:
            result = "\n## {}\n".format(self.block_name)
            for s in self.start_checkpoints:
                if s.name != STORY_START:
                    result += self._checkpoint_string(s)

        for s in self.events:
            if isinstance(s, UserUttered):
                if self.story_string_helper.active_form is None:
                    result += self._user_string(s, e2e)
                else:
                    # form is active
                    # it is not known whether the form will be
                    # successfully executed, so store this
                    # story string for later
                    self._store_user_strings(s, e2e, FORM_PREFIX)

            elif isinstance(s, Form):
                # form got either activated or deactivated
                self.story_string_helper.active_form = s.name

                if self.story_string_helper.active_form is None:
                    # form deactivated, so form succeeded,
                    # so add story string with form prefix
                    result += self.story_string_helper.form_prefix_string
                    # remove all stored story strings
                    self._reset_stored_strings()

                result += self._bot_string(s)

            elif isinstance(s, FormValidation):
                self.story_string_helper.form_validation = s.validate

            elif isinstance(s, ActionExecutionRejected):
                if s.action_name == self.story_string_helper.active_form:
                    # form rejected
                    self.story_string_helper.form_rejected = True

            elif isinstance(s, ActionExecuted):
                if self._is_action_listen(s):
                    pass
                elif self.story_string_helper.active_form is None:
                    result += self._bot_string(s)
                else:
                    # form is active
                    if self.story_string_helper.form_rejected:
                        if (self.story_string_helper.form_validation and
                                s.action_name ==
                                self.story_string_helper.active_form):
                            result += self._bot_string(
                                ActionExecuted(ACTION_LISTEN_NAME))
                            result += (self.story_string_helper.
                                       form_prefix_string)
                        else:
                            result += (self.story_string_helper.
                                       no_form_prefix_string)
                        # form rejected, add story string without form prefix
                        result += self._bot_string(s)
                    else:
                        # form succeeded, so add story string with form prefix
                        result += (self.story_string_helper.
                                   form_prefix_string)
                        result += self._bot_string(s, FORM_PREFIX)

                    # remove all stored story strings
                    self._reset_stored_strings()

                    if s.action_name == self.story_string_helper.active_form:
                        # form was successfully executed
                        self.story_string_helper.form_rejected = False

                self.story_string_helper.form_validation = True

            elif isinstance(s, SlotSet):
                if self.story_string_helper.active_form is None:
                    result += self._bot_string(s)
                else:
                    # form is active
                    # it is not known whether the form will be
                    # successfully executed, so store this
                    # story string for later
                    # slots should be always printed without prefix
                    self._store_bot_strings(s)

            elif isinstance(s, Event):
                converted = s.as_story_string()
                if converted:
                    if self.story_string_helper.active_form is None:
                        result += self._bot_string(s)
                    else:
                        # form is active
                        # it is not known whether the form will be
                        # successfully executed, so store this
                        # story string for later
                        self._store_bot_strings(s, FORM_PREFIX)

            else:
                raise Exception("Unexpected element in story step: "
                                "{}".format(s))

        if (not self.end_checkpoints and
                self.story_string_helper.active_form is not None):
            # there are no end checkpoints
            # form is active
            # add story string with form prefix
            result += self.story_string_helper.form_prefix_string
            # remove all stored story strings
            self._reset_stored_strings()

        if not flat:
            for e in self.end_checkpoints:
                result += "> {}\n".format(e.as_story_string())
        return result
コード例 #2
0
async def test_handle_message_with_session_start(
    default_channel: CollectingOutputChannel,
    default_processor: MessageProcessor,
    monkeypatch: MonkeyPatch,
):
    sender_id = uuid.uuid4().hex

    entity = "name"
    slot_1 = {entity: "Core"}
    await default_processor.handle_message(
        UserMessage(f"/greet{json.dumps(slot_1)}", default_channel, sender_id)
    )

    assert default_channel.latest_output() == {
        "recipient_id": sender_id,
        "text": "hey there Core!",
    }

    # patch processor so a session start is triggered
    monkeypatch.setattr(default_processor, "_has_session_expired", lambda _: True)

    slot_2 = {entity: "post-session start hello"}
    # handle a new message
    await default_processor.handle_message(
        UserMessage(f"/greet{json.dumps(slot_2)}", default_channel, sender_id)
    )

    tracker = default_processor.tracker_store.get_or_create_tracker(sender_id)

    # make sure the sequence of events is as expected
    assert list(tracker.events) == [
        ActionExecuted(ACTION_SESSION_START_NAME),
        SessionStarted(),
        ActionExecuted(ACTION_LISTEN_NAME),
        UserUttered(
            f"/greet{json.dumps(slot_1)}",
            {"name": "greet", "confidence": 1.0},
            [{"entity": entity, "start": 6, "end": 22, "value": "Core"}],
        ),
        SlotSet(entity, slot_1[entity]),
        ActionExecuted("utter_greet"),
        BotUttered("hey there Core!"),
        ActionExecuted(ACTION_LISTEN_NAME),
        ActionExecuted(ACTION_SESSION_START_NAME),
        SessionStarted(),
        # the initial SlotSet is reapplied after the SessionStarted sequence
        SlotSet(entity, slot_1[entity]),
        ActionExecuted(ACTION_LISTEN_NAME),
        UserUttered(
            f"/greet{json.dumps(slot_2)}",
            {"name": "greet", "confidence": 1.0},
            [
                {
                    "entity": entity,
                    "start": 6,
                    "end": 42,
                    "value": "post-session start hello",
                }
            ],
        ),
        SlotSet(entity, slot_2[entity]),
        ActionExecuted(ACTION_LISTEN_NAME),
    ]
コード例 #3
0
async def test_persist_form_story(tmpdir):
    domain = Domain.load("data/test_domains/form.yml")

    tracker = DialogueStateTracker("", domain.slots)

    story = ("* greet\n"
             "    - utter_greet\n"
             "* start_form\n"
             "    - some_form\n"
             '    - form{"name": "some_form"}\n'
             "* default\n"
             "    - utter_default\n"
             "    - some_form\n"
             "* stop\n"
             "    - utter_ask_continue\n"
             "* affirm\n"
             "    - some_form\n"
             "* stop\n"
             "    - utter_ask_continue\n"
             "    - action_listen\n"
             "* form: inform\n"
             "    - some_form\n"
             '    - form{"name": null}\n'
             "* goodbye\n"
             "    - utter_goodbye\n")

    # simulate talking to the form
    events = [
        UserUttered(intent={"name": "greet"}),
        ActionExecuted("utter_greet"),
        ActionExecuted("action_listen"),
        # start the form
        UserUttered(intent={"name": "start_form"}),
        ActionExecuted("some_form"),
        Form("some_form"),
        ActionExecuted("action_listen"),
        # out of form input
        UserUttered(intent={"name": "default"}),
        ActionExecutionRejected("some_form"),
        ActionExecuted("utter_default"),
        ActionExecuted("some_form"),
        ActionExecuted("action_listen"),
        # out of form input
        UserUttered(intent={"name": "stop"}),
        ActionExecutionRejected("some_form"),
        ActionExecuted("utter_ask_continue"),
        ActionExecuted("action_listen"),
        # out of form input but continue with the form
        UserUttered(intent={"name": "affirm"}),
        FormValidation(False),
        ActionExecuted("some_form"),
        ActionExecuted("action_listen"),
        # out of form input
        UserUttered(intent={"name": "stop"}),
        ActionExecutionRejected("some_form"),
        ActionExecuted("utter_ask_continue"),
        ActionExecuted("action_listen"),
        # form input
        UserUttered(intent={"name": "inform"}),
        FormValidation(True),
        ActionExecuted("some_form"),
        ActionExecuted("action_listen"),
        Form(None),
        UserUttered(intent={"name": "goodbye"}),
        ActionExecuted("utter_goodbye"),
        ActionExecuted("action_listen"),
    ]
    [tracker.update(e) for e in events]

    assert story in tracker.export_stories()
コード例 #4
0
async def test_can_read_test_story_with_entities_slot_autofill(
        default_domain: Domain):
    trackers = await training.load_data(
        "data/test_yaml_stories/story_with_or_and_entities.yml",
        default_domain,
        use_story_concatenation=False,
        tracker_limit=1000,
        remove_duplicates=False,
    )
    assert len(trackers) == 2

    assert trackers[0].events[-3] == UserUttered(
        "greet",
        intent={
            "name": "greet",
            "confidence": 1.0
        },
        parse_data={
            "text": "/greet",
            "intent_ranking": [{
                "confidence": 1.0,
                "name": "greet"
            }],
            "intent": {
                "confidence": 1.0,
                "name": "greet"
            },
            "entities": [],
        },
    )
    assert trackers[0].events[-2] == ActionExecuted("utter_greet")
    assert trackers[0].events[-1] == ActionExecuted("action_listen")

    assert trackers[1].events[-4] == UserUttered(
        "greet",
        intent={
            "name": "greet",
            "confidence": 1.0
        },
        entities=[{
            "entity": "name",
            "value": "peter"
        }],
        parse_data={
            "text": "/greet",
            "intent_ranking": [{
                "confidence": 1.0,
                "name": "greet"
            }],
            "intent": {
                "confidence": 1.0,
                "name": "greet"
            },
            "entities": [{
                "entity": "name",
                "value": "peter"
            }],
        },
    )
    assert trackers[1].events[-3] == SlotSet(key="name", value="peter")
    assert trackers[1].events[-2] == ActionExecuted("utter_greet")
    assert trackers[1].events[-1] == ActionExecuted("action_listen")
コード例 #5
0
    )

    # retrieve the updated tracker
    t = default_processor.tracker_store.retrieve(sender_id)
    assert len(t.events) == 4  # nothing should have been executed


@pytest.mark.parametrize(
    "event_to_apply,session_expiration_time_in_minutes,has_expired",
    [
        # last user event is way in the past
        (UserUttered(timestamp=1), 60, True),
        # user event are very recent
        (UserUttered("hello", timestamp=time.time()), 60, False,),
        # there is user event
        (ActionExecuted(ACTION_LISTEN_NAME, timestamp=time.time()), 60, False),
        # Old event, but sessions are disabled
        (UserUttered("hello", timestamp=1), 0, False),
        # there is no event
        (None, 1, False),
    ],
)
async def test_has_session_expired(
    event_to_apply: Optional[Event],
    session_expiration_time_in_minutes: float,
    has_expired: bool,
    default_processor: MessageProcessor,
):
    sender_id = uuid.uuid4().hex

    default_processor.domain.session_config = SessionConfig(
コード例 #6
0
ファイル: test_events.py プロジェクト: zuiwanting/rasa
            }, []),
            UserUttered("/goodbye", {
                "name": "goodbye",
                "confidence": 1.0
            }, []),
        ),
        (SlotSet("my_slot", "value"), SlotSet("my__other_slot", "value")),
        (Restarted(), None),
        (AllSlotsReset(), None),
        (ConversationPaused(), None),
        (ConversationResumed(), None),
        (StoryExported(), None),
        (ActionReverted(), None),
        (UserUtteranceReverted(), None),
        (SessionStarted(), None),
        (ActionExecuted("my_action"), ActionExecuted("my_other_action")),
        (FollowupAction("my_action"), FollowupAction("my_other_action")),
        (
            BotUttered("my_text", {"my_data": 1}),
            BotUttered("my_other_test", {"my_other_data": 1}),
        ),
        (
            AgentUttered("my_text", "my_data"),
            AgentUttered("my_other_test", "my_other_data"),
        ),
        (
            ReminderScheduled("my_intent", datetime.now()),
            ReminderScheduled("my_other_intent", datetime.now()),
        ),
    ],
)
コード例 #7
0
ファイル: test_events.py プロジェクト: zuiwanting/rasa
def test_json_parse_action():
    evt = {"event": "action", "name": "my_action"}
    assert Event.from_parameters(evt) == ActionExecuted("my_action")
コード例 #8
0
def test_get_last_event_with_reverted():
    events = [ActionExecuted("one"), ActionReverted(), user_uttered("two", 1)]

    tracker = get_tracker(events)

    assert tracker.get_last_event_for(ActionExecuted) is None
コード例 #9
0
ファイル: interactive.py プロジェクト: zammitjames/rasa
async def record_messages(
    endpoint: EndpointConfig,
    sender_id: Text = UserMessage.DEFAULT_SENDER_ID,
    max_message_limit: Optional[int] = None,
    finetune: bool = False,
    stories: Optional[Text] = None,
    skip_visualization: bool = False,
):
    """Read messages from the command line and print bot responses."""

    from rasa.core import training

    try:
        _print_help(skip_visualization)

        try:
            domain = await retrieve_domain(endpoint)
        except ClientError:
            logger.exception(
                "Failed to connect to Rasa Core server at '{}'. "
                "Is the server running?".format(endpoint.url)
            )
            return

        trackers = await training.load_data(
            stories,
            Domain.from_dict(domain),
            augmentation_factor=0,
            use_story_concatenation=False,
        )

        intents = [next(iter(i)) for i in (domain.get("intents") or [])]

        num_messages = 0
        sender_ids = [t.events for t in trackers] + [sender_id]

        if not skip_visualization:
            plot_file = "story_graph.dot"
            await _plot_trackers(sender_ids, plot_file, endpoint)
        else:
            plot_file = None

        while not utils.is_limit_reached(num_messages, max_message_limit):
            try:
                if await is_listening_for_message(sender_id, endpoint):
                    await _enter_user_message(sender_id, endpoint)
                    await _validate_nlu(intents, endpoint, sender_id)
                await _predict_till_next_listen(
                    endpoint, sender_id, finetune, sender_ids, plot_file
                )

                num_messages += 1
            except RestartConversation:
                await send_event(endpoint, sender_id, Restarted().as_dict())

                await send_event(
                    endpoint, sender_id, ActionExecuted(ACTION_LISTEN_NAME).as_dict()
                )

                logger.info("Restarted conversation, starting a new one.")
            except UndoLastStep:
                await _undo_latest(sender_id, endpoint)
                await _print_history(sender_id, endpoint)
            except ForkTracker:
                await _print_history(sender_id, endpoint)

                evts_fork = await _request_fork_from_user(sender_id, endpoint)

                await send_event(endpoint, sender_id, Restarted().as_dict())

                if evts_fork:
                    for evt in evts_fork:
                        await send_event(endpoint, sender_id, evt)
                logger.info("Restarted conversation at fork.")

                await _print_history(sender_id, endpoint)
                await _plot_trackers(sender_ids, plot_file, endpoint)

    except Abort:
        return
    except Exception:
        logger.exception("An exception occurred while recording messages.")
        raise
コード例 #10
0
ファイル: test_trackers.py プロジェクト: zuiwanting/rasa
    tracker._set_slot(slot_name, value_to_set)

    # assert that the tracker contains the slot with the modified value
    assert tracker.get_slot(slot_name) == value_to_set

    # assert that the initial slot has not been affected
    assert slot.value == initial_value


@pytest.mark.parametrize(
    "events, expected_applied_events",
    [
        (
            [
                # Form gets triggered.
                ActionExecuted(ACTION_LISTEN_NAME),
                user_uttered("fill_whole_form"),
                # Form executes and fills slots.
                ActionExecuted("loop"),
                ActiveLoop("loop"),
                SlotSet("slot1", "value"),
                SlotSet("slot2", "value2"),
            ],
            [
                ActionExecuted(ACTION_LISTEN_NAME),
                user_uttered("fill_whole_form"),
                ActionExecuted("loop"),
                ActiveLoop("loop"),
                SlotSet("slot1", "value"),
                SlotSet("slot2", "value2"),
            ],
コード例 #11
0
def test_get_last_event_for():
    events = [ActionExecuted("one"), user_uttered("two", 1)]

    tracker = get_tracker(events)

    assert tracker.get_last_event_for(ActionExecuted).action_name == "one"
コード例 #12
0
ファイル: test_rule_policy.py プロジェクト: xeronith/rasa
async def test_one_stage_fallback_rule():
    domain = Domain.from_yaml(f"""
        intents:
        - {GREET_INTENT_NAME}
        - {DEFAULT_NLU_FALLBACK_INTENT_NAME}
        actions:
        - {UTTER_GREET_ACTION}
    """)

    fallback_recover_rule = TrackerWithCachedStates.from_events(
        "bla",
        domain=domain,
        slots=domain.slots,
        evts=[
            ActionExecuted(RULE_SNIPPET_ACTION_NAME),
            ActionExecuted(ACTION_LISTEN_NAME),
            UserUttered("haha", {"name": DEFAULT_NLU_FALLBACK_INTENT_NAME}),
            ActionExecuted(ACTION_DEFAULT_FALLBACK_NAME),
            ActionExecuted(ACTION_LISTEN_NAME),
        ],
        is_rule_tracker=True,
    )

    greet_rule_which_only_applies_at_start = TrackerWithCachedStates.from_events(
        "bla",
        domain=domain,
        evts=[
            ActionExecuted(ACTION_LISTEN_NAME),
            UserUttered("haha", {"name": GREET_INTENT_NAME}),
            ActionExecuted(UTTER_GREET_ACTION),
            ActionExecuted(ACTION_LISTEN_NAME),
        ],
        is_rule_tracker=True,
    )
    policy = RulePolicy()
    policy.train(
        [greet_rule_which_only_applies_at_start, fallback_recover_rule],
        domain,
        RegexInterpreter(),
    )

    # RulePolicy predicts fallback action
    conversation_events = [
        ActionExecuted(ACTION_LISTEN_NAME),
        UserUttered("dasdakl;fkasd",
                    {"name": DEFAULT_NLU_FALLBACK_INTENT_NAME}),
    ]
    tracker = DialogueStateTracker.from_events("casd",
                                               evts=conversation_events,
                                               slots=domain.slots)
    action_probabilities = policy.predict_action_probabilities(tracker, domain)
    assert_predicted_action(action_probabilities, domain,
                            ACTION_DEFAULT_FALLBACK_NAME)

    # Fallback action reverts fallback events, next action is `ACTION_LISTEN`
    conversation_events += await ActionDefaultFallback().run(
        CollectingOutputChannel(),
        TemplatedNaturalLanguageGenerator(domain.templates),
        tracker,
        domain,
    )

    # Rasa is back on track when user rephrased intent
    conversation_events += [
        ActionExecuted(ACTION_LISTEN_NAME),
        UserUttered("haha", {"name": GREET_INTENT_NAME}),
    ]
    tracker = DialogueStateTracker.from_events("casd",
                                               evts=conversation_events,
                                               slots=domain.slots)

    action_probabilities = policy.predict_action_probabilities(tracker, domain)
    assert_predicted_action(action_probabilities, domain, UTTER_GREET_ACTION)
コード例 #13
0
ファイル: test_rule_policy.py プロジェクト: xeronith/rasa
async def test_form_unhappy_path_triggering_form_again():
    form_name = "some_form"
    handle_rejection_action_name = "utter_handle_rejection"

    domain = Domain.from_yaml(f"""
        intents:
        - {GREET_INTENT_NAME}
        actions:
        - {UTTER_GREET_ACTION}
        - {handle_rejection_action_name}
        - some-action
        slots:
          {REQUESTED_SLOT}:
            type: unfeaturized
        forms:
        - {form_name}
    """)

    unhappy_rule = TrackerWithCachedStates.from_events(
        "bla",
        domain=domain,
        slots=domain.slots,
        evts=[
            # We are in an active form
            Form(form_name),
            SlotSet(REQUESTED_SLOT, "bla"),
            ActionExecuted(RULE_SNIPPET_ACTION_NAME),
            ActionExecuted(ACTION_LISTEN_NAME),
            # When a user says "hi", and the form is unhappy, we want to run a specific
            # action
            UserUttered("haha", {"name": GREET_INTENT_NAME}),
            ActionExecuted(handle_rejection_action_name),
            ActionExecuted(form_name),
            ActionExecuted(ACTION_LISTEN_NAME),
        ],
        is_rule_tracker=True,
    )

    policy = RulePolicy()
    policy.train([unhappy_rule], domain, RegexInterpreter())

    # Check that RulePolicy predicts action to handle unhappy path
    conversation_events = [
        ActionExecuted(form_name),
        Form(form_name),
        SlotSet(REQUESTED_SLOT, "some value"),
        ActionExecuted(ACTION_LISTEN_NAME),
        UserUttered("haha", {"name": GREET_INTENT_NAME}),
        ActionExecutionRejected(form_name),
    ]

    action_probabilities = policy.predict_action_probabilities(
        DialogueStateTracker.from_events("casd",
                                         evts=conversation_events,
                                         slots=domain.slots),
        domain,
    )
    assert_predicted_action(action_probabilities, domain,
                            handle_rejection_action_name)

    # Check that RulePolicy triggers form again after handling unhappy path
    conversation_events.append(ActionExecuted(handle_rejection_action_name))
    action_probabilities = policy.predict_action_probabilities(
        DialogueStateTracker.from_events("casd",
                                         evts=conversation_events,
                                         slots=domain.slots),
        domain,
    )
    assert_predicted_action(action_probabilities, domain, form_name)
コード例 #14
0
ファイル: test_rule_policy.py プロジェクト: xeronith/rasa
    Form,
    SlotSet,
    ActionExecutionRejected,
)
from rasa.core.interpreter import RegexInterpreter
from rasa.core.nlg import TemplatedNaturalLanguageGenerator
from rasa.core.policies.rule_policy import RulePolicy
from rasa.core.trackers import DialogueStateTracker
from rasa.core.training.generator import TrackerWithCachedStates

UTTER_GREET_ACTION = "utter_greet"
GREET_INTENT_NAME = "greet"
GREET_RULE = DialogueStateTracker.from_events(
    "bla",
    evts=[
        ActionExecuted(RULE_SNIPPET_ACTION_NAME),
        ActionExecuted(ACTION_LISTEN_NAME),
        # Greet is a FAQ here and gets triggered in any context
        UserUttered("haha", {"name": GREET_INTENT_NAME}),
        ActionExecuted(UTTER_GREET_ACTION),
    ],
)
GREET_RULE.is_rule_tracker = True


def test_rule_policy_has_max_history_none():
    policy = RulePolicy()
    assert policy.featurizer.max_history is None


def _form_submit_rule(domain: Domain, submit_action_name: Text,
コード例 #15
0
ファイル: structures.py プロジェクト: Robosensus1/rasa-1
 def _add_action_listen(self, events) -> None:
     if not events or not self._is_action_listen(events[-1]):
         # do not add second action_listen
         events.append(ActionExecuted(ACTION_LISTEN_NAME))
コード例 #16
0
def _two_stage_clarification_request() -> List[Event]:
    return [ActionExecuted(ACTION_TWO_STAGE_FALLBACK_NAME), BotUttered("please affirm")]