Ejemplo n.º 1
0
def test_message_processor(default_processor):
    out = CollectingOutputChannel()
    default_processor.handle_message(UserMessage('/greet{"name":"Core"}', out))
    assert {
        'recipient_id': 'default',
        'text': 'hey there Core!'
    } == out.latest_output()
Ejemplo n.º 2
0
def test_message_processor(default_domain, capsys):
    story_filename = "data/dsl_stories/stories_defaultdomain.md"
    ensemble = SimplePolicyEnsemble([ScoringPolicy()])
    interpreter = RegexInterpreter()

    PolicyTrainer(ensemble, default_domain,
                  BinaryFeaturizer()).train(story_filename, max_history=3)

    tracker_store = InMemoryTrackerStore(default_domain)
    processor = MessageProcessor(interpreter, ensemble, default_domain,
                                 tracker_store)

    out = CollectingOutputChannel()
    processor.handle_message(UserMessage("_greet[name=Core]", out))
    assert ("default", "hey there Core!") == out.latest_output()
    def email(sender_id):
        request_params = request_parameters()

        if 'query' in request_params:
            message = request_params.get('query')
        elif 'q' in request_params:
            message = request_params.get('q')
        else:
            return Response(jsonify(error="Invalid respond parameter "
                                    "specified."),
                            status=400,
                            mimetype="application/json")

        global usr_channel
        teams_channel = usr_channel
        teams_channel.output_channel.id_map.update(
            env_config.inter_channel_mapper)
        #temporary code follows
        teams_channel.output_channel.id_map.update({
            sender_id:
            env_config.inter_channel_mapper[list(
                env_config.inter_channel_mapper.keys())[0]]
        })
        teams_channel.output_channel.reverse_id_map.update(
            {list(env_config.inter_channel_mapper.keys())[0]: sender_id})
        #temporary code ends

        email_id = request_params.get('email_id')
        preprocessor = partial(idare.email_preprocessor, email_id=email_id)
        try:
            # Set the output channel
            out = CollectingOutputChannel()
            # Fetches the appropriate bot response in a json format
            agent().handle_email(message,
                                 email_preprocessor=preprocessor,
                                 output_channel=out,
                                 alternate_channel=teams_channel,
                                 sender_id=sender_id)
            response = out.latest_output()

            return jsonify(response)

        except Exception as e:
            logger.exception("Caught an exception during respond.")
            return Response(jsonify(error="Server failure. Error: {}"
                                    "".format(e)),
                            status=500,
                            content_type="application/json")
Ejemplo n.º 4
0
def test_restaurant_form_skipahead():
    domain = TemplateDomain.load("data/test_domains/restaurant_form.yml")
    nlg = TemplatedNaturalLanguageGenerator(domain.templates)
    tracker_store = InMemoryTrackerStore(domain)
    out = CollectingOutputChannel()
    sender_id = "test-restaurant"
    dispatcher = Dispatcher(sender_id, out, nlg)
    tracker = tracker_store.get_or_create_tracker(sender_id)

    # first user utterance
    entities = [{
        "entity": "cuisine",
        "value": "chinese"
    }, {
        "entity": "number",
        "value": 8
    }]
    tracker.update(
        UserUttered("", intent={"name": "inform"}, entities=entities))

    events = ActionSearchRestaurants().run(dispatcher, tracker, domain)
    s = events[0].as_story_string()
    print(events[0].as_story_string())
    print(events[1].as_story_string())
    assert len(events) == 3
    assert events[2].key == "requested_slot"
    assert events[2].value == "vegetarian"
Ejemplo n.º 5
0
def test_restaurant_form_unhappy_1():
    domain = TemplateDomain.load("data/test_domains/restaurant_form.yml")
    nlg = TemplatedNaturalLanguageGenerator(domain.templates)
    tracker_store = InMemoryTrackerStore(domain)
    out = CollectingOutputChannel()
    sender_id = "test-restaurant"
    dispatcher = Dispatcher(sender_id, out, nlg)
    tracker = tracker_store.get_or_create_tracker(sender_id)

    # first user utterance
    tracker.update(UserUttered("", intent={"name": "inform"}))
    events = ActionSearchRestaurants().run(dispatcher, tracker, domain)
    assert len(events) == 1
    assert isinstance(events[0], SlotSet)
    assert events[0].key == "requested_slot"
    assert events[0].value == "cuisine"
    tracker.update(events[0])

    # second user utterance does not provide what's asked
    tracker.update(UserUttered("", intent={"name": "inform"}))

    events = ActionSearchRestaurants().run(dispatcher, tracker, domain)
    print([(e.key, e.value) for e in events])
    assert len(events) == 1
    assert isinstance(events[0], SlotSet)

    # same slot requested again
    assert events[0].key == "requested_slot"
    assert events[0].value == "cuisine"
