Questionnaire Form Fills all slots after answering first question

I am trying to create a questionnaire form with a likert scale from 1-4. But after the first user input the form gets filled with the same answer for all the slots.

I am using Duckling to extract numbers and time. I know it works, since I am able to extract the time correctly.

Do I have to reset the numbers slot set by Duckling after each question? If so, how?

stories.md:

*ces
- ces_form
- form{“name”:“ces_form”}
- form{“name”: null}
- slot{“ces_results”: “Q1: 1 Q2: 2 Q3: 3 Q4: 4 Q5: 2 Q6: 3 Q7: 4 Q8: 1 Q9: 1 Q10: 4”}

domain.yml:

intents:
- ces
- choose_likert

entities:
- number
- time

slots:
ces_q1:
type: unfeaturized
auto_fill: false
ces_q2:…
number:
type: unfeaturized

responses:
utter_ask_ces_q1:
- text: “…”
utter_ask_ces_q2:
- text: “…”
etc. (10 questions)

actions:
- utter_ask_ces_q1
etc. (…q2 to q10)

forms:
- ces_form

nlu.md

## intent:choose_likert
- 1
- 2
- 3
- 4

actions.py

class CESForm(FormAction):

def name(self) -> Text:
    """Unique identifier of the form"""

    return "ces_form"

@staticmethod
def required_slots(tracker: Tracker) -> List[Text]:
    """A list of required slots that the form has to fill"""

    return ["ces_q1", "ces_q2", "ces_q3", "ces_q4", "ces_q5", "ces_q6", "ces_q7", "ces_q8", "ces_q9", "ces_q10"]

def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:

    return {
        "ces_q1": [
            self.from_entity(
                entity="number", intent=["choose_likert"]
            ),
        ],
        "ces_q2": [
            self.from_entity(
                entity="number", intent=["choose_likert"]
            ),
        ], etc....
    }

@staticmethod
def is_int(string: Text) -> bool:
    """Check if a string is an integer"""

    try:
        int(string)
        return True
    except ValueError:
        return False

def validate_ces_q1(
    self,
    value: Text,
    dispatcher: CollectingDispatcher,
    tracker: Tracker,
    domain: Dict[Text, Any],
) -> Dict[Text, Any]:
    """Validate Q1 value."""
    if self.is_int(value) and int(value) > 0 and int(value) < 5:
        return {"ces_q1": value}
    else:
        dispatcher.utter_message("Please write a number between 1 and 4")
        # validation failed, set slot to None
        return {"ces_q1": None}

def validate_ces_q2(
    self,
    value: Text,
    dispatcher: CollectingDispatcher,
    tracker: Tracker,
    domain: Dict[Text, Any],
) -> Dict[Text, Any]:
    """Validate Q2 value."""
    if self.is_int(value) and int(value) > 0 and int(value) < 5:
        return {"ces_q2": value}
    else:
        dispatcher.utter_message("Please write a number between 1 and 4")
        # validation failed, set slot to None
        return {"ces_q2": None} etc... (All the validate functions are the same)
    
def submit(
    self,
    dispatcher: CollectingDispatcher,
    tracker: Tracker,
    domain: Dict[Text, Any],
) -> List[Dict]:
    """Define what the form has to do
        after all required slots are filled"""
    q1 = tracker.get_slot("ces_q1")
    q2 = tracker.get_slot("ces_q2")
    q3 = tracker.get_slot("ces_q3")
    q4 = tracker.get_slot("ces_q4")
    q5 = tracker.get_slot("ces_q5")
    q6 = tracker.get_slot("ces_q6")
    q7 = tracker.get_slot("ces_q7")
    q8 = tracker.get_slot("ces_q8")
    q9 = tracker.get_slot("ces_q9")
    q10 = tracker.get_slot("ces_q10")
    resultList = ["Q1: {}".format(q1), "Q2: {}".format(q2), "Q3: {}".format(q3), "Q4: {}".format(q4), "Q5: {}".format(q5), "Q6: {}".format(q6), "Q7: {}".format(q7), "Q8: {}".format(q8), "Q9: {}".format(q9), "Q10: {}".format(q10)]
    return [SlotSet("ces_results", '\n'.join(resultList)), SlotSet("ces_q1", None), SlotSet("ces_q2", None), SlotSet("ces_q3", None), SlotSet("ces_q4", None), SlotSet("ces_q5", None), SlotSet("ces_q6", None), SlotSet("ces_q7", None), SlotSet("ces_q8", None), SlotSet("ces_q9", None), SlotSet("ces_q10", None)]

Output:

Bot loaded. Type a message and press enter (use ‘/stop’ to exit):
Your input → CES
Please indicate how often in the past week have you felt this way (type a number between 1-4):
I was bothered by things that usually do not bother me
1: rarely (less than 1 day)
2: sometimes (1-2 days)
3: occasionally (3-4 days)
4: all the time (5-7 days)
Your input → 2
(Here it should ask the next question but instead immediately jumps to the submit method and sets all the values to the first input)
Q1: 2
Q2: 2
Q3: 2
Q4: 2
Q5: 2
Q6: 2
Q7: 2
Q8: 2
Q9: 2
Q10: 2

Well I fixed it myself by changing the slot_mapping to:

def slot_mappings(self) → Dict[Text, Union[Dict, List[Dict]]]:

return {
“ces_q1”: [
self.from_text(),
],
“ces_q2”: [
self.from_text(),
], etc…

I guess the Duckling “number” entity and slot are now useless, since I am validating the text in the validate methods anyways.