Filter buttons the user has clicked before

I want to loop three options until the user has clicked all three. Each clicked option should be filtered out.

For example:

utter_learn_more:
    - text: 'Learn more'
      buttons:
        - title: "Option A"
          payload: '/opta'
        - title: "Option B"
          payload: '/optb'
        - title: "Option C"
          payload: '/optc'

Bot: Learn more

  • A
  • B
  • C

User: A

Bot: Explains A

Bot: Learn more

  • B
  • C

User: C

Bot: Explains C

Bot: Learn More: B

Should I use custom actions or forms for this? And how would I do that?

1 Like

Hello @MartinJaskulla

To implement your logic I would define a list slot available_options that holds the currently available options with influence_conversation: true. Then Iā€™d write a custom action action_learn_more that shows buttons based on available_options, and have the buttons send the same intent with an entity that indicates the chosen option: payload: '/choose{{"option": "A"}}', etc. The entity should have a corresponding slot option that holds the option that the user chose. Finally, a second custom action explain_option gives the explanation to the option stored in the option slot and removes that option from available_options.

With this setup, I think you can define the entire logic in two rules: one that applies when available_options still contains something, and one that applies when available_slots is empty.

2 Likes

Thanks for your help!

Very new to Rasa and struggled quite a bit, so here is what I did if anyone else is interested:

domain.yml

utter_all_options:
- text: 'Choose option'
  buttons:
    - title: "Option A"
      payload: '/choose_option{"option": "utter_option_a"}'
    - title: "Option B"
      payload: '/choose_option{"option": "utter_option_b"}'
    - title: "Option C"
      payload: '/choose_option{"option": "utter_option_c"}'
utter_option_a:
- text: 'Option A'
utter_option_b:
- text: 'Option B'
utter_option_c:
- text: 'Option C'

slots:
      option:
        type: text
      options:
        type: list
        influence_conversation: true
        initial_value: [
          {title: 'Option A',payload: '/choose_option{"option": "utter_option_a"}'},
          {title: 'Option B',payload: '/choose_option{"option": "utter_option_b"}'},
          {title: 'Option C',payload: '/choose_option{"option": "utter_option_c"}'}
        ]
entities:
      - option

actions.py

class ActionChooseOption(Action):

def name(self) -> Text:
    return "action_choose_option"

def run(self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:
    options = tracker.get_slot('options')
    option = tracker.latest_message['entities'][0]['value']

    for i in range(len(options)):
        if option in options[i]['payload']:
            del options[i]
            break
    dispatcher.utter_message(template=option, buttons=options)
    return [SlotSet("options", options)]

@j.mosig I would be happy about any improvements. Any reasons why two actions are needed?

2 Likes

@j.mosig reply makes more sense to me now that I learned about:

  • categorical slots
  • calling more than one action in rules.yml

This allows me to list remaining options whenever I need to, and show remaining options only for some options (maybe option b wants to show sub options instead).

     rule: Show options in the beginning
     steps:
        - intent: /get_started
        - action: action_remaining_options

    - rule: Show remaining suggestions after option a
      steps:
        - intent: choose_option # Shows option a and removes it from options
        - action: action_choose_option
        - slot_was_set:
          - suggestion: option_a
        - action: action_remaining_options
2 Likes