Ejemplo n.º 6
0
def test_query_form_set_username_in_form():
    domain = TemplateDomain.load("data/test_domains/query_form.yml")
    nlg = TemplatedNaturalLanguageGenerator(domain.templates)
    tracker_store = InMemoryTrackerStore(domain)
    out = CollectingOutputChannel()
    sender_id = "test-form"
    dispatcher = Dispatcher(sender_id, out, nlg)
    tracker = tracker_store.get_or_create_tracker(sender_id)

    # first user utterance
    tracker.update(UserUttered("", intent={"name": "inform"}))
    events = ActionSearchQuery().run(dispatcher, tracker, domain)
    last_message = dispatcher.latest_bot_messages[-1]
    assert len(events) == 1
    assert isinstance(events[0], SlotSet)
    assert events[0].key == "requested_slot"
    assert events[0].value == "username"
    assert last_message.text == 'what is your name?'
    tracker.update(events[0])

    # second user utterance
    username = '******'
    tracker.update(UserUttered(username, intent={"name": "inform"}))
    events = ActionSearchQuery().run(dispatcher, tracker, domain)
    last_message = dispatcher.latest_bot_messages[-1]
    assert len(events) == 2
    assert isinstance(events[0], SlotSet)
    assert events[0].key == "username"
    assert events[0].value == username
    assert events[1].key == "requested_slot"
    assert events[1].value == "query"
    assert username in last_message.text
Ejemplo n.º 7
0
def test_restaurant_form():
    domain = TemplateDomain.load("data/test_domains/restaurant_form.yml")
    nlg = TemplatedNaturalLanguageGenerator(domain.templates)
    tracker_store = InMemoryTrackerStore(domain)
    out = CollectingOutputChannel()
    sender_id = "test-restaurant"
    dispatcher = Dispatcher(sender_id, out, nlg)
    tracker = tracker_store.get_or_create_tracker(sender_id)

    # first user utterance
    tracker.update(UserUttered("", intent={"name": "inform"}))
    events = ActionSearchRestaurants().run(dispatcher, tracker, domain)
    assert len(events) == 1
    assert isinstance(events[0], SlotSet)
    assert events[0].key == "requested_slot"
    assert events[0].value == "cuisine"
    tracker.update(events[0])

    # second user utterance
    entities = [{"entity": "cuisine", "value": "chinese"}]
    tracker.update(
        UserUttered("", intent={"name": "inform"}, entities=entities))

    events = ActionSearchRestaurants().run(dispatcher, tracker, domain)
    assert len(events) == 2
    assert isinstance(events[0], SlotSet)
    assert isinstance(events[1], SlotSet)

    assert events[0].key == "cuisine"
    assert events[0].value == "chinese"

    assert events[1].key == "requested_slot"
    assert events[1].value == "people"
Ejemplo n.º 8
0
    def respond(sender_id):
        request_params = request_parameters()

        if 'query' in request_params:
            message = request_params.pop('query')
        elif 'q' in request_params:
            message = request_params.pop('q')
        else:
            return Response(jsonify(error="Invalid respond parameter "
                                    "specified."),
                            status=400,
                            mimetype="application/json")

        try:
            out = CollectingOutputChannel()
            responses = agent().handle_message(message,
                                               output_channel=out,
                                               sender_id=sender_id)
            return jsonify(responses)

        except Exception as e:
            logger.exception("Caught an exception during respond.")
            return Response(jsonify(error="Server failure. Error: {}"
                                    "".format(e)),
                            status=500,
                            content_type="application/json")
