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.
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)?
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 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?