How to utter a custom json which holds a template response inside it

Hi,

I’m designing my action server to reply back with the bot message and the slots. To do so, my action server responds in form of a custom json: {“message”: “Hi there”, “slots”: <list_of_slots>"} and I am using the default webhooks/rest/webhook endpoint to talk to my bot.

  1. Although this works as expected. I was wondering if this is the best practice to send back a) bot response message b) slot information back to the client all in one response? This design works for me right now, but I’m wondering if designing a custom connector (my own output channel) might have any benefits over sending back the custom jsons (maybe in future)?

  2. I do have a couple of utter_ask_SLOT utterances in my domain.yml which I would like to wrap them in my custom json response. How do I retrieve the text of those utter_ask_SLOTs in my custom action? I’ve used to do utter_template to dispatch the template. However, right now I do need to get the text, so that I can reformat it and put it as part of the “message” section of my custom json response.

    A workaround would be to do something like: domain.get("responses").get(f"utter_ask_{slot}")[0].get("text") to get the template utterance, but then I would need to replace my slots in the template and the whole thing seems a bit unusual. So if anyone knows what’s the best way to do it, I appreciate it.

Thanks!

Hi Behnam,

Your action server should be using the utter_message and SlotSet calls to send a message and set slots.

You can retrieve the user utterance text by calling tracker.latest_message.get("text"). You’ll find an example of this in our Sara demo bot here.

You can find metadata from the slot in the tracker event. There’s an example here.

You can get the value of a slot via tracker.get_slot("slot_name").

Greg

Hi Greg,

Thanks for the the information! I kind of figured out what I’ve been looking for. Thus, I’m also posting my proposed solution here in case anyone else is interested. However, if you or anyone else has better solutions/suggestions, please share!

To restate the situation with examples: I want to send back what bot has said (e.g. hi there) along with some slots that’s been set (e.g. person: behnam). Let’s assume my slots are already set and my response text is ready as well and all its remained is to dispatch a message (json) to user. I can achieve my desire by dispatching a json message which has everything needed (e.g. utter_message(json_message{"bot_message": hi there, "my_slots": {"person":"behnam"}).

Now in some cases instead of a fixed message like hi there, I had a predefined template utterance of utter_person which returns hi {person} and the person slot needs to be replaced with behnam to form a proper response text. In those cases, I was wondering what’s the easiest way to fill out the slots in a template utterance and then return the filled text. I looked at rasa source and found that there is an interpolate_text function in rasa.core.nlg code which can fill the slots in a template. So I’ve added that function to my action code and that gave me what I was looking for. Here is the sample code if anyone is interested:


def interpolate_text(template, values):
    """Interpolate values into templates with placeholders.
    Transform template tags from "{tag_name}" to "{0[tag_name]}" as described here:
    https://stackoverflow.com/questions/7934620/python-dots-in-the-name-of-variable-in-a-format-string#comment9695339_7934969
    Block characters, making sure not to allow:
    (a) newline in slot name
    (b) { or } in slot name
    Args:
        template: The piece of text that should be interpolated.
        values: A dictionary of keys and the values that those
            keys should be replaced with.
    Returns:
        The piece of text with any replacements made.
    """

    try:
        text = re.sub(r"{([^\n{}]+?)}", r"{0[\1]}", template)
        text = text.format(values)
        if "0[" in text:
            # regex replaced tag but format did not replace
            # likely cause would be that tag name was enclosed
            # in double curly and format func simply escaped it.
            # we don't want to return {0[SLOTNAME]} thus
            # restoring original value with { being escaped.
            return template.format({})

        return text
    except KeyError as e:
        logger.exception(
            f"Failed to fill utterance template '{template}'. "
            f"Tried to replace '{e.args[0]}' but could not find "
            f"a value for it. There is no slot with this "
            f"name nor did you pass the value explicitly "
            f"when calling the template. Return template "
            f"without filling the template. "
        )
        return template

def get_filled_template(domain, tracker, template_name):
    slots = tracker.slots
    # Let's pick the first one (you can randomly pick one as well if that's what you want)
    template_text = domain.get("responses").get(template_name)[0].get("text")
    res = interpolate_text(template_text, slots)
    return res

#### In custom actions, you can fill the template and include it in a json response.
template_name = f"utter_ask_{slot}"
template_text_filled = get_filled_template(domain=domain, tracker=tracker, template_name=template_name)
dispatcher.utter_message(json_message={"bot_message": template_text_filled, "my_slots": tracker.slots})
...

Hope this helps …

Also, a follow up question: if my desire is to send bot message together with tracker slots, then I’m using the /webhooks/rest/webhook endpoint and dispatch a json response from my action server. So there is no need (or benefits) to design custom output channels here, is that right? Also, what are the main use-cases for designing custom output channels?

Thanks!

Best, Behnam

1 Like