Ejemplo n.º 9
0
        def receive():
            payload = request.json
            sender_id = payload.get("sender", None)
            text = payload.get("message", None)
            out = CollectingOutputChannel()
            # processor.MessageProcessor.handleMessage
            on_new_message(UserMessage(text, out, sender_id))
            #tracker_store = TrackerStore()
            #tracker = tracker_store.get_or_create_tracker(sender_id)

            responses = [m for _, m in out.messages]
            recipes = []
            for response in responses:
                search_recipes = SearchUtil.search({
                    'q': response,
                    'portions': 2,
                    'fields': 'title,ingress,url,ingredients',
                    'size': 5
                })
                if len(search_recipes) > 0:
                    recipes.append(search_recipes)

            if len(recipes) > 0:
                return jsonify(recipes)
            else:
                return jsonify(responses)
Ejemplo n.º 10
0
    def respond(self, request, sender_id):
        request.setHeader('Content-Type', 'application/json')
        request_params = request_parameters(request)

        if 'query' in request_params:
            message = request_params.pop('query')
        elif 'q' in request_params:
            message = request_params.pop('q')
        else:
            request.setResponseCode(400)
            return json.dumps({"error": "Invalid respond parameter specified"})

        try:
            out = CollectingOutputChannel()
            responses = self.agent.handle_message(message,
                                                  output_channel=out,
                                                  sender_id=sender_id)
            request.setResponseCode(200)
            return json.dumps(responses)

        except Exception as e:
            request.setResponseCode(500)
            logger.error("Caught an exception during "
                         "respond: {}".format(e), exc_info=1)
            return json.dumps({"error": "{}".format(e)})
Ejemplo n.º 11
0
def test_remote_client(http_app, default_agent, tmpdir):
    model_path = tmpdir.join("persisted_model").strpath

    default_agent.persist(model_path)

    remote_agent = RemoteAgent.load(model_path, EndpointConfig(http_app))

    message = UserMessage("""/greet{"name":"Rasa"}""",
                          output_channel=CollectingOutputChannel())

    remote_agent.process_message(message)

    tracker = remote_agent.core_client.tracker_json("default")

    assert len(tracker.get("events")) == 6

    # listen
    assert tracker["events"][0]["name"] == "action_listen"
    # this should be the utterance
    assert tracker["events"][1]["text"] == """/greet{"name":"Rasa"}"""
    # set slot event
    assert tracker["events"][2]["value"] == "Rasa"
    # utter action
    assert tracker["events"][3]["name"] == "utter_greet"
    # this should be the bot utterance
    assert tracker["events"][4]["text"] == "hey there Rasa!"
    # listen
    assert tracker["events"][5]["name"] == "action_listen"
Ejemplo n.º 12
0
 def respond(self, request, sender_id):
     request.setHeader('Content-Type', 'application/json')
     request.setHeader('Access-Control-Allow-Origin', '*')
     request_params = request_parameters(request)
     if 'query' in request_params:
         message = request_params.pop('query')
     elif 'q' in request_params:
         message = request_params.pop('q')
     else:
         request.setResponseCode(400)
         return json.dumps({"error": "Invalid parse parameter specified"})
     try:
         parse_data = self.agent.start_message_handling(message, sender_id)
         out = CollectingOutputChannel()
         response_data = self.agent.handle_message(message,
                                                   output_channel=out,
                                                   sender_id=sender_id)
         response = low_confidence_filter(message, sender_id, parse_data,
                                          response_data)
         request.setResponseCode(200)
         return json.dumps(response)
     except Exception as e:
         request.setResponseCode(500)
         logger.error("Caught an exception during "
                      "parse: {}".format(e),
                      exc_info=1)
         return json.dumps({"error": "{}".format(e)})
Ejemplo n.º 13
0
    def respond(sender_id):
        request_params = request_parameters()

        if 'query' in request_params:
            message = request_params.get('query')
        elif 'q' in request_params:
            message = request_params.get('q')
        else:
            return Response(jsonify(error="Invalid respond parameter "
                                    "specified."),
                            status=400,
                            mimetype="application/json")

        try:
            # Set the output channel
            out = CollectingOutputChannel()
            # Fetches the appropriate bot response in a json format
            responses = agent().handle_message(message,
                                               output_channel=out,
                                               sender_id=sender_id)
            tracker = agent().tracker_store.get_or_create_tracker(sender_id)
            result = {
                "responses": responses,
                "confidence": tracker.latest_message.intent["confidence"],
                "alternatives": _get_alternatives(agent(), sender_id)
            }
            return jsonify(result)

        except Exception as e:
            logger.exception("Caught an exception during respond.")
            return Response(jsonify(error="Server failure. Error: {}"
                                    "".format(e)),
                            status=500,
                            content_type="application/json")
