Repository: RasaHQ/rasa
Pull request: Updated Rasa example bots for 2.0
**Proposed changes**:
- Updated Rasa example bots for 2.0

**Status (please check what you already did)**:
- [ ] added some tests for the functionality
- [ ] updated the documentation
- [ ] updated the changelog (please check [changelog](https://github.com/RasaHQ/rasa/tree/master/changelog) for instructions)
- [ ] reformat files using `black` (please check [Readme](https://github.com/RasaHQ/rasa#code-style) for instructions)


Problem statement:
Revisit example bots before RC
Before the RC, we need to make sure that all the example bots are:
- [x] written as rules/stories according to our docs recommendations
- [x] used as examples in our documentation. if they aren't, we should consider getting rid of them or adapt them to what we describe in the docs

Hints from issue discussion:
@melindaloubser1 can you take this one on? 
Yes
@b-quachtran did we check if e.g. the `rules` example bot is actually used anywhere? 
@akelad The rules doc has the example

`- intent: greet`
`- action: utter_greet`

that's in the rules example bot, but other than that no. 
does it refer to the rules bot example? I'm perfectly happy keeping the bot around, but we should refer to it from the docs, otherwise it's kind of random to have it in the examples
It does not, I'll get that updated today

Commit message: Merge branch '2.0.x' into rc_example_updates

Patch to review:
diff --git a/examples/concertbot/data/nlu.yaml b/examples/concertbot/data/nlu.yml
similarity index 100%
rename from examples/concertbot/data/nlu.yaml
rename to examples/concertbot/data/nlu.yml
diff --git a/examples/concertbot/data/rules.yaml b/examples/concertbot/data/rules.yml
similarity index 100%
rename from examples/concertbot/data/rules.yaml
rename to examples/concertbot/data/rules.yml
diff --git a/examples/concertbot/data/stories.yaml b/examples/concertbot/data/stories.yml
similarity index 100%
rename from examples/concertbot/data/stories.yaml
rename to examples/concertbot/data/stories.yml
diff --git a/examples/formbot/actions/actions.py b/examples/formbot/actions/actions.py
index 8ab8310083b8..a0485c68f17c 100644
--- a/examples/formbot/actions/actions.py
+++ b/examples/formbot/actions/actions.py
@@ -1,140 +1,103 @@
 from typing import Dict, Text, Any, List, Union
 
-from rasa_sdk import Tracker
+from rasa_sdk import Action, Tracker
 from rasa_sdk.executor import CollectingDispatcher
 from rasa_sdk.forms import FormAction
+from rasa_sdk.events import (
+    SlotSet,
+    EventType,
+)
 
+class ValidateRestaurantForm(Action):
+    def name(self) -> Text:
+        return "validate_restaurant_form"
+
+    async def run(self, dispatcher, tracker, domain) -> List[EventType]:
+        extracted_slots = tracker.slots_to_validate()
+        validation_events = []
+
+        for slot_name, slot_value in extracted_slots.items():
+            if slot_name == "cuisine":
+                validation_events.append(validate_cuisine(slot_value, dispatcher))
+            elif slot_name == "num_people":
+                validation_events.append(validate_num_people(slot_value, dispatcher))
+            elif slot_name == "outdoor_seating":
+                validation_events.append(
+                    validate_outdoor_seating(slot_value, dispatcher)
+                )
+            else:
+                validation_events.append(SlotSet(slot_name, slot_value))
 
-class RestaurantForm(FormAction):
-    """Example of a custom form action."""
+        return validation_events
 
-    def name(self) -> Text:
-        """Unique identifier of the form."""
-
-        return "restaurant_form"
-
-    @staticmethod
-    def required_slots(tracker: Tracker) -> List[Text]:
-        """A list of required slots that the form has to fill."""
-
-        return ["cuisine", "num_people", "outdoor_seating", "preferences", "feedback"]
-
-    def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:
-        """A dictionary to map required slots to
-            - an extracted entity
-            - intent: value pairs
-            - a whole message
-        or a list of them, where a first match will be picked."""
-
-        return {
-            "cuisine": self.from_entity(entity="cuisine", not_intent="chitchat"),
-            "num_people": [
-                self.from_entity(
-                    entity="number", intent=["inform", "request_restaurant"]
-                ),
-            ],
-            "outdoor_seating": [
-                self.from_entity(entity="seating"),
-                self.from_intent(intent="affirm", value=True),
-                self.from_intent(intent="deny", value=False),
-            ],
-            "preferences": [
-                self.from_intent(intent="deny", value="no additional preferences"),
-                self.from_text(not_intent="affirm"),
-            ],
-            "feedback": [self.from_entity(entity="feedback"), self.from_text()],
-        }
-
-    @staticmethod
-    def cuisine_db() -> List[Text]:
-        """Database of supported cuisines."""
-
-        return [
-            "caribbean",
-            "chinese",
-            "french",
-            "greek",
-            "indian",
-            "italian",
-            "mexican",
-        ]
-
-    @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_cuisine(
-        self,
-        value: Text,
-        dispatcher: CollectingDispatcher,
-        tracker: Tracker,
-        domain: Dict[Text, Any],
-    ) -> Dict[Text, Any]:
-        """Validate cuisine value."""
-
-        if value.lower() in self.cuisine_db():
-            # validation succeeded, set the value of the "cuisine" slot to value
-            return {"cuisine": value}
-        else:
-            dispatcher.utter_message(template="utter_wrong_cuisine")
-            # validation failed, set this slot to None, meaning the
-            # user will be asked for the slot again
-            return {"cuisine": None}
-
-    def validate_num_people(
-        self,
-        value: Text,
-        dispatcher: CollectingDispatcher,
-        tracker: Tracker,
-        domain: Dict[Text, Any],
-    ) -> Dict[Text, Any]:
-        """Validate num_people value."""
-
-        if self.is_int(value) and int(value) > 0:
-            return {"num_people": value}
+
+def cuisine_db() -> List[Text]:
+    """Database of supported cuisines."""
+
+    return [
+        "caribbean",
+        "chinese",
+        "french",
+        "greek",
+        "indian",
+        "italian",
+        "mexican",
+    ]
+
+
+def is_int(string: Text) -> bool:
+    """Check if a string is an integer."""
+
+    try:
+        int(string)
+        return True
+    except ValueError:
+        return False
+
+
+def validate_cuisine(value: Text, dispatcher: CollectingDispatcher) -> Dict[Text, Any]:
+    """Validate cuisine value."""
+
+    if value.lower() in cuisine_db():
+        # validation succeeded, set the value of the "cuisine" slot to value
+        return SlotSet("cuisine", value)
+    else:
+        dispatcher.utter_message(template="utter_wrong_cuisine")
+        # validation failed, set this slot to None, meaning the
+        # user will be asked for the slot again
+        return SlotSet("cuisine", None)
+
+
+def validate_num_people(
+    value: Text, dispatcher: CollectingDispatcher
+) -> Dict[Text, Any]:
+    """Validate num_people value."""
+
+    if is_int(value) and int(value) > 0:
+        return SlotSet("num_people", value)
+    else:
+        dispatcher.utter_message(template="utter_wrong_num_people")
+        # validation failed, set slot to None
+        return SlotSet("num_people", None)
+
+
+def validate_outdoor_seating(
+    value: Text, dispatcher: CollectingDispatcher
+) -> Dict[Text, Any]:
+    """Validate outdoor_seating value."""
+
+    if isinstance(value, str):
+        if "out" in value:
+            # convert "out..." to True
+            return SlotSet("outdoor_seating", True)
+        elif "in" in value:
+            # convert "in..." to False
+            return SlotSet("outdoor_seating", False)
         else:
-            dispatcher.utter_message(template="utter_wrong_num_people")
+            dispatcher.utter_message(template="utter_wrong_outdoor_seating")
             # validation failed, set slot to None
-            return {"num_people": None}
-
-    def validate_outdoor_seating(
-        self,
-        value: Text,
-        dispatcher: CollectingDispatcher,
-        tracker: Tracker,
-        domain: Dict[Text, Any],
-    ) -> Dict[Text, Any]:
-        """Validate outdoor_seating value."""
-
-        if isinstance(value, str):
-            if "out" in value:
-                # convert "out..." to True
-                return {"outdoor_seating": True}
-            elif "in" in value:
-                # convert "in..." to False
-                return {"outdoor_seating": False}
-            else:
-                dispatcher.utter_message(template="utter_wrong_outdoor_seating")
-                # validation failed, set slot to None
-                return {"outdoor_seating": None}
+            return SlotSet("outdoor_seating", None)
 
-        else:
-            # affirm/deny was picked up as True/False by the from_intent mapping
-            return {"outdoor_seating": value}
-
-    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."""
-
-        dispatcher.utter_message(template="utter_submit")
-        return []
+    else:
+        # affirm/deny was picked up as True/False by the from_intent mapping
+        return SlotSet("outdoor_seating", value)
diff --git a/examples/formbot/data/rules.yml b/examples/formbot/data/rules.yml
index 5c164694ae08..ca4638c240f3 100644
--- a/examples/formbot/data/rules.yml
+++ b/examples/formbot/data/rules.yml
@@ -15,17 +15,17 @@ rules:
       - intent: bot_challenge
       - action: utter_iamabot
 
+  - rule: Chitchat
+    steps:
+      - intent: chitchat
+      - action: utter_chitchat
+
   - rule: activate restaurant form
     steps:
       - intent: request_restaurant
       - action: restaurant_form
       - active_loop: restaurant_form
 
-  - rule: Chitchat
-    steps:
-      - intent: chitchat
-      - action: utter_chitchat
-
   - rule: submit form
     condition:
     - active_loop: restaurant_form
diff --git a/examples/formbot/domain.yml b/examples/formbot/domain.yml
index 34cdc4b7b37e..714ef2cddde3 100644
--- a/examples/formbot/domain.yml
+++ b/examples/formbot/domain.yml
@@ -1,3 +1,4 @@
+version: "2.0"
 intents:
   - request_restaurant:
       use_entities: []
@@ -83,35 +84,38 @@ responses:
   utter_restart:
     - text: "restarted"
 
+actions:
+- validate_restaurant_form
+
 forms:
   restaurant_form:
-      cuisine:
-        - type: from_entity
-          entity: cuisine
-          not_intent: chitchat
-      num_people:
-        - type: from_entity
-          entity: number
-          intent: [inform, request_restaurant]
-      outdoor_seating:
-        - type: from_entity
-          entity: seating
-        - type: from_intent
-          intent: affirm
-          value: true
-        - type: from_intent
-          intent: deny
-          value: false
-      preferences:
-        - type: from_intent
-          intent: deny
-          value: no additional preferences
-        - type: from_text
-          not_intent: affirm
-      feedback:
-        - type: from_entity
-          entity: feedback
-        - type: from_text
+    cuisine:
+    - type: from_entity
+      entity: cuisine
+      not_intent: chitchat
+    num_people:
+    - type: from_entity
+      entity: number
+      intent: [inform, request_restaurant]
+    outdoor_seating:
+    - type: from_entity
+      entity: seating
+    - type: from_intent
+      intent: affirm
+      value: true
+    - type: from_intent
+      intent: deny
+      value: false
+    preferences:
+    - type: from_intent
+      intent: deny
+      value: no additional preferences
+    - type: from_text
+      not_intent: affirm
+    feedback:
+    - type: from_entity
+      entity: feedback
+    - type: from_text
 
 session_config:
   session_expiration_time: 60  # value in minutes
diff --git a/examples/formbot/tests/test_stories.yml b/examples/formbot/tests/test_stories.yml
index 10d26db64d27..31ceefc88cdb 100644
--- a/examples/formbot/tests/test_stories.yml
+++ b/examples/formbot/tests/test_stories.yml
@@ -11,6 +11,7 @@ stories:
   - action: restaurant_form
   - active_loop: restaurant_form
   - active_loop: null
+  - action: utter_submit
   - action: utter_slots_values
   - user: |
       thanks
@@ -33,6 +34,7 @@ stories:
     intent: inform
   - action: restaurant_form
   - active_loop: null
+  - action: utter_submit
   - action: utter_slots_values
   - user: |
       thanks
@@ -56,6 +58,7 @@ stories:
   - action: utter_chitchat
   - action: restaurant_form
   - active_loop: null
+  - action: utter_submit
   - action: utter_slots_values
   - user: |
       thanks
diff --git a/examples/knowledgebasebot/data/nlu.yaml b/examples/knowledgebasebot/data/nlu.yml
similarity index 100%
rename from examples/knowledgebasebot/data/nlu.yaml
rename to examples/knowledgebasebot/data/nlu.yml
diff --git a/examples/knowledgebasebot/data/rules.yaml b/examples/knowledgebasebot/data/rules.yml
similarity index 100%
rename from examples/knowledgebasebot/data/rules.yaml
rename to examples/knowledgebasebot/data/rules.yml
diff --git a/examples/knowledgebasebot/data/stories.yml b/examples/knowledgebasebot/data/stories.yml
new file mode 100644
index 000000000000..10bf942ac8b2
--- /dev/null
+++ b/examples/knowledgebasebot/data/stories.yml
@@ -0,0 +1,8 @@
+version: "2.0"
+stories:
+  - story: greet and then query knowledge base
+    steps:
+    - intent: greet
+    - action: utter_greet
+    - intent: query_knowledge_base
+    - action: action_query_knowledge_base
\ No newline at end of file
diff --git a/examples/reminderbot/data/stories.yml b/examples/reminderbot/data/stories.yml
new file mode 100644
index 000000000000..9ce6000320a3
--- /dev/null
+++ b/examples/reminderbot/data/stories.yml
@@ -0,0 +1,8 @@
+version: "2.0"
+stories:
+  - story: Happy path
+    steps:
+    - intent: greet
+    - action: utter_what_can_do
+    - intent: ask_remind_call
+    - action: action_set_reminder
\ No newline at end of file
diff --git a/examples/rules/data/rules.yml b/examples/rules/data/rules.yml
index a56dff3508e8..46722d2f6acc 100644
--- a/examples/rules/data/rules.yml
+++ b/examples/rules/data/rules.yml
@@ -44,11 +44,6 @@ rules:
   - intent: ask_possibilities
   - action: utter_list_possibilities
 
-- rule: Another FAQ example
-  steps:
-  - intent: switch_faq
-  - action: action_switch_faq
-
 - rule: FAQ simple
   condition:
   - slot_was_set:


Code review comments:
diff_hunk: @@ -0,0 +1,8 @@
+version: "2.0"
+stories:
+  - story: Happy path
+    steps:
+    - intent: greet
+    - action: utter_what_can_do
+    - intent: ask_remind_call
+    - action: action_set_reminder
path: examples/reminderbot/data/stories.yml
text: @user1:
```suggestion
    - action: action_set_reminder

```

diff_hunk: @@ -1,140 +1,103 @@
 from typing import Dict, Text, Any, List, Union
 
-from rasa_sdk import Tracker
+from rasa_sdk import Action, Tracker
 from rasa_sdk.executor import CollectingDispatcher
 from rasa_sdk.forms import FormAction
+from rasa_sdk.events import (
+    SlotSet,
+    EventType,
+)
 
+class ValidateRestaurantForm(Action):
+    def name(self) -> Text:
+        return "validate_restaurant_form"
+
+    async def run(self, dispatcher, tracker, domain) -> List[EventType]:
path: examples/formbot/actions/actions.py
text: @user1:
```suggestion
    async def run(self, dispatcher: CollectingDispatcher, tracker: Tracker, domain: Domain) -> List[EventType]:
```

I think `Domain` needs to be imported

diff_hunk: @@ -0,0 +1,8 @@
+version: "2.0"
+stories:
+  - story: greet and then query knowledge base
+    steps:
+    - intent: greet
+    - action: utter_greet
+    - intent: query_knowledge_base
+    - action: action_query_knowledge_base
path: examples/knowledgebasebot/data/stories.yml
text: @user1:
```suggestion
    - action: action_query_knowledge_base

```