Ejemplo n.º 14
0
def test_travel_form():
    domain = TemplateDomain.load("data/test_domains/travel_form.yml")
    nlg = TemplatedNaturalLanguageGenerator(domain.templates)
    tracker_store = InMemoryTrackerStore(domain)
    out = CollectingOutputChannel()
    sender_id = "test-travel"
    dispatcher = Dispatcher(sender_id, out, nlg)
    tracker = tracker_store.get_or_create_tracker(sender_id)

    # first user utterance
    tracker.update(UserUttered("", intent={"name": "inform"}))
    events = ActionSearchTravel().run(dispatcher, tracker, domain)
    assert len(events) == 1
    assert isinstance(events[0], SlotSet)
    assert events[0].key == "requested_slot"
    assert events[0].value == "GPE_origin"
    tracker.update(events[0])

    # second user utterance
    entities = [{"entity": "GPE", "value": "Berlin"}]
    tracker.update(
        UserUttered("", intent={"name": "inform"}, entities=entities))
    events = ActionSearchTravel().run(dispatcher, tracker, domain)
    for e in events:
        print(e.as_story_string())
    assert len(events) == 2
    assert isinstance(events[0], SlotSet)
    assert events[0].key == "GPE_origin"
    assert events[0].value == "Berlin"
    assert events[1].key == "requested_slot"
    assert events[1].value == "GPE_destination"
Ejemplo n.º 15
0
def test_people_form():
    domain = TemplateDomain.load("data/test_domains/people_form.yml")
    nlg = TemplatedNaturalLanguageGenerator(domain.templates)
    tracker_store = InMemoryTrackerStore(domain)
    out = CollectingOutputChannel()
    sender_id = "test-people"
    dispatcher = Dispatcher(sender_id, out, nlg)
    tracker = tracker_store.get_or_create_tracker(sender_id)

    # first user utterance
    tracker.update(UserUttered("", intent={"name": "inform"}))
    events = ActionSearchPeople().run(dispatcher, tracker, domain)
    assert len(events) == 1
    assert isinstance(events[0], SlotSet)
    assert events[0].key == "requested_slot"
    assert events[0].value == "person_name"
    tracker.update(events[0])

    # second user utterance
    name = "Rasa Due"
    tracker.update(UserUttered(name, intent={"name": "inform"}))

    events = ActionSearchPeople().run(dispatcher, tracker, domain)
    assert len(events) == 1
    assert isinstance(events[0], SlotSet)

    assert events[0].key == "person_name"
    assert events[0].value == name
Ejemplo n.º 16
0
    def replace_last_intent(sender_id):
        """Replaces the last intent from the user in the tracker state with the one provided
        and executes the next actions."""

        request_params = request_parameters()

        if 'intent' in request_params:
            intent = request_params.get('intent')
        else:
            return Response(jsonify(error="No intent parameter specified."),
                            status=400,
                            mimetype="application/json")

        tracker = agent().tracker_store.get_or_create_tracker(sender_id)

        while len(tracker.events) > 0 and not isinstance(
                tracker.events[-1], UserUttered):
            tracker.events.pop()

        if len(tracker.events) == 0:
            logger.debug("No user utterance in history of tracker")
            return jsonify(tracker.current_state())

        user_utterance = tracker.events.pop()
        tracker.update(
            UserUttered(text=user_utterance.text,
                        intent={
                            "name": intent,
                            "confidence": 1.0,
                        },
                        entities=user_utterance.entities))

        out = CollectingOutputChannel()
        message = UserMessage(text=user_utterance.text,
                              output_channel=out,
                              sender_id=sender_id)
        processor = agent()._create_processor()
        processor._predict_and_execute_next_action(message, tracker)
        agent().tracker_store.save(tracker)

        response = {
            "responses": message.output_channel.messages,
            "confidence": 1,
            "alternatives": [],
        }

        # save utterance in training data for nlu
        file_path = "/app/nlu/" + os.environ[
            "RASA_NLU_PROJECT_NAME"] + "/user_input/" + intent + ".md"
        add_intent_definition = False
        if not os.path.exists(file_path):
            add_intent_definition = True

        with open(file_path, "a") as file:
            if add_intent_definition:
                file.write("## intent:" + intent + "\n")
            file.write("- " + user_utterance.text + "\n")

        return jsonify(response)
Ejemplo n.º 17
0
 def receive():
     payload = request.json
     sender_id = payload.get("sender", None)
     text = payload.get("message", None)
     out = CollectingOutputChannel()
     on_new_message(UserMessage(text, out, sender_id))
     responses = [m for _, m in out.messages]
     return jsonify(responses)
Ejemplo n.º 18
0
    def get_agent_response(self, message, sender_id):
        """Sends the user message and sender_id to Rasa Core and collects the agent response"""

        out = CollectingOutputChannel()
        response = self.agent.handle_message(message,
                                             output_channel=out,
                                             sender_id=sender_id)
        return response
def test_dispatcher_utter_buttons_from_domain_templ(capsys):
    domain_file = "examples/moodbot/domain.yml"
    domain = TemplateDomain.load(domain_file)
    bot = CollectingOutputChannel()
    dispatcher = Dispatcher("my-sender", bot, domain)
    dispatcher.utter_template("utter_greet")
    assert bot.messages[0][1] == "Hey! How are you?"
    assert bot.messages[1][1] == "1: great (great)"
    assert bot.messages[2][1] == "2: super sad (super sad)"
Ejemplo n.º 20
0
def test_dispatcher_utter_buttons_from_domain_templ(capsys):
    domain_file = "examples/restaurant_domain.yml"
    domain = TemplateDomain.load(domain_file)
    bot = CollectingOutputChannel()
    dispatcher = Dispatcher("my-sender", bot, domain)
    dispatcher.utter_template("utter_ask_price")
    assert bot.messages[0][1] == "in which price range?"
    assert bot.messages[1][1] == "1: cheap (cheap)"
    assert bot.messages[2][1] == "2: expensive (expensive)"
Ejemplo n.º 21
0
    def user_message_controller(self, message, sender_id):
        """Sends the user message and sender_id to Rasa Core and collects the parsed response and agent response"""

        parse_data = self.agent.start_message_handling(message, sender_id)
        out = CollectingOutputChannel()
        response_data = self.agent.handle_message(message,
                                                  output_channel=out,
                                                  sender_id=sender_id)
        return [parse_data, response_data]
Ejemplo n.º 22
0
 def receive():
     payload = json.loads(request.data)
     sender_id = payload["sender"]
     text = payload["message"]
     print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
     print(sender_id)
     print(text)
     out = CollectingOutputChannel()
     on_new_message(UserMessage(text, out, sender_id))
     responses = [m for _, m in out.messages]
     return jsonify(responses)
Ejemplo n.º 23
0
 def handleCoreQuery(self, msg):
     self.log("Core query {}".format(msg.topic))
     payload = json.loads(msg.payload.decode('utf-8'))
     print(payload)
     print('#######################')
     #print(payload.get('slots','fail'))
     if 'input' in payload:
         theId = payload.get('id')
         sessionId = payload.get('sessionId')
         siteId = payload.get('siteId', 'default')
         entities = []
         ## strip snips user id from entity name
         #intentNameParts = payload['intent']['intentName'].split('__')
         #print(intentNameParts)
         #if len(intentNameParts) > 1:
         #intentName = intentNameParts[1]
         ##intentName = '__'.join(intentNameParts)
         #if 'slots' in payload and payload['slots'] is not None:
         #for entity in payload['slots']:
         #entities.append({ "start": entity['range']['start'],"end": entity['range']['end'],"value": entity['rawValue'],"entity": entity['slotName']})
         ##output = {
         #"text": payload['input'],
         #"intent": {
         #"name": intentName,
         #"confidence": 1.0
         #},
         #"entities": entities
         #}
         #self.log("CORE HANDLER {}".format(json.dumps(output)))
         #message = json.dumps(output)
         print('IN')
         print(payload['input'])
         response = self.agentLoaded.handle_message(
             payload['input'], output_channel=CollectingOutputChannel())
         print("OUT")
         print(response)
         if response is not None and len(response) > 0:
             self.client.publish('hermes/tts/say',
                                 payload=json.dumps({
                                     "lang": self.lang,
                                     "sessionId": sessionId,
                                     "text": response[0],
                                     "siteId": siteId,
                                     "id": theId
                                 }),
                                 qos=0,
                                 retain=False)
             self.client.publish(
                 'hermes/dialogue/endSession',
                 json.dumps({
                     "sessionId": sessionId,
                     "siteId": siteId
                 }))
Ejemplo n.º 24
0
 def receive():
     if request.method == 'POST':
         data = request.get_json()
         query = data['query']
         sender_id = data['id']
     elif request.method == 'GET':
         query = request.args.get('q')
         sender_id = request.args.get('id')
     out = CollectingOutputChannel()
     on_new_message(UserMessage(query, out, sender_id))
     responses = [m for _, m in out.messages]
     return jsonify(responses)
Ejemplo n.º 25
0
def test_dispatcher_template_invalid_vars():
    domain = TemplateDomain(
            [], [], [], {
                "my_made_up_template": [{
                    "text": "a template referencing an invalid {variable}."}]},
            [], [], None)
    bot = CollectingOutputChannel()
    dispatcher = Dispatcher("my-sender", bot, domain)
    dispatcher.utter_template("my_made_up_template")
    collected = dispatcher.output_channel.latest_output()
    assert collected['text'].startswith(
            "a template referencing an invalid {variable}.")
Ejemplo n.º 26
0
def test_dispatcher_utter_buttons_from_domain_templ():
    domain_file = "examples/moodbot/domain.yml"
    domain = TemplateDomain.load(domain_file)
    bot = CollectingOutputChannel()
    dispatcher = Dispatcher("my-sender", bot, domain)
    dispatcher.utter_template("utter_greet")
    assert len(bot.messages) == 1
    assert bot.messages[0]['text'] == "Hey! How are you?"
    assert bot.messages[0]['data'] == [
        {'payload': 'great', 'title': 'great'},
        {'payload': 'super sad', 'title': 'super sad'}
    ]
Ejemplo n.º 27
0
 def receive():
     payload = request.json
     print(request)
     print('in receive bot')
     print(payload)
     sender_id = payload.get("sender", None)
     text = payload.get("message", None)
     print(text)
     out = CollectingOutputChannel()
     on_new_message(UserMessage(text, out, sender_id))
     print(out.messages)
     ##            responses = [m for _, m in out.messages]
     return jsonify(out.messages)
Ejemplo n.º 28
0
 def receive():
     opjson = {}
     payload = request.json
     sender_id = 'jv'                                       # static for testing, otherwise payload.get()
     text = payload.get("message", None)
     out = CollectingOutputChannel()
     on_new_message(UserMessage(text, out, sender_id))
     opjson["text"] = out.messages[0]["text"]
     try:
         opjson["option"] = out.messages[1]["text"]          #Pass other data as response.
     except:
         pass
     return jsonify(opjson)
Ejemplo n.º 29
0
 def receive():
     payload = json.loads(str(request.data, "utf-8"))
     print(payload)
     print("responses")
     sender_id = payload.get("sender", None)
     text = payload.get("message", None)
     #sender_id = payload.get("sender", None)
     #text = payload.get("message", None)
     out = CollectingOutputChannel()
     on_new_message(UserMessage(text, out, sender_id))
     #responses = [m for _, m in out.messages]
     responses = [m["text"] for m in out.messages]
     print(str(responses).strip('[]').replace('},', '}'))
     return json.dumps(str(responses).strip('[]').replace('},', '}'))
Ejemplo n.º 30
0
 def receive():
     payload = request.json
     sender_id = payload.get("sender", None)
     text = payload.get("message", None)
     logger.info(text)
     out = CollectingOutputChannel()
     on_new_message(UserMessage(text, out, sender_id))
     # responses = [m for _, m in out.messages]
     if out.messages:
         logger.info(out.messages[0])
         logger.info(out.messages)
         logger.info(Response())
         return jsonify(out.messages[0]['text']), 200
     return jsonify('unable to process request'), 200
Ejemplo n.º 31
0
def test_message_processor(default_processor):
    out = CollectingOutputChannel()
    default_processor.handle_message(UserMessage('/greet{"name":"Core"}', out))
    assert {'recipient_id': 'default',
            'text': 'hey there Core!'} == out.latest_output()