From d804a9d06df1eea9ffbde7a2252225d9d3c9788b Mon Sep 17 00:00:00 2001 From: Alan Nichol Date: Wed, 12 Sep 2018 14:54:57 +0200 Subject: [PATCH 001/112] wip form using activated/ deactivated events and parse_data slots attribute --- rasa_core_sdk/events.py | 18 ++++++ rasa_core_sdk/forms.py | 129 +++++----------------------------------- 2 files changed, 33 insertions(+), 114 deletions(-) diff --git a/rasa_core_sdk/events.py b/rasa_core_sdk/events.py index eb1ef0ee8..088184057 100644 --- a/rasa_core_sdk/events.py +++ b/rasa_core_sdk/events.py @@ -139,3 +139,21 @@ def AgentUttered(text=None, data=None, timestamp=None): "data": data, "timestamp": timestamp, } + +# noinspection PyPep8Naming +def FormActivated(name, timestamp=None): + return { + "event": "form", + "timestamp": timestamp, + "name": name, + "value": True, + } + + +# noinspection PyPep8Naming +def FormDeactivated(timestamp=None): + return { + "event": "form", + "timestamp": timestamp, + "value": False, + } diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 3e499b938..41173aa2d 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -9,7 +9,7 @@ from typing import List, Text from rasa_core_sdk import Action, Tracker -from rasa_core_sdk.events import SlotSet, EventType +from rasa_core_sdk.events import SlotSet, EventType, FormActivated, FormDeactivated logger = logging.getLogger(__name__) @@ -19,75 +19,6 @@ FORM_SLOT_NAME = "requested_slot" -class FormField(object): - - # noinspection PyMethodMayBeStatic - def validate(self, value): - """Check if extracted value for a requested slot is valid. - - Users should override this to implement custom validation logic, - returning None indicates a negative validation result, and the slot - will not be set. - """ - return value - - -class EntityFormField(FormField): - - def __init__(self, entity_name, slot_name): - self.entity_name = entity_name - self.slot_name = slot_name - - def extract(self, tracker): - # type: (Tracker) -> List[EventType] - - value = next(tracker.get_latest_entity_values(self.entity_name), None) - validated = self.validate(value) - if validated is not None: - return [SlotSet(self.slot_name, validated)] - else: - return [] - - -class BooleanFormField(FormField): - """A form field that prompts the user for a yes or no answer. - The interpreter should map positive and negative answers to - the intents ``affirm_intent`` and ``deny_intent``. - """ - - def __init__(self, slot_name, affirm_intent, deny_intent): - self.slot_name = slot_name - self.affirm_intent = affirm_intent - self.deny_intent = deny_intent - - def extract(self, tracker): - # type: (Tracker) -> List[EventType] - - intent = tracker.latest_message.get("intent", {}).get("name") - if intent == self.affirm_intent: - value = True - elif intent == self.deny_intent: - value = False - else: - return [] - - return [SlotSet(self.slot_name, value)] - - -class FreeTextFormField(FormField): - - def __init__(self, slot_name): - self.slot_name = slot_name - - def extract(self, tracker): - # type: (Tracker) -> List[EventType] - - validated = self.validate(tracker.latest_message.get("text")) - if validated is not None: - return [SlotSet(self.slot_name, validated)] - return [] - - class FormAction(Action): def name(self): # type: () -> Text @@ -95,7 +26,7 @@ def name(self): raise NotImplementedError - RANDOMIZE = True + RANDOMIZE = False @staticmethod def required_fields(): @@ -106,55 +37,25 @@ def should_request_slot(tracker, slot_name): existing_val = tracker.get_slot(slot_name) return existing_val is None - def get_other_slots(self, tracker): - # type: (Tracker) -> List[EventType] - - requested_slot = tracker.get_slot(FORM_SLOT_NAME) - - requested_entity = None - for f in self.required_fields(): - if f.slot_name == requested_slot: - requested_entity = getattr(f, 'entity_name', None) - - slot_events = [] - extracted_entities = {requested_entity} - - for f in self.required_fields(): - if (isinstance(f, EntityFormField) - and f.slot_name != requested_slot - and f.entity_name not in extracted_entities): - slot_events.extend(f.extract(tracker)) - extracted_entities.add(f.entity_name) - return slot_events + def activate_if_required(self, tracker): + if tracker.active_form == self.name(): + return [] + else: + return [FormActivated(self.name())] def get_requested_slot(self, tracker): - # type: (Tracker) -> List[EventType] - - requested_slot = tracker.get_slot(FORM_SLOT_NAME) - required = self.required_fields() - - if self.RANDOMIZE: - random.shuffle(required) - - if requested_slot is None: - return [] - else: - fields = [f - for f in required - if f.slot_name == requested_slot] + events = [] + if tracker.latest_message.data["intent"] == "extracted_slot": + for key, val in tracker.latest_message.data["slots"].items(): + events.append([SlotSet(key, val)]) - if len(fields) == 1: - return fields[0].extract(tracker) - else: - logger.debug("Unable to extract value " - "for requested slot: {}".format(requested_slot)) - return [] + return events def run(self, dispatcher, tracker, domain): - events = (self.get_requested_slot(tracker) + - self.get_other_slots(tracker)) + events = (self.activate_if_required(tracker) + + self.get_requested_slot(tracker)) temp_tracker = tracker.copy() for e in events: @@ -173,7 +74,7 @@ def run(self, dispatcher, tracker, domain): # there is nothing more to request, so we can submit events_from_submit = self.submit(dispatcher, temp_tracker, domain) or [] - return events + events_from_submit + return events + events_from_submit + [FormDeactivated()] def submit(self, dispatcher, tracker, domain): raise NotImplementedError( From a8a38565f6c68eb22e2df71a8f8b11febc2cdc54 Mon Sep 17 00:00:00 2001 From: Alan Nichol Date: Wed, 12 Sep 2018 16:09:05 +0200 Subject: [PATCH 002/112] add slots endpoint with example --- .gitignore | 5 +- examples/moodbot/Makefile | 2 +- examples/moodbot/myslots.py | 15 ++++++ rasa_core_sdk/endpoint.py | 27 +++++++++-- rasa_core_sdk/slots.py | 4 ++ rasa_core_sdk/validator.py | 97 +++++++++++++++++++++++++++++++++++++ 6 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 examples/moodbot/myslots.py create mode 100644 rasa_core_sdk/slots.py create mode 100644 rasa_core_sdk/validator.py diff --git a/.gitignore b/.gitignore index f389d14d7..2ee1d933c 100644 --- a/.gitignore +++ b/.gitignore @@ -103,4 +103,7 @@ venv.bak/ # mypy .mypy_cache/ .idea/ -examples/moodbot/models/ \ No newline at end of file +examples/moodbot/models/ + +# emacs +*~ \ No newline at end of file diff --git a/examples/moodbot/Makefile b/examples/moodbot/Makefile index 157088bbe..ac6394b63 100644 --- a/examples/moodbot/Makefile +++ b/examples/moodbot/Makefile @@ -17,7 +17,7 @@ run-core: python -m rasa_core.run --nlu models/nlu/current --core models/dialogue --debug --endpoints endpoints.yml run-actions: - python -m rasa_core_sdk.endpoint --actions myactions + python -m rasa_core_sdk.endpoint --actions myactions --slots myslots run: make run-actions& diff --git a/examples/moodbot/myslots.py b/examples/moodbot/myslots.py new file mode 100644 index 000000000..6f10f9a72 --- /dev/null +++ b/examples/moodbot/myslots.py @@ -0,0 +1,15 @@ + +from rasa_core_sdk.slots import Slot + +class MyFreeTextSlot(Slot): + + def name(self): + return "my_free_text_slot" + + def validate(self, parse_data): + new_parse_data = parse_data.copy() + del new_parse_data["entities"] + text = parse_data.get("text") + new_parse_data["slots"] = {} + new_parse_data["slots"][self.name()] = text + return new_parse_data diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index bcb3162ee..4e1eb494a 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -11,6 +11,7 @@ from gevent.pywsgi import WSGIServer from rasa_core_sdk.executor import ActionExecutor +from rasa_core_sdk.validator import InputValidator DEFAULT_SERVER_PORT = 5055 @@ -39,12 +40,18 @@ def create_argument_parser(): default=None, help="name of action package to be loaded" ) - + parser.add_argument( + '--slots', + type=str, + default=None, + help="name of action package to be loaded" + ) return parser def endpoint_app(cors_origins=None, - action_package_name=None + action_package_name=None, + slot_package_name=None ): app = Flask(__name__) @@ -53,6 +60,8 @@ def endpoint_app(cors_origins=None, executor = ActionExecutor() executor.register_package(action_package_name) + validator = InputValidator() + validator.register_package(slot_package_name) CORS(app, resources={r"/*": {"origins": cors_origins}}) @@ -73,6 +82,17 @@ def webhook(): return jsonify(response) + @app.route("/validate", + methods=['POST', 'OPTIONS']) + @cross_origin() + def validate(): + """Webhook to retrieve action calls.""" + data = request.json + response = validator.validate(data) + + return jsonify(response) + + return app @@ -86,7 +106,8 @@ def webhook(): logger.info("Starting action endpoint server...") edp_app = endpoint_app(cors_origins=cmdline_args.cors, - action_package_name=cmdline_args.actions) + action_package_name=cmdline_args.actions, + slot_package_name=cmdline_args.slots) http_server = WSGIServer(('0.0.0.0', cmdline_args.port), edp_app) diff --git a/rasa_core_sdk/slots.py b/rasa_core_sdk/slots.py new file mode 100644 index 000000000..7865e85c6 --- /dev/null +++ b/rasa_core_sdk/slots.py @@ -0,0 +1,4 @@ + + +class Slot(object): + pass diff --git a/rasa_core_sdk/validator.py b/rasa_core_sdk/validator.py new file mode 100644 index 000000000..08d503a79 --- /dev/null +++ b/rasa_core_sdk/validator.py @@ -0,0 +1,97 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import importlib +import inspect +import logging +import pkgutil + +import six +from typing import Text, List, Dict, Any + +from rasa_core_sdk import utils, Action, Tracker +from rasa_core_sdk.slots import Slot + +logger = logging.getLogger(__name__) + + +class InputValidator(object): + def __init__(self): + self.validators = {} + + def register_slot(self, slot): + if inspect.isclass(slot): + if slot.__module__.startswith("rasa_core."): + logger.warning("Skipping built in Slot {}.".format(action)) + return + else: + slot = slot() + if isinstance(slot, Slot): + self.register_function(slot.name(), slot.validate) + else: + raise Exception("You can only register instances or subclasses of " + "type Slot. If you want to directly register " + "a function, use `register_function` instead.") + + + def register_function(self, name, f): + logger.info("Registered function for '{}'.".format(name)) + valid_keys = utils.arguments_of(f) + if len(valid_keys) < 1: + raise Exception("You can only register functions that take " + "1 parameters as arguments. Your function accepts only {} " + "parameters.".format(len(valid_keys))) + self.validators[name] = f + + def _import_submodules(self, package, recursive=True): + """ Import all submodules of a module, recursively, including + subpackages + + :param package: package (name or actual module) + :type package: str | module + :rtype: dict[str, types.ModuleType] + """ + if isinstance(package, six.string_types): + package = importlib.import_module(package) + if not getattr(package, '__path__', None): + return + + results = {} + for loader, name, is_pkg in pkgutil.walk_packages(package.__path__): + full_name = package.__name__ + '.' + name + results[full_name] = importlib.import_module(full_name) + if recursive and is_pkg: + self._import_submodules(full_name) + + def register_package(self, package): + try: + self._import_submodules(package) + except ImportError: + logger.exception("Failed to register package '{}'." + "".format(package)) + + slots = utils.all_subclasses(Slot) + + for slot in slots: + if (not slot.__module__.startswith("rasa_core.") and + not slot.__module__.startswith("rasa_core_sdk.")): + self.register_slot(slot) + + + def validate(self, data): + slot_name = data.get("slot") + if slot_name: + logger.debug("Received request to validate '{}'".format(slot_name)) + validator = self.validators.get(slot_name) + if not validator: + raise Exception("No registered Validator found for name '{}'." + "".format(slot_name)) + + parse_data = data.get("parse_data") + new_parse_data = validator(parse_data) + logger.debug("Successfully validated '{}'".format(slot_name)) + return new_parse_data + else: + logger.warning("Received an action call without an action.") From e2b00ccb7327df1f202f4ba1cf5346f1c5faa43c Mon Sep 17 00:00:00 2001 From: Alan Nichol Date: Wed, 12 Sep 2018 17:16:48 +0200 Subject: [PATCH 003/112] wip example form bot --- examples/formbot/Makefile | 30 ++++++++++++++++++++++++++++++ examples/formbot/actions.py | 16 ++++++++++++++++ examples/formbot/data/stories.md | 7 +++++++ examples/formbot/domain.yml | 21 +++++++++++++++++++++ rasa_core_sdk/forms.py | 8 ++++---- 5 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 examples/formbot/Makefile create mode 100644 examples/formbot/actions.py create mode 100644 examples/formbot/data/stories.md create mode 100644 examples/formbot/domain.yml diff --git a/examples/formbot/Makefile b/examples/formbot/Makefile new file mode 100644 index 000000000..3bd3f08a1 --- /dev/null +++ b/examples/formbot/Makefile @@ -0,0 +1,30 @@ +help: + @echo " train-nlu" + @echo " Train the natural language understanding using Rasa NLU." + @echo " train-core" + @echo " Train a dialogue model using Rasa core." + @echo " run" + @echo " Spin up a server that serves as an endpoint to receive facebook user messages." + +train-nlu: + python -m rasa_nlu.train -c nlu_model_config.yml --fixed_model_name current \ + --data ./data/nlu.md --path models/ --project nlu + +train-core: + python -m rasa_core.train -s data/stories.md -d domain.yml -o models/dialogue --epochs 1 --debug + +run-core: + python -m rasa_core.run --nlu models/nlu/current --core models/dialogue --debug --endpoints endpoints.yml + +run-actions: + python -m rasa_core_sdk.endpoint --actions myactions --slots myslots + +run: + make run-actions& + make run-core + +evaluate-nlu: + python -m rasa_nlu.evaluate --model models/nlu/current --data ./data/nlu.md + +visualize: + python -m rasa_core.visualize -s data/stories.md -d domain.yml -o story_graph.png diff --git a/examples/formbot/actions.py b/examples/formbot/actions.py new file mode 100644 index 000000000..96df454e0 --- /dev/null +++ b/examples/formbot/actions.py @@ -0,0 +1,16 @@ +from rasa_core_sdk import FormAction + + +class RestaurantForm(FormAction): + + def name(self): + return "restaurant_form" + + @staticmethod + def required_slots(): + return ["cuisine", "num_people"] + + def submit(self, dispatcher, tracker, domain): + dispatcher.utter_message("done!") + return [] + diff --git a/examples/formbot/data/stories.md b/examples/formbot/data/stories.md new file mode 100644 index 000000000..54a09d5aa --- /dev/null +++ b/examples/formbot/data/stories.md @@ -0,0 +1,7 @@ +## happy path +* request_restaurant + - activate_restaurant + - slot{"num_people": 4, "cuisine": "chinese"} +* thank + - utter_noworries + diff --git a/examples/formbot/domain.yml b/examples/formbot/domain.yml new file mode 100644 index 000000000..c13098388 --- /dev/null +++ b/examples/formbot/domain.yml @@ -0,0 +1,21 @@ +intents: + - thank + - request_restaurant + +slots: + cuisine: + type: text + num_people: + type: text + +templates: + - utter_ask_num_people: + text: "how many people?" + - utter_ask_cuisine: + text: "what cuisine?" + - utter_noworries: + text: "you are welcome :)" + +actions: + utter_noworries + restaurant_form \ No newline at end of file diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 41173aa2d..068222df2 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -61,14 +61,14 @@ def run(self, dispatcher, tracker, domain): for e in events: temp_tracker.slots[e["name"]] = e["value"] - for field in self.required_fields(): - if self.should_request_slot(temp_tracker, field.slot_name): + for field in self.required_slots(): + if self.should_request_slot(temp_tracker, slot): dispatcher.utter_template( - "utter_ask_{}".format(field.slot_name), + "utter_ask_{}".format(slot), tracker) - events.append(SlotSet(FORM_SLOT_NAME, field.slot_name)) + events.append(SlotSet(FORM_SLOT_NAME, slot)) return events # there is nothing more to request, so we can submit From 5e34d00498f64bea1a675d328d65a1203a30bd32 Mon Sep 17 00:00:00 2001 From: akelad Date: Wed, 12 Sep 2018 18:17:06 +0200 Subject: [PATCH 004/112] update forms --- examples/formbot/Makefile | 2 +- examples/formbot/actions.py | 3 +- examples/formbot/data/stories.md | 3 +- examples/formbot/domain.yml | 18 ++++++----- examples/formbot/endpoints.yml | 2 ++ examples/formbot/models/dialogue/domain.json | 13 ++++++++ examples/formbot/models/dialogue/domain.yml | 30 +++++++++++++++++ .../featurizer.json | 1 + .../memorized_turns.json | 15 +++++++++ .../models/dialogue/policy_metadata.json | 27 ++++++++++++++++ examples/formbot/train.py | 32 +++++++++++++++++++ rasa_core_sdk/events.py | 4 +-- rasa_core_sdk/forms.py | 14 ++++---- 13 files changed, 142 insertions(+), 22 deletions(-) create mode 100644 examples/formbot/endpoints.yml create mode 100644 examples/formbot/models/dialogue/domain.json create mode 100644 examples/formbot/models/dialogue/domain.yml create mode 100644 examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json create mode 100644 examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json create mode 100644 examples/formbot/models/dialogue/policy_metadata.json create mode 100644 examples/formbot/train.py diff --git a/examples/formbot/Makefile b/examples/formbot/Makefile index 3bd3f08a1..fafe7c47b 100644 --- a/examples/formbot/Makefile +++ b/examples/formbot/Makefile @@ -17,7 +17,7 @@ run-core: python -m rasa_core.run --nlu models/nlu/current --core models/dialogue --debug --endpoints endpoints.yml run-actions: - python -m rasa_core_sdk.endpoint --actions myactions --slots myslots + python -m rasa_core_sdk.endpoint --actions actions run: make run-actions& diff --git a/examples/formbot/actions.py b/examples/formbot/actions.py index 96df454e0..d03d8eaf7 100644 --- a/examples/formbot/actions.py +++ b/examples/formbot/actions.py @@ -1,4 +1,4 @@ -from rasa_core_sdk import FormAction +from rasa_core_sdk.forms import FormAction class RestaurantForm(FormAction): @@ -13,4 +13,3 @@ def required_slots(): def submit(self, dispatcher, tracker, domain): dispatcher.utter_message("done!") return [] - diff --git a/examples/formbot/data/stories.md b/examples/formbot/data/stories.md index 54a09d5aa..2be128eb9 100644 --- a/examples/formbot/data/stories.md +++ b/examples/formbot/data/stories.md @@ -1,7 +1,6 @@ ## happy path * request_restaurant - - activate_restaurant + - restaurant_form - slot{"num_people": 4, "cuisine": "chinese"} * thank - utter_noworries - diff --git a/examples/formbot/domain.yml b/examples/formbot/domain.yml index c13098388..d6a6150fc 100644 --- a/examples/formbot/domain.yml +++ b/examples/formbot/domain.yml @@ -7,15 +7,17 @@ slots: type: text num_people: type: text + requested_slot: + type: unfeaturized templates: - - utter_ask_num_people: - text: "how many people?" - - utter_ask_cuisine: - text: "what cuisine?" - - utter_noworries: - text: "you are welcome :)" + utter_ask_num_people: + - text: "how many people?" + utter_ask_cuisine: + - text: "what cuisine?" + utter_noworries: + - text: "you are welcome :)" actions: - utter_noworries - restaurant_form \ No newline at end of file + - utter_noworries + - restaurant_form diff --git a/examples/formbot/endpoints.yml b/examples/formbot/endpoints.yml new file mode 100644 index 000000000..e8d74ad61 --- /dev/null +++ b/examples/formbot/endpoints.yml @@ -0,0 +1,2 @@ +action_endpoint: + url: http://localhost:5055/webhook diff --git a/examples/formbot/models/dialogue/domain.json b/examples/formbot/models/dialogue/domain.json new file mode 100644 index 000000000..78568c1e5 --- /dev/null +++ b/examples/formbot/models/dialogue/domain.json @@ -0,0 +1,13 @@ +{ + "states": [ + "intent_request_restaurant", + "intent_thank", + "slot_cuisine_0", + "slot_num_people_0", + "prev_action_listen", + "prev_action_restart", + "prev_action_default_fallback", + "prev_utter_noworries", + "prev_restaurant_form" + ] +} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/domain.yml b/examples/formbot/models/dialogue/domain.yml new file mode 100644 index 000000000..111d587c2 --- /dev/null +++ b/examples/formbot/models/dialogue/domain.yml @@ -0,0 +1,30 @@ +%YAML 1.1 +--- +actions: +- utter_noworries +- restaurant_form +config: + store_entities_as_slots: true +entities: [] +intents: +- thank: + use_entities: true +- request_restaurant: + use_entities: true +slots: + cuisine: + initial_value: null + type: rasa_core.slots.TextSlot + num_people: + initial_value: null + type: rasa_core.slots.TextSlot + requested_slot: + initial_value: null + type: rasa_core.slots.UnfeaturizedSlot +templates: + utter_ask_cuisine: + - text: what cuisine? + utter_ask_num_people: + - text: how many people? + utter_noworries: + - text: you are welcome :) diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json new file mode 100644 index 000000000..9b5b69869 --- /dev/null +++ b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json @@ -0,0 +1 @@ +{"py/object": "rasa_core.featurizers.MaxHistoryTrackerFeaturizer", "max_history": 5, "remove_duplicates": true, "state_featurizer": {"py/object": "rasa_core.featurizers.SingleStateFeaturizer", "slot_feature_len": null, "user_feature_len": null}, "use_intent_probabilities": false} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json new file mode 100644 index 000000000..6ee575031 --- /dev/null +++ b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json @@ -0,0 +1,15 @@ +{ + "max_history": 5, + "lookup": { + "eJyLzivNydFRwCSra2MBj0wJzQ==": 0, + "eJyLzivNydFRQCara4E4M68kNa8kvii1sDS1GEQXlySWFiXmlVgpGOoZ6CgUFKWWxScml2Tm58XnZBYDFYMlamMBwE8dUg==": 4, + "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESRGtFiMan5RflQqWKc/JL4pNLM4sz81LjDZAF80pz4wtS8wtyYOK1sQCHakC1": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBcqVZyTXxKfXJpZnJmXGm+ALJhXmhtfkJpfkAMTR7KpJCMxLxu/u0gzOhYABg1djQ==": 3, + "eJytjs0KwkAMhF9lH6BIe/VVRMJSUgxuk21+6qH47lZR7ElEPA18M5nMYbk2aSF2ZAfFKdDuap5DM/s+dbu2SVVxhtw7CUMhW8MP4+vTN4VBdHxaVsShDzJihHYLOUaoKLW8+OaTnzKfP+/6W3W4owLLRVQJ7Yfy4w0n5Xt0": 0, + "eJytkE0KwkAMha8yByhSt15FJAwlxeA0GfNTF+Ld/UGxK7G0q8D3kvce2V+JHdlB8Rxoz2meQzP7Lm03bZOq4gi5cxKGQvZYfgm3Jv13+qXQiw5vyYo4dEFGjNBOIccAFaWWD58k+THz6Xev1azDHRVYLqJKaIvM5z53Xs7hDs9Pnqo=": 4, + "eJy1kU0KwkAMRq8yByhSt15FJAwlpcFpMuanLop3V8HCrNxYV4H3wQvJd16JHdlB8RZo72meQzP7KR0PfZeq4tJQGEXnT2RFHIYgI0boW8gxQ0WpZeOPLm2bfMp8beV5cBKGQvbK91WHOyqw3EWV0H6Sf//Qfkf8u4nLE9MVrog=": 0, + "eJy1kEsKwkAQRK8yBwiSbL2KhGYIHWycdE/6Exfi3VUw6EY30VXBK6ii6nAhdmQHP2Y+7VO3a5tUFRfIg5MwFLK7/zSsiMMQZMQI7TvkmKCi1LLya5M+Roc7KrCcRZXQNoUrzoH2UPMcmtn/M+J7z4vCKDr99q6tI/obVNSn/Q==": 3, + "eJy1kU0KwkAMRq8yByhSt15FJAwlxdBpMuanLsS724LFgkvtKvDykUeS84PYkR38mnk4peOhbVJVnCDcUYHlLqqE9m5ZEYcuyIgR2i3kGKGi1LLyZ5PW4Yq3QFuqeQ7N7FtT7pyEoZDN4R09Hwq96PiT6etc/1tih09cXtIBqQw=": 0, + "eJy1kM0KwkAMhF9lH6BIvfoqUsJSUhrcJmt+6qH47ipYFLwIpaeBL0OGmfNC7MgOitdAe6l5Ds3sp3Q8tE2qijPk3kkYCtnT/D5YEYc+yIgR2m/IMUFFqWXl9yb9l/OhMIhOm5J8zHzZp8TP63BHBZabqBLajgttLdE9AAG6rZg=": 4 + } +} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json new file mode 100644 index 000000000..7ba99bc09 --- /dev/null +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -0,0 +1,27 @@ +{ + "action_fingerprints": { + "action_listen": { + "slots": [] + }, + "restaurant_form": { + "slots": [ + "cuisine", + "num_people" + ] + }, + "utter_noworries": { + "slots": [] + } + }, + "rasa_core": "0.11.3", + "python": "3.6.6", + "max_histories": [ + 5, + null + ], + "ensemble_name": "rasa_core.policies.ensemble.SimplePolicyEnsemble", + "policy_names": [ + "rasa_core.policies.memoization.MemoizationPolicy", + "rasa_core.policies.form_policy.FormPolicy" + ] +} \ No newline at end of file diff --git a/examples/formbot/train.py b/examples/formbot/train.py new file mode 100644 index 000000000..ebc728a62 --- /dev/null +++ b/examples/formbot/train.py @@ -0,0 +1,32 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from rasa_core import utils +from rasa_core.agent import Agent +from rasa_core.policies.keras_policy import KerasPolicy +from rasa_core.policies.memoization import MemoizationPolicy +from rasa_core.policies.form_policy import FormPolicy + +if __name__ == '__main__': + utils.configure_colored_logging(loglevel="INFO") + + training_data_file = 'data/stories.md' + model_path = 'models/dialogue' + + agent = Agent("domain.yml", + policies=[MemoizationPolicy(), + FormPolicy()]) + + training_data = agent.load_data(training_data_file) + + agent.train( + training_data, + augmentation_factor=50, + epochs=200, + batch_size=10, + validation_split=0.2 + ) + + agent.persist(model_path) diff --git a/rasa_core_sdk/events.py b/rasa_core_sdk/events.py index 088184057..57d2523b0 100644 --- a/rasa_core_sdk/events.py +++ b/rasa_core_sdk/events.py @@ -143,7 +143,7 @@ def AgentUttered(text=None, data=None, timestamp=None): # noinspection PyPep8Naming def FormActivated(name, timestamp=None): return { - "event": "form", + "event": "form_activated", "timestamp": timestamp, "name": name, "value": True, @@ -153,7 +153,7 @@ def FormActivated(name, timestamp=None): # noinspection PyPep8Naming def FormDeactivated(timestamp=None): return { - "event": "form", + "event": "form_deactivated", "timestamp": timestamp, "value": False, } diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 068222df2..52f5c7f07 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -38,16 +38,16 @@ def should_request_slot(tracker, slot_name): return existing_val is None def activate_if_required(self, tracker): - if tracker.active_form == self.name(): - return [] - else: - return [FormActivated(self.name())] + # if tracker.active_form == self.name(): + # return [] + # else: + return [FormActivated(self.name())] def get_requested_slot(self, tracker): events = [] - if tracker.latest_message.data["intent"] == "extracted_slot": - for key, val in tracker.latest_message.data["slots"].items(): + if tracker.latest_message["intent"] == "extracted_slot": + for key, val in tracker.latest_message["slots"].items(): events.append([SlotSet(key, val)]) return events @@ -61,7 +61,7 @@ def run(self, dispatcher, tracker, domain): for e in events: temp_tracker.slots[e["name"]] = e["value"] - for field in self.required_slots(): + for slot in self.required_slots(): if self.should_request_slot(temp_tracker, slot): dispatcher.utter_template( From 31b6fc951438195343ba11fa6f6d3e61d3f2851c Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 13 Sep 2018 11:29:52 +0200 Subject: [PATCH 005/112] fix form events --- examples/formbot/domain.yml | 5 +++++ examples/formbot/models/dialogue/domain.json | 3 +++ examples/formbot/models/dialogue/domain.yml | 6 ++++- .../models/dialogue/policy_metadata.json | 2 +- rasa_core_sdk/events.py | 7 +++--- rasa_core_sdk/forms.py | 22 +++++++++---------- 6 files changed, 27 insertions(+), 18 deletions(-) diff --git a/examples/formbot/domain.yml b/examples/formbot/domain.yml index d6a6150fc..f191a2893 100644 --- a/examples/formbot/domain.yml +++ b/examples/formbot/domain.yml @@ -1,6 +1,11 @@ intents: - thank - request_restaurant + - inform + +entities: + - cuisine + - num_people slots: cuisine: diff --git a/examples/formbot/models/dialogue/domain.json b/examples/formbot/models/dialogue/domain.json index 78568c1e5..ec60a0604 100644 --- a/examples/formbot/models/dialogue/domain.json +++ b/examples/formbot/models/dialogue/domain.json @@ -1,7 +1,10 @@ { "states": [ + "intent_inform", "intent_request_restaurant", "intent_thank", + "entity_cuisine", + "entity_num_people", "slot_cuisine_0", "slot_num_people_0", "prev_action_listen", diff --git a/examples/formbot/models/dialogue/domain.yml b/examples/formbot/models/dialogue/domain.yml index 111d587c2..1b39b888a 100644 --- a/examples/formbot/models/dialogue/domain.yml +++ b/examples/formbot/models/dialogue/domain.yml @@ -5,12 +5,16 @@ actions: - restaurant_form config: store_entities_as_slots: true -entities: [] +entities: +- cuisine +- num_people intents: - thank: use_entities: true - request_restaurant: use_entities: true +- inform: + use_entities: true slots: cuisine: initial_value: null diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index 7ba99bc09..c1ddf4de5 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -14,7 +14,7 @@ } }, "rasa_core": "0.11.3", - "python": "3.6.6", + "python": "3.6.5", "max_histories": [ 5, null diff --git a/rasa_core_sdk/events.py b/rasa_core_sdk/events.py index 57d2523b0..d0e58464c 100644 --- a/rasa_core_sdk/events.py +++ b/rasa_core_sdk/events.py @@ -140,13 +140,13 @@ def AgentUttered(text=None, data=None, timestamp=None): "timestamp": timestamp, } + # noinspection PyPep8Naming -def FormActivated(name, timestamp=None): +def FormActivated(form_name, timestamp=None): return { "event": "form_activated", + "form_name": form_name, "timestamp": timestamp, - "name": name, - "value": True, } @@ -155,5 +155,4 @@ def FormDeactivated(timestamp=None): return { "event": "form_deactivated", "timestamp": timestamp, - "value": False, } diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 52f5c7f07..737257874 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -5,18 +5,17 @@ from __future__ import unicode_literals import logging -import random -from typing import List, Text +from typing import Text -from rasa_core_sdk import Action, Tracker -from rasa_core_sdk.events import SlotSet, EventType, FormActivated, FormDeactivated +from rasa_core_sdk import Action +from rasa_core_sdk.events import SlotSet, FormActivated, FormDeactivated logger = logging.getLogger(__name__) # this slot is used to store information needed # to do the form handling, needs to be part # of the domain -FORM_SLOT_NAME = "requested_slot" +REQUESTED_SLOT = "requested_slot" class FormAction(Action): @@ -29,8 +28,8 @@ def name(self): RANDOMIZE = False @staticmethod - def required_fields(): - return [] + def required_slots(): + raise NotImplementedError @staticmethod def should_request_slot(tracker, slot_name): @@ -44,7 +43,6 @@ def activate_if_required(self, tracker): return [FormActivated(self.name())] def get_requested_slot(self, tracker): - events = [] if tracker.latest_message["intent"] == "extracted_slot": for key, val in tracker.latest_message["slots"].items(): @@ -54,8 +52,7 @@ def get_requested_slot(self, tracker): def run(self, dispatcher, tracker, domain): - events = (self.activate_if_required(tracker) + - self.get_requested_slot(tracker)) + events = self.get_requested_slot(tracker) temp_tracker = tracker.copy() for e in events: @@ -68,8 +65,9 @@ def run(self, dispatcher, tracker, domain): "utter_ask_{}".format(slot), tracker) - events.append(SlotSet(FORM_SLOT_NAME, slot)) - return events + events.append(SlotSet(REQUESTED_SLOT, slot)) + + return events + self.activate_if_required(tracker) # there is nothing more to request, so we can submit events_from_submit = self.submit(dispatcher, temp_tracker, domain) or [] From 842895f26e6a98b34433d7af3e12199ad8dc7f04 Mon Sep 17 00:00:00 2001 From: akelad Date: Thu, 13 Sep 2018 12:58:04 +0200 Subject: [PATCH 006/112] add active_form to tracker --- examples/formbot/data/stories.md | 1 - examples/formbot/domain.yml | 4 ++-- examples/formbot/models/dialogue/domain.json | 2 -- examples/formbot/models/dialogue/domain.yml | 4 ++-- .../memorized_turns.json | 15 +++++++-------- .../formbot/models/dialogue/policy_metadata.json | 8 +------- rasa_core_sdk/__init__.py | 9 ++++++--- rasa_core_sdk/forms.py | 16 +++++++--------- 8 files changed, 25 insertions(+), 34 deletions(-) diff --git a/examples/formbot/data/stories.md b/examples/formbot/data/stories.md index 2be128eb9..beb77158f 100644 --- a/examples/formbot/data/stories.md +++ b/examples/formbot/data/stories.md @@ -1,6 +1,5 @@ ## happy path * request_restaurant - restaurant_form - - slot{"num_people": 4, "cuisine": "chinese"} * thank - utter_noworries diff --git a/examples/formbot/domain.yml b/examples/formbot/domain.yml index f191a2893..3a48fda3e 100644 --- a/examples/formbot/domain.yml +++ b/examples/formbot/domain.yml @@ -9,9 +9,9 @@ entities: slots: cuisine: - type: text + type: unfeaturized num_people: - type: text + type: unfeaturized requested_slot: type: unfeaturized diff --git a/examples/formbot/models/dialogue/domain.json b/examples/formbot/models/dialogue/domain.json index ec60a0604..44cb01ad2 100644 --- a/examples/formbot/models/dialogue/domain.json +++ b/examples/formbot/models/dialogue/domain.json @@ -5,8 +5,6 @@ "intent_thank", "entity_cuisine", "entity_num_people", - "slot_cuisine_0", - "slot_num_people_0", "prev_action_listen", "prev_action_restart", "prev_action_default_fallback", diff --git a/examples/formbot/models/dialogue/domain.yml b/examples/formbot/models/dialogue/domain.yml index 1b39b888a..6afce74f6 100644 --- a/examples/formbot/models/dialogue/domain.yml +++ b/examples/formbot/models/dialogue/domain.yml @@ -18,10 +18,10 @@ intents: slots: cuisine: initial_value: null - type: rasa_core.slots.TextSlot + type: rasa_core.slots.UnfeaturizedSlot num_people: initial_value: null - type: rasa_core.slots.TextSlot + type: rasa_core.slots.UnfeaturizedSlot requested_slot: initial_value: null type: rasa_core.slots.UnfeaturizedSlot diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json index 6ee575031..40f4563c7 100644 --- a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json +++ b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json @@ -3,13 +3,12 @@ "lookup": { "eJyLzivNydFRwCSra2MBj0wJzQ==": 0, "eJyLzivNydFRQCara4E4M68kNa8kvii1sDS1GEQXlySWFiXmlVgpGOoZ6CgUFKWWxScml2Tm58XnZBYDFYMlamMBwE8dUg==": 4, - "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESRGtFiMan5RflQqWKc/JL4pNLM4sz81LjDZAF80pz4wtS8wtyYOK1sQCHakC1": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBcqVZyTXxKfXJpZnJmXGm+ALJhXmhtfkJpfkAMTR7KpJCMxLxu/u0gzOhYABg1djQ==": 3, - "eJytjs0KwkAMhF9lH6BIe/VVRMJSUgxuk21+6qH47lZR7ElEPA18M5nMYbk2aSF2ZAfFKdDuap5DM/s+dbu2SVVxhtw7CUMhW8MP4+vTN4VBdHxaVsShDzJihHYLOUaoKLW8+OaTnzKfP+/6W3W4owLLRVQJ7Yfy4w0n5Xt0": 0, - "eJytkE0KwkAMha8yByhSt15FJAwlxeA0GfNTF+Ld/UGxK7G0q8D3kvce2V+JHdlB8Rxoz2meQzP7Lm03bZOq4gi5cxKGQvZYfgm3Jv13+qXQiw5vyYo4dEFGjNBOIccAFaWWD58k+THz6Xev1azDHRVYLqJKaIvM5z53Xs7hDs9Pnqo=": 4, - "eJy1kU0KwkAMRq8yByhSt15FJAwlpcFpMuanLop3V8HCrNxYV4H3wQvJd16JHdlB8RZo72meQzP7KR0PfZeq4tJQGEXnT2RFHIYgI0boW8gxQ0WpZeOPLm2bfMp8beV5cBKGQvbK91WHOyqw3EWV0H6Sf//Qfkf8u4nLE9MVrog=": 0, - "eJy1kEsKwkAQRK8yBwiSbL2KhGYIHWycdE/6Exfi3VUw6EY30VXBK6ii6nAhdmQHP2Y+7VO3a5tUFRfIg5MwFLK7/zSsiMMQZMQI7TvkmKCi1LLya5M+Roc7KrCcRZXQNoUrzoH2UPMcmtn/M+J7z4vCKDr99q6tI/obVNSn/Q==": 3, - "eJy1kU0KwkAMRq8yByhSt15FJAwlxdBpMuanLsS724LFgkvtKvDykUeS84PYkR38mnk4peOhbVJVnCDcUYHlLqqE9m5ZEYcuyIgR2i3kGKGi1LLyZ5PW4Yq3QFuqeQ7N7FtT7pyEoZDN4R09Hwq96PiT6etc/1tih09cXtIBqQw=": 0, - "eJy1kM0KwkAMhF9lH6BIvfoqUsJSUhrcJmt+6qH47ipYFLwIpaeBL0OGmfNC7MgOitdAe6l5Ds3sp3Q8tE2qijPk3kkYCtnT/D5YEYc+yIgR2m/IMUFFqWXl9yb9l/OhMIhOm5J8zHzZp8TP63BHBZabqBLajgttLdE9AAG6rZg=": 4 + "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESRGtFiMan5RflQjTHAgBtMjHH": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc0lGYl42AatiAWukP7E=": 3, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LRNZdkJOZlE20VhurSkpLUovi8/PL8oqLM1GLSnIbFqlgAm+9i8g==": 4, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHQWY5pKMxLxsZPWJySWZ+XnxOZnFQHmCqktLSlKL4vPyy/OLijJTi9HV43caXqvI8FUsACHwY+I=": 0, + "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEoti09MLsnMz4vPySwGyoMlanUUcKouLSlJLYrPyy/PLyrKTC1GV1+UWliaWgyii0sSS4sS80qItgq/VoRofFp+US5Bd2KxKhYA0aldVw==": 3, + "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEotiy8tKUktis/LL88vKspMLQZL1eoowNQXpRaWphaD6OKSxNKixLwSZM2JySWZ+XnxOZnFQMWkaUWIxqflF+Wia8ZwJ16riPNVLAAVVl5m": 0 } } \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index c1ddf4de5..5bbb50d64 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -3,18 +3,12 @@ "action_listen": { "slots": [] }, - "restaurant_form": { - "slots": [ - "cuisine", - "num_people" - ] - }, "utter_noworries": { "slots": [] } }, "rasa_core": "0.11.3", - "python": "3.6.5", + "python": "3.6.6", "max_histories": [ 5, null diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index f74830757..01fef89cb 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -30,10 +30,11 @@ def from_dict(cls, state): state.get("latest_message", {}), state.get("events"), state.get("paused"), - state.get("followup_action")) + state.get("followup_action"), + state.get("active_form")) def __init__(self, sender_id, slots, - latest_message, events, paused, followup_action): + latest_message, events, paused, followup_action, active_form): """Initialize the tracker.""" # list of previously seen events @@ -52,6 +53,7 @@ def __init__(self, sender_id, slots, # "entities": UserUttered.entities, # "text": text} self.latest_message = latest_message if latest_message else {} + self.active_form = active_form def current_state(self, should_include_events=False): # type: (bool) -> Dict[Text, Any] @@ -142,7 +144,8 @@ def copy(self): copy.deepcopy(self.latest_message), copy.deepcopy(self.events), self._paused, - self.followup_action) + self.followup_action, + self.active_form) class Action(object): diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 737257874..a6075c298 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -37,27 +37,25 @@ def should_request_slot(tracker, slot_name): return existing_val is None def activate_if_required(self, tracker): - # if tracker.active_form == self.name(): - # return [] - # else: - return [FormActivated(self.name())] + if tracker.active_form == self.name(): + return [] + else: + return [FormActivated(self.name())] def get_requested_slot(self, tracker): events = [] - if tracker.latest_message["intent"] == "extracted_slot": - for key, val in tracker.latest_message["slots"].items(): - events.append([SlotSet(key, val)]) + if tracker.latest_message["intent"].get("name") == "extracted_slot": + for slot in tracker.latest_message["slots"]: + events.append(SlotSet(slot['name'], slot['value'])) return events def run(self, dispatcher, tracker, domain): events = self.get_requested_slot(tracker) - temp_tracker = tracker.copy() for e in events: temp_tracker.slots[e["name"]] = e["value"] - for slot in self.required_slots(): if self.should_request_slot(temp_tracker, slot): From a035cc951d79b91c19213e2a3b07706a87cdd8e3 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 13 Sep 2018 14:58:02 +0200 Subject: [PATCH 007/112] add chitchat story --- examples/formbot/data/stories.md | 13 +++++++++++-- examples/formbot/domain.yml | 4 ++++ examples/formbot/models/dialogue/domain.json | 4 +++- examples/formbot/models/dialogue/domain.yml | 5 +++++ .../policy_0_MemoizationPolicy/memorized_turns.json | 9 +++++++++ .../formbot/models/dialogue/policy_metadata.json | 2 +- rasa_core_sdk/forms.py | 3 ++- 7 files changed, 35 insertions(+), 5 deletions(-) diff --git a/examples/formbot/data/stories.md b/examples/formbot/data/stories.md index beb77158f..2619fb082 100644 --- a/examples/formbot/data/stories.md +++ b/examples/formbot/data/stories.md @@ -1,5 +1,14 @@ ## happy path * request_restaurant - - restaurant_form + - restaurant_form * thank - - utter_noworries + - utter_noworries + +## unhappy path +* request_restaurant + - restaurant_form +* chitchat + - utter_chitchat + - restaurant_form +* thank + - utter_noworries diff --git a/examples/formbot/domain.yml b/examples/formbot/domain.yml index 3a48fda3e..a260589dd 100644 --- a/examples/formbot/domain.yml +++ b/examples/formbot/domain.yml @@ -2,6 +2,7 @@ intents: - thank - request_restaurant - inform + - chitchat entities: - cuisine @@ -22,7 +23,10 @@ templates: - text: "what cuisine?" utter_noworries: - text: "you are welcome :)" + utter_chitchat: + - text: "chitchat" actions: - utter_noworries - restaurant_form + - utter_chitchat diff --git a/examples/formbot/models/dialogue/domain.json b/examples/formbot/models/dialogue/domain.json index 44cb01ad2..45b0d7b66 100644 --- a/examples/formbot/models/dialogue/domain.json +++ b/examples/formbot/models/dialogue/domain.json @@ -1,5 +1,6 @@ { "states": [ + "intent_chitchat", "intent_inform", "intent_request_restaurant", "intent_thank", @@ -9,6 +10,7 @@ "prev_action_restart", "prev_action_default_fallback", "prev_utter_noworries", - "prev_restaurant_form" + "prev_restaurant_form", + "prev_utter_chitchat" ] } \ No newline at end of file diff --git a/examples/formbot/models/dialogue/domain.yml b/examples/formbot/models/dialogue/domain.yml index 6afce74f6..039849cbd 100644 --- a/examples/formbot/models/dialogue/domain.yml +++ b/examples/formbot/models/dialogue/domain.yml @@ -3,6 +3,7 @@ actions: - utter_noworries - restaurant_form +- utter_chitchat config: store_entities_as_slots: true entities: @@ -15,6 +16,8 @@ intents: use_entities: true - inform: use_entities: true +- chitchat: + use_entities: true slots: cuisine: initial_value: null @@ -30,5 +33,7 @@ templates: - text: what cuisine? utter_ask_num_people: - text: how many people? + utter_chitchat: + - text: chitchat utter_noworries: - text: you are welcome :) diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json index 40f4563c7..6cc835fab 100644 --- a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json +++ b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json @@ -9,6 +9,15 @@ "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LRNZdkJOZlE20VhurSkpLUovi8/PL8oqLM1GLSnIbFqlgAm+9i8g==": 4, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHQWY5pKMxLxsZPWJySWZ+XnxOZnFQHmCqktLSlKL4vPyy/OLijJTi9HV43caXqvI8FUsACHwY+I=": 0, "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEoti09MLsnMz4vPySwGyoMlanUUcKouLSlJLYrPyy/PLyrKTC1GV1+UWliaWgyii0sSS4sS80qItgq/VoRofFp+US5Bd2KxKhYA0aldVw==": 3, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc3JGZklyRiIh22IBM5pA4w==": 5, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TUnZ2SWJGckEm8bNg2lJSWpRagytbEA2hxQbg==": 4, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LRNSdnZJYkZyQSbxs2DaUlJalFqDIEdGB1VSwAYUlhPQ==": 0, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHQWY5uSMzJLkjEQULYnJJZn5efE5mcVAJcRoKC0pSS1ClSGgg4CrSjIS87IJOCkWAP10W6I=": 3, + "eJyLrs7MK0nNK4lPzsgsSc5ILLFSMNQz0FEoKEoti09MLsnMz4vPySwGKgFL1Ooo4NNQWlKSWoQqQ0BHUWpxSWJpUSJQOi2/KBddS0lGYl420U7CUA1xT15+eX5RUWZqMUR9LAB4SlYm": 0, + "eJyLrs7MK0nNK4lPzsgsSc5ILLFSMNQz0FEoKEotiy8tKUktQpWp1VHAp6MotbgksbQoESidll+Ui66lJCMxLxtZfWJySWZ+XnxOZjFQnqBqiHvy8svzi4oyU4vR1RelFpYC7UdyBAGrYgEm0FqP": 4, + "eJyLrs7MK0nNK4lPzsgsSc5ILLFSMNQz0FEoKEotiy9KLS5JLC1KBEqn5RflgqVqdRRgWkoyEvOykdUnJpdk5ufF52QWA+UJqi4tKUktis/LL88vKspMLUZXX5RaWAq0H8kRRFuFXytWX8UCAHusX3k=": 0, + "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEoti09MLsnMz4vPySwGyoMlanUUcKouLSlJLYrPyy/PLyrKTC1GV1+UWliaWgyii0sSS4sS80qItgq/VoRofFp+US665uSMzJLkjERCtsUCAPKRXok=": 5, + "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEotiy8tKUktis/LL88vKspMLQZL1eoowNQXpRaWphaD6OKSxNKixLwSZM2JySWZ+XnxOZnFQMWkaUWIxqflF+Wia07OyCxJzkgk3jZsGiB+Q5GpjQUAAidgKg==": 4, "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEotiy8tKUktis/LL88vKspMLQZL1eoowNQXpRaWphaD6OKSxNKixLwSZM2JySWZ+XnxOZnFQMWkaUWIxqflF+Wia8ZwJ16riPNVLAAVVl5m": 0 } } \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index 5bbb50d64..13d38c158 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -8,7 +8,7 @@ } }, "rasa_core": "0.11.3", - "python": "3.6.6", + "python": "3.6.5", "max_histories": [ 5, null diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index a6075c298..4adbbc988 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -44,7 +44,8 @@ def activate_if_required(self, tracker): def get_requested_slot(self, tracker): events = [] - if tracker.latest_message["intent"].get("name") == "extracted_slot": + intent = tracker.latest_message["intent"].get("name") + if intent == "extracted_slot": for slot in tracker.latest_message["slots"]: events.append(SlotSet(slot['name'], slot['value'])) From 1703efd01b5330af35e947734839ea316667ad8c Mon Sep 17 00:00:00 2001 From: Alan Nichol Date: Thu, 13 Sep 2018 17:09:02 +0200 Subject: [PATCH 008/112] remove unused stuff --- rasa_core_sdk/endpoint.py | 13 ----- rasa_core_sdk/slots.py | 4 -- rasa_core_sdk/validator.py | 97 -------------------------------------- 3 files changed, 114 deletions(-) delete mode 100644 rasa_core_sdk/slots.py delete mode 100644 rasa_core_sdk/validator.py diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index 4e1eb494a..7bdf69055 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -11,7 +11,6 @@ from gevent.pywsgi import WSGIServer from rasa_core_sdk.executor import ActionExecutor -from rasa_core_sdk.validator import InputValidator DEFAULT_SERVER_PORT = 5055 @@ -60,8 +59,6 @@ def endpoint_app(cors_origins=None, executor = ActionExecutor() executor.register_package(action_package_name) - validator = InputValidator() - validator.register_package(slot_package_name) CORS(app, resources={r"/*": {"origins": cors_origins}}) @@ -82,16 +79,6 @@ def webhook(): return jsonify(response) - @app.route("/validate", - methods=['POST', 'OPTIONS']) - @cross_origin() - def validate(): - """Webhook to retrieve action calls.""" - data = request.json - response = validator.validate(data) - - return jsonify(response) - return app diff --git a/rasa_core_sdk/slots.py b/rasa_core_sdk/slots.py deleted file mode 100644 index 7865e85c6..000000000 --- a/rasa_core_sdk/slots.py +++ /dev/null @@ -1,4 +0,0 @@ - - -class Slot(object): - pass diff --git a/rasa_core_sdk/validator.py b/rasa_core_sdk/validator.py deleted file mode 100644 index 08d503a79..000000000 --- a/rasa_core_sdk/validator.py +++ /dev/null @@ -1,97 +0,0 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import importlib -import inspect -import logging -import pkgutil - -import six -from typing import Text, List, Dict, Any - -from rasa_core_sdk import utils, Action, Tracker -from rasa_core_sdk.slots import Slot - -logger = logging.getLogger(__name__) - - -class InputValidator(object): - def __init__(self): - self.validators = {} - - def register_slot(self, slot): - if inspect.isclass(slot): - if slot.__module__.startswith("rasa_core."): - logger.warning("Skipping built in Slot {}.".format(action)) - return - else: - slot = slot() - if isinstance(slot, Slot): - self.register_function(slot.name(), slot.validate) - else: - raise Exception("You can only register instances or subclasses of " - "type Slot. If you want to directly register " - "a function, use `register_function` instead.") - - - def register_function(self, name, f): - logger.info("Registered function for '{}'.".format(name)) - valid_keys = utils.arguments_of(f) - if len(valid_keys) < 1: - raise Exception("You can only register functions that take " - "1 parameters as arguments. Your function accepts only {} " - "parameters.".format(len(valid_keys))) - self.validators[name] = f - - def _import_submodules(self, package, recursive=True): - """ Import all submodules of a module, recursively, including - subpackages - - :param package: package (name or actual module) - :type package: str | module - :rtype: dict[str, types.ModuleType] - """ - if isinstance(package, six.string_types): - package = importlib.import_module(package) - if not getattr(package, '__path__', None): - return - - results = {} - for loader, name, is_pkg in pkgutil.walk_packages(package.__path__): - full_name = package.__name__ + '.' + name - results[full_name] = importlib.import_module(full_name) - if recursive and is_pkg: - self._import_submodules(full_name) - - def register_package(self, package): - try: - self._import_submodules(package) - except ImportError: - logger.exception("Failed to register package '{}'." - "".format(package)) - - slots = utils.all_subclasses(Slot) - - for slot in slots: - if (not slot.__module__.startswith("rasa_core.") and - not slot.__module__.startswith("rasa_core_sdk.")): - self.register_slot(slot) - - - def validate(self, data): - slot_name = data.get("slot") - if slot_name: - logger.debug("Received request to validate '{}'".format(slot_name)) - validator = self.validators.get(slot_name) - if not validator: - raise Exception("No registered Validator found for name '{}'." - "".format(slot_name)) - - parse_data = data.get("parse_data") - new_parse_data = validator(parse_data) - logger.debug("Successfully validated '{}'".format(slot_name)) - return new_parse_data - else: - logger.warning("Received an action call without an action.") From 10af8bb569c2f95cfccfedf58075556fce4d2b91 Mon Sep 17 00:00:00 2001 From: Alan Nichol Date: Thu, 13 Sep 2018 18:22:58 +0200 Subject: [PATCH 009/112] return 400 bad request if action run raises InputValidationError --- rasa_core_sdk/__init__.py | 21 +++++++++++++++++++++ rasa_core_sdk/endpoint.py | 10 +++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index 01fef89cb..d10e6cf66 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -157,6 +157,12 @@ def name(self): raise NotImplementedError + def validate(self, tracker): + # type: (Tracker) -> Dict[Text, Any] + """"Validate the user input.""" + + raise InputValidationError("validation not implemented") + def run(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[dict] """Execute the side effects of this action.""" @@ -165,3 +171,18 @@ def run(self, dispatcher, tracker, domain): def __str__(self): return "Action('{}')".format(self.name()) + + +class InputValidationError(Exception): + """Raised when form cannot validate user input + + Attributes: + message -- explanation why form could not validate user input + """ + + def __init__(self, message): + self.message = message + + def __str__(self): + return self.message + diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index 7bdf69055..0493c190c 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -11,6 +11,7 @@ from gevent.pywsgi import WSGIServer from rasa_core_sdk.executor import ActionExecutor +from rasa_core_sdk import InputValidationError DEFAULT_SERVER_PORT = 5055 @@ -75,7 +76,14 @@ def health(): def webhook(): """Webhook to retrieve action calls.""" action_call = request.json - response = executor.run(action_call) + try: + response = executor.run(action_call) + except InputValidationError as e: + result = {"error": "could not validate input."} + result["payload"] = action_call + response = jsonify(result) + response.status_code = 400 + return response return jsonify(response) From 9bddaa3e20eca216c551d4a2d055d4f50d26420b Mon Sep 17 00:00:00 2001 From: Alan Nichol Date: Thu, 13 Sep 2018 18:23:41 +0200 Subject: [PATCH 010/112] fixes https://github.com/RasaHQ/roadmap/issues/246 --- examples/formbot/actions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/formbot/actions.py b/examples/formbot/actions.py index d03d8eaf7..8bdbb593f 100644 --- a/examples/formbot/actions.py +++ b/examples/formbot/actions.py @@ -1,5 +1,5 @@ from rasa_core_sdk.forms import FormAction - +from rasa_core_sdk import InputValidationError class RestaurantForm(FormAction): @@ -13,3 +13,6 @@ def required_slots(): def submit(self, dispatcher, tracker, domain): dispatcher.utter_message("done!") return [] + + def run(self, dispatcher, tracker, domain): + raise InputValidationError("bad input") From 21fdfa19b37f8c74bead4e3f4b7fbcad4892dd6e Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 13 Sep 2018 21:11:28 +0200 Subject: [PATCH 011/112] add form activated to stories --- examples/formbot/data/stories.md | 15 ++++++++++++ .../memorized_turns.json | 24 +++++++++---------- .../models/dialogue/policy_metadata.json | 2 +- examples/formbot/train.py | 4 ++-- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/examples/formbot/data/stories.md b/examples/formbot/data/stories.md index 2619fb082..4a8cd9216 100644 --- a/examples/formbot/data/stories.md +++ b/examples/formbot/data/stories.md @@ -1,14 +1,29 @@ ## happy path * request_restaurant - restaurant_form + - form_activated{"form_name": "restaurant_form"} + - form_deactivated * thank - utter_noworries ## unhappy path * request_restaurant - restaurant_form + - form_activated{"form_name": "restaurant_form"} * chitchat - utter_chitchat - restaurant_form + - form_deactivated * thank - utter_noworries + +## unhappy path +* request_restaurant + - restaurant_form + - form_activated{"form_name": "restaurant_form"} +* thank + - utter_noworries + - restaurant_form + - form_deactivated +* thank + - utter_noworries \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json index 6cc835fab..8d8459a50 100644 --- a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json +++ b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json @@ -6,18 +6,16 @@ "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESRGtFiMan5RflQjTHAgBtMjHH": 0, "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc0lGYl42AatiAWukP7E=": 3, "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LRNZdkJOZlE20VhurSkpLUovi8/PL8oqLM1GLSnIbFqlgAm+9i8g==": 4, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHQWY5pKMxLxsZPWJySWZ+XnxOZnFQHmCqktLSlKL4vPyy/OLijJTi9HV43caXqvI8FUsACHwY+I=": 0, - "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEoti09MLsnMz4vPySwGyoMlanUUcKouLSlJLYrPyy/PLyrKTC1GV1+UWliaWgyii0sSS4sS80qItgq/VoRofFp+US5Bd2KxKhYA0aldVw==": 3, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc3JGZklyRiIh22IBM5pA4w==": 5, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TUnZ2SWJGckEm8bNg2lJSWpRagytbEA2hxQbg==": 4, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LRNSdnZJYkZyQSbxs2DaUlJalFqDIEdGB1VSwAYUlhPQ==": 0, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHQWY5uSMzJLkjEQULYnJJZn5efE5mcVAJcRoKC0pSS1ClSGgg4CrSjIS87IJOCkWAP10W6I=": 3, - "eJyLrs7MK0nNK4lPzsgsSc5ILLFSMNQz0FEoKEoti09MLsnMz4vPySwGKgFL1Ooo4NNQWlKSWoQqQ0BHUWpxSWJpUSJQOi2/KBddS0lGYl420U7CUA1xT15+eX5RUWZqMUR9LAB4SlYm": 0, - "eJyLrs7MK0nNK4lPzsgsSc5ILLFSMNQz0FEoKEotiy8tKUktQpWp1VHAp6MotbgksbQoESidll+Ui66lJCMxLxtZfWJySWZ+XnxOZjFQnqBqiHvy8svzi4oyU4vR1RelFpYC7UdyBAGrYgEm0FqP": 4, - "eJyLrs7MK0nNK4lPzsgsSc5ILLFSMNQz0FEoKEotiy9KLS5JLC1KBEqn5RflgqVqdRRgWkoyEvOykdUnJpdk5ufF52QWA+UJqi4tKUktis/LL88vKspMLUZXX5RaWAq0H8kRRFuFXytWX8UCAHusX3k=": 0, - "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEoti09MLsnMz4vPySwGyoMlanUUcKouLSlJLYrPyy/PLyrKTC1GV1+UWliaWgyii0sSS4sS80qItgq/VoRofFp+US665uSMzJLkjERCtsUCAPKRXok=": 5, - "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEotiy8tKUktis/LL88vKspMLQZL1eoowNQXpRaWphaD6OKSxNKixLwSZM2JySWZ+XnxOZnFQMWkaUWIxqflF+Wia07OyCxJzkgk3jZsGiB+Q5GpjQUAAidgKg==": 4, - "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEotiy8tKUktis/LL88vKspMLQZL1eoowNQXpRaWphaD6OKSxNKixLwSZM2JySWZ+XnxOZnFQMWkaUWIxqflF+Wia8ZwJ16riPNVLAAVVl5m": 0 + "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESIK0g0bLU+LT8olwkfWA+VDNRhmPTWhsLAO32PkY=": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjC/JSMzLJuDcWAAMcFiv": 3, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn4kozEvGwqOhfDvNKSktSi+Lz88vyioszUYoiJsQDD5nQn": 4, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsKjoXw7zSkpLUovi8/PL8oqLM1GK4ibjUY/VRLABawYPE": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktis/LL88vKspMLYabiEs9Lh+R4N5YAEG3fik=": 3, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4vPyy/OLijJTi+Em4lKPzXR86rG7lyTXxAIAKhBsLg==": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjE/OyCxJzkgk5OJYAB9gWeE=": 5, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn45IzMkuSMRGq6GJuRpSUlqUWoMrWxACCydes=": 4, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERquhibkaUlJalFqDIgM/HowOqzWACfCIa6": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDc+OSOzJDkjEcVQkOb8vPiczGKgEuoYWVpSklqEKgMyE48OXD6DainJSMzLJuDoWABh0oEf": 3, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocqAzMSjA5sVSFpKMhLzsgk7GpdqiHvy8svzi4oyU4sh6mMBwGRvJA==": 0 } } \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index 13d38c158..6173a49ab 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -3,7 +3,7 @@ "action_listen": { "slots": [] }, - "utter_noworries": { + "restaurant_form": { "slots": [] } }, diff --git a/examples/formbot/train.py b/examples/formbot/train.py index ebc728a62..8a30677c8 100644 --- a/examples/formbot/train.py +++ b/examples/formbot/train.py @@ -19,11 +19,11 @@ policies=[MemoizationPolicy(), FormPolicy()]) - training_data = agent.load_data(training_data_file) + training_data = agent.load_data(training_data_file, + augmentation_factor=0) agent.train( training_data, - augmentation_factor=50, epochs=200, batch_size=10, validation_split=0.2 From d0b5c32df07f4c7c7206ad9c340173432e044f77 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 14 Sep 2018 11:01:09 +0200 Subject: [PATCH 012/112] retrain --- .../policy_0_MemoizationPolicy/memorized_turns.json | 12 ++++++------ .../formbot/models/dialogue/policy_metadata.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json index 8d8459a50..cfbbe1bb7 100644 --- a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json +++ b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json @@ -9,13 +9,13 @@ "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESIK0g0bLU+LT8olwkfWA+VDNRhmPTWhsLAO32PkY=": 0, "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjC/JSMzLJuDcWAAMcFiv": 3, "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn4kozEvGwqOhfDvNKSktSi+Lz88vyioszUYoiJsQDD5nQn": 4, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsKjoXw7zSkpLUovi8/PL8oqLM1GK4ibjUY/VRLABawYPE": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktis/LL88vKspMLYabiEs9Lh+R4N5YAEG3fik=": 3, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4vPyy/OLijJTi+Em4lKPzXR86rG7lyTXxAIAKhBsLg==": 0, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsKjoXw7zSkpLUovi8/PL8oqLM1GK4ieR7LxYAXa2JXw==": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktis/LL88vKspMLYabSJH3SHB8LAAuYYPE": 3, + "eJyljk0OQDAQha/SA4iwdRWRSSMjGkyZTlmIuysR6cKCWM77+eaVq67FzAiN5QEYnWjPmuS8C5WnWaIMCQZFWk3dJY2MMxxNS9AbF/zT2BL1g+dFkIHsYpkNupt45RknH3gRNC4/vYrKL8d/mlbtXPxxyQ==": 0, "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjE/OyCxJzkgk5OJYAB9gWeE=": 5, "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn45IzMkuSMRGq6GJuRpSUlqUWoMrWxACCydes=": 4, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERquhibkaUlJalFqDIgM/HowOqzWACfCIa6": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDc+OSOzJDkjEcVQkOb8vPiczGKgEuoYWVpSklqEKgMyE48OXD6DainJSMzLJuDoWABh0oEf": 3, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocqAzMSjA5sVSFpKMhLzsgk7GpdqiHvy8svzi4oyU4sh6mMBwGRvJA==": 0 + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERquhibkaUlJalFqDIgM8n3YywAH9mLIw==": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDc+OSOzJDkjEcVQkOb8vPiczGKgEuoYWVpSklqEKgMykyI/QjWXZCTmZRNwfiwA1fqFiA==": 3, + "eJyljs0KAjEMhF+lD7CIXn0VkRJKpGHdVNPpehDf3e7PoQuKB4+ZmW8ypycFyMj+kmzwxhlUjBTzfXSH3b5zouCqhCgIkbCqN+PRT3BSf5VcI7Px6tx/lQVg2zpT50oY30ttbGpb9tOzBkYk7X/P/5Zelml6JDPhvOTPb6puc40=": 0 } } \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index 6173a49ab..c2f72fe81 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -7,7 +7,7 @@ "slots": [] } }, - "rasa_core": "0.11.3", + "rasa_core": "0.12.0a1", "python": "3.6.5", "max_histories": [ 5, From f001453b58d58957050e9d573dc599a438163c3c Mon Sep 17 00:00:00 2001 From: Alan Nichol Date: Fri, 14 Sep 2018 11:23:28 +0200 Subject: [PATCH 013/112] move validate to form class --- rasa_core_sdk/__init__.py | 6 ------ rasa_core_sdk/forms.py | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index d10e6cf66..bc5a61628 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -157,12 +157,6 @@ def name(self): raise NotImplementedError - def validate(self, tracker): - # type: (Tracker) -> Dict[Text, Any] - """"Validate the user input.""" - - raise InputValidationError("validation not implemented") - def run(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[dict] """Execute the side effects of this action.""" diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 4adbbc988..eeb03c311 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -36,6 +36,12 @@ def should_request_slot(tracker, slot_name): existing_val = tracker.get_slot(slot_name) return existing_val is None + def validate(self, tracker): + # type: (Tracker) -> Dict[Text, Any] + """"Validate the user input.""" + + raise InputValidationError("validation not implemented") + def activate_if_required(self, tracker): if tracker.active_form == self.name(): return [] From 247097b1b8d309a71f6a1813bdb5134c533571a2 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 14 Sep 2018 13:40:22 +0200 Subject: [PATCH 014/112] add import --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index eeb03c311..8527604a9 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -7,7 +7,7 @@ import logging from typing import Text -from rasa_core_sdk import Action +from rasa_core_sdk import Action, InputValidationError from rasa_core_sdk.events import SlotSet, FormActivated, FormDeactivated logger = logging.getLogger(__name__) From 1489a008829487b6d027d0c16eec224261cd44e3 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 14 Sep 2018 14:02:42 +0200 Subject: [PATCH 015/112] move form error to forms.py --- rasa_core_sdk/__init__.py | 15 --------------- rasa_core_sdk/forms.py | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index bc5a61628..01fef89cb 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -165,18 +165,3 @@ def run(self, dispatcher, tracker, domain): def __str__(self): return "Action('{}')".format(self.name()) - - -class InputValidationError(Exception): - """Raised when form cannot validate user input - - Attributes: - message -- explanation why form could not validate user input - """ - - def __init__(self, message): - self.message = message - - def __str__(self): - return self.message - diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 8527604a9..113f36ba5 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -7,7 +7,7 @@ import logging from typing import Text -from rasa_core_sdk import Action, InputValidationError +from rasa_core_sdk import Action from rasa_core_sdk.events import SlotSet, FormActivated, FormDeactivated logger = logging.getLogger(__name__) @@ -18,6 +18,20 @@ REQUESTED_SLOT = "requested_slot" +class InputValidationError(Exception): + """Raised when form cannot validate user input + + Attributes: + message -- explanation why form could not validate user input + """ + + def __init__(self, message): + self.message = message + + def __str__(self): + return self.message + + class FormAction(Action): def name(self): # type: () -> Text From 1dc970a3e0e54fcb67cc39a66f62729a90e6bb73 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 14 Sep 2018 14:31:55 +0200 Subject: [PATCH 016/112] move validation error to forms.py --- examples/formbot/actions.py | 2 +- rasa_core_sdk/endpoint.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/formbot/actions.py b/examples/formbot/actions.py index 8bdbb593f..2e0f0f7db 100644 --- a/examples/formbot/actions.py +++ b/examples/formbot/actions.py @@ -1,5 +1,5 @@ from rasa_core_sdk.forms import FormAction -from rasa_core_sdk import InputValidationError +from rasa_core_sdk.forms import InputValidationError class RestaurantForm(FormAction): diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index 0493c190c..d20f7d704 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -11,7 +11,7 @@ from gevent.pywsgi import WSGIServer from rasa_core_sdk.executor import ActionExecutor -from rasa_core_sdk import InputValidationError +from rasa_core_sdk.forms import InputValidationError DEFAULT_SERVER_PORT = 5055 From c265a55d1a568f3c2223e3dc4e023612eec9772b Mon Sep 17 00:00:00 2001 From: akelad Date: Fri, 14 Sep 2018 16:16:35 +0200 Subject: [PATCH 017/112] fix validation method --- rasa_core_sdk/forms.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 113f36ba5..cb0e01653 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -54,7 +54,16 @@ def validate(self, tracker): # type: (Tracker) -> Dict[Text, Any] """"Validate the user input.""" - raise InputValidationError("validation not implemented") + events = [] + entities = tracker.latest_message["entities"] + print(entities) + for e in entities: + if e.get("name") == tracker.slots[REQUESTED_SLOT]: + events.append(SlotSet(e['name'], e['value'])) + if events: + return events + else: + raise InputValidationError("validation not implemented") def activate_if_required(self, tracker): if tracker.active_form == self.name(): @@ -62,18 +71,11 @@ def activate_if_required(self, tracker): else: return [FormActivated(self.name())] - def get_requested_slot(self, tracker): - events = [] - intent = tracker.latest_message["intent"].get("name") - if intent == "extracted_slot": - for slot in tracker.latest_message["slots"]: - events.append(SlotSet(slot['name'], slot['value'])) - - return events - def run(self, dispatcher, tracker, domain): - - events = self.get_requested_slot(tracker) + if tracker.active_form == self.name(): + events = self.validate(tracker) + else: + events = [] temp_tracker = tracker.copy() for e in events: temp_tracker.slots[e["name"]] = e["value"] From 851efec2591efe0d28b112d327b5e230b76f7e4d Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 14 Sep 2018 16:32:16 +0200 Subject: [PATCH 018/112] fix validation --- examples/formbot/actions.py | 4 +--- examples/formbot/domain.yml | 2 -- .../policy_0_MemoizationPolicy/memorized_turns.json | 6 +++--- examples/formbot/models/dialogue/policy_metadata.json | 2 +- rasa_core_sdk/forms.py | 6 +++--- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/examples/formbot/actions.py b/examples/formbot/actions.py index 2e0f0f7db..1320776bc 100644 --- a/examples/formbot/actions.py +++ b/examples/formbot/actions.py @@ -1,6 +1,7 @@ from rasa_core_sdk.forms import FormAction from rasa_core_sdk.forms import InputValidationError + class RestaurantForm(FormAction): def name(self): @@ -13,6 +14,3 @@ def required_slots(): def submit(self, dispatcher, tracker, domain): dispatcher.utter_message("done!") return [] - - def run(self, dispatcher, tracker, domain): - raise InputValidationError("bad input") diff --git a/examples/formbot/domain.yml b/examples/formbot/domain.yml index a260589dd..824da6136 100644 --- a/examples/formbot/domain.yml +++ b/examples/formbot/domain.yml @@ -13,8 +13,6 @@ slots: type: unfeaturized num_people: type: unfeaturized - requested_slot: - type: unfeaturized templates: utter_ask_num_people: diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json index cfbbe1bb7..5868a3948 100644 --- a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json +++ b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json @@ -3,15 +3,15 @@ "lookup": { "eJyLzivNydFRwCSra2MBj0wJzQ==": 0, "eJyLzivNydFRQCara4E4M68kNa8kvii1sDS1GEQXlySWFiXmlVgpGOoZ6CgUFKWWxScml2Tm58XnZBYDFYMlamMBwE8dUg==": 4, - "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESRGtFiMan5RflQjTHAgBtMjHH": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc0lGYl42AatiAWukP7E=": 3, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0, "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESIK0g0bLU+LT8olwkfWA+VDNRhmPTWhsLAO32PkY=": 0, "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjC/JSMzLJuDcWAAMcFiv": 3, "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn4kozEvGwqOhfDvNKSktSi+Lz88vyioszUYoiJsQDD5nQn": 4, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsKjoXw7zSkpLUovi8/PL8oqLM1GK4ieR7LxYAXa2JXw==": 0, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktis/LL88vKspMLYabSJH3SHB8LAAuYYPE": 3, "eJyljk0OQDAQha/SA4iwdRWRSSMjGkyZTlmIuysR6cKCWM77+eaVq67FzAiN5QEYnWjPmuS8C5WnWaIMCQZFWk3dJY2MMxxNS9AbF/zT2BL1g+dFkIHsYpkNupt45RknH3gRNC4/vYrKL8d/mlbtXPxxyQ==": 0, + "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESRGtFiMan5RflQjTHAgBtMjHH": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc0lGYl42AatiAWukP7E=": 3, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0, "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjE/OyCxJzkgk5OJYAB9gWeE=": 5, "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn45IzMkuSMRGq6GJuRpSUlqUWoMrWxACCydes=": 4, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERquhibkaUlJalFqDIgM8n3YywAH9mLIw==": 0, diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index c2f72fe81..c70293b61 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -7,7 +7,7 @@ "slots": [] } }, - "rasa_core": "0.12.0a1", + "rasa_core": "0.12.0a2", "python": "3.6.5", "max_histories": [ 5, diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index cb0e01653..e5c5d73f7 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -56,10 +56,10 @@ def validate(self, tracker): events = [] entities = tracker.latest_message["entities"] - print(entities) + for e in entities: - if e.get("name") == tracker.slots[REQUESTED_SLOT]: - events.append(SlotSet(e['name'], e['value'])) + if e.get("entity") == tracker.slots[REQUESTED_SLOT]: + events.append(SlotSet(e['entity'], e['value'])) if events: return events else: From bac0493721df5522a5be2cd111f4dfd027f8d3fc Mon Sep 17 00:00:00 2001 From: akelad Date: Fri, 14 Sep 2018 18:26:25 +0200 Subject: [PATCH 019/112] added latest_action_name to tracker --- rasa_core_sdk/__init__.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index 01fef89cb..89a822cbf 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -31,10 +31,11 @@ def from_dict(cls, state): state.get("events"), state.get("paused"), state.get("followup_action"), - state.get("active_form")) + state.get("active_form"), + state.get("latest_action_name")) def __init__(self, sender_id, slots, - latest_message, events, paused, followup_action, active_form): + latest_message, events, paused, followup_action, active_form, latest_action_name): """Initialize the tracker.""" # list of previously seen events @@ -54,6 +55,7 @@ def __init__(self, sender_id, slots, # "text": text} self.latest_message = latest_message if latest_message else {} self.active_form = active_form + self.latest_action_name = latest_action_name def current_state(self, should_include_events=False): # type: (bool) -> Dict[Text, Any] @@ -75,7 +77,9 @@ def current_state(self, should_include_events=False): "latest_message": self.latest_message, "latest_event_time": latest_event_time, "paused": self.is_paused(), - "events": evts + "events": evts, + "active_form": self.active_form, + "latest_action_name": self.latest_action_name } def current_slot_values(self): @@ -145,7 +149,8 @@ def copy(self): copy.deepcopy(self.events), self._paused, self.followup_action, - self.active_form) + self.active_form, + self.latest_action_name) class Action(object): From ed53ec7a262eab9fd808e25b557bb9e7ffeb4553 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 14 Sep 2018 18:37:00 +0200 Subject: [PATCH 020/112] add check if latest action is action listen --- rasa_core_sdk/forms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index e5c5d73f7..bfe42224d 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -72,7 +72,8 @@ def activate_if_required(self, tracker): return [FormActivated(self.name())] def run(self, dispatcher, tracker, domain): - if tracker.active_form == self.name(): + + if tracker.active_form == self.name() and tracker.latest_action_name == 'action_listen': events = self.validate(tracker) else: events = [] From 4d42f416b79a38f60c10bd4e9e56c98d6d98b49e Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Mon, 17 Sep 2018 16:23:14 +0200 Subject: [PATCH 021/112] add FormIsBack event --- examples/formbot/data/stories.md | 2 +- .../memorized_turns.json | 23 +++++++++---------- rasa_core_sdk/events.py | 8 +++++++ rasa_core_sdk/forms.py | 8 +++++-- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/examples/formbot/data/stories.md b/examples/formbot/data/stories.md index 4a8cd9216..6b699fd29 100644 --- a/examples/formbot/data/stories.md +++ b/examples/formbot/data/stories.md @@ -22,7 +22,7 @@ - restaurant_form - form_activated{"form_name": "restaurant_form"} * thank - - utter_noworries + - utter_chitchat - restaurant_form - form_deactivated * thank diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json index 5868a3948..f72ea738f 100644 --- a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json +++ b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json @@ -3,19 +3,18 @@ "lookup": { "eJyLzivNydFRwCSra2MBj0wJzQ==": 0, "eJyLzivNydFRQCara4E4M68kNa8kvii1sDS1GEQXlySWFiXmlVgpGOoZ6CgUFKWWxScml2Tm58XnZBYDFYMlamMBwE8dUg==": 4, - "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESIK0g0bLU+LT8olwkfWA+VDNRhmPTWhsLAO32PkY=": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjC/JSMzLJuDcWAAMcFiv": 3, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn4kozEvGwqOhfDvNKSktSi+Lz88vyioszUYoiJsQDD5nQn": 4, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsKjoXw7zSkpLUovi8/PL8oqLM1GK4ieR7LxYAXa2JXw==": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktis/LL88vKspMLYabSJH3SHB8LAAuYYPE": 3, - "eJyljk0OQDAQha/SA4iwdRWRSSMjGkyZTlmIuysR6cKCWM77+eaVq67FzAiN5QEYnWjPmuS8C5WnWaIMCQZFWk3dJY2MMxxNS9AbF/zT2BL1g+dFkIHsYpkNupt45RknH3gRNC4/vYrKL8d/mlbtXPxxyQ==": 0, "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESRGtFiMan5RflQjTHAgBtMjHH": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBeuGWRkWSpYDKsaHQWo8SUZiXnZBBwTCwDon0ww": 5, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty4ZpBRpalgsWwqtFRgBpfkpGYl03YMeSbV1pSkloUn5yRWZKckQhxe20sAHjEZwg=": 4, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LhmkFGlqWCxbCq0VGAGl+SkZiXTdgx5JtXWlKSWhSfnJFZkpyRWEIFA7F6ORYAiOeDJA==": 0, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHYXqxOSSzLJUsBhWNToKUONLMhLzspFNBOnMz4vPySwGylPBvNKSktSi+OSMzJLkjMQSKhiIy8skeCgWAGipfYk=": 3, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4pMzMkuSMxJLqGAgNtUgE0nzEH7n5uWX5xcVZaYWQ9THAgATpXgN": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBeuGWRkWSpYDKsaHQWo8ckZmSXJGYmE3BMLANYSTWI=": 5, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty4ZpBRpalgsWwqtFRgBqfnJFZkpyRSIR7KDKytKQktQhVpjYWAAJ5aWw=": 4, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LhmkFGlqWCxbCq0VGAGp+ckVmSnJFIhHsoMrK0pCS1CFWGYjOx+j0WAGgqhro=": 0, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHYXqxOSSzLJUsBhWNToKUOOTMzJLkjMSUQwFac7Pi8/JLAYqoY6RpSUlqUWoMhSbicvvUC0lGYl52QS8FQsAKvSBHw==": 3, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocpQbCY2DSBDoVpKMhLzsgl7C5dqiIvz8svzi4oyU4sh6mMBt6V7ow==": 0, "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc0lGYl42AatiAWukP7E=": 3, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjE/OyCxJzkgk5OJYAB9gWeE=": 5, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn45IzMkuSMRGq6GJuRpSUlqUWoMrWxACCydes=": 4, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERquhibkaUlJalFqDIgM8n3YywAH9mLIw==": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDc+OSOzJDkjEcVQkOb8vPiczGKgEuoYWVpSklqEKgMykyI/QjWXZCTmZRNwfiwA1fqFiA==": 3, - "eJyljs0KAjEMhF+lD7CIXn0VkRJKpGHdVNPpehDf3e7PoQuKB4+ZmW8ypycFyMj+kmzwxhlUjBTzfXSH3b5zouCqhCgIkbCqN+PRT3BSf5VcI7Px6tx/lQVg2zpT50oY30ttbGpb9tOzBkYk7X/P/5Zelml6JDPhvOTPb6puc40=": 0 + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0 } } \ No newline at end of file diff --git a/rasa_core_sdk/events.py b/rasa_core_sdk/events.py index 4dd7b50ef..40de86d07 100644 --- a/rasa_core_sdk/events.py +++ b/rasa_core_sdk/events.py @@ -156,3 +156,11 @@ def FormDeactivated(timestamp=None): "event": "form_deactivated", "timestamp": timestamp, } + + +# noinspection PyPep8Naming +def FormIsBack(timestamp=None): + return { + "event": "form_is_back", + "timestamp": timestamp, + } diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index bfe42224d..2289077bc 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -8,7 +8,7 @@ from typing import Text from rasa_core_sdk import Action -from rasa_core_sdk.events import SlotSet, FormActivated, FormDeactivated +from rasa_core_sdk.events import SlotSet, FormActivated, FormDeactivated, FormIsBack logger = logging.getLogger(__name__) @@ -67,7 +67,10 @@ def validate(self, tracker): def activate_if_required(self, tracker): if tracker.active_form == self.name(): - return [] + if tracker.latest_action_name == 'action_listen': + return [] + else: + return [FormIsBack()] else: return [FormActivated(self.name())] @@ -77,6 +80,7 @@ def run(self, dispatcher, tracker, domain): events = self.validate(tracker) else: events = [] + temp_tracker = tracker.copy() for e in events: temp_tracker.slots[e["name"]] = e["value"] From b043aedef45ea532495d6de9512d0402f1b35cfc Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Mon, 17 Sep 2018 18:38:53 +0200 Subject: [PATCH 022/112] dirty fixes --- .../policy_0_MemoizationPolicy/memorized_turns.json | 6 +++--- .../dialogue/policy_2_FallbackPolicy/fallback_policy.json | 5 +++++ examples/formbot/models/dialogue/policy_metadata.json | 4 +++- examples/formbot/train.py | 8 +++++--- 4 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json index f72ea738f..aa1572b1d 100644 --- a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json +++ b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json @@ -9,12 +9,12 @@ "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LhmkFGlqWCxbCq0VGAGl+SkZiXTdgx5JtXWlKSWhSfnJFZkpyRWEIFA7F6ORYAiOeDJA==": 0, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHYXqxOSSzLJUsBhWNToKUONLMhLzspFNBOnMz4vPySwGylPBvNKSktSi+OSMzJLkjMQSKhiIy8skeCgWAGipfYk=": 3, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4pMzMkuSMxJLqGAgNtUgE0nzEH7n5uWX5xcVZaYWQ9THAgATpXgN": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc0lGYl42AatiAWukP7E=": 3, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0, "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBeuGWRkWSpYDKsaHQWo8ckZmSXJGYmE3BMLANYSTWI=": 5, "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty4ZpBRpalgsWwqtFRgBqfnJFZkpyRSIR7KDKytKQktQhVpjYWAAJ5aWw=": 4, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LhmkFGlqWCxbCq0VGAGp+ckVmSnJFIhHsoMrK0pCS1CFWGYjOx+j0WAGgqhro=": 0, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHYXqxOSSzLJUsBhWNToKUOOTMzJLkjMSUQwFac7Pi8/JLAYqoY6RpSUlqUWoMhSbicvvUC0lGYl52QS8FQsAKvSBHw==": 3, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocpQbCY2DSBDoVpKMhLzsgl7C5dqiIvz8svzi4oyU4sh6mMBt6V7ow==": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc0lGYl42AatiAWukP7E=": 3, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0 + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocpQbCY2DSBDoVpKMhLzsgl7C5dqiIvz8svzi4oyU4sh6mMBt6V7ow==": 0 } } \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json b/examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json new file mode 100644 index 000000000..257a77819 --- /dev/null +++ b/examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json @@ -0,0 +1,5 @@ +{ + "nlu_threshold": 0.3, + "core_threshold": 0.3, + "fallback_action_name": "action_default_fallback" +} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index c70293b61..4deb1f560 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -11,11 +11,13 @@ "python": "3.6.5", "max_histories": [ 5, + null, null ], "ensemble_name": "rasa_core.policies.ensemble.SimplePolicyEnsemble", "policy_names": [ "rasa_core.policies.memoization.MemoizationPolicy", - "rasa_core.policies.form_policy.FormPolicy" + "rasa_core.policies.form_policy.FormPolicy", + "rasa_core.policies.fallback.FallbackPolicy" ] } \ No newline at end of file diff --git a/examples/formbot/train.py b/examples/formbot/train.py index 8a30677c8..22956184f 100644 --- a/examples/formbot/train.py +++ b/examples/formbot/train.py @@ -6,8 +6,9 @@ from rasa_core import utils from rasa_core.agent import Agent from rasa_core.policies.keras_policy import KerasPolicy -from rasa_core.policies.memoization import MemoizationPolicy +from rasa_core.policies.memoization import AugmentedMemoizationPolicy from rasa_core.policies.form_policy import FormPolicy +from rasa_core.policies.fallback import FallbackPolicy if __name__ == '__main__': utils.configure_colored_logging(loglevel="INFO") @@ -16,8 +17,9 @@ model_path = 'models/dialogue' agent = Agent("domain.yml", - policies=[MemoizationPolicy(), - FormPolicy()]) + policies=[AugmentedMemoizationPolicy(), + FormPolicy(), + FallbackPolicy()]) training_data = agent.load_data(training_data_file, augmentation_factor=0) From 7b6761a916b8db020e90032173fbd589c7dda62b Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Tue, 18 Sep 2018 11:06:24 +0200 Subject: [PATCH 023/112] add FormIsBack event to first call as well --- rasa_core_sdk/forms.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 2289077bc..d997801fb 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -72,7 +72,7 @@ def activate_if_required(self, tracker): else: return [FormIsBack()] else: - return [FormActivated(self.name())] + return [FormActivated(self.name()), FormIsBack()] def run(self, dispatcher, tracker, domain): @@ -87,9 +87,7 @@ def run(self, dispatcher, tracker, domain): for slot in self.required_slots(): if self.should_request_slot(temp_tracker, slot): - dispatcher.utter_template( - "utter_ask_{}".format(slot), - tracker) + dispatcher.utter_template("utter_ask_{}".format(slot), tracker) events.append(SlotSet(REQUESTED_SLOT, slot)) From 6144cd4e056f5bb788c4d0b10eea354f7ba7960d Mon Sep 17 00:00:00 2001 From: akelad Date: Wed, 19 Sep 2018 13:07:59 +0200 Subject: [PATCH 024/112] remove all custom slot stuff https://github.com/RasaHQ/roadmap/issues/263 --- examples/moodbot/Makefile | 2 +- examples/moodbot/myslots.py | 15 --------------- rasa_core_sdk/endpoint.py | 12 ++---------- 3 files changed, 3 insertions(+), 26 deletions(-) delete mode 100644 examples/moodbot/myslots.py diff --git a/examples/moodbot/Makefile b/examples/moodbot/Makefile index ac6394b63..157088bbe 100644 --- a/examples/moodbot/Makefile +++ b/examples/moodbot/Makefile @@ -17,7 +17,7 @@ run-core: python -m rasa_core.run --nlu models/nlu/current --core models/dialogue --debug --endpoints endpoints.yml run-actions: - python -m rasa_core_sdk.endpoint --actions myactions --slots myslots + python -m rasa_core_sdk.endpoint --actions myactions run: make run-actions& diff --git a/examples/moodbot/myslots.py b/examples/moodbot/myslots.py deleted file mode 100644 index 6f10f9a72..000000000 --- a/examples/moodbot/myslots.py +++ /dev/null @@ -1,15 +0,0 @@ - -from rasa_core_sdk.slots import Slot - -class MyFreeTextSlot(Slot): - - def name(self): - return "my_free_text_slot" - - def validate(self, parse_data): - new_parse_data = parse_data.copy() - del new_parse_data["entities"] - text = parse_data.get("text") - new_parse_data["slots"] = {} - new_parse_data["slots"][self.name()] = text - return new_parse_data diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index d20f7d704..8403c43f5 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -40,18 +40,11 @@ def create_argument_parser(): default=None, help="name of action package to be loaded" ) - parser.add_argument( - '--slots', - type=str, - default=None, - help="name of action package to be loaded" - ) return parser def endpoint_app(cors_origins=None, - action_package_name=None, - slot_package_name=None + action_package_name=None ): app = Flask(__name__) @@ -101,8 +94,7 @@ def webhook(): logger.info("Starting action endpoint server...") edp_app = endpoint_app(cors_origins=cmdline_args.cors, - action_package_name=cmdline_args.actions, - slot_package_name=cmdline_args.slots) + action_package_name=cmdline_args.actions) http_server = WSGIServer(('0.0.0.0', cmdline_args.port), edp_app) From f5439a668e7ac72dd1d546745429ba1a2ad16eb9 Mon Sep 17 00:00:00 2001 From: akelad Date: Wed, 19 Sep 2018 13:22:24 +0200 Subject: [PATCH 025/112] rename inputvalidationerror https://github.com/RasaHQ/roadmap/issues/263 --- rasa_core_sdk/__init__.py | 14 ++++++++++++++ rasa_core_sdk/endpoint.py | 3 +-- rasa_core_sdk/forms.py | 20 ++++---------------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index 03be18182..f34eccf29 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -165,3 +165,17 @@ def run(self, dispatcher, tracker, domain): def __str__(self): return "Action('{}')".format(self.name()) + + +class ActionExecutionError(Exception): + """Raised when an action failes to execute + + Attributes: + message -- explanation why the action failed to execute + """ + + def __init__(self, message): + self.message = message + + def __str__(self): + return self.message diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index 8403c43f5..2d84f3890 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -71,7 +71,7 @@ def webhook(): action_call = request.json try: response = executor.run(action_call) - except InputValidationError as e: + except InputValidationError: result = {"error": "could not validate input."} result["payload"] = action_call response = jsonify(result) @@ -80,7 +80,6 @@ def webhook(): return jsonify(response) - return app diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index d997801fb..56037d8dc 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -7,7 +7,7 @@ import logging from typing import Text -from rasa_core_sdk import Action +from rasa_core_sdk import Action, ActionExecutionError from rasa_core_sdk.events import SlotSet, FormActivated, FormDeactivated, FormIsBack logger = logging.getLogger(__name__) @@ -18,20 +18,6 @@ REQUESTED_SLOT = "requested_slot" -class InputValidationError(Exception): - """Raised when form cannot validate user input - - Attributes: - message -- explanation why form could not validate user input - """ - - def __init__(self, message): - self.message = message - - def __str__(self): - return self.message - - class FormAction(Action): def name(self): # type: () -> Text @@ -63,7 +49,9 @@ def validate(self, tracker): if events: return events else: - raise InputValidationError("validation not implemented") + raise ActionExecutionError("Failed to validate slot {0} with " + "action {1}".format(REQUESTED_SLOT, + self.name())) def activate_if_required(self, tracker): if tracker.active_form == self.name(): From 2b70563fe807935f48d023e3f9bc0866d9c9a21c Mon Sep 17 00:00:00 2001 From: akelad Date: Wed, 19 Sep 2018 13:48:23 +0200 Subject: [PATCH 026/112] format the actionexecutederror better https://github.com/RasaHQ/roadmap/issues/263 --- examples/formbot/actions.py | 1 - rasa_core_sdk/endpoint.py | 9 +++++---- rasa_core_sdk/forms.py | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/formbot/actions.py b/examples/formbot/actions.py index 1320776bc..d03d8eaf7 100644 --- a/examples/formbot/actions.py +++ b/examples/formbot/actions.py @@ -1,5 +1,4 @@ from rasa_core_sdk.forms import FormAction -from rasa_core_sdk.forms import InputValidationError class RestaurantForm(FormAction): diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index 2d84f3890..12c9e0ecd 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -11,7 +11,7 @@ from gevent.pywsgi import WSGIServer from rasa_core_sdk.executor import ActionExecutor -from rasa_core_sdk.forms import InputValidationError +from rasa_core_sdk import ActionExecutionError DEFAULT_SERVER_PORT = 5055 @@ -71,9 +71,10 @@ def webhook(): action_call = request.json try: response = executor.run(action_call) - except InputValidationError: - result = {"error": "could not validate input."} - result["payload"] = action_call + except ActionExecutionError as e: + logger.error(str(e)) + result = {"error": str(e), + "action": action_call.get("next_action")} response = jsonify(result) response.status_code = 400 return response diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 56037d8dc..ba3cf61c4 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -50,8 +50,9 @@ def validate(self, tracker): return events else: raise ActionExecutionError("Failed to validate slot {0} with " - "action {1}".format(REQUESTED_SLOT, - self.name())) + "action {1}".format( + tracker.slots[REQUESTED_SLOT], + self.name())) def activate_if_required(self, tracker): if tracker.active_form == self.name(): From 4fbd66c28d3c078aa052f4ca067bf0fceb3d9505 Mon Sep 17 00:00:00 2001 From: akelad Date: Wed, 19 Sep 2018 15:02:42 +0200 Subject: [PATCH 027/112] remove action name from error response https://github.com/RasaHQ/roadmap/issues/263 --- rasa_core_sdk/endpoint.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index 12c9e0ecd..ada717046 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -73,8 +73,7 @@ def webhook(): response = executor.run(action_call) except ActionExecutionError as e: logger.error(str(e)) - result = {"error": str(e), - "action": action_call.get("next_action")} + result = {"error": str(e)} response = jsonify(result) response.status_code = 400 return response From d89ac0b9a48239d9f129187ebc98849a8382928c Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 20 Sep 2018 11:21:47 +0200 Subject: [PATCH 028/112] change FormActivated/Deactivated events to Form RasaHQ/roadmap#263 --- examples/formbot/data/stories.md | 12 ++++++------ .../featurizer.json | 0 .../memorized_turns.json | 12 ++++++------ .../formbot/models/dialogue/policy_metadata.json | 2 +- rasa_core_sdk/events.py | 12 ++---------- rasa_core_sdk/forms.py | 6 +++--- 6 files changed, 18 insertions(+), 26 deletions(-) rename examples/formbot/models/dialogue/{policy_0_MemoizationPolicy => policy_0_AugmentedMemoizationPolicy}/featurizer.json (100%) rename examples/formbot/models/dialogue/{policy_0_MemoizationPolicy => policy_0_AugmentedMemoizationPolicy}/memorized_turns.json (97%) diff --git a/examples/formbot/data/stories.md b/examples/formbot/data/stories.md index 6b699fd29..f52c8926b 100644 --- a/examples/formbot/data/stories.md +++ b/examples/formbot/data/stories.md @@ -1,29 +1,29 @@ ## happy path * request_restaurant - restaurant_form - - form_activated{"form_name": "restaurant_form"} - - form_deactivated + - form{"form_name": "restaurant_form"} + - form{"form_name": null} * thank - utter_noworries ## unhappy path * request_restaurant - restaurant_form - - form_activated{"form_name": "restaurant_form"} + - form{"form_name": "restaurant_form"} * chitchat - utter_chitchat - restaurant_form - - form_deactivated + - form{"form_name": null} * thank - utter_noworries ## unhappy path * request_restaurant - restaurant_form - - form_activated{"form_name": "restaurant_form"} + - form{"form_name": "restaurant_form"} * thank - utter_chitchat - restaurant_form - - form_deactivated + - form{"form_name": null} * thank - utter_noworries \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json b/examples/formbot/models/dialogue/policy_0_AugmentedMemoizationPolicy/featurizer.json similarity index 100% rename from examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json rename to examples/formbot/models/dialogue/policy_0_AugmentedMemoizationPolicy/featurizer.json diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_0_AugmentedMemoizationPolicy/memorized_turns.json similarity index 97% rename from examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json rename to examples/formbot/models/dialogue/policy_0_AugmentedMemoizationPolicy/memorized_turns.json index aa1572b1d..83cc04632 100644 --- a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json +++ b/examples/formbot/models/dialogue/policy_0_AugmentedMemoizationPolicy/memorized_turns.json @@ -4,17 +4,17 @@ "eJyLzivNydFRwCSra2MBj0wJzQ==": 0, "eJyLzivNydFRQCara4E4M68kNa8kvii1sDS1GEQXlySWFiXmlVgpGOoZ6CgUFKWWxScml2Tm58XnZBYDFYMlamMBwE8dUg==": 4, "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESRGtFiMan5RflQjTHAgBtMjHH": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBeuGWRkWSpYDKsaHQWo8ckZmSXJGYmE3BMLANYSTWI=": 5, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty4ZpBRpalgsWwqtFRgBqfnJFZkpyRSIR7KDKytKQktQhVpjYWAAJ5aWw=": 4, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LhmkFGlqWCxbCq0VGAGp+ckVmSnJFIhHsoMrK0pCS1CFWGYjOx+j0WAGgqhro=": 0, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHYXqxOSSzLJUsBhWNToKUOOTMzJLkjMSUQwFac7Pi8/JLAYqoY6RpSUlqUWoMhSbicvvUC0lGYl52QS8FQsAKvSBHw==": 3, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocpQbCY2DSBDoVpKMhLzsgl7C5dqiIvz8svzi4oyU4sh6mMBt6V7ow==": 0, "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBeuGWRkWSpYDKsaHQWo8SUZiXnZBBwTCwDon0ww": 5, "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty4ZpBRpalgsWwqtFRgBpfkpGYl03YMeSbV1pSkloUn5yRWZKckQhxe20sAHjEZwg=": 4, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LhmkFGlqWCxbCq0VGAGl+SkZiXTdgx5JtXWlKSWhSfnJFZkpyRWEIFA7F6ORYAiOeDJA==": 0, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHYXqxOSSzLJUsBhWNToKUONLMhLzspFNBOnMz4vPySwGylPBvNKSktSi+OSMzJLkjMQSKhiIy8skeCgWAGipfYk=": 3, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4pMzMkuSMxJLqGAgNtUgE0nzEH7n5uWX5xcVZaYWQ9THAgATpXgN": 0, "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc0lGYl42AatiAWukP7E=": 3, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBeuGWRkWSpYDKsaHQWo8ckZmSXJGYmE3BMLANYSTWI=": 5, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty4ZpBRpalgsWwqtFRgBqfnJFZkpyRSIR7KDKytKQktQhVpjYWAAJ5aWw=": 4, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LhmkFGlqWCxbCq0VGAGp+ckVmSnJFIhHsoMrK0pCS1CFWGYjOx+j0WAGgqhro=": 0, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHYXqxOSSzLJUsBhWNToKUOOTMzJLkjMSUQwFac7Pi8/JLAYqoY6RpSUlqUWoMhSbicvvUC0lGYl52QS8FQsAKvSBHw==": 3, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocpQbCY2DSBDoVpKMhLzsgl7C5dqiIvz8svzi4oyU4sh6mMBt6V7ow==": 0 + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0 } } \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index 4deb1f560..1f2661682 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -16,7 +16,7 @@ ], "ensemble_name": "rasa_core.policies.ensemble.SimplePolicyEnsemble", "policy_names": [ - "rasa_core.policies.memoization.MemoizationPolicy", + "rasa_core.policies.memoization.AugmentedMemoizationPolicy", "rasa_core.policies.form_policy.FormPolicy", "rasa_core.policies.fallback.FallbackPolicy" ] diff --git a/rasa_core_sdk/events.py b/rasa_core_sdk/events.py index 40de86d07..62c4dc435 100644 --- a/rasa_core_sdk/events.py +++ b/rasa_core_sdk/events.py @@ -142,22 +142,14 @@ def AgentUttered(text=None, data=None, timestamp=None): # noinspection PyPep8Naming -def FormActivated(form_name, timestamp=None): +def Form(form_name, timestamp=None): return { - "event": "form_activated", + "event": "form", "form_name": form_name, "timestamp": timestamp, } -# noinspection PyPep8Naming -def FormDeactivated(timestamp=None): - return { - "event": "form_deactivated", - "timestamp": timestamp, - } - - # noinspection PyPep8Naming def FormIsBack(timestamp=None): return { diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index ba3cf61c4..7d47f3108 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -8,7 +8,7 @@ from typing import Text from rasa_core_sdk import Action, ActionExecutionError -from rasa_core_sdk.events import SlotSet, FormActivated, FormDeactivated, FormIsBack +from rasa_core_sdk.events import SlotSet, Form, FormIsBack logger = logging.getLogger(__name__) @@ -61,7 +61,7 @@ def activate_if_required(self, tracker): else: return [FormIsBack()] else: - return [FormActivated(self.name()), FormIsBack()] + return [Form(self.name()), FormIsBack()] def run(self, dispatcher, tracker, domain): @@ -85,7 +85,7 @@ def run(self, dispatcher, tracker, domain): # there is nothing more to request, so we can submit events_from_submit = self.submit(dispatcher, temp_tracker, domain) or [] - return events + events_from_submit + [FormDeactivated()] + return events + events_from_submit + [Form(None)] def submit(self, dispatcher, tracker, domain): raise NotImplementedError( From 0049b4629e0b20eb2ea3b0ec96a50bededc5a605 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 20 Sep 2018 17:22:55 +0200 Subject: [PATCH 029/112] remove FormIsBack event change unfeaturization logic, remove postpone in events RasaHQ/roadmap#263 --- examples/formbot/data/stories.md | 18 ++++++++----- .../memorized_turns.json | 20 -------------- .../featurizer.json | 0 .../memorized_turns.json | 26 +++++++++++++++++++ .../models/dialogue/policy_metadata.json | 2 +- examples/formbot/train.py | 4 +-- rasa_core_sdk/events.py | 12 ++------- rasa_core_sdk/forms.py | 9 +++---- 8 files changed, 46 insertions(+), 45 deletions(-) delete mode 100644 examples/formbot/models/dialogue/policy_0_AugmentedMemoizationPolicy/memorized_turns.json rename examples/formbot/models/dialogue/{policy_0_AugmentedMemoizationPolicy => policy_0_MemoizationPolicy}/featurizer.json (100%) create mode 100644 examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json diff --git a/examples/formbot/data/stories.md b/examples/formbot/data/stories.md index f52c8926b..f46d70e30 100644 --- a/examples/formbot/data/stories.md +++ b/examples/formbot/data/stories.md @@ -1,29 +1,35 @@ ## happy path * request_restaurant - restaurant_form - - form{"form_name": "restaurant_form"} - - form{"form_name": null} + - form{"name": "restaurant_form"} + - form{"name": null} * thank - utter_noworries ## unhappy path * request_restaurant - restaurant_form - - form{"form_name": "restaurant_form"} + - form{"name": "restaurant_form"} * chitchat - utter_chitchat - restaurant_form - - form{"form_name": null} +* chitchat + - utter_chitchat + - restaurant_form +* chitchat + - utter_chitchat + - restaurant_form + - form{"name": null} * thank - utter_noworries ## unhappy path * request_restaurant - restaurant_form - - form{"form_name": "restaurant_form"} + - form{"name": "restaurant_form"} * thank - utter_chitchat - restaurant_form - - form{"form_name": null} + - form{"name": null} * thank - utter_noworries \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_0_AugmentedMemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_0_AugmentedMemoizationPolicy/memorized_turns.json deleted file mode 100644 index 83cc04632..000000000 --- a/examples/formbot/models/dialogue/policy_0_AugmentedMemoizationPolicy/memorized_turns.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "max_history": 5, - "lookup": { - "eJyLzivNydFRwCSra2MBj0wJzQ==": 0, - "eJyLzivNydFRQCara4E4M68kNa8kvii1sDS1GEQXlySWFiXmlVgpGOoZ6CgUFKWWxScml2Tm58XnZBYDFYMlamMBwE8dUg==": 4, - "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESRGtFiMan5RflQjTHAgBtMjHH": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBeuGWRkWSpYDKsaHQWo8ckZmSXJGYmE3BMLANYSTWI=": 5, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty4ZpBRpalgsWwqtFRgBqfnJFZkpyRSIR7KDKytKQktQhVpjYWAAJ5aWw=": 4, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LhmkFGlqWCxbCq0VGAGp+ckVmSnJFIhHsoMrK0pCS1CFWGYjOx+j0WAGgqhro=": 0, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHYXqxOSSzLJUsBhWNToKUOOTMzJLkjMSUQwFac7Pi8/JLAYqoY6RpSUlqUWoMhSbicvvUC0lGYl52QS8FQsAKvSBHw==": 3, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocpQbCY2DSBDoVpKMhLzsgl7C5dqiIvz8svzi4oyU4sh6mMBt6V7ow==": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBeuGWRkWSpYDKsaHQWo8SUZiXnZBBwTCwDon0ww": 5, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty4ZpBRpalgsWwqtFRgBpfkpGYl03YMeSbV1pSkloUn5yRWZKckQhxe20sAHjEZwg=": 4, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LhmkFGlqWCxbCq0VGAGl+SkZiXTdgx5JtXWlKSWhSfnJFZkpyRWEIFA7F6ORYAiOeDJA==": 0, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHYXqxOSSzLJUsBhWNToKUONLMhLzspFNBOnMz4vPySwGylPBvNKSktSi+OSMzJLkjMQSKhiIy8skeCgWAGipfYk=": 3, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4pMzMkuSMxJLqGAgNtUgE0nzEH7n5uWX5xcVZaYWQ9THAgATpXgN": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc0lGYl42AatiAWukP7E=": 3, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0 - } -} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_0_AugmentedMemoizationPolicy/featurizer.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json similarity index 100% rename from examples/formbot/models/dialogue/policy_0_AugmentedMemoizationPolicy/featurizer.json rename to examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json new file mode 100644 index 000000000..f9ff5a022 --- /dev/null +++ b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json @@ -0,0 +1,26 @@ +{ + "max_history": 5, + "lookup": { + "eJyLzivNydFRwCSra2MBj0wJzQ==": 0, + "eJyLzivNydFRQCara4E4M68kNa8kvii1sDS1GEQXlySWFiXmlVgpGOoZ6CgUFKWWxScml2Tm58XnZBYDFYMlamMBwE8dUg==": 4, + "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESIK0g0bLU+LT8olwkfWA+VDNRhmPTWhsLAO32PkY=": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjC/JSMzLJuDcWAAMcFiv": 5, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn4kozEvGwqOhfDvNKSktSi+OSMzJLkjESI22tjAUwDc4c=": 4, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsKjoXw7zSkpLUovjkjMyS5IzEEriBuJRj9VAsAMYTgyQ=": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktik/OyCxJzkgsgRuISzkuD5Hg3FgApdV9iQ==": 3, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4pMzMkuSMxJL4AbiUo7NcHzqsTsXv2Py8svzi4oyU4sh6mMBkylrjg==": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjE/OyCxJzkgk5OJYAB9gWeE=": 5, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn45IzMkuSMRGq6GJuRpSUlqUWoMrWxACCydes=": 4, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERquhibkaUlJalFqDIUm4nV77EAgcKTOQ==": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDc+OSOzJDkjEcVQkOb8vPiczGKgEuoYWVpSklqEKkOxmfTyeywAVRqbTw==": 5, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocpQbCY2DRQbSie/xwIAUdyWZQ==": 4, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh0FyszEpoFiQ0Ga8/PiczKLgUqoYyTd/B4LAKOql1U=": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUMqwaanUUKDMUpDk/Lz4nsxiohDpGlpaUpBahylBsJr38HgsAHCGW5g==": 5, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh0FyszEpoFiQ0Ga8/PiczKLgUqoYyQOv5Pqs1gANTiK1g==": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUMqwaanUUKDMUpDk/Lz4nsxiohDpGlpaUpBahyoDMJMNnUC0lGYl52QQcHQsAYgd8tg==": 3, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocqAzMSjA5sVSFpKMhLzsgk7GpdqiHvy8svzi4oyU4sh6mMBwGRvJA==": 0, + "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESRGtFiMan5RflQjTHAgBtMjHH": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc0lGYl42AatiAWukP7E=": 3, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0 + } +} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index 1f2661682..4deb1f560 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -16,7 +16,7 @@ ], "ensemble_name": "rasa_core.policies.ensemble.SimplePolicyEnsemble", "policy_names": [ - "rasa_core.policies.memoization.AugmentedMemoizationPolicy", + "rasa_core.policies.memoization.MemoizationPolicy", "rasa_core.policies.form_policy.FormPolicy", "rasa_core.policies.fallback.FallbackPolicy" ] diff --git a/examples/formbot/train.py b/examples/formbot/train.py index 22956184f..0199f2322 100644 --- a/examples/formbot/train.py +++ b/examples/formbot/train.py @@ -6,7 +6,7 @@ from rasa_core import utils from rasa_core.agent import Agent from rasa_core.policies.keras_policy import KerasPolicy -from rasa_core.policies.memoization import AugmentedMemoizationPolicy +from rasa_core.policies.memoization import MemoizationPolicy from rasa_core.policies.form_policy import FormPolicy from rasa_core.policies.fallback import FallbackPolicy @@ -17,7 +17,7 @@ model_path = 'models/dialogue' agent = Agent("domain.yml", - policies=[AugmentedMemoizationPolicy(), + policies=[MemoizationPolicy(), FormPolicy(), FallbackPolicy()]) diff --git a/rasa_core_sdk/events.py b/rasa_core_sdk/events.py index 62c4dc435..6131dec85 100644 --- a/rasa_core_sdk/events.py +++ b/rasa_core_sdk/events.py @@ -142,17 +142,9 @@ def AgentUttered(text=None, data=None, timestamp=None): # noinspection PyPep8Naming -def Form(form_name, timestamp=None): +def Form(name, timestamp=None): return { "event": "form", - "form_name": form_name, - "timestamp": timestamp, - } - - -# noinspection PyPep8Naming -def FormIsBack(timestamp=None): - return { - "event": "form_is_back", + "name": name, "timestamp": timestamp, } diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 7d47f3108..dc28577a1 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -8,7 +8,7 @@ from typing import Text from rasa_core_sdk import Action, ActionExecutionError -from rasa_core_sdk.events import SlotSet, Form, FormIsBack +from rasa_core_sdk.events import SlotSet, Form logger = logging.getLogger(__name__) @@ -56,12 +56,9 @@ def validate(self, tracker): def activate_if_required(self, tracker): if tracker.active_form == self.name(): - if tracker.latest_action_name == 'action_listen': - return [] - else: - return [FormIsBack()] + return [] else: - return [Form(self.name()), FormIsBack()] + return [Form(self.name())] def run(self, dispatcher, tracker, domain): From f288b3a51e1520aa72607c76cb2a115cfad8312a Mon Sep 17 00:00:00 2001 From: akelad Date: Fri, 21 Sep 2018 10:37:36 +0200 Subject: [PATCH 030/112] wip actionexecutionerror https://github.com/RasaHQ/roadmap/issues/263 --- .../policy_0_MemoizationPolicy/featurizer.json | 1 - .../fallback_policy.json | 5 ----- .../models/dialogue/policy_metadata.json | 13 +++++++++---- rasa_core_sdk/__init__.py | 14 -------------- rasa_core_sdk/endpoint.py | 17 ++++++++--------- rasa_core_sdk/forms.py | 5 +++-- 6 files changed, 20 insertions(+), 35 deletions(-) delete mode 100644 examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json delete mode 100644 examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json deleted file mode 100644 index 9b5b69869..000000000 --- a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json +++ /dev/null @@ -1 +0,0 @@ -{"py/object": "rasa_core.featurizers.MaxHistoryTrackerFeaturizer", "max_history": 5, "remove_duplicates": true, "state_featurizer": {"py/object": "rasa_core.featurizers.SingleStateFeaturizer", "slot_feature_len": null, "user_feature_len": null}, "use_intent_probabilities": false} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json b/examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json deleted file mode 100644 index 257a77819..000000000 --- a/examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "nlu_threshold": 0.3, - "core_threshold": 0.3, - "fallback_action_name": "action_default_fallback" -} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index 4deb1f560..78876bd04 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -5,19 +5,24 @@ }, "restaurant_form": { "slots": [] + }, + "utter_noworries": { + "slots": [] } }, "rasa_core": "0.12.0a2", - "python": "3.6.5", + "python": "3.6.6", "max_histories": [ - 5, null, + 3, + 3, null ], "ensemble_name": "rasa_core.policies.ensemble.SimplePolicyEnsemble", "policy_names": [ + "rasa_core.policies.fallback.FallbackPolicy", "rasa_core.policies.memoization.MemoizationPolicy", - "rasa_core.policies.form_policy.FormPolicy", - "rasa_core.policies.fallback.FallbackPolicy" + "rasa_core.policies.keras_policy.KerasPolicy", + "rasa_core.policies.form_policy.FormPolicy" ] } \ No newline at end of file diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index f34eccf29..03be18182 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -165,17 +165,3 @@ def run(self, dispatcher, tracker, domain): def __str__(self): return "Action('{}')".format(self.name()) - - -class ActionExecutionError(Exception): - """Raised when an action failes to execute - - Attributes: - message -- explanation why the action failed to execute - """ - - def __init__(self, message): - self.message = message - - def __str__(self): - return self.message diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index ada717046..887e2e2ea 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -11,7 +11,6 @@ from gevent.pywsgi import WSGIServer from rasa_core_sdk.executor import ActionExecutor -from rasa_core_sdk import ActionExecutionError DEFAULT_SERVER_PORT = 5055 @@ -69,14 +68,14 @@ def health(): def webhook(): """Webhook to retrieve action calls.""" action_call = request.json - try: - response = executor.run(action_call) - except ActionExecutionError as e: - logger.error(str(e)) - result = {"error": str(e)} - response = jsonify(result) - response.status_code = 400 - return response + # try: + response = executor.run(action_call) + # except ActionExecutionError as e: + # logger.error(str(e)) + # result = {"error": str(e)} + # response = jsonify(result) + # response.status_code = 400 + # return response return jsonify(response) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index dc28577a1..021201f90 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -7,8 +7,9 @@ import logging from typing import Text -from rasa_core_sdk import Action, ActionExecutionError +from rasa_core_sdk import Action from rasa_core_sdk.events import SlotSet, Form +from rasa_core.utils import ActionExecutionError logger = logging.getLogger(__name__) @@ -52,7 +53,7 @@ def validate(self, tracker): raise ActionExecutionError("Failed to validate slot {0} with " "action {1}".format( tracker.slots[REQUESTED_SLOT], - self.name())) + self.name()), self.name()) def activate_if_required(self, tracker): if tracker.active_form == self.name(): From c8cc18a30508e1baaabdc3515061a47fe5058dea Mon Sep 17 00:00:00 2001 From: akelad Date: Fri, 21 Sep 2018 11:31:45 +0200 Subject: [PATCH 031/112] handle the ActionExecutionError properly https://github.com/RasaHQ/roadmap/issues/263 --- .gitignore | 4 +- examples/formbot/Makefile | 2 +- examples/formbot/data/stories.md | 15 +++- .../featurizer.json | 1 + .../memorized_turns.json | 18 +++-- .../fallback_policy.json | 5 ++ .../models/dialogue/policy_metadata.json | 11 +-- examples/formbot/stories.md | 77 +++++++++++++++++++ rasa_core_sdk/endpoint.py | 17 ++-- 9 files changed, 123 insertions(+), 27 deletions(-) create mode 100644 examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json create mode 100644 examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json create mode 100644 examples/formbot/stories.md diff --git a/.gitignore b/.gitignore index 2ee1d933c..4cba9f029 100644 --- a/.gitignore +++ b/.gitignore @@ -106,4 +106,6 @@ venv.bak/ examples/moodbot/models/ # emacs -*~ \ No newline at end of file +*~ + +*.DS_Store diff --git a/examples/formbot/Makefile b/examples/formbot/Makefile index fafe7c47b..742a1b6f2 100644 --- a/examples/formbot/Makefile +++ b/examples/formbot/Makefile @@ -14,7 +14,7 @@ train-core: python -m rasa_core.train -s data/stories.md -d domain.yml -o models/dialogue --epochs 1 --debug run-core: - python -m rasa_core.run --nlu models/nlu/current --core models/dialogue --debug --endpoints endpoints.yml + python -m rasa_core.run --core models/dialogue --debug --endpoints endpoints.yml run-actions: python -m rasa_core_sdk.endpoint --actions actions diff --git a/examples/formbot/data/stories.md b/examples/formbot/data/stories.md index f46d70e30..dd125a631 100644 --- a/examples/formbot/data/stories.md +++ b/examples/formbot/data/stories.md @@ -6,6 +6,19 @@ * thank - utter_noworries + + +## chitchat once +* request_restaurant + - restaurant_form + - form{"name": "restaurant_form"} +* chitchat + - utter_chitchat + - restaurant_form + - form{"name": null} +* thank + - utter_noworries + ## unhappy path * request_restaurant - restaurant_form @@ -32,4 +45,4 @@ - restaurant_form - form{"name": null} * thank - - utter_noworries \ No newline at end of file + - utter_noworries diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json new file mode 100644 index 000000000..9b5b69869 --- /dev/null +++ b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json @@ -0,0 +1 @@ +{"py/object": "rasa_core.featurizers.MaxHistoryTrackerFeaturizer", "max_history": 5, "remove_duplicates": true, "state_featurizer": {"py/object": "rasa_core.featurizers.SingleStateFeaturizer", "slot_feature_len": null, "user_feature_len": null}, "use_intent_probabilities": false} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json index f9ff5a022..886aba235 100644 --- a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json +++ b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json @@ -3,12 +3,10 @@ "lookup": { "eJyLzivNydFRwCSra2MBj0wJzQ==": 0, "eJyLzivNydFRQCara4E4M68kNa8kvii1sDS1GEQXlySWFiXmlVgpGOoZ6CgUFKWWxScml2Tm58XnZBYDFYMlamMBwE8dUg==": 4, + "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESRGtFiMan5RflQjTHAgBtMjHH": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc0lGYl42AatiAWukP7E=": 3, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0, "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESIK0g0bLU+LT8olwkfWA+VDNRhmPTWhsLAO32PkY=": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjC/JSMzLJuDcWAAMcFiv": 5, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn4kozEvGwqOhfDvNKSktSi+OSMzJLkjESI22tjAUwDc4c=": 4, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsKjoXw7zSkpLUovjkjMyS5IzEEriBuJRj9VAsAMYTgyQ=": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktik/OyCxJzkgsgRuISzkuD5Hg3FgApdV9iQ==": 3, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4pMzMkuSMxJL4AbiUo7NcHzqsTsXv2Py8svzi4oyU4sh6mMBkylrjg==": 0, "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjE/OyCxJzkgk5OJYAB9gWeE=": 5, "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn45IzMkuSMRGq6GJuRpSUlqUWoMrWxACCydes=": 4, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERquhibkaUlJalFqDIUm4nV77EAgcKTOQ==": 0, @@ -19,8 +17,12 @@ "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh0FyszEpoFiQ0Ga8/PiczKLgUqoYyQOv5Pqs1gANTiK1g==": 0, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUMqwaanUUKDMUpDk/Lz4nsxiohDpGlpaUpBahyoDMJMNnUC0lGYl52QQcHQsAYgd8tg==": 3, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocqAzMSjA5sVSFpKMhLzsgk7GpdqiHvy8svzi4oyU4sh6mMBwGRvJA==": 0, - "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESRGtFiMan5RflQjTHAgBtMjHH": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc0lGYl42AatiAWukP7E=": 3, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0 + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjC/JSMzLJuDcWAAMcFiv": 5, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn4kozEvGwqOhfDvNKSktSi+OSMzJLkjESI22tjAUwDc4c=": 4, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsKjoXw7zSkpLUovjkjMyS5IzEEriBuJRj9VAsAMYTgyQ=": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktik/OyCxJzkgsgRuISzkuD5Hg3FgApdV9iQ==": 3, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4pMzMkuSMxJL4AbiUo7NcHzqsTsXv2Py8svzi4oyU4sh6mMBkylrjg==": 0, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERquhibkaUlJalFqDIgM/HowOqzWACfCIa6": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDc+OSOzJDkjEcVQkOb8vPiczGKgEuoYWVpSklqEKgMyE48OXD6DainJSMzLJuDoWABh0oEf": 3 } } \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json b/examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json new file mode 100644 index 000000000..257a77819 --- /dev/null +++ b/examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json @@ -0,0 +1,5 @@ +{ + "nlu_threshold": 0.3, + "core_threshold": 0.3, + "fallback_action_name": "action_default_fallback" +} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index 78876bd04..d37d123ed 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -5,24 +5,19 @@ }, "restaurant_form": { "slots": [] - }, - "utter_noworries": { - "slots": [] } }, "rasa_core": "0.12.0a2", "python": "3.6.6", "max_histories": [ + 5, null, - 3, - 3, null ], "ensemble_name": "rasa_core.policies.ensemble.SimplePolicyEnsemble", "policy_names": [ - "rasa_core.policies.fallback.FallbackPolicy", "rasa_core.policies.memoization.MemoizationPolicy", - "rasa_core.policies.keras_policy.KerasPolicy", - "rasa_core.policies.form_policy.FormPolicy" + "rasa_core.policies.form_policy.FormPolicy", + "rasa_core.policies.fallback.FallbackPolicy" ] } \ No newline at end of file diff --git a/examples/formbot/stories.md b/examples/formbot/stories.md new file mode 100644 index 000000000..2200a9a7c --- /dev/null +++ b/examples/formbot/stories.md @@ -0,0 +1,77 @@ +## Generated Story -473632501256446142 +* request_restaurant + - restaurant_form + - slot{"requested_slot": "cuisine"} + - form_activated{"form_name": "restaurant_form"} +* chitchat + - restaurant_form + - undo + - utter_chitchat + - restaurant_form + - slot{"requested_slot": "cuisine"} +* inform{"cuisine": "1"} + - slot{"cuisine": "1"} + - restaurant_form + - slot{"cuisine": "1"} + - slot{"requested_slot": "num_people"} +* inform{"num_people": "1"} + - slot{"num_people": "1"} + - restaurant_form + - slot{"num_people": "1"} + - form_deactivated + +## Generated Story -1191751619141022584 +* request_restaurant + - restaurant_form + - slot{"requested_slot": "cuisine"} + - form_activated{"form_name": "restaurant_form"} +* inform{"cuisine": "cuisine"} + - slot{"cuisine": "cuisine"} + - restaurant_form + - slot{"cuisine": "cuisine"} + - slot{"requested_slot": "num_people"} + +## Generated Story 3504123482747434858 +* chitchat + - utter_chitchat + +## Generated Story 2890106331063190341 +* request_restaurant + - restaurant_form + - slot{"requested_slot": "cuisine"} + - form_activated{"form_name": "restaurant_form"} +* chitchat + - restaurant_form + - undo + - utter_chitchat + - restaurant_form + - slot{"requested_slot": "cuisine"} + +## Generated Story 5856759686029092329 +* request_restaurant + - restaurant_form + - slot{"requested_slot": "cuisine"} + - form_activated{"form_name": "restaurant_form"} +* chitchat + - form: restaurant_form + - undo + - utter_chitchat + - restaurant_form + - slot{"requested_slot": "cuisine"} + +## Generated Story 4051894201894602515 +* request_restaurant + - restaurant_form + - slot{"requested_slot": "cuisine"} + - form_activated{"form_name": "restaurant_form"} +* inform{"cuisine": "cuisine"} + - slot{"cuisine": "cuisine"} + - form: restaurant_form + - slot{"cuisine": "cuisine"} + - slot{"requested_slot": "num_people"} +* inform{"num_people": "1"} + - slot{"num_people": "1"} + - form: restaurant_form + - slot{"num_people": "1"} + - form_deactivated + diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index 887e2e2ea..556b15621 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -11,6 +11,7 @@ from gevent.pywsgi import WSGIServer from rasa_core_sdk.executor import ActionExecutor +from rasa_core.utils import ActionExecutionError DEFAULT_SERVER_PORT = 5055 @@ -68,14 +69,14 @@ def health(): def webhook(): """Webhook to retrieve action calls.""" action_call = request.json - # try: - response = executor.run(action_call) - # except ActionExecutionError as e: - # logger.error(str(e)) - # result = {"error": str(e)} - # response = jsonify(result) - # response.status_code = 400 - # return response + try: + response = executor.run(action_call) + except ActionExecutionError as e: + logger.error(str(e)) + result = {"error": str(e), "action_name": e.action_name} + response = jsonify(result) + response.status_code = 400 + return response return jsonify(response) From 7af9fa32f94301437a6caa678eb735b4f6bdcbf5 Mon Sep 17 00:00:00 2001 From: akelad Date: Fri, 21 Sep 2018 18:22:01 +0200 Subject: [PATCH 032/112] move actionexecution error https://github.com/RasaHQ/roadmap/issues/263 --- rasa_core_sdk/endpoint.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index 556b15621..9c81b16f1 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -11,7 +11,7 @@ from gevent.pywsgi import WSGIServer from rasa_core_sdk.executor import ActionExecutor -from rasa_core.utils import ActionExecutionError +from rasa_core.actions.action import ActionExecutionError DEFAULT_SERVER_PORT = 5055 From 5141295ef0b1156fef45e8e383a7c51a79d25385 Mon Sep 17 00:00:00 2001 From: akelad Date: Mon, 24 Sep 2018 17:44:49 +0200 Subject: [PATCH 033/112] fix import https://github.com/RasaHQ/roadmap/issues/263 --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 021201f90..c51dfc7f8 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -9,7 +9,7 @@ from rasa_core_sdk import Action from rasa_core_sdk.events import SlotSet, Form -from rasa_core.utils import ActionExecutionError +from rasa_core.actions.action import ActionExecutionError logger = logging.getLogger(__name__) From 2a261f047992958e1d6c903cbb0dd1f6d20cff2a Mon Sep 17 00:00:00 2001 From: akelad Date: Tue, 25 Sep 2018 10:34:10 +0200 Subject: [PATCH 034/112] upload new model https://github.com/RasaHQ/roadmap/issues/263 --- examples/formbot/domain.yml | 5 ++ examples/formbot/models/dialogue/domain.json | 2 + examples/formbot/models/dialogue/domain.yml | 5 +- .../fallback_policy.json | 4 +- .../memorized_turns.json | 28 -------- .../featurizer.json | 2 +- .../memorized_turns.json | 27 +++++++ .../policy_2_KerasPolicy/featurizer.json | 1 + .../policy_2_KerasPolicy/keras_model.h5 | Bin 0 -> 70080 bytes .../policy_2_KerasPolicy/keras_policy.json | 4 ++ .../models/dialogue/policy_metadata.json | 13 ++-- examples/formbot/stories.md | 66 ++---------------- rasa_core_sdk/forms.py | 4 +- 13 files changed, 62 insertions(+), 99 deletions(-) rename examples/formbot/models/dialogue/{policy_2_FallbackPolicy => policy_0_FallbackPolicy}/fallback_policy.json (52%) delete mode 100644 examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json rename examples/formbot/models/dialogue/{policy_0_MemoizationPolicy => policy_1_MemoizationPolicy}/featurizer.json (77%) create mode 100644 examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json create mode 100644 examples/formbot/models/dialogue/policy_2_KerasPolicy/featurizer.json create mode 100644 examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_model.h5 create mode 100644 examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_policy.json diff --git a/examples/formbot/domain.yml b/examples/formbot/domain.yml index 824da6136..e13b51c1d 100644 --- a/examples/formbot/domain.yml +++ b/examples/formbot/domain.yml @@ -13,6 +13,11 @@ slots: type: unfeaturized num_people: type: unfeaturized + requested_slot: + type: categorical + values: + - cuisine + - num_people templates: utter_ask_num_people: diff --git a/examples/formbot/models/dialogue/domain.json b/examples/formbot/models/dialogue/domain.json index 45b0d7b66..c6835f17a 100644 --- a/examples/formbot/models/dialogue/domain.json +++ b/examples/formbot/models/dialogue/domain.json @@ -6,6 +6,8 @@ "intent_thank", "entity_cuisine", "entity_num_people", + "slot_requested_slot_0", + "slot_requested_slot_1", "prev_action_listen", "prev_action_restart", "prev_action_default_fallback", diff --git a/examples/formbot/models/dialogue/domain.yml b/examples/formbot/models/dialogue/domain.yml index 039849cbd..b9d935609 100644 --- a/examples/formbot/models/dialogue/domain.yml +++ b/examples/formbot/models/dialogue/domain.yml @@ -27,7 +27,10 @@ slots: type: rasa_core.slots.UnfeaturizedSlot requested_slot: initial_value: null - type: rasa_core.slots.UnfeaturizedSlot + type: rasa_core.slots.CategoricalSlot + values: + - cuisine + - num_people templates: utter_ask_cuisine: - text: what cuisine? diff --git a/examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json b/examples/formbot/models/dialogue/policy_0_FallbackPolicy/fallback_policy.json similarity index 52% rename from examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json rename to examples/formbot/models/dialogue/policy_0_FallbackPolicy/fallback_policy.json index 257a77819..4357d9cce 100644 --- a/examples/formbot/models/dialogue/policy_2_FallbackPolicy/fallback_policy.json +++ b/examples/formbot/models/dialogue/policy_0_FallbackPolicy/fallback_policy.json @@ -1,5 +1,5 @@ { - "nlu_threshold": 0.3, - "core_threshold": 0.3, + "nlu_threshold": 0.0, + "core_threshold": 0.0, "fallback_action_name": "action_default_fallback" } \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json deleted file mode 100644 index 886aba235..000000000 --- a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/memorized_turns.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "max_history": 5, - "lookup": { - "eJyLzivNydFRwCSra2MBj0wJzQ==": 0, - "eJyLzivNydFRQCara4E4M68kNa8kvii1sDS1GEQXlySWFiXmlVgpGOoZ6CgUFKWWxScml2Tm58XnZBYDFYMlamMBwE8dUg==": 4, - "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESRGtFiMan5RflQjTHAgBtMjHH": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMliBaK0I0Pi2/KBddc0lGYl42AatiAWukP7E=": 3, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8oty0TWXZCTmZRNtFYbq0pKS1KL4vPzy/KKizNRiiPpYADwGTqo=": 0, - "eJyLzivNydFRgJDVtUCcmVeSmlcSX5RaWJpaDKKLSxJLixLzSqwUDPUMdBQKilLL4hOTSzLz8+JzMouBisESIK0g0bLU+LT8olwkfWA+VDNRhmPTWhsLAO32PkY=": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjE/OyCxJzkgk5OJYAB9gWeE=": 5, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn45IzMkuSMRGq6GJuRpSUlqUWoMrWxACCydes=": 4, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERquhibkaUlJalFqDIUm4nV77EAgcKTOQ==": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDc+OSOzJDkjEcVQkOb8vPiczGKgEuoYWVpSklqEKkOxmfTyeywAVRqbTw==": 5, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocpQbCY2DRQbSie/xwIAUdyWZQ==": 4, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh0FyszEpoFiQ0Ga8/PiczKLgUqoYyTd/B4LAKOql1U=": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUMqwaanUUKDMUpDk/Lz4nsxiohDpGlpaUpBahylBsJr38HgsAHCGW5g==": 5, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh0FyszEpoFiQ0Ga8/PiczKLgUqoYyQOv5Pqs1gANTiK1g==": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUMqwaanUUKDMUpDk/Lz4nsxiohDpGlpaUpBahyoDMJMNnUC0lGYl52QQcHQsAYgd8tg==": 3, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocqAzMSjA5sVSFpKMhLzsgk7GpdqiHvy8svzi4oyU4sh6mMBwGRvJA==": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlgBpBYmWpcan5RflIukD86GaiTIcm1aSjC/JSMzLJuDcWAAMcFiv": 5, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FpJcn4kozEvGwqOhfDvNKSktSi+OSMzJLkjESI22tjAUwDc4c=": 4, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsKjoXw7zSkpLUovjkjMyS5IzEEriBuJRj9VAsAMYTgyQ=": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktik/OyCxJzkgsgRuISzkuD5Hg3FgApdV9iQ==": 3, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4pMzMkuSMxJL4AbiUo7NcHzqsTsXv2Py8svzi4oyU4sh6mMBkylrjg==": 0, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERquhibkaUlJalFqDIgM/HowOqzWACfCIa6": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDc+OSOzJDkjEcVQkOb8vPiczGKgEuoYWVpSklqEKgMyE48OXD6DainJSMzLJuDoWABh0oEf": 3 - } -} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json b/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/featurizer.json similarity index 77% rename from examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json rename to examples/formbot/models/dialogue/policy_1_MemoizationPolicy/featurizer.json index 9b5b69869..abaea9c5d 100644 --- a/examples/formbot/models/dialogue/policy_0_MemoizationPolicy/featurizer.json +++ b/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/featurizer.json @@ -1 +1 @@ -{"py/object": "rasa_core.featurizers.MaxHistoryTrackerFeaturizer", "max_history": 5, "remove_duplicates": true, "state_featurizer": {"py/object": "rasa_core.featurizers.SingleStateFeaturizer", "slot_feature_len": null, "user_feature_len": null}, "use_intent_probabilities": false} \ No newline at end of file +{"py/object": "rasa_core.featurizers.MaxHistoryTrackerFeaturizer", "max_history": 3, "remove_duplicates": true, "state_featurizer": {"py/object": "rasa_core.featurizers.SingleStateFeaturizer", "slot_feature_len": null, "user_feature_len": null}, "use_intent_probabilities": false} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json new file mode 100644 index 000000000..0f6bcf65d --- /dev/null +++ b/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json @@ -0,0 +1,27 @@ +{ + "max_history": 3, + "lookup": { + "eJyLzivNydFRgJDVtbEAMW4Fvw==": 0, + "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlqiNBQCLfBlE": 4, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FprY0FAFA2Ojg=": 0, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERCLo4FAP4oVpY=": 5, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDc+OSOzJDkjEcVQkOb8vPiczGKgEuoYWVpSklqEKlMbCwDDQl8b": 4, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocpQbCY2DbWxAFCPWrI=": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh0FyszEpoFiQ0Ga8/PiczKLgUogRsYCAGl2WrI=": 5, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUMqwaanUUKDMUpDk/Lz4nsxiohDpGlpaUpBahytTGAgBvXlqy": 4, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocqAzMSjA5sVtbEA8CxOMw==": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh2Fajw6sFmBpKUkIzEvG1k9yHn5efE5mcVAeYjqWABix0CC": 3, + "eJyFy9EKQEAQRuFX2QeQuPUq0jZto50wq9kfF/LuRMqd6/OddhcFK3yIghAJjavLqnCz8eqNM2gxunKfbLrTUbh3QSQdvp4CJKkfJV/9Vy8Am9e0JTPh/PjuBNMuM3E=": 0, + "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEoti09MLsnMz4vPySwGyoMlanUUcKouLSlJLYrPyy/PLyrKTC1GV1+UWliaWgyii0sSS4sS80oIWBULAIxrNuo=": 4, + "eJyVi8sJgEAMBVvZAkT0aisiYZGIQc1qktWD2Ls/RK+eHsybKVdiQzaw1nNXuDzNEjcKzhDNUIDDEkQI9bq2xD2+4BRRz1XzUTzbN/a1UWDoSQ/5X/pSaIIMd1ztazw9dQ==": 0, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LRNZdkJOZlE7AqFgA0Vzxm": 3, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsAs6NBQD1GVVk": 5, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktik/OyCxJzkiEuL02FgB3gly3": 4, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4pMzMkuSMxJL4AbiUo7N8NpYABQjSp0=": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsvjSkpLUovjkjMyS5IzEErBMrY5CNS7l2AzHpx7ksPy8+JzMYqA8RHUsAMQvPh4=": 3, + "eJyFyjEKgDAMAMCv9AFFdPUrIiFIxKAmkqY6iH+36CzOd93J4iQOPqHMbWiqOobNaAej5JgNi41q60NXDJ8fB2cVWDgV/93ZnQxEDzVjSu/vbyaAMj8=": 0, + "eJyNzMEOQDAMBuBX2QMswtWriDSLVCzo6Lo5iHdngjg4ODXt/3+tVkuCJCCdob5URZZrNTFGCCLIQG5xzBb9GW1a3X3GOaBP04sJbEje2DRiHcFg/VF+aLpGhNbx+HLnfuFfz7/oVu95Okn0": 0, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8otyIZpjAVlOLbk=": 0, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHQWY5pKMxLxsZPWJySWZ+XnxOZnFQHmCqktLSlKL4vPyy/OLijJTiyHqYwFoHDfa": 0 + } +} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_2_KerasPolicy/featurizer.json b/examples/formbot/models/dialogue/policy_2_KerasPolicy/featurizer.json new file mode 100644 index 000000000..acb94e308 --- /dev/null +++ b/examples/formbot/models/dialogue/policy_2_KerasPolicy/featurizer.json @@ -0,0 +1 @@ +{"py/object": "rasa_core.featurizers.MaxHistoryTrackerFeaturizer", "max_history": 3, "remove_duplicates": true, "state_featurizer": {"py/object": "rasa_core.featurizers.BinarySingleStateFeaturizer", "input_state_map": {"entity_cuisine": 4, "entity_num_people": 5, "intent_chitchat": 0, "intent_inform": 1, "intent_request_restaurant": 2, "intent_thank": 3, "prev_action_default_fallback": 10, "prev_action_listen": 8, "prev_action_restart": 9, "prev_restaurant_form": 12, "prev_utter_chitchat": 13, "prev_utter_noworries": 11, "slot_requested_slot_0": 6, "slot_requested_slot_1": 7}, "num_features": 14, "slot_feature_len": 2, "user_feature_len": 6}, "use_intent_probabilities": false} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_model.h5 b/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_model.h5 new file mode 100644 index 0000000000000000000000000000000000000000..0fb6b4f2bdce5ff6532111616ae60fa3c6aa3ae6 GIT binary patch literal 70080 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
H3mQb z3tGoqJ-8cFIeXa}1rs=z%%N+B?c|NgIt3FSSwe^KSzJ<+1Abg(TB6a9=-pMP)7u5f zhBzToGG;ULY-u6#G}rS&=H}sft$aMc`XSRAdYDZ*n!`Gq%)+3-K4ep36;un2$JO^_ z=#uu4XtH%ED8>}PV2?=5pF9LwTGCnjm!G-q)2E@vm}^inH5Rk|6oF0bOZFF>W2O{G z@nYf&(6;EZ)x(4V(Ei=De0i!WofVq_!@|U1L1Wp24B6Cpi8nBaU3t1e2y{wGIR$qiHSv1bLYn0_5x zTGgLGMOI^G;@+VnV*(WxQu(V8w!5Xed05eUnSsXET(c z(Le*v*!X}?-@=MI$D7b+vJ6D4DAR=aT99c-VOw7vDa)Ozhqq2##hVuenf7JA5TS4d zM7BOa*^(RJd9puI?Gz?wmHW{4ewSeNy`!kEuoj$+Sg0BI7B9!%$Ee0@_}bz+n6I;e zSd;m@o3yNs)Mbm)}T`6SD=*PpG`4QF#2i`4Ajnm8SGv5L}MtW z3=r)R8N7{VHO#1$?_t%ULipI9Uo#jVfospSvl|OWl8!nZ@+28 zsy0LNdiMb~tS}uDpkogtR!)OZsEqx*tvT|I^dZ)F5t*5LZ|8z2zc2ju#CfR%_dT>a7jlWw2E z!r_WE>!dpK-C-qDv1lHSk6HzfV<&@8^ByKI@EnFN@}wqZ#k_Axmtb8Lh05gqJf(^O z^m}#&BWn=J+}8gAFT7pAi?@ZH?FG#FIo>oo;y{I=8xO9Z4ungkLUh@BU5~MYa#$h##}%JVuHrjaLF$@q&#nqrz`+8- z+|ViqoO65yObX9~*M=8BY~w-@-Bri}&95^DHv<8;{*&{1E)PXW3_lC)fe@ z_1vA|Gf}r?9BSKIl6{uz!Nuz%45gidR}o41+4v3fM)hBO{mX~02g z*)aZ?JMQoZf#aZupT9Z4x5p;{Wk7H8m9y~jHlp~$!% zLEoT2Ja$nX#;9Lm^@6s+?y#?{Z1hkXZl{ZFM>E+g{fl{RQ})B88z(XO)MG|7rVliQ z%F+)zs_}5!YG~nCbWU=0g_|Aaz;(#OQKf_F%k>w*@39!XeSZSX`xapFqdc@z*v+n) z-pq^>1GE|V9_zoB@i^Y6FwJ^0S+P|ER#%R|)0NdQ^;8}+X5BjWNxA@$JT(!X4oySm zRt5`?N3$zV34xCcKerKj9Xyw$V?XaOda=6Q`kK~SY<|&%gOaYX!j*!Id72!NzFo?; zKlI@3^{^m%o-dgJ)7C>^>4j*E4QFueFtE6(KDEQ=K>^cSdGue3sRHy^SIUGLiFqHO^oKlb*z%f zb<}>a24zFfqlPyJH>z!csXwA%L%RpjtTRM^IZI5s^#zu%&P7R|A8a#MiCn(G_p5VF z$+FC1*uSt6C#RM`no%06R2V{Ez7N?mYXw`Bti#;!a%Y^C?xEPmPjF_f8N9Ta1L3cy zLv+t7d+u>FSHmkGB!=yU^k)(@ ztf~>`*t_H4Df>_*p%fPlAtb^B=+KYD`95BPsaLyLt}e3<;_HPN_pb(I<%)A~Z>~0m z%PU~?kezUMgAzHQHytMh=|jWG2_&&VpS66w9Ctc?2JN#OnM>B9(3#o-dvgvjb2eUQ z`tI3}#^b-j8R<<}=2!>U)mO6Gc1U9_d-|I`G3xk)d%nD46qLN7&iFIyWv2njS_or5?V|Viu~}_W4CCTDj2*1$ zgsW?Y5Qj!(Ue5D*e48*0?4}x$F=LD1NZ)A8dvgVpg4QypEjHkN{@MSqEr(#!z=8_G zx3iN^NTa276C-YL3SaDN;i-qKp~Dh^Ls~sZX04 zj#^iZjl!k3FJsc@0C1E{XClXYL%}dHtZ}c#@_Cyv;G`cq8g=69_$FBVBNa3&&M>2n zB;chd={R_%Alc-sN~hOuV+-qz@TrG3_f5Jzb9J&6RK^COXQU3c*JLxYp60ZGMP_i_NhoY)<#l~?|O?Mymd(nr?``!-8N&`Sgrw|q^ z#vya)I(z7A951BlsP*#$$01`}G5QW2K#$~_Lglgqyb)i-6l~aqrUB*P^fewkVsr7a zU>>{Z@MN4VFHKq@6HKe7^U6&^(DddZ6r1Zxovbdv)NnwD!gEl4W)vj%sQ}N|Qt){e z22qC`tk>_1CJ~~KA)zgXMYl?BEHj2$@uB=Mc(c$ zOObz6LdT9&BjE=QV(F{BtT%tpYh%1X|LQ83d@G7A9(NeZ+XCUj17}9Fzy{u$Y~?!C zY=E66TChnW1@z+gV86Lz7^ST-xbV(l);@X$9@u-LLS5UI7VSREEog|v*OePlD>oc= zsl5T=r)StKaR%Rvy#OZl1K52`o36O^5ZsIh5+akuddoz^ z4*t1TOiL}ai47&EG$K)URuk6mdcKVwcERNyX+_JQ>6 z<6+{Bp|E9T5bOCwAJ64Ef$#K@4703^`&=R&U1OHxmRm`f)4vJkUVjB=0|VfaswdhH z+6cB%jwr0BND_AsVe_TDh}|_+vSI!hX!{VrOw;0@(@h`6@Jnc*kIQhhxz9h}Hc_IN zOcY^?t1&fvEk*1u+oFv$nawZL;-p9sb&7zOI^u;rnS=t+5kZKmp zJXX)W(~!Vi?mSJ%L}EibY8Wg_%%?Z7i}gINb@ z19JYOJFHzb5{=^XFoCXTR}MP?f`OJGRy_`6bssUqA1!3ly^o_o=6W;`+lLG19%IhT z+62|}TxiS<2bBM)hMRRo=&tlzFmUc*CggkwGqPVbyPobxO?Mqw=J6TFM@W)VPhoVs zZ3}mDwy}GKvzV`|lkv3K2u!s2!K`=C0@dfUanPD1j5Tt=O>>j+_&QalaN~Qne!B5&hQ52Ovl(Zd0O;LgUr8^gT;cTbW~0( zG(?M&GnX{6|9A&Dg+pP?l%=?Ja2-}wC}U-fE*997FhXt|BIa9+8FmfOe6|>4pB#Ya z)usHHNQ{4`Fpiu(%Kr|5$0Rfh%HXZGNv3;`OS92O${5+j#vt1mg4IC5L$yPL=zb)RWp9f!#YQ?bB%GYIJIf~L_Axud-6cxjeG zl$m`3E_xq@q+6R9fz@I>!MRS+kR3|=J|1L)EeMpXS0kPbhn`jD*AHxe1-swu z;m31BP`6(qbM9j^?(;0eCGm#1XW}=kRI7xm+eGLpeKW$CyV93Sw0V0rm&0)bY5bBp zj=4T%J9~YEJ48>g=9wP4#)JgPlf?qkKRif6#`olvLSIt018gt&GAGh=8SW|ZP&R> z={F9hxr`)5gJy$wjTX*bdYSz^Lyly59cMHy-AB2?RrvX9U#M|7hC99pKqbGv=Sb6K z=IG8imUC_+Nc+~p2w`&~Y`KxS|7|G7=DCqM%j(hHPJ!C(Ob3%O zF7PmCKL~C%B4+&hFkUOamTIvip2!&jt^0O?z`Z(F>~jE{7^g^yV-RqjnU?eX`Srcc zX5fEr4SQHfmx;CJ&`!A~6n6XyM;zoSZ;%r`^~w__X!V6JnS9wfs181h%CVvI8QeXj zh@NvN({J}!uFPf?GP0l)n4qsPS5K5(w8o2}EVlFOg`P35y>CG5{bJbO zH

PLWo!nS_zXS+VP~mEbeRHXe}a3Aw%ONZhDr=J~+G@?#VxgHRU0Y$&Y=8D0X7U zlF4+|u?+YjIEXxOi^mVQ1DN;a*{CGphGVs7;E|&*S<0D71BUd$m!8!)Pk20=#wIZu zP5hb%-gsEt-w0oKeB`>uZbRP#x6rIgm{=aW#oX}70OJlJdS#XhtF&bT+LCBp)^ubRTnH*;)Wi3&d&>Ht z`jq1^nZuzvc9mG0YepAa=dvav=dw>XB{4cRMbNzLG;HPQkvx@bkYjaGG3hfLzqJIi zc30!c{BR8VsDptarey5fTIRrDEvmobEpzFk2z?f}1fGhTF~uvDINyu#>>&nJC9j-Iv)BN*5e+NiS+K47MT4#jY*St!5J4u!|4a> zFznST=1{{*;GTBDr!l**A|ehX=8wjKeUz~_MU7rJO@L>&ghD+$i9Q4TEv*#U0lBW z$9O8RN{Q%HBr+e<3K{>FO^`0J9iJY&3mTQL*oVpcpTFF@LjAe!#s>F6dA2y+Q7dy;`iw=(hSnG6t9id4mO4vvxY)rGQq zwAw+lRT(!+SW^WPPZ*&)lnk3`&CZ@vi(V$1VA9hSY{wFDQUDj3{xvIbvPdNBOs=tg z*Ve$mEnZY7u$DX4&KAW2iy>iI0hZ01&mPE@qa#{Qu=NksK=+kCp))DdIp7h7tpC7H zaNG~$_6U;YGV9qh8%m%^%9356n2+^oa?I=e>G(x=Ib(e*6`P7K!R+}eytb^Q&Vs$f>GtOlRILcCYk(_UJwx z+L5x8jd@$nPTy$7F1)BtAI(0`T>Wtr2AJ*R`I$RI?Ce^WcS91_Y@15lTZe+5zzoKD zfF^u<5ryYE$1?_QA{ZjH9;5#zaNnEo=|{fH%dKLZ*}sJ6chTH14fo_D7aOzDFIzyB=Ud-ij0d z^JlP)jRZ@D*Eps1CLGI?!Z&@aUKNY~DCkZj?i8nAQ_hS<(Lu&h$y=U^q_m33l824@aDuDy;^ zhfD#Rcm1JeUMiRMJ;Veqo(*C`rc6Q1Gp<2x1NXK8U^P}v}7q6JzPh}`K&<>ZptYLI()G(0U11-Cp z;MFD(`tph_ij*tTr&8m1hNg!wVWd7Yu+5f4WS2sL}f1LX!9Uamhurn=Jf-nC&CExh@D~{(e?t-QjcxrMyH_-V4bNWsto9V?R ze${4}c5N?IiVwm`1)s1z=`g$h%w@bCIDk~#aif(xfE!||f~6^GVCIy7?&6xvvc(B7 zb3!3<--W`ZP8}S%w20lLm(9I7aWv!gl!yB?w&6oTxu& zUFIkTFZAIp=+BUWoN7o5U5f+ei9y8-8KS(cKQ4+81WvLCdA5XQJWfp@^CL=_sh*0| z?z=T8&Jc#V4+l^^Q4Si~)x)|SO-wxBhWf~yViGn=Q-?NVD6!4so#W&}Le_g2Wl+Ej zHmSo>*G;J2zlPbeY&S!$R{Q4apU7 zM&%2mxV4Cp?ROTO-OJIhy@nT_{TwbF?aO|uz6KilVkFEu6U4Oo;eMyf?C1~uX{2T; z&i$ee6QK{)%U{6sS-A?Hs;_4Q8|3Ns@UwU%FNHn*UXRLc;@1oc>l0JD6WKv5`)Ji* zEE*hy`+pq9+7BgcQ0yTT_$~s2Ruq6jzz?+2GGG@AW#iV>vh=A*El3QvLCI;>=&?+V zn2cP9Il;)>cG009<@o0)m8+R)TW>QB&)#66HVZpOTGN$cx503r7u(o8kOs}OV5F#Wn1JYX}x>tvqQ@+v`8*`iKk!@SucoV6Ia_6qlcm?w8YvjX&K8_wR8%L1K) zhcPjFAqWU~k z_y!M6etUi$S39A?T9`ml$IYZHc$Gi;XY~F~Sj#%d0$jnzLWs8dk z(E5X3xVy}qhE))77-mPF$z8+nOi8>_;g^(0kMnAC+nolV} zKc(ZWabX4~er;gC#*315BL|a-G@J~+$H%*KIWv2^A4)!+0*PY=a1Z+4#Zf*R@Kyqc z9j0=Z(a^MDzTdlzWji_ee%xl{NXWAL_B(+|Oef>5zl_!4ua&aj2To>3Li~AEC~a5` z>lW>Ro$GR8Ysog8RwhJJBHp2K-dFIR{Rqx<42I;*dvTQQTDCm@2LJm<(!{7GiEtLn zk&ARTBO&`0D+TT`M=G^>$*F5#Rge^w6Y9fCt=$Xa{SJXKX97uazQbmZdBrF@k7Z=7 zEO6G-Cs3PN0#c>+5EQur2JTbDW0LD}$_NL#bVf2G6S)>EuLnV^yBC{LZ3!1IDlkTe zkvT7$f+R|TJ#uX&&VA5?U*@jBH+!zKIkQ|yLuEV`b+)i6Qyej`Ed^44s9`-d;3uQ! zp`WD>9eSBVrYfI?^{MILbP}0I+g4T-7AiB7Tr9Y9C%3?Z4X2S9oq-`pp54T`-D)fnHWItZ@N~!;gy)S{rs_XlfDPu$#izY+3t}|T|*V%`P(nKT; zB$bi|O0!ais0@WDLlRNuh%#JfA4EwiLL;SF8ighm-?`trzVCVOd%f?ozUN)v_q@Y> z*4pcT_HfQV`*-%)!+-zx??Al)mV4p}33pq~uaubE!97q0%rt%gho`OtzI$SToXi;* zKW+kbpG_(QB6bXpeO8B;l{!vQF`7&9wY zzk%EX)$nvw8XdJF6yU@5G5LzGAZqGI*_W_;hlQhH;f?2DlW8Q~;cp2xwka}u^335> zS%2U&DIeDIgMqD#0qOnaGU&{E3cJFh!LXTjVCi@xAeX%z-l)G1vfpHboqbEGPFe+S zG<78ff*H`Y(Ga|Q{|RDG2)F};k(o2Lg5YJ9bcLBVjg0sp-f*-V$XiyC0~XTEohgc- zf08>enk)-~&fXO|YsNs8o>+3jl*_T{3zdnH*8XTc@UOqTQ z4}zxpB*6E%1=2nV>BqqJ*8L5+FLs=Yp3Z}k{0kZD5 zNv$=3u>623^^%^#=!ah;H@#g5;(Uw1`%U>^*I8rgFq{C#KbZv1?`{L%qvAl(1|z7# z>bo7ae1K&04zO{38?BuDgS20pMOS~`D$4aZ1Kt}nlg;)g;gi}7lGqssj9RWhg&boz zaPuB0oJfl&DLFyou8FYmyDGD(t(ZI>y^pD}EQ2ffJHQd!N#Odzoq)U;z>Z~_cQe5*iKk-P#Kgxsf3H(#WPz+r2vmN z$H}(m3G{H&UBK{ykPKn>(aH_CWE?j~(TAH7#b+*KjBM*vc+>kf93hDYD;DU|qVw^U zkEKV`npxwR$O>Tfp$6$Lq@&Hh4Ic9;Q=6I{euXVyTt!H%-v6alpl(O^?oG)(AGgJ0rOLG}C` zU^#aR^d|;EaK|mUq)7r*HQbpmG)JO0j0C&hlH%$xJDBu&5ZEr(0?sah64pM1&c7ZA zVxA?y5fzW&l^06ltCRTPnZ{F?;NA;c`V_$@$zIq|^og9CJCY1}CT65}n=={3-qiDG zRchGgPw+sYJ;Y5+j+W8Is)`2ZGxLJr$Y<9iF9X}CcUstjaj&U7TAAAnlaSU5!?G1QL^c! zm6Kehz?@Yw@J8lUI%DAh@tO$&>hhihXcOTNR^)nu)||$yEnbn98NbV$TzvR;opF!Nf5K zV9zKkP*^U@P@84h{b;fvdA=c)BtL^0_|XI>tWyDD+u645M-DjNr3$ue9|_~iYoUXQ z0yr)s&4{inq=%x4A=iKh<~fvr2cdxK-;hM>O}$Aji+c>0yv+k`QuC~~S`KHVIuRJX z$O)8}?t?Ckv2dNe5773ZD2-X6V1vvHx+QYTL$umLwXM%$dq$79Jwk zkfT7EOb~g*r;g0cECe^FCP02~8_@qMf%!`uD06nNu$$gdu*Ee5PCL;CtGrNZVc$b~ z=hub6V*D~V>WC_cp0*CS*3Kj?ti3aA&@ z15%QrBsLje>AlPC#SeVC!GK7KHfMPk-e%kb)4t}=rMGJ&>tl|A%brqXq`D5U=4gY1 z4Q=4vwb3x#=`2__TwnaSTo$~%DS<`BSs*C72P7Vt!+c@q{gyM#Dg2%tv*_?Sa5-5l z#!GS}@mc4@nk?7T=RO1S=)pCh=qNi!7F7YW1B>C4>yJs3j!}SDEJIlYgloy;cZ3bW!@c z(MakptLMG{TngkaQ=;b9wE$E11X?L2+iGv@WV%UOhf3Uc6P{1~LQi^dO49oF8PHxe z1{(JaN&Ipi!J8)%p{#vAsqgZF93K=7Zcr)kfJrC(;$H!`S;bWL+@3^6D!w6WfetL* zGzY5OT?T7q7t${`=>yLt+F;f`#zFj?GSp6Jiw~}XLgNM06uko z2b`j0i6q4dsJ0viYvu`{^Od6@*|`&(d-Dz+I+YBq7n@Mkr}Jq?xlFn)&V~$g(uKb7 zZQ-}zm$31D3)t@#1g8f(P_L+3+O<3mwzb>QiJzW<9P&I^+m;CStPOz!OVr@F+xlRP zRxfQjNepM&q(W)UExiv3w$jZe1{77>T{KL zT2KSdx*nkIO-mkHBvyd<@xv%xFped1*QUTB{ZRgrnW6JAKwgpUJMsh}T< zaB*KY+$wh(7|QEWbt!kr*2^+fXtXUe!R!VYv(66QGLxf@w8~IomRIGK!#mhFJOhsY zF_M~{Qx692p9U$XDUSb7Covov{4&=ei)33o5om5GA&1@!C{}Pz|E{M7#SN3rSE=*U;WEqm-zx93fK>y6ZIfw zpc&|XJ^{2g#?iKz6*I?+1_5c(aNP16pjdS#6~8$i=w`hj`$K0i(%av|dZ_@=I@yA> zUXOvC=t!_zJ_UYW9u6+_H^REPdmwXc7^VK^DOvb#CBy5gqpgTD@YZ~DdYzLlfRo~Z z{kGu%`UV8iZhrf0>2AeD)La_j+-hb^hPn(5HF=((v9XT}!FN_?LBQ-Qw9&qKK&NN~(2~62UE}DU5vKRQi2qB>iUF zakymvb8tg#I1I6tqAa4FnZcNQ!2UwGW!$(Ul9{3e%JW<}Xc3n}1Gi`(Gbs_8W_Q4t zQ}das+D(k@lsMp!l1_hiP9nFKz6LK%3P^vwNibmcI_Q>=1-soMfMX>OnyylyY;>Za z&udvKzJ4Rz!RWwl2N5WIHk}N3t4BY4dkiREt$}`xCA72T2HpDX5Pame1msM)2;W7X z0XA#Gn5y)3ROa33R^hV-;f&Vvv|6Y%6}m^C@>P`3h5ld2>UTE4ef&&v{q->bQ{4b$ zbvJ<9+xC-r-7;YG#!uw^_bPC4XB2IduS98B&IQ%GUcyw}Zji9y5~*SC0CFEJ0-1Mn z#pC#IVP{y7QT)SBYfzn_jYa`C913@-cX> z?<0*@CD4Iu57S=SCjb%4gF?YDdd2uEz}0N0m(12=;=qvAFxgn>zE_{zm7NB@v?(+7 zWIK7RaTHt-BBoaNRY{2V=fJaLM?n1(3KYa0g@pG9`n13sCf_Kdk!cTLa^L}=QhEy( zkGf5FEKZ^8&TfEBu0O!5o7(dPjUFW(7!PL8vzF;-<3eX1w5awB0+ zqYQj#xeeBw-bR{r7%^YAXu_;zQyER7E35(BHz0v9p0a z`Hi%i-#j>GESG+3R1Ibyo<-{4c>z{gp99%lV`+4<6r?3%3< zwfvh56T15;oilh2z7EU=ZqmVE7jG@FIK3b6wQb0yEkm&Ot~q?^bOXHXpGoc7pASQx zG{G2KU-7rvOkh7Qj^3pp%jyWqB*TxUfWQfN;cW8?a%Y}ClxOzCdu_&)%Dt^*;kX-g zkh_5NIQ0Z>y!Vmpxj<5dFV4Zmx`x2Sz6HjuEu*IwQ?Tm!HTu(?BQWA}1YH+gM!pG2 zg59wV;_4|n^gNw$WV%ZXc-6}C3Dld=qb%N#-3v~F@w4>l#q2y}zs^CJIAId7XXl3r z8x7zUtyDU5coppL9Yq!FI10MwFJNX^-nJ?YBPi(y6s7-l3J8C^1IjF1tU$*6$I0d7&hTClRc;O4*;@yX3x|Pnr5<>8s0h9v`3&3* zzX+1&od!O=(STo63X81YaqkJ#OK!-G4bb-ggr`P=UkAF7lwLMh2*L zh{5?FMp7wl3dNd7N!N($aQ>$n@J#GNH@pjjgzZQsCZrg8-UEO?ja8r6w6He5t?)cZ z0cI12F=aZ*;KB1PV3sHZuCOQuhvzS)np3Tyk!2-#e=!T3#JJS{(MLhV8ABNIY8_d= z6~Ye1+04M~`}9!CH@fLsIINnRMy{LLMeE`gU{=a28m(%t^a>7_Jn7m1B=wKL20R3w zFCT!Oz4hQm+iJ>QV=3JD)(~DB!|HRWt>Ui^*+9GK5Q$w*?2VyA3QZoJ-A>+#Dx{IC6JdXjJd>JI z1Ow9_0KL>5q$4(#_G0G?Yd*Y!y7o4}BR2tRJY5bA8MLwzSMr zOHku}jNY#%PcN`t2d=z-O3xnkg6=(V9hyB(gc?g$F}CkaNtv7OaKk1ws%Q3a%E_yi z_Wvw~h2=|ucG`VXN$)vbmC{SvPPt6S&ro4#meW?W_>pCG-x&Ca6ADv4*1~`(Q9#L$ zfH2aBdRW4)V_P+Wh{GtOoDoj9&+oI^Gddf*bIk@v*EWFdwaLIEJ(O&$!eO242v`{$ z2{tx7fZCEV)Nrq{;9R#i=v5d3_}%xCSqWjl*tH2xt{#BZRxYs2`VzeMbuJitU=Y6h zo&&0uO$GZ7EP$1tZo<~$VnAG<0t5?WnEMVNzcr{b(-ZP9DU10&IFktu8;zyEOwoq3SkA3Wn@2HP{{C=H8ILI${RGS^mIET~ixrzSE|WXM z?VxXuC2RjIOU3#(L4_&Z@Wzg{ROwhD$uCcY)OmZ-q0O1q;~fManD5Ybww;)6OsCh! zDTsqx)tMC+(jj&yl&p-Yp&t*8WlS=qnU|yL>E4?a2e7};YD46Zzf^S0*0FR2&d zok9v2+@1^5_P2nW$G0(aI-b(|z186LBP;12`#R}j?|sm_`3p>Vr^EcPb^?6a-JprJ zXH)7|BDE&6Iwp}F75ZollzUV}X3b>n+e_v%O7{&sTcdo@DnG)am)uR4|S+*Jpg z55|#Lv>f$t)hV*YvrDpxzXWV#*W^}fIKVK;c*DAz1uD>X z8FY4HHSSnHDV5nTSX(rSu^{e&2ixVrm565gML`|Ff7FUU&klv($6tlUU>2MgHJ%yl zsfVj(^a8W{T8yOiBJdtQ1TKyikuP)O=pbcNAhXyETu9KP?$s8M`5C$J<#Hag|C+ma zl^OyBximZ))Cu~!tWUFPNZ`*S+U+@-Qe^LWO|f?3{cxA!McPOfgOBIBfx&EN;2DwymUNeZ zaHf!+5>QE(+Ju0igY!Xi&l&N{mES6(a`%E<*52{`RX$mWHUn>iVlYXm3kK}T0279m zfnmF^fVrk=z;(I>#f^PIyFX4OlLDp#yP_62!@nDRsVRWz&5Cef{v&W|W+z>+wgXrf zzZGx#Xvj2I()8`os#Kl05ekC1%(|+HRJ}H&Ce-lBY}Q2dYu0 zeB;^nFoCT0BPI2FF2YFFQ0Q>hkmBq=4jW1}sQnAN;I7Bxfp)wCSX&ju?sGAMJC27y zuY?Ym9SmqSU!lQ&!0L5HY6RDXf5xC zItfRB{^%d{PN`$!e$^lFo&0lHnyn1>mOm5+$PB?R$uppN4h>p&D*=O@AIZ*%6REn7 zO0Lo($lwFu67MkiU1=dB(4gV9-GL-9 zWyc~PPXfitRM3$$2hJ)egVIma$!53xjH(f94_tN$)_GcrUvIGhM}%46(C!8FQV%|r z%-XI51o^==@&3%tfn2)xniWi-!(hXQP`HG1QbMju1hQ%Vbdvi$V7}ZLhMo!qt+B_z zv+mijiM8o0kTn30J~)E=t7FJ}?&si54_!E{Adj>+^@6hO9*78!0djgoEZOP4eT7 z+vKcuU#(6Cm;z_luW*jL5j4{H*uX+whzymmVFv3r_n?QCCFOAn1b{ z10G6K+ao)`to9Z%AY4GJT#SP&)0-h)J{9)jI&eNZ5@cQ-2~YjVl{j)(Taip{O5tmg zWZ;DcS*}Ggbvh$SzB9s9`D%icw-Aw(?0+ie;G*iIMJ9d+dU*-M)-|e!q^btFo;EZjHK|o^>*4+DBsr{i zJFPr3gnShr1JLF^l6og0&%1@g!=jb2vakl2e>H>Y?9QQ}3Uw-XMn_Y$ z6I(ce=MIkM?SWgLJ3~EqgjOh!vRc*@L+h=ngk>NBG#p`Qw*^gbct#V*K<|-F^Iw73 zLL+!~@eUAsY&n>$d>O3ObfUSp9@5PEWAM2bMrHMwfT@bf@R@xRu#X8A7kz#}IwWLT zDOs%sm*V9iq3#T)-_U24_1}Wu(Iif* zQT9m_!TXehl0q~MW+u8pcNKGZ>Ec}Y^x-rr$UXsFu+e9tdaeMsef4m3$yre55)QU) zy$c?(oTRAM1$aJ_pgVk)(u%sP=m{z>CF`+%_vpTDwqOk+6ELFWx`CJV@*FiWhM!c8wXmaBte^eDY&6PhI%t@GWA_glUj5BJ{?-L z4v4lnfY+1*@B#wLvp5Y3zGGC;{8w;}TMzlg;ULr>bsuPXUxgDgmrz=Bs>ob5b!Nfp zT44L$8_pTAmkc^`6Rfwp2z_o$klZiK0$k-F5M}Nl&UhIO?ez_S#TzAR)-#Io*sVe; z0}i8`9RcFoy~NK#{o$pncI-T;SCVfx5+1&64)&Z5w^FvtgIBx{v2$c?CewWpxOu0G zd}B}!u=h(Cl^S)*S1Ve4Cgc<#AGZKpUlDeImB7dT5UD-!5Pkl^av1RGF=$X+NHfyw z$Z@+hneN3T{CqVLEVUw;a`R${wg$m?w-m`DZyQj6nNrS9VLe`M{$Ow zqqtV&DE`*!ATbm;N<2**#j`CPCA~aHvAL0>c!ve6^A|cwMzZOc$&Qls?D^TTj*`Uj zj*@1=Q9PCBAnqRSC~-A)5WBGPd8ni09DAQ5-a*z^(7o0vtUSiL+w6HVD-E!{)!MlrTX+EL8rif7tPEz;PG;pb)*r>nN349wI%&@!>sZ$RTORjT2U)*o z-4-^j=Bgm;y=+)Bo93I?QSt>iieCXoNgeA_+4K9X>}SKDvvMTGV!?(_wiZ`1(N+z=SJFxLyEVf&|If#4tjuJ;>M{z8R z|84e~(dLfg5;I3hF^f5eO~W;ElssUueLl`nvfRW`vYmZL+par^HGViq-X%GRm$3It zW%0LW)0|F-q8grGqBNgcQ6t~&VM;ezGDvX+BPo%in!gt^ciIw|TSK{2lgNk?=80h&#DQ7P00nNX0wD~~crwWUVv-c6-WK1CPG|DZ7y3 z3Tm1Av7um6u?`$_Y%de7LxYMD(Ujwb0!qj47`63U4dXrc2@G&m0T!%|qBBCBZJTPT z(1&W&0rtNS+jLJ+x5h+Laxy-YhUQ`_eM3Jy(_=&RJc^zYnrld1Q`wlZUnwP*0$D@?(D;>&J_Bb82+=iL0WWvM>!kHh! z^HlT8O2$Jyl=-}&lIgClr_{I1qo&XIqz3MIP+{tosN!kIz?QvLOwxA|pjBE2qAEaiI!@9k97bWG z2SfG_lDBJfm}8&EF{bSIYQTQS`mD2<F}^(=2!s0}gm zQ5EqqeG)$<>JY!iG={kMaw0#_?it?ap-J3170FXOb{7$xnTQWMTt?;vMi2|iC-I)X zmE*TerU-?yMf`?KHN2P{DW2qD9O1noiZ>(N3rn8&2^;NNk5;J#;&Nue#Mo!^c%#0Q z;0LR|VzZy-Vjo69USm!;F=C4oS|6{7K5~!Z^~>krxbJX6x#0{R)6r?3;HyrYDO*My z$eV)5wr)e5?rua^Z;Rk$JnhCd8jRt6NrnQ|_fl3_&rVtDtk<$U?{Hd_*0RzPPbwFP z6cR0mugbJ4Go5bfp@<27qW;$NDBkR7UQ-z)AxQWPeX5?@|hgpLt`OR%%?wC5u4IV2kwZBU7 zR_413kgG<585T9Xna>&#>k}HhrhzElboF%nO+p?~Q;hPiW_;!cEY0ADZn}#(OFiHv zZ=O%!9}D@LJ8$#CRkMg&87GKgp&0*hS3Lhyp$##;UYQ^3qe2vvdJ~t2rST30YGWO4 zPqE>f522qMbBL6M6M5Dj-|`H@Mv%a50zvC$VzyDi$GV$Lp?Jxc? z&zb*zy8mghf9B8o{f~{mc=~?vB{};}G=F8jhE9##je{a8~q=x=* z{Cb$b+V5XT{LhrX_%Dl+elNf1Cm-Seum8sXpM2EW|I;f-ZSi03EBZR`S^R|7}Lz(uld!k{`n4nh5UC$ z;E(*AzgqP#C;oM*f7Q=lp}*>(&98v}OZn^jD?k4Gg?E4Ery|w-cll?>vX}ho*W`ZY z`%C_MPQ-cc{Ehy88xhy#{yxiBtY7sT_36JExeT*H|LsWp--{&vy-42wAzb~JFZs_P z{kh*Z{g*`l@Bd?8o%{1?{!9GFJ|_M5r!6}5yZn3Y8k+rky1&}BpKtkpnD}G6_N#Yn z{#k#1!SS=1_(f|kW&bPUAC!Nb7q0nPZ-2Fyf2_y@RS?^$qr^$ea?GpChX{`- z!Tpn#@s5AX<3;JG5jUoU@H(nBkaKfAi6_pbc$P)jiTl2ghGMIJYjb_Pk1}oP_#o7D&l#45Uf|s z6Qr!1z-xbh|2R2qIpvo5#sQKP#hIX z95a}Wi+e-x`5!v4xTXX6WMmUDquhlkd^iUGR$_z=IA-IIK?F_)2=StOPm%Vor|?y4 z>WJ~t^2BKBHL)|s5BJ{q0aFz=5uc6@;JJ=I*fjT2Vutw&Bzw6Wo@dM9s#!MR%1IS? z)vk2>KSLe->lyxe2uxdZ6MHgc4tDY3E^OuxHKN2Wn`^$^3A5Nl<7o{D9v!g?#dfI> zZ#54S?ZYn+@g>8Fw!H&rY(9+*ZCZjCU|o1Zr73<^a*h+7qKKdHN#psYe!&;kc4KiH z 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 literal 0 HcmV?d00001 diff --git a/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_policy.json b/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_policy.json new file mode 100644 index 000000000..0353c3345 --- /dev/null +++ b/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_policy.json @@ -0,0 +1,4 @@ +{ + "model": "keras_model.h5", + "epochs": 1 +} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index d37d123ed..11618ad95 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -5,19 +5,24 @@ }, "restaurant_form": { "slots": [] + }, + "utter_noworries": { + "slots": [] } }, - "rasa_core": "0.12.0a2", + "rasa_core": "0.12.0a4", "python": "3.6.6", "max_histories": [ - 5, null, + 3, + 3, null ], "ensemble_name": "rasa_core.policies.ensemble.SimplePolicyEnsemble", "policy_names": [ + "rasa_core.policies.fallback.FallbackPolicy", "rasa_core.policies.memoization.MemoizationPolicy", - "rasa_core.policies.form_policy.FormPolicy", - "rasa_core.policies.fallback.FallbackPolicy" + "rasa_core.policies.keras_policy.KerasPolicy", + "rasa_core.policies.form_policy.FormPolicy" ] } \ No newline at end of file diff --git a/examples/formbot/stories.md b/examples/formbot/stories.md index 2200a9a7c..3dec7fdd6 100644 --- a/examples/formbot/stories.md +++ b/examples/formbot/stories.md @@ -1,11 +1,9 @@ -## Generated Story -473632501256446142 +## Generated Story 6616794937268014982 * request_restaurant - restaurant_form - slot{"requested_slot": "cuisine"} - - form_activated{"form_name": "restaurant_form"} + - form{"name": "restaurant_form"} * chitchat - - restaurant_form - - undo - utter_chitchat - restaurant_form - slot{"requested_slot": "cuisine"} @@ -18,60 +16,6 @@ - slot{"num_people": "1"} - restaurant_form - slot{"num_people": "1"} - - form_deactivated - -## Generated Story -1191751619141022584 -* request_restaurant - - restaurant_form - - slot{"requested_slot": "cuisine"} - - form_activated{"form_name": "restaurant_form"} -* inform{"cuisine": "cuisine"} - - slot{"cuisine": "cuisine"} - - restaurant_form - - slot{"cuisine": "cuisine"} - - slot{"requested_slot": "num_people"} - -## Generated Story 3504123482747434858 -* chitchat - - utter_chitchat - -## Generated Story 2890106331063190341 -* request_restaurant - - restaurant_form - - slot{"requested_slot": "cuisine"} - - form_activated{"form_name": "restaurant_form"} -* chitchat - - restaurant_form - - undo - - utter_chitchat - - restaurant_form - - slot{"requested_slot": "cuisine"} - -## Generated Story 5856759686029092329 -* request_restaurant - - restaurant_form - - slot{"requested_slot": "cuisine"} - - form_activated{"form_name": "restaurant_form"} -* chitchat - - form: restaurant_form - - undo - - utter_chitchat - - restaurant_form - - slot{"requested_slot": "cuisine"} - -## Generated Story 4051894201894602515 -* request_restaurant - - restaurant_form - - slot{"requested_slot": "cuisine"} - - form_activated{"form_name": "restaurant_form"} -* inform{"cuisine": "cuisine"} - - slot{"cuisine": "cuisine"} - - form: restaurant_form - - slot{"cuisine": "cuisine"} - - slot{"requested_slot": "num_people"} -* inform{"num_people": "1"} - - slot{"num_people": "1"} - - form: restaurant_form - - slot{"num_people": "1"} - - form_deactivated - + - form{"name": null} +* thank + - utter_noworries diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index c51dfc7f8..e76a75f54 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -78,12 +78,12 @@ def run(self, dispatcher, tracker, domain): events.append(SlotSet(REQUESTED_SLOT, slot)) - return events + self.activate_if_required(tracker) + return self.activate_if_required(tracker) + events # there is nothing more to request, so we can submit events_from_submit = self.submit(dispatcher, temp_tracker, domain) or [] - return events + events_from_submit + [Form(None)] + return events + events_from_submit + [Form(None)] + [SlotSet(REQUESTED_SLOT, None)] def submit(self, dispatcher, tracker, domain): raise NotImplementedError( From 6a2d9a34ea119988477cfd339a1d1dbf2923ba9a Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Wed, 26 Sep 2018 14:10:58 +0200 Subject: [PATCH 035/112] activate form first and deactivate last --- examples/formbot/models/dialogue/domain.yml | 1 + .../memorized_turns.json | 14 +++++++------- .../policy_2_KerasPolicy/keras_model.h5 | Bin 70080 -> 70112 bytes .../policy_2_KerasPolicy/keras_policy.json | 2 +- .../models/dialogue/policy_metadata.json | 2 +- examples/formbot/stories.md | 3 ++- rasa_core_sdk/forms.py | 2 +- 7 files changed, 13 insertions(+), 11 deletions(-) diff --git a/examples/formbot/models/dialogue/domain.yml b/examples/formbot/models/dialogue/domain.yml index b9d935609..66305842b 100644 --- a/examples/formbot/models/dialogue/domain.yml +++ b/examples/formbot/models/dialogue/domain.yml @@ -9,6 +9,7 @@ config: entities: - cuisine - num_people +forms: [] intents: - thank: use_entities: true diff --git a/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json index 0f6bcf65d..32b9678e3 100644 --- a/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json +++ b/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json @@ -6,22 +6,22 @@ "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FprY0FAFA2Ojg=": 0, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERCLo4FAP4oVpY=": 5, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDc+OSOzJDkjEcVQkOb8vPiczGKgEuoYWVpSklqEKlMbCwDDQl8b": 4, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocqAzMSjA5sVtbEA8CxOMw==": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh2Fajw6sFmBpKUkIzEvG1k9yHn5efE5mcVAeYjqWABix0CC": 3, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocpQbCY2DbWxAFCPWrI=": 0, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh0FyszEpoFiQ0Ga8/PiczKLgUogRsYCAGl2WrI=": 5, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUMqwaanUUKDMUpDk/Lz4nsxiohDpGlpaUpBahytTGAgBvXlqy": 4, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocqAzMSjA5sVtbEA8CxOMw==": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh2Fajw6sFmBpKUkIzEvG1k9yHn5efE5mcVAeYjqWABix0CC": 3, "eJyFy9EKQEAQRuFX2QeQuPUq0jZto50wq9kfF/LuRMqd6/OddhcFK3yIghAJjavLqnCz8eqNM2gxunKfbLrTUbh3QSQdvp4CJKkfJV/9Vy8Am9e0JTPh/PjuBNMuM3E=": 0, "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEoti09MLsnMz4vPySwGyoMlanUUcKouLSlJLYrPyy/PLyrKTC1GV1+UWliaWgyii0sSS4sS80oIWBULAIxrNuo=": 4, - "eJyVi8sJgEAMBVvZAkT0aisiYZGIQc1qktWD2Ls/RK+eHsybKVdiQzaw1nNXuDzNEjcKzhDNUIDDEkQI9bq2xD2+4BRRz1XzUTzbN/a1UWDoSQ/5X/pSaIIMd1ztazw9dQ==": 0, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LRNZdkJOZlE7AqFgA0Vzxm": 3, + "eJyNzMEOQDAMBuBX2QMswtWriDSLVCzo6Lo5iHdngjg4ODXt/3+tVkuCJCCdob5URZZrNTFGCCLIQG5xzBb9GW1a3X3GOaBP04sJbEje2DRiHcFg/VF+aLpGhNbx+HLnfuFfz7/oVu95Okn0": 0, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsAs6NBQD1GVVk": 5, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktik/OyCxJzkiEuL02FgB3gly3": 4, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4pMzMkuSMxJL4AbiUo7N8NpYABQjSp0=": 0, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsvjSkpLUovjkjMyS5IzEErBMrY5CNS7l2AzHpx7ksPy8+JzMYqA8RHUsAMQvPh4=": 3, - "eJyFyjEKgDAMAMCv9AFFdPUrIiFIxKAmkqY6iH+36CzOd93J4iQOPqHMbWiqOobNaAej5JgNi41q60NXDJ8fB2cVWDgV/93ZnQxEDzVjSu/vbyaAMj8=": 0, - "eJyNzMEOQDAMBuBX2QMswtWriDSLVCzo6Lo5iHdngjg4ODXt/3+tVkuCJCCdob5URZZrNTFGCCLIQG5xzBb9GW1a3X3GOaBP04sJbEje2DRiHcFg/VF+aLpGhNbx+HLnfuFfz7/oVu95Okn0": 0, "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8otyIZpjAVlOLbk=": 0, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHQWY5pKMxLxsZPWJySWZ+XnxOZnFQHmCqktLSlKL4vPyy/OLijJTiyHqYwFoHDfa": 0 + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LRNZdkJOZlE7AqFgA0Vzxm": 3, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHQWY5pKMxLxsZPWJySWZ+XnxOZnFQHmCqktLSlKL4vPyy/OLijJTiyHqYwFoHDfa": 0, + "eJyFyjEKgDAMAMCv9AFFdPUrIiFIxKAmkqY6iH+36CzOd93J4iQOPqHMbWiqOobNaAej5JgNi41q60NXDJ8fB2cVWDgV/93ZnQxEDzVjSu/vbyaAMj8=": 0, + "eJyVi8sJgEAMBVvZAkT0aisiYZGIQc1qktWD2Ls/RK+eHsybKVdiQzaw1nNXuDzNEjcKzhDNUIDDEkQI9bq2xD2+4BRRz1XzUTzbN/a1UWDoSQ/5X/pSaIIMd1ztazw9dQ==": 0 } } \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_model.h5 b/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_model.h5 index 0fb6b4f2bdce5ff6532111616ae60fa3c6aa3ae6..a2e62888c19c69dce58c95a0c5c9605481dc2f16 100644 GIT binary patch literal 70112 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 CWV&zw literal 70080 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

H3mQb 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 z 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 diff --git a/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_policy.json b/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_policy.json index 0353c3345..e2ba110ec 100644 --- a/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_policy.json +++ b/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_policy.json @@ -1,4 +1,4 @@ { "model": "keras_model.h5", - "epochs": 1 + "epochs": 100 } \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index 11618ad95..8f54dd131 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -11,7 +11,7 @@ } }, "rasa_core": "0.12.0a4", - "python": "3.6.6", + "python": "3.6.5", "max_histories": [ null, 3, diff --git a/examples/formbot/stories.md b/examples/formbot/stories.md index 3dec7fdd6..2ff0ed90c 100644 --- a/examples/formbot/stories.md +++ b/examples/formbot/stories.md @@ -1,8 +1,8 @@ ## Generated Story 6616794937268014982 * request_restaurant - restaurant_form - - slot{"requested_slot": "cuisine"} - form{"name": "restaurant_form"} + - slot{"requested_slot": "cuisine"} * chitchat - utter_chitchat - restaurant_form @@ -16,6 +16,7 @@ - slot{"num_people": "1"} - restaurant_form - slot{"num_people": "1"} + - slot{"requested_slot": null} - form{"name": null} * thank - utter_noworries diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index e76a75f54..96098a834 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -83,7 +83,7 @@ def run(self, dispatcher, tracker, domain): # there is nothing more to request, so we can submit events_from_submit = self.submit(dispatcher, temp_tracker, domain) or [] - return events + events_from_submit + [Form(None)] + [SlotSet(REQUESTED_SLOT, None)] + return events + events_from_submit + [SlotSet(REQUESTED_SLOT, None), Form(None)] def submit(self, dispatcher, tracker, domain): raise NotImplementedError( From 3d0391eb433143d36d4b43191b819025db426615 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Wed, 26 Sep 2018 15:25:03 +0200 Subject: [PATCH 036/112] add dispatcher to validate signature --- rasa_core_sdk/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 96098a834..7d0b10a92 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -37,7 +37,7 @@ def should_request_slot(tracker, slot_name): existing_val = tracker.get_slot(slot_name) return existing_val is None - def validate(self, tracker): + def validate(self, dispatcher, tracker): # type: (Tracker) -> Dict[Text, Any] """"Validate the user input.""" @@ -64,7 +64,7 @@ def activate_if_required(self, tracker): def run(self, dispatcher, tracker, domain): if tracker.active_form == self.name() and tracker.latest_action_name == 'action_listen': - events = self.validate(tracker) + events = self.validate(dispatcher, tracker) else: events = [] From 1519f16ed0bd616ffc0758fe7978d605a79cf64b Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Wed, 26 Sep 2018 15:40:14 +0200 Subject: [PATCH 037/112] make validate signature the same as run --- rasa_core_sdk/forms.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 7d0b10a92..fbc58defa 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -37,7 +37,7 @@ def should_request_slot(tracker, slot_name): existing_val = tracker.get_slot(slot_name) return existing_val is None - def validate(self, dispatcher, tracker): + def validate(self, dispatcher, tracker, domain): # type: (Tracker) -> Dict[Text, Any] """"Validate the user input.""" @@ -64,13 +64,14 @@ def activate_if_required(self, tracker): def run(self, dispatcher, tracker, domain): if tracker.active_form == self.name() and tracker.latest_action_name == 'action_listen': - events = self.validate(dispatcher, tracker) + events = self.validate(dispatcher, tracker, domain) else: events = [] temp_tracker = tracker.copy() for e in events: - temp_tracker.slots[e["name"]] = e["value"] + if e['event'] == 'slot': + temp_tracker.slots[e["name"]] = e["value"] for slot in self.required_slots(): if self.should_request_slot(temp_tracker, slot): From 34949efba1281c7cc718bd9743c75c73a2eb5e81 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Wed, 26 Sep 2018 18:47:42 +0200 Subject: [PATCH 038/112] first working implementation of 'forms: ' RasaHQ/roadmap#263 --- examples/formbot/data/stories.md | 23 ++++++++++++++++ .../memorized_turns.json | 18 ++++++++----- .../policy_2_KerasPolicy/keras_model.h5 | Bin 70112 -> 70112 bytes .../models/dialogue/policy_metadata.json | 4 ++- examples/formbot/stories.md | 25 ++++++++++++++++++ examples/formbot/train.py | 2 +- 6 files changed, 64 insertions(+), 8 deletions(-) diff --git a/examples/formbot/data/stories.md b/examples/formbot/data/stories.md index dd125a631..35bbbaeaa 100644 --- a/examples/formbot/data/stories.md +++ b/examples/formbot/data/stories.md @@ -46,3 +46,26 @@ - form{"name": null} * thank - utter_noworries + +## Generated Story -9155310465400161964 +* request_restaurant + - restaurant_form + - form{"name": "restaurant_form"} + - slot{"requested_slot": "cuisine"} +* chitchat + - utter_chitchat + - restaurant_form + - form: slot{"requested_slot": "cuisine"} +* form: inform{"cuisine": "1"} + - form: slot{"cuisine": "1"} + - form: restaurant_form + - form: slot{"cuisine": "1"} + - form: slot{"requested_slot": "num_people"} +* form: inform{"num_people": "1"} + - form: slot{"num_people": "1"} + - form: restaurant_form + - form: slot{"num_people": "1"} + - form{"name": null} + - slot{"requested_slot": null} +* thank + - utter_noworries \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json index 32b9678e3..65b1a1802 100644 --- a/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json +++ b/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json @@ -3,25 +3,31 @@ "lookup": { "eJyLzivNydFRgJDVtbEAMW4Fvw==": 0, "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlqiNBQCLfBlE": 4, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FrLc7Jh2tMTYkHcw0gVscCAELDRBA=": 0, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxay3OyYdrTE2JB3MNSLc6OSOzJDkjkYBv8FoXCwAfOmpG": 5, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMhxai3Py4RpTU+LBXAOwXK2OAvFWJ2dkliRnJKJYCNKcnxefkwk0OY/21pWWlKQWocvgsS8WAMZ+fKM=": 4, + "eJyljUEKgDAMBL/SB4jo1a+IhFIjFrTVdOtF/Lu1eBLx4nF3k5l21wZ2Yxq8zCQcoKNoh5wbVZdVoawDp8aMFmbUuNtFeKPr2TuabEgn9xAmj0RaY4JxTzlWeTsK9U8XAZbn8u37oL3pj+4EMRRh4w==": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUInSZ4pz8EqBRhaVA01JT4sFcA7BcrY5CNR7TsFmPpKUkIzEvG1k9yOn5efE5mUB78iCqYwEH40pa": 3, "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FprY0FAFA2Ojg=": 0, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERCLo4FAP4oVpY=": 5, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDc+OSOzJDkjEcVQkOb8vPiczGKgEuoYWVpSklqEKlMbCwDDQl8b": 4, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocqAzMSjA5sVtbEA8CxOMw==": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh2Fajw6sFmBpKUkIzEvG1k9yHn5efE5mcVAeYjqWABix0CC": 3, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocpQbCY2DbWxAFCPWrI=": 0, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh0FyszEpoFiQ0Ga8/PiczKLgUogRsYCAGl2WrI=": 5, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUMqwaanUUKDMUpDk/Lz4nsxiohDpGlpaUpBahytTGAgBvXlqy": 4, - "eJyFy9EKQEAQRuFX2QeQuPUq0jZto50wq9kfF/LuRMqd6/OddhcFK3yIghAJjavLqnCz8eqNM2gxunKfbLrTUbh3QSQdvp4CJKkfJV/9Vy8Am9e0JTPh/PjuBNMuM3E=": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocqAzMSjA5sVtbEA8CxOMw==": 0, + "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh2Fajw6sFmBpKUkIzEvG1k9yHn5efE5mcVAeYjqWABix0CC": 3, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8otyIZpjAVlOLbk=": 0, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LRNZdkJOZlE7AqFgA0Vzxm": 3, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHQWY5pKMxLxsZPWJySWZ+XnxOZnFQHmCqktLSlKL4vPyy/OLijJTiyHqYwFoHDfa": 0, "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEoti09MLsnMz4vPySwGyoMlanUUcKouLSlJLYrPyy/PLyrKTC1GV1+UWliaWgyii0sSS4sS80oIWBULAIxrNuo=": 4, "eJyNzMEOQDAMBuBX2QMswtWriDSLVCzo6Lo5iHdngjg4ODXt/3+tVkuCJCCdob5URZZrNTFGCCLIQG5xzBb9GW1a3X3GOaBP04sJbEje2DRiHcFg/VF+aLpGhNbx+HLnfuFfz7/oVu95Okn0": 0, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsAs6NBQD1GVVk": 5, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktik/OyCxJzkiEuL02FgB3gly3": 4, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4pMzMkuSMxJL4AbiUo7N8NpYABQjSp0=": 0, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsvjSkpLUovjkjMyS5IzEErBMrY5CNS7l2AzHpx7ksPy8+JzMYqA8RHUsAMQvPh4=": 3, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8otyIZpjAVlOLbk=": 0, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LRNZdkJOZlE7AqFgA0Vzxm": 3, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHQWY5pKMxLxsZPWJySWZ+XnxOZnFQHmCqktLSlKL4vPyy/OLijJTiyHqYwFoHDfa": 0, "eJyFyjEKgDAMAMCv9AFFdPUrIiFIxKAmkqY6iH+36CzOd93J4iQOPqHMbWiqOobNaAej5JgNi41q60NXDJ8fB2cVWDgV/93ZnQxEDzVjSu/vbyaAMj8=": 0, + "eJyNzUEOg0AIBdCrzAGM0a1XMQ2ZKE0nKigwumh6d3WsxkUXXRHgP6jfgQzJwF6eusqVeZG5UXCGaIYCxAuLBNS0+mTuzAtOEXWvaj6KJ7tj31hggj7oFr7oPp0RnizDzaX+i/86/ptqzxfEFlJbHK8fKyR1U8w=": 0, + "eJyFy9EKQEAQRuFX2QeQuPUq0jZto50wq9kfF/LuRMqd6/OddhcFK3yIghAJjavLqnCz8eqNM2gxunKfbLrTUbh3QSQdvp4CJKkfJV/9Vy8Am9e0JTPh/PjuBNMuM3E=": 0, "eJyVi8sJgEAMBVvZAkT0aisiYZGIQc1qktWD2Ls/RK+eHsybKVdiQzaw1nNXuDzNEjcKzhDNUIDDEkQI9bq2xD2+4BRRz1XzUTzbN/a1UWDoSQ/5X/pSaIIMd1ztazw9dQ==": 0 } } \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_model.h5 b/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_model.h5 index a2e62888c19c69dce58c95a0c5c9605481dc2f16..a4df69794907e49929eb6c8a68d3cc471b98845e 100644 GIT binary patch literal 70112 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 z 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 CWV&zw diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json index 8f54dd131..009c98e3f 100644 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ b/examples/formbot/models/dialogue/policy_metadata.json @@ -4,7 +4,9 @@ "slots": [] }, "restaurant_form": { - "slots": [] + "slots": [ + "requested_slot" + ] }, "utter_noworries": { "slots": [] diff --git a/examples/formbot/stories.md b/examples/formbot/stories.md index 2ff0ed90c..2237273e4 100644 --- a/examples/formbot/stories.md +++ b/examples/formbot/stories.md @@ -20,3 +20,28 @@ - form{"name": null} * thank - utter_noworries + +## Generated Story -9155310465400161964 +* request_restaurant + - restaurant_form + - form{"name": "restaurant_form"} + - slot{"requested_slot": "cuisine"} +* chitchat + - utter_chitchat + - restaurant_form + - form: slot{"requested_slot": "cuisine"} +* form: inform{"cuisine": "1"} + - form: slot{"cuisine": "1"} + - form: restaurant_form + - form: slot{"cuisine": "1"} + - form: slot{"requested_slot": "num_people"} +* form: inform{"num_people": "1"} + - form: slot{"num_people": "1"} + - form: restaurant_form + - form: slot{"num_people": "1"} + - form{"name": null} + - slot{"requested_slot": null} +* thank + - utter_noworries + + diff --git a/examples/formbot/train.py b/examples/formbot/train.py index 0199f2322..5130f43c3 100644 --- a/examples/formbot/train.py +++ b/examples/formbot/train.py @@ -11,7 +11,7 @@ from rasa_core.policies.fallback import FallbackPolicy if __name__ == '__main__': - utils.configure_colored_logging(loglevel="INFO") + utils.configure_colored_logging(loglevel="DEBUG") training_data_file = 'data/stories.md' model_path = 'models/dialogue' From 4a126dfa6ddc1f588f849cc419b06d3af17b3f26 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 27 Sep 2018 11:50:40 +0200 Subject: [PATCH 039/112] change order: first deactivate form than set requested slot to None RasaHQ/roadmap#263 --- .../memorized_turns.json | 12 +++---- .../policy_2_KerasPolicy/keras_model.h5 | Bin 70112 -> 70112 bytes examples/formbot/stories.md | 31 ++++++++++++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json index 65b1a1802..fef039f5f 100644 --- a/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json +++ b/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json @@ -16,18 +16,18 @@ "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUMqwaanUUKDMUpDk/Lz4nsxiohDpGlpaUpBahytTGAgBvXlqy": 4, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocqAzMSjA5sVtbEA8CxOMw==": 0, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh2Fajw6sFmBpKUkIzEvG1k9yHn5efE5mcVAeYjqWABix0CC": 3, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8otyIZpjAVlOLbk=": 0, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LRNZdkJOZlE7AqFgA0Vzxm": 3, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHQWY5pKMxLxsZPWJySWZ+XnxOZnFQHmCqktLSlKL4vPyy/OLijJTiyHqYwFoHDfa": 0, - "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEoti09MLsnMz4vPySwGyoMlanUUcKouLSlJLYrPyy/PLyrKTC1GV1+UWliaWgyii0sSS4sS80oIWBULAIxrNuo=": 4, - "eJyNzMEOQDAMBuBX2QMswtWriDSLVCzo6Lo5iHdngjg4ODXt/3+tVkuCJCCdob5URZZrNTFGCCLIQG5xzBb9GW1a3X3GOaBP04sJbEje2DRiHcFg/VF+aLpGhNbx+HLnfuFfz7/oVu95Okn0": 0, "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsAs6NBQD1GVVk": 5, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktik/OyCxJzkiEuL02FgB3gly3": 4, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4pMzMkuSMxJL4AbiUo7N8NpYABQjSp0=": 0, "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsvjSkpLUovjkjMyS5IzEErBMrY5CNS7l2AzHpx7ksPy8+JzMYqA8RHUsAMQvPh4=": 3, "eJyFyjEKgDAMAMCv9AFFdPUrIiFIxKAmkqY6iH+36CzOd93J4iQOPqHMbWiqOobNaAej5JgNi41q60NXDJ8fB2cVWDgV/93ZnQxEDzVjSu/vbyaAMj8=": 0, - "eJyNzUEOg0AIBdCrzAGM0a1XMQ2ZKE0nKigwumh6d3WsxkUXXRHgP6jfgQzJwF6eusqVeZG5UXCGaIYCxAuLBNS0+mTuzAtOEXWvaj6KJ7tj31hggj7oFr7oPp0RnizDzaX+i/86/ptqzxfEFlJbHK8fKyR1U8w=": 0, + "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEoti09MLsnMz4vPySwGyoMlanUUcKouLSlJLYrPyy/PLyrKTC1GV1+UWliaWgyii0sSS4sS80oIWBULAIxrNuo=": 4, + "eJyNzMEOQDAMBuBX2QMswtWriDSLVCzo6Lo5iHdngjg4ODXt/3+tVkuCJCCdob5URZZrNTFGCCLIQG5xzBb9GW1a3X3GOaBP04sJbEje2DRiHcFg/VF+aLpGhNbx+HLnfuFfz7/oVu95Okn0": 0, "eJyFy9EKQEAQRuFX2QeQuPUq0jZto50wq9kfF/LuRMqd6/OddhcFK3yIghAJjavLqnCz8eqNM2gxunKfbLrTUbh3QSQdvp4CJKkfJV/9Vy8Am9e0JTPh/PjuBNMuM3E=": 0, + "eJyNzUEOg0AIBdCrzAGM0a1XMQ2ZKE0nKigwumh6d3WsxkUXXRHgP6jfgQzJwF6eusqVeZG5UXCGaIYCxAuLBNS0+mTuzAtOEXWvaj6KJ7tj31hggj7oFr7oPp0RnizDzaX+i/86/ptqzxfEFlJbHK8fKyR1U8w=": 0, + "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8otyIZpjAVlOLbk=": 0, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LRNZdkJOZlE7AqFgA0Vzxm": 3, + "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHQWY5pKMxLxsZPWJySWZ+XnxOZnFQHmCqktLSlKL4vPyy/OLijJTiyHqYwFoHDfa": 0, "eJyVi8sJgEAMBVvZAkT0aisiYZGIQc1qktWD2Ls/RK+eHsybKVdiQzaw1nNXuDzNEjcKzhDNUIDDEkQI9bq2xD2+4BRRz1XzUTzbN/a1UWDoSQ/5X/pSaIIMd1ztazw9dQ==": 0 } } \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_model.h5 b/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_model.h5 index a4df69794907e49929eb6c8a68d3cc471b98845e..e9f9a4e580006f43439c4efb352ec140b1c95fff 100644 GIT binary patch literal 70112 zcmeFZ30RI@*D!pgBq>NuhZpDpW{CDhrlh;0N4gVzV+A6QV%C(%pNvtc)csdKZ zi)DOrXlzJa(6rFlxQOT|ak-kfgJw-?{%h$=M*P^YXOiR(vdO5+MR(vu)@r?@^ zKRGmN!oS4xQ*m4IG_&G&y5xW2Y2nBC=%|SiVa;9sE4maq{7zT1ini%%On!4~fgrpk z5!3v~dfh190`R02?qBJ3jAjd9>kwL7{gwPr;lQ8ibwR5fweev}NZjOzs4zifT>O;g zvI(J4aiQWgWPE(Yw2=4~r4#4>UA+!&R$}p!)Y_k%Ah=Zz<-`)MDu@h86x)udkSU?f z)Y1it`1wZYFxM-H% z_{fmBxSyQUv(?q}4V{`08WkTA5~*jTtJh+W#N~m%!nwE1^FIL1(re~_GnQ8FHrK|- zhD1b#jEih8kB?0V6&H;Qi60*x6cH7Z5FZp59um`B6&RHe8EK?zZlr5!F-8nCAwKbE zxt^Hxka%;mW>n2sgQkTs_O%gF5zW)u>|@1szcS7*BsL->YJ8~gcri#+*e^Vc6PGp*)zsKT+(omm zY9?)BNK{bBw4bmOBI4p>BgQ2(D|mBNLR7@W=-8k9jtdQ)ATDZ|-+!Lh=2`r^!TrZn zdd9|wM~6j6{UV*S^uokK_BZTW|KdSxN z;U85@h>ebkPH4tsV%#jCzoYs4nr6v}m=Y5iIz=p`KP`Rpg#3|`IyU=+Ul`kFIez+! z=DL5=ZkYX<(P*BVW@WGv5zBdKNNjUo|3~V~|5gcZ6`DU3sQ(uP@sCCAk7ULE zZ|jCX5tzT5(?6D)e^S|h5`?(uiSbiHX6VhCGiFZPI^oa!XZsd6+V)8xX!E;m%b<3x zj(L{e?_V^3^P;~RT_1Pf7_n~sGP1~K`)p!tVq$92@>mSCuo-A!X)1P(da>crKWnXR zem(=OOf9WVEzC^CRTDzTha|Q%73V@@;vyn{8e`MY{w4!|+NsFsxMo$*8!z@qVbQS> zVyhlBUToyz#2PE6Be9u~DWPIRKE4?&P^>RvpA#}ZQLkCK;zF90yY=%dsMQCwnzClZ zZ69vpmd&MeS{c=vI5mH-{Pq0BfxkHL7YF|0z+W8riv#}_2Yfv|9VGM&>t^0$G>;J9p|E~f5ec$!Jz8~DO7Hr*+`GrFg;+>{up0~|I z+y2jCtrozfR``~ESHYj3r?kTTmHf}-z_0tRZTo$yt^4t9Txrv9ueO5!V%lPFqy)OH zcuan=BNx`RRE%mZZ_&JJfvsqbg`Q3HyBmyQ{8UtF0d-u;?VP_|dv?+qR$m&*N_6 z>_680nH_4>ZGk)73jVLof0BB&0JhCbYqP(S{~;Xs)ef~uTY>IBGNA3;r%hj5)&3uW z{*7Fl{`_hm&dp`qFmp(UvyTIiO|N%hu#~dVUuFzQ5T9 z@Uysi)9UYGTU)hp;&&v?ZxRCOmNS}GMo0+y`~;9_-5~1R3ZB%O{+gE*+l1z(U3GO@ z&#ZI>Dl!6nQrP%1_&% z+BCN<|Ia$zQq!i?&9d42=>Giu=bx7KP;)dz%X73cSj(S-wG=Pf#^ETka@ZJqQmW6 z4C)(%_6ua#Kmja3e0iIdA=nDPf$4qA9>_AO3&S8uAc zv0^M(1pf6JEaYlWx+0^26N6q-dH8&qB)5tN7}!8ra4&p4q5!TPR7C204Gt?RF{e)k z%vz(Aea_g&_uBD>-mL4z0;7-djk+DNWPT0=(xniHI|945rck z7l1$MB3kIU7qtE5G00Atjz8BVNlh&_(n^j6K3>U1u3ZMZM|Pn*j@nEpVF_8HJUCpG z$yanmx>qoUoYEeXlkYNahvF>MdBRh_8Z{Oo6^eRw7a?`AA+xnNWu+S4oZyX{wd(rq z9ADU;?KACzMP=^jCKw5aUsTWr3jrI>T)_V{qK(Lg4KkMF@2(Z`eRv}pR^oy>0Ryn# z7Z;3qo=dy$39u~63*FxBrKaK@Y|leUmX-4rlJ0e2zGod+NAFt7vVRK#b$`C^j2`G; zI{@8|%8YI|(1B-J%Qk zv$<(q55R>Bqqtbx`=q*b1!q2|3-k7LX459Urx;-`e)MC2#<45N?TwIa3SUbO*X~mm zmkCKOJw-Bdhxz*(>p{E69!UBkFLVnX2espjSazN&N%WV&1$OS}Qda}oBd)>992HE9 zwnb^@R5<1TnQW)Nqrp`_U|Z8jD$vj83$xc#YMBf49jcC#c5Nhf`xx!tFGVjNbz~P# z`IF%4BhiG9PVn11IhHvQ5Q5> zX~^}toJK3kRq2lPYsyFT55}#<6p7>EbH1zgbC-GkUYz zd*^a}yFGw;{#UspS3R)(aTE0OAA-G3k45d>W6{_90jwN77qh}buyY@6KHSTRQs3KP z<<5C@YSB?Xsh13UYFR+gt^n3+vJ3{TEEa%!J+(9>-?rcoe^DOJQY zZD}kGehhw-a$%3g9vJN63!6+{a3&8-P(HXFYS$EUDvzw_Nn|!f7wjVCM0s>8%m?Yp zM6edy9)(RCF-FS|{ocu7>A1C+@GYNwH40Gu`2zHG%tft$A5@ggPJ!P47%>b zp{nKJz3u?{J(+}j#%4@b9gTOFsgh7;FR5KwgcGf2VeS0KqKOhxEb7`l8@~o`sIsZ$ zP9NeJf2|+9x;BgXTPU!mE0%2K*dp*wnaerlk74=V4N!4^7DZ?$z71@tQDlefY*|{KRJPP~1JOt%MV=&lm5x$L#=G-+6 z#|+wlD&sVnSI6b({K*E*x}@WX=g1C+`{JAOF1UG?6L!qoj}tA0VxQ0rJktXyz}l1Q z-6p^-*#%IOXMl^N4$&*zeVc!NiMXF8$Q4JUVfO@-Q{&|JC~s{5?K7nZj~maa2k)Kq;~ zGNCUy-BDt?f=%??Aqe;G8Ow@}b;VsLv*B!23{>|_X@*Fyhl5* zsXiTG+|^fH$DFh9;m&Y0T(=*lj*(#d1OnKH>u~7%cF=Gl1(NbCA$Caso_!w=-Qt4~ zmzQE`|3uMC&m7b#?20Z@j!bLc3fem^mn-~~gWj2iaC4P0?DI>eMavKIn|wJIeR&>j zUsi(Ya+%mi{{Y$aQDln6>gX9J&m1!s0AF8(RqFXPHztE*tNOBORl!uCW(NK9ZDH^J z_Bh-rhu76}#G|GXELZJ2NGaEGuLm#0$}vlDdbt7ftdb=ErWc~IA4>QT?>aD8RzU_& zY}i@O33lCtI-guVsB9UDj`m?w*1NDTMk(O+$q>|+ z)WJo20d{$0jzy6Tbf|bBZlC9k50xZYwZ9|4s6p)NcNt8b*q()J{NM-Ow&H8An=zC8 zX>_=!8$GTtV=s;kAhTJo;QsrI;8M2@0y3}l*%K2Ew zL!f32s7JbNcZeq%NfuCbdwWng9L|rMkpwSF=YrgIAsOFU2YU8(pf@`ct{szNZ&osH zx2!Ik@xzg9ou0#n+>hk(={Zcys)Y1eU*Y3#RdD#)bI$3;dOmfU1B`uP%Cu+RrUJ>aOD(LgMUr!_qgA6*y0jGT5X zDb0XQ`2La>>f1s08?UJGL?AVwK9e-GVnfQy>8$!yI)6eEy*`}fj#enslh}16RjN)O z-=3tg*Y1IAQX1qrXy6ci2j+ZoAUl%ajMdd{P&FbShI8|%(7}g|cxr){hH-egzabUu zw4@J4n`n4c58Btw6~FbcCU? zSiEH+EaY~OV&`lsGU<=&ZBII_A!vY0G=8lu%pC@#MvHq0}{Z(3R8<{wS` zJ_{B$PRNQM-5`x=YIx?#T6on|1KDSt#Xc&HW^^!y0?#8{-8pldUt!0RGGCO>mhywF zNt-~XWCwqt&W9TuF2|Io-G}}w)p4AnBAff=ILz*w00Q?=E<4ATNk4RBr-jBi>BvGb zsmiA}1>NC&!ysG~p@Fp<-@&+k#iTK?3%W&op{`*P_;5r|I4bKeI_#tb)pMs&lH^$~ z9rNI=iVw;Rs^gz!ogz&?LwcrE12d(2vf9p_S>s&|2oBuN4Z5kxy_D{WN&1cDI^lQ7 z#9Nj(S-p!3J~0Z@D(qm%YzeH+lYxFmd@(o5jcW)%T=`=?&IxwMjYoW`#;7~0-Z5j{ z-`=D5A<5`)V})(igA4HbsxzMK76G~IM-!M@rC#$j`Dtskv2sHbU93pq)m=iE!Qd=5F0K;JrAs4x ze@KTF6sFw-d24TeL7}}d$yWlndlBBYJcUFE5fKy z!;wM)lCWmS5@>&5KCO9}z;4I(r;7K(=(>y(jOp~6TYoa&_Nep#hA~mp^kWcv<$fAQ zC55mneYSEPrMk1xL$;9LXi4IDGq^9%!0rz|(q@(8WN&j2oI4fLjmraAq>VD9GFJ#aV zf`Uh5(C$tq?~>LYUtQnD8N3fXxx>b3G~&4>t~tiBBgSs{a^P6ZZQ2W)HJ;PRAI@l-bC+K1 zbHWu?`J#av&j|h06QIXfRoptIHy$=}rrah^noriKZm}7u4buT=X(}7i< z=*Uc`dt=@VYwYJ^$9g#(rtW=+5(X<^V@G{DAZ^M93^Qa_4;j2USHKTicvCp&h!!tB zhlf(x5&T}mTaYrs3=5W)aZ^9trMKRFanw5rrm#hZ39?Gd9M7Aw*KhjahOZi|v}78; z_;VU`UUnC(mHWaem#@6tRcm}))`htS$YS@d`b^8x2&b9=CfWN4pUpiE`jeGW_s(VR z#4c%c&eOzUF)wK0lR!!t{**K>DPq!A8E(|!07}}vy4)l383ivo&6iH*IH_;;Fn945 z=<%p4dZ-R$8N&rwyC8)G8LLFM4rqf44aI2-PlJ){beQ;M5L@P=g_0U3>`h%K>ilve zfTAtR#Lgq*acOi{*9WkAUR4W=D;3)VC(5=IYGlk<qeQ@LMsg$Do1)O(GM`p1GLd$ZP zy=5s4)5_(XMmwVJ&>ZMJe>eZ_^(3s}15s;X2JEA`%rTPZ(p9AK$enh$D%lgV1MUco zt^qT8w+>%kU&R*ZbVQTIgUF`G9+tv~Q*vKq32ReW<5DF3`c$eozY*6Sn+koZd$Ue! zgL(RG1i+-#16hpV+ z;-YFyOtEBx<9yglv2VQ}I2{|;uS9qA+1!N~O}5%p0Siul5kZ~)C$)dDY6%9M!@n8IxO&{ zCRX1=(&#gg#at>PT|b_JxevUq={D~BS|euu))MPXEwM!W&F?m@3rp;)iPK&lgNJhR zWOAf{Gg&9e&u$2y@cnnF|1%Rz8j{JWIw^upGO}%(GWlhe9dOt^Sr%ikl$sK@QpHCV zmK0c7ezZnKw9mkS-92`Sd(54o;u$^IlNIN zcIF!+#OtlxVEQs+5KA)KDZ1-!$qCeFm)G7E&qG8p^uMc(*0m}u$fGa&$Ys!jTw^x; z%M8+*qm02DJHl~E0~G9jZhJF8ozSe3~)kHJ4J;WTn9=fwv9kxMF{sx`w z(i1f%>)~@f6&$6%owhkTu+JMMaOc=akd=D?ch7WZ{)XnbZCxi;qU?Z?YMFGVrwhyM zsKaG0&*8iI`rwUYdMw1_75N95;gkJgR9w1&tgAlqnnB&^&K4W&@x~QptY33i`t60( zrtvVq{uT`g60j@fNg(4v-2AUMXu$EtTXXQx%>o~H zZPs}YLEZN4G%8Dq4@xU=+JDE_=ABgIKsqR@cV}l3PM`gpjdhuHR^fKt})&!YlqqtNX2cRHDlo5aD zdmVpI-DHFqBNNBX@$bW|Z0%)vBf zLuttw{^2JZCR4JIuVGzr?eWu~5?MpmZ$oIUR0#}ntrd3c49xz)U{v4m6zT^=ohp#cV2Z)8c`S7C$KOJ1w^Abk@o9=$4&0)L8gBIQ!78ZfvK75To5)$GJt^eu7QM|4fZf*e(Y+ zSt|v+JN6+b|8f_4#;s>cL2hi0YZX1z+Q_tfErsR%6R_k62UZm?sM|?ZbloG%_YR(f z7bSx*Z(e8do|waAlMYU?+s)m&>W=9_j_ldM zot*m?0hFG(3RW(usOdO`gmaGzIbjOsOJv$yFv9hKK6U0VUK)(km*3*G_^qts z(4_Kiz21Yw>idwsClb#(<>G)NsVwsHb-KHGD05ug9^JnYr59-6mAkTVe|`rxa!NAu z9d(er)*j+Q7cUcTvbY9Vkj=_7ZL!C>e)R1{Cl-8QHU7w+kGad1QjCF!zV)BZt=c^c z6<0Ykw@;U0^sHr^QhGOhddV4dpRK}E4?{6M&XQH98)LYi6BU17feCy*9A7Hp3<7%t z>^ll)4Daz)H=c`i&5FrQh{4OMnk=aAN7#1U4GsBi_^w1)uD-@6W+g=j;aqrab#v1b`8w{ zXP5ps+&Ti|)-8oqN@Cf+G8pV%rGVRqNA$Tz8h75(7ftXH?XZ`k`H7uS!pf949=Z+v zwL&0VwpuiDXt>BQ@*-GF+lc;RKOuEvB|O!VMB`!|VMe|iYQ88&O_yw}>aD|#b{A0a zkZ>$3p2P$d$ymF23yzp5kLr$|n5(mq!Lw8RQqx|fc;T2Z%Zr13b{|RKWg+u*xDPAm zY-ZmL(nxP&DV{lWhPT@rh+g;R^9nEoD_qmS;({D1bTY>&V;?+GKoKaF9!B+D-;#a>W=#@my$`a zGJXkNj`CW)SS2;V9*-6DWF3z+^0SezD#CZKG!B>kFM5Or%Q{MJ~6XO*(KahI(4H>ZWT#y*KP%B!(+W~SJ^<4Oz*naMWy7eZ0R zap=Bh6>bxSQo4yg{I+x<%sf4T)t%Tv!t`8BTlXFYc`rgqiL;!?)AKOYafWcp0#l0F zkZE3SqQXW+7BJ$8P};o-x{xpb=-_NLnv}s0`KU^6C13fP zFm;T{jpf!)SqSQSd8kmp!Nl-+;J#=f-af6uyDpoI<~~ha)`Kl9@4-&a_mC{~vKq-! zUo$v%CYkNe%SM@%qglj*6jt}TQ0#Y7I3vsBy!qWS(0QH$EAx{0xgth`Yb5Yq_YoNW z^(HMdI0Ps9tO3us2CDmUg5(3j0hCop({~e24V!^lTMu!gOct=0#!GRGw+B`YHKO3$ zTo4+?;OG;jLerPCG0SBEThMMR9?V_Ksvef|!;%s)o*Pfvrc-dnlgaqPX9fEf?+rEv z&TMVu5)2umfMa8laP+L+%xh^11sqUf9ju*U-FO91zqSQ;X-y{IBkj1SAq8yVku+4C z>xQ9%VzzDIKD1d;&Ht`uUwx1e{CV78b1Yq( z-nS`fTl$mEzXkraWt8Ka1y6$ZqNXuuDt&tNiG;l6S4OzBSwYe|g0Jq~Q1P%CFi{z*Tv^(o9);R2}N*I=sy z@ALIK`B3`7i5(iM!8E71vy{XV_G~~`W_0Q!J-9anm~2ndj}mZckI@*O3sw@dAFPg~(H7qYw4-Vxd>N^aRZFbs&d%@rL!~INo~Xm(KuxGJ_dER< zyO|$wGzpxi?IT)~1iLGHVYj?Gnp9N>LBn_O#k-Q&ob3B#_al=IRbQdnave4#;~~7| zr{YDYm$2uVJjxlKgs>X}aDVL>eDqSlre9FTKFO-&clIT1oaBbz?kZu5`0R4{lMwFs z>R9Tuw~|CaL!kz_i&4*ubBLM1OUDUpnz%eT zl8y(slXp!LJJHSy{k^Q2%t(DQ8QKn(ydK8yey_kb@#f^@B*jLr{7%O#h+fCVvdzUG z_~1{+IonI~U~xwkHuH`oj@Pt}YSM>Euz^z?<$PPzy#&FM$SB34t?4Oi0nt%e4D z@nE-pe1PFsj&M0fBJR_D8K(ST3Z3mYpB5V=K(fa^&gq35`$!+?oLeFFmGt8*f4c)y zRENTCwVSXnUk<;TD8p#2?)V|!2hZBCg7^0f@mUXZeC@P`UN7l_5$0#$WK#l;Xv)Ck zC>?Shvxe)oyNFwFwu3*{I32sUe@$l(>a#8NMwq?6Ug();&SvXpV`Xs|ZoKONRi2AM zX2BA)UJwEcr^})KkPfW9Y$;@C*kG@>Tj7rDN9sItDLt?Z7tNe91jpY{rWL35Z%qxl?`8--)h(U*|2sVD1uSsQjH6~MlOx_D*i2Vg~~ zVOi!@vdo#ttlRB|v~CGtw@V9W+&{o%9$RuUwvWmcHj2+~i!br-PkG^#Ev}%f`-GeE zb^zPtGL=O4)Zyy(1(0Im4_n?HfR1txiG5kg&ySyv{ zN}jz3T?ZFHXQw2Ro4y>b9C!~V17=}r?^lo;aGo=jng~anufxQT??ry58?a`44V{}g zj-Os3gVoCSg*UsnU|pmyxPE&o?0H}je4m^M!_ptq=F1f@!2z*mP6zz)?K)V<#9;1| zb>Mxfk>?j*q6a;C@~Xv0=-rD-aLZ|+w5;`XPrRSA?28f`9H7c}rz$i1jW0>*^ln%a z8OP5^w_q6OFkxWUamJS(>VuPC;*qjYDyiB4i8QUx3&BuE{r97U! zT)q;uih^m+bT1bFOp>vh}F@0JqC-vfS^B6CeDGA#;b}-WdFK{>MrPOAi#Y@=m?_nYZ#kmyz>=|X5mcfgu1`sn8KzEK2+mO?bB`BGr z-l>r+cyu;=m)B!Xvv!spT0VlgKI(ua19d6Q(vqnx83=1`4P;*V$zBA40Z^gYevNJ)~Gkym7k&+DaLs zZSX-n_cU90Z1rMczhN?%KcoO64|~z56;)h?;d}o5&4Ik`=u%j{s*(@eS_6+ubK&yR z<@9k(3RQZH1Ko~2VA#cjcp+I4FY3<0+O^9`XLcHNv$v!bHSRd@%mdoT6++C|vt%!S zfxalWW9hoaIIw9Cz26vw?x)^@OME_9Z&6?+VFZ;&5zQAVV^~!HeC_RpHkEO3JTIQ^ zH)cS{{4~C>yB%KrrjAGW`E+8ZB+IKf4_!Qaa@Vz8vB~@}J@gHuD{;M8>Ba@%rmziH zG$`T|{svSG(gCTP197DF7?^ZyIdjT4Wr_<{fRW-r)HDCatIX8{8QXjqX&H*a?~K4; z#1G288HWAayW`lNizzWJ5oR6VLMN&+uyUm%zUU!O-9y)qd4eQ6@;r|{*fxhLMh*bQ z+jl@+=OZ-*JK4;hDZ1U480TgY>O2xk2d zAjsUnS)mKD)Ahcfa>O5c4;#RRyD73mW`S5%dt2o1>B=UKxGYRQm%(=Rc}n&f8mx=4 zG3yj~l2*_Az>RTBW{>+$XFYER(%M)}@;a3SpS&&D#Z~g;;IkaB_^o8U^L9``kTN>D zXiyjVeRN3SJUAH0FtQoPbi8lV&f(wr(~q;cqM<{}W1pAM`p|(a^Yvr0T;)U04bM_{ z{r)WWR6CZTwTE+9{+g2eEr!^D<6y94nAn%CF`1H+Xyf->Dxbd^(ndJI z5Q}7{eoqO{o_oprEAmh+(}%mz;LpzOTtZrwjx1M>ps7P|)N}ME9oqmrvuisz25g1V zTi4U$iJjT%MnBG>CV*vUZsmXMPr&7NMdd^56Y=?tNmxCs7e-#TWg)(q>~M`b>YJ%i z#pdC3I>Fr$wfq$7YZqQ zwkqy9;=qQCcu4})&7yIhC&+h!3>Oim!k68?O{=EYaRSqBY+C&Seoes-;mx%__$P-~ z(cpv5q%@-+b9!mT>Bm8R4=)K6B=};4_7n!(~E6bQ$#tZrz6E~?cKx-i+Jwb z!re5;eFY7dc|y-a?m*V~DkwTp1}j!ta1w9kv7Ha4SlKLVwqwa;HtwDUyBVs(=DoTL z-t&yuY3mQ=V^%L_Dv4_F{mlk&2Y^GxMSNjM2ps3qX%A%5wZ@eoIH=8{GTVtR*$ihn zFFp#zC$Kd0X*-guUClkZt;|gGJ^9m;3gGn3m`OeE$Ch{~0jNrfV`a1;ROL4HoV}P; zF-5emH0HN$-U(kr_7bj~!X};YghiJgK)X%uI5+Je#oOM1#w!c)$}}nVu1g^LdUm63 z>&Bt<&`7@X<`Jx4v^E>JQI|Hf8%62*$*ju!0vvKqr7i=8+v7B}NU(tA4uc{2RfFx<$PCD@^@2gya#?iYVCpX7_+7Ug zZZt`48RcwsVxP)%sr{LoP&|AQSkFJi^l!wnfQLuOTI~*L9px5l!J-CLl<^A)_Ax}A&K(u1iP`;pumXJ+K> z!wR0ZXCDjl;HK+3{?+$WR4LrRo(|2WT}r8(nTIXJ$e)6u;`^jjITbp^oFVzqMf7Bi zh|Ct0i9U}qz;=Dj*~Z_N^7|IgXZB6Aq5nxGRx+&z8dgpaEg9Ms8&Y?%!F}?m!k!0* z!;wtlX(y~o?ZitcyrCz0?&27$TIw@mHcs0*96#T>#BIPUFv>1~ZQ@q42}f5zis2dV z?S{8-;Bq+Jy#A3NJYXLs1zNDVr{AGdOm&-3B7|4e>atvE!7SH#9F5H> zW-CK&Si6*?9NLN3_=4S}UGS28zozqnKALQ8(o(K;UkTM0Il<++P4vP0IDP3bn=@=D z$+TYQvxnN3h5HRmDB+?aNG$sf?dl)!=^GZ%#KGQZ{A?jp|DwjU$LYb^hHqpvv5`7e zcjTkMg&U{p#`4V*!Bf(MxoQn&lGnUJa>zZZ@|(=;_XV)}%f`%M=yRHPP8{V{e;y0dQ5%UPq$0#ctV!+~5cWx_bpKDdC5y!)1`#&B9UYYkH>9I>E)YQ`qH`k2w9h6Y%nb zHos~?C)UBKh!3Bx&GL+TG3~v3`5#bEZtOGnxN-$uy0ekCkGMqfPvW7^@;XsK;|~}T zDZ!>joZ?7V8Vxf?&~|aW-p+4g8())yPmMBIyP~xe<&sF#qXq0{NIZ2@lEvON+v$Vz zau94WLX*f{!Znwi*#W6oJXF|3y;A+zEomyWMUpl#uDdASseGcBJBmuA5ciEFKV$NMrLf$^RN7xIoHTY zB8>3G3Q|(<$;MCVNjj_U(B`!vl>cKGJK}hj&fi#0Imzbi^J!i7SY-p~Kb^%S)2&(j z%Mh9oG?s=wQ)UlmxG~syh94C;5sR0*P?cLdM3po&dcA=izn>~R(Qy&U_qSrvtAkl# zy&5hlE2Oalr(>w~q4JnQQx^N=INLJY2*6r}=AGs6kkod%9Qu*+`gCIvNu!v~<-;Jr zR}|@Uj!YyCxYfl9Y^i7pSqM8)oM#^vw|O-@V%z!l$=A8wYfn?ckr|lB`O>W}8)=+` zEnl)i3j5}FW#$8iq1#v?Tk&xrrHZ^EGyMP^zOtScSL(5q+EQqGD3Zq3>UhdqW$_OxJ8o}^V&}3w5EPF&s>+#a>c$R==Fe( zNqBN;rXl#GbO=kjGlp%olVRvFMV2uZth4YLshyZk z(FY@#QP*7fdifAHZ@?sSKXwvM4E#V1JI!#ZcMp~mLEO;2!(p0fKMXv5gQ_NUK#%8k z%#(BH3d)*9mu?NDyF=FV{>zL-2bUZrjnU39L^+qU*elP@)lXt;vIlY*YcA4_;o2_V;GKh8nE*Q*o^3{m~G8x{w~!7ebaHM-K%`n0i@W)J}RN7+b9eL*L`j;5?fe z`~zrP=3S7EOaZK z8lUn{hU?(BdrfdOMV=)NlH&Z9ML}NoRC+XhEae0c1YF9Z2^D==Sy39hA-n~>v^216 z_iCyT-*0;S_!ddsOarZHeK9}o1DMCXfrCwX{Jn!CnC8V43hD35uDmy4%teab?R1sY z#@R#3jy(RA#7cIsQk$Q3eGC~|i~_5gc&0nBhE_b4r*7#v&{Z7Mt-q>-)an(OWqluZ zV3GuA$zP$olRON5xC@@oog`Xmb&TJ6Zj(?^d`{eB|7I4qN)kEv3R1J5!dC7hw?aZ3 z2XlWR^n23)_s&emYnyp6-o71s$!UYp?e2KX+aI1x9gND#he7}LDKO0goUZkRzuoa0 ztjzVRBh~ zWl|Q*dz6bdm*nxN^a2>S=^?z?A3`gVE^@JFPk}_4G@g4czK1j29KE~j1nq&b7+WHP zL+2F1`_7X5n@dJ8Ep`mFOO51TdG)%q4zv|U&Jo-Esxa%+jySMPEE)Du|)#Y3KmDz`|6LhJ2 zwW#5XG&@-6504Z!!GL8}Fyi_$zTNCDSfQg#c3n5qMU5PoUswr?zV74gL~pp#qmEGB zcP}RuD#VhrUii`TF+V}Ij20&rak;lvz$bKPKK0H_vZ4ve^rnC#ge zz1DBRS&NIPA3I5<(qly>(|7Zq%fsOF$rY&4dl#h6FlQh2=80+s2oNjnQLgV!nls}q ze9K*UPXuE)~z(EvCaaJ7A5$Wtx2~jjy%c4A~EhMMgDABqfms&vnuvVEYnKFCUG0 zJ|ofO*fQ~cjRdL|?StS6KCpFV8s3dhnMi{r5mXFPA?WJbs5&Y@W2;K&wv%a zg@f0#XvD+maAeFX5UU3?=)HlJL%f7pQOl{&;t}=VYexr-JqS(u9NhQ2R`Q8AA|@nQ%@XLdntrAoeZTp4E(F%y&LY5tgkJvj-Ogil~$W(gly;E4M>o#Y*FbjE2%h$cL}3j^$=abdkX?%3yn zs!t!m`hKd+S5*~_KklL{8&w%QZi;(LH^ZuVzL;)OB>eov51aaQVIyVluzOTvo2{-Aa#o!o;waKV>re1%US zdwjk-*h3u^oN~tYThDV3(!JTH$;%)_?<2eqv}2ts)S=s@wcx8aA13Y@Mo*5;!u!SF zpp(^Y*wYDd?vMj;f7wo<&)^tLW%K!_uRkFEhBK>KeV>+Uh$up3hv;%3~CgPw6 z;(NA=i_kP*73c9~!t#MCsAwvIzSpOL?VV@PFrkXHo`0iE^$Kvw`wgP5eWNnx2)gyk zgB9F$hv{*{aESR5(2Q0@6}SNRMzzC}57xp&-*K4XDS^+P>?kkE*GJ)-zHs~TY<&Lm zv#_bdDtucg-tXckL-(Rp^!%PGHXPgw(RPPuzp@B&Za;wib>cg&9>cl#!cll(s|OX| z*1$DppCC0{9S!crao=BVV;4ilKw;KG=D+AYC=IBERlOzg+y-6#WI;G}A5+0y?tQzw z%%n&hYn_MO%uZY}_TvP*FY-1TyC^5_2~6L32TI!Cf&9t=aI>*J`j}g?+)p0ZR9**J zK`u09c{Dv1pV3Ih4PrK%R#WPFJACN|)I)AMG=}ZxW_Rs^%IfMwekLeY|BMq&+|Efa znM!@Y1pv&uz&hm!Ymrbh%L-F1Cik^4Ntx5}F?vnlV zP<*#eXNn?9Z(hgSbPq)9byo1QPZBq7f-c-%Pzwv6s@q<0*}{z;6bxXg%VOtQp_Q5@ zf<-?x634(wA1sCIDfVc6X`V%?A^_XJw`Y46$Kx{pb8t_go{CP7 z0hL-Ek}7s&x*boTvV;nL`nZFR1l|Yt1WC*{zf3!}$kE|jYPdJ^Bc#3%`>44)`67>g z%(Z$9)y|xRkESMK@Zw%`9ekp=ZV*@}t&6#z+xr;98 z8?n*5A}}g5lI^I@Li4F&88mgPioy%FeIf~_-)QEa^lwt&mVaX9$VWnWm_?y zdzz8k1RZ8t>dLR<9mV?#Q}Nk5Wwtk?97gOKj@=#S!{GT4a z7kpLNy`Vd$_Af#^W8xKymO}BdNhq^v3cY(ei1sY%h}PB5V5937h}7a?>N6qBR@g^Z zcINUWVYZ~Rasgbh9)iZVrt^7YwDOz=t?d&^!`~>e zPoBnnL2?5;-<8EzgsXvQuo{~tFO8v!8>q}*#1EbC&wX-{#^WQmLe7&|+wRwok=|JG zJ#1ZloUIgyRaYi3lfXA18700Sq`sNnjT_B9dpZ-&S0;g|(2FxV{)X>;WC&RMNuq=6 zG#Xwx3R4e#hAg$2T%Kw!B*yvBa{IAtl;%`0Q5wP~j0$F+&*#Gl0}GZgM*_O`e#fm* zECvV9@%*;!>&5H#9IEQ&jfd}-lA6pqOu1x1OST6=fcjUu82yYpzMb)nUTgUI8tt+7 zZGF5otd2K!Si$A2H^Sj=7vYhIK7X(52BZYXV#GQJ)>QyB+__F%cKG!FVedV_qG;B%VX}bajAR5skRa1t-80iwpooG21Q8VzAYwuhb0$g_ z2__Iw6az{I0cEPdjG3T|97wb-~IM`=enS}s(QMro~e4O z`l)}@~MI;~YqLPa-n;p-skI zis91sSCH<7vS>_bH{Le{68-15;fq6OxbnInth%weF12C#Jd=yD}QKRiBtOU+0)n&pBSsZ2b7uBY0E1 z&KhKP8D>06AVvya*lzMmVMEmx{J2b6B$^b6uYK<2Wc%mfnp#ip@U$D8(vgAW;kgXh zoPG>OH1d)3ZWmFGKoWH<4iN2^Ho&f?>D=-XePpvJOVn#5o}XUyaZ$siV6np$?%+CO zu`_Tey!XTssp~El9xWJ>wIQZ_G2J%DWk$v;Fy1DWP zN$__~+_^Isu6#HcwdcjdZ<=lJOqL89X59~`Pw$7TeZ_kKGZvwfleJNc*pFpslBR6hil?q;)wj2$Z2Jj0P9(H_MAn;CW#;B$fS8A zh-&w3PVJ&vU5fT?{G+=BA6&%2r4u&8Z&mt%}IvUvCuhs|o_a;zqzZL|~0=$cE; zY9`{o$nogI&8bLP?lfGSKL$;zI}Sxz+Gv~We$k`(Lqv)v4Up!KQZBus7*2TlLKqab z?8GCnPkEa|1fi~lqt#P(;^b*rSY!1U$Q4zI=G(fU=$`%9CUQSsH);oN+Oe6Ojvp;N zAnX=xuHGyWeZ=boyKtB35mDmQ1bC~(9DaXiOf0+} za}(kLnO>Gj?lvC7g%Rhu4Liq>mR3FPdO{e?a?>Rho9=PpT|<$mD+s!!x#KZ8i{Mn% zc(Sk}8GnCl1Rvb%z`478P`KK2sAate#%KHEmp7zw%#};nv9ANl?(#rivk$;2_L)c} zBObDWR%HEf3%t$N8NOPnM0V>v#NTTfGC+M6Srr(I!@vbmTEY$77_Nv#+X7&DnhbWR z*Cp0NufPvUU0CwsK=fAZui?H9!^3AQxd($+qB19dTWg1+%{8m={xV6#Yjwq6aIEOU zUKY3YctW~t8h&P*h8?I*xWC1QB+FXB4c21+{-8{F_U2eTsb7tRj8KJBw%@|?Z7y*2 zIYg`$Ul#j?8W4xk4y5*J95eJ!{4_8*~L}7LsWYNVv zsL%DO=tu2x?m&emwyn`e=0~&Z9`Q4f=-Y5~MW8R5CjX3^RqKqdX!b((`ChK-`3_X{ zAd4)m`vUJ~Jrmi?H6lr#a%ipNY&gsp!w3(5Qh4v7=yIP4G}HeoS}bh>50Bnaw`uA& zSYmM$y1c4^owl#=k-UDmqbr_hS|5gkm1c-^EQ;Z(Q;}TJ5-|se!)dtY)+o_1uO@sq z-w(!Jdy8kYyEwbp$N0h6arohZBY4`2W_-2iApBGu2j9qK;8{-kWTFuus(Z6UwK@!0 z>=X@YD@&+#Uy)N8c3yO*s~ygtJrt2S>+$8_99$*W$W0S&7b)DbL*Ikc;F6CzNG{41 zZRP z9V(g~&ZW!|UMj_1r|V^MrwT45Wo07Amgr6^YW^kgQ}sH;|P;S_@lY zu2iXLzTGUeWKR;>*f|m!vj)(CZH2t-et7qqG!``vgu=pP?5?sNJ{5?46lSTTnl*E= z)=G1g<}$)e5xvCsQ5Eg00RMAl!7fnIHgutUNE zGTJT)hkt0qCL2Jabzz*3pZ{i)LQKZXd+l zEP1vlR)(#!&U_5dgZjF8%__M5kS^L*o`?bms-U{{GH9e2Loof5HGDj)6iWGpK=(82 zpyT~7I3hg(EnjsEzVhybr4o&BU*~&H(aaN$K9dATWM?DKULGgEM+qv9tL9cdbwukk zzKC%nli+UE2=r3xIHc>I;o?X5 z{+NPaWE5eW9F|DphuD2h5b;$VLtK+Ou|;S-HuXrv_B~68iAP7><$LPT{CxoVxXg;& zmcJt06k$b{p2;L3)!m|qphWm^N*9jWfv~^v6C5l56z5I0CedMoxnX5NMNPj+iai+KOb^37s=I&UR8dP5$`+8V&39U7$Sx`>-|yqsHFzY@E7IY54I zG|{w~!sS2f{@vvec2x~4+XI?C}4nQ-Cfk^;D^-473Hl_kA1cR}=R1a|SAKs+Z}i6pPz z$8M54@v>Z3Y{D0>o1f1*;aGMZUk{KbH(erd#Hk_Z__i)wyhatFZSP>)Swr+GbOmai zuY_D&+hFUX?P$^oP87cX9b8r}=20kAK!*2@aFZvxBksvveEc(mYP>GM#Sgv7Q-yJ8 z?HMEPBm4|QHgaN{jS_0pmq&}XMMBq$!AR5mF!b4!fxdlGL&Zue=)jtCs5I6C%PD<< zLDLgN?^e%-7u(`+#>h`txWiFeiaUXqEvV4vLiCq zp9^jH+p*KDR<8Ey7g{7E0f@7|khAMwZJ@b2m%c;3{1cPWPoNl=q&1 zRKhpm;1M_YQ;xOatSJXziN+_`7MKX7G!;;5YCJ9LMHB>t^zb@xJqtsOM!LuAWNZrWE({)E{6{< zyylKt#Gs?Yy-=EMEnK`M4$hD{2D_4?(U)_+XvKF~;Z{X6^k&&u_%vVzI=j4+8(kNO zc8GoS270f6%>`RgzrYhev9T6So<9H$nqq+1bqf%@tcNZ(92HHFcSdjwr)Y zXgPZk*WT}h)1|y%R1iRwi_2j^4X%q-3`3jruRw!i-*L0eK-Bc31WuH_2zAVxxJ?r; zi>@g}qr36SNJD-m`7Fli*$v*y<@JX_sY~aeQ-h8ehh~HK+%|`uZ*!ntr~*py%z&rY z%tltW=`caN7S6tzkLzq|;JOGIY~?Z^twYA5jRO^7o%~ANnyrO2og;DM-5Rufi8S`J z%oV9$vP7#@O#~ZVBjD{-JK=%GrRb~Ju4JX-j&8lzBfesL^YUaPQq?sRp3V7G*HWN` z&0;RYn{`F-L9zgbiMbALEUUnz@*&65Fu)xHKEnW+E@)=T!=V?y;PI9Q_5hR5r| z@d(s9a69qM^+6e#snBm=K1$EiM!x$UVB_rvoMjY4R?T;WE9?aD%B#WT_%UU)Fjo%g zyirH$0z=%o^aV`srI8jBBi`fL3^Q*Vpv_TcDD>ql6g%}ZEVug(M@L7%S6Q(rO@jvm zLZ%>J-XWO!))KmIN=6eBjzc@MLKrkA8uKjlQP#{UFs(EmPR+Uq=f289^|hKPWWY_) z!p8|{?etDOY>*u4EearIom-*V!<%&nd&c74<|%0HH63(M1w%QRanP`>0g^O#IO6y% z7-MP{`>Vrl=)0S*~wjSfg#!DPWZm{KrDWF+xSv|-5+=r;c@<{#*Xj;1ZdY@7wkU)aeVNX^4d zM{Lm1k-Fq`OO`PE@)oQd)P-M-?!u4a_3`TiLt#bR0r*Tl8uso>)8qY-FZTBXLMkg%6qFjIyC6!V6gI3)A%vf~Y(}3vO zQBXI4#!J^Ofy*rxh@|D+;0Z1gdEcB>cYD1(lu)d}-BIBv{3+oc)=j|Q!q%W%^=!CF z$`Uz9xxg>t-CM`|@#wVoMeKNANf?g1xoG1MC|YlZKMc9bNzPLzX>of+Z?8$i;+`mU z_Q7G%ARiIlRC62_czwb-^-E!zlOoh{yZ}QJFF`7EHL{An0-Yj~VZ!*g@Q}Bfn9FS@ zy8TcMEuQO%ANa|_ZEeFv>H7wwRyPN9^bsW*t|rDh4nK&t#GQu@Q4KKdY9!hg>&J~1 zyH@8e+AVr#r-{rT7!#>nPq@9q9`%Pip@Y}VQT9h^Gcxe6vY;&am zR<86R;WI2y)@;}NpOQ10G(IBMQ=(XQU5D2ZnXlN;rsR)`3mKClsOl(9f(`cp+cjZ2Vr z@f+x-w@oyz(^!-@$Os49O+yc=XOX0nVq0c`GJg5Z7wtbK-uv~qccQ5(557=*27OlR z3qNZ6azuM9dRlY@&rctSCTutfd-*nK(?f_tj>p59nHH!m=?(nxVklZ3dS7(lnig6( z=4hSKI0|NoZKrv3H#{V_4-bsE4TBvQpcUifIqcdBh4D@>M|m!ie!B&Rjg=#+kzO$M zRXo(L-H%r}r=u+ja!6iw7_Q3H5GmH{!us7s9KX8&tt@p$Jt7-;`t5Gf(C0@uvgkZa zDK$WaK4z$1M-~rV`UT2u*ba?UQpmN5<6zm%nJ~q12P8~4Zkc)##x>BwvqrYKU-kq1 z{ACFV)>6S`JqpBPR|4E}A_9+BPO94!l*_&BEERLQmcYc@7qR!WrSRhSfp~D-JhY+l zI1bB5BD=$t$rA%u_oII^thjI-t1EhvRxw{|iWpD$dh=*e>+4FKoDf8+Ue1L_GI!zE zO}a$s#!yn<+5(Te%fm`j3wX%sAYKmgaNiJFxL0_JyA#5|$!l7%?Els?@|<0rN;;^)4uTVLG|go{1IeEXb3) z9^{RpJz1COg?qnhiTT}v$d^e}KX+#PtKj`Fi0Kep#Wj{{}VJEBs212OHPy74NHD5G{=9!=tCJ zg$<&cIA!xLQSuG9x}x;)q#>|~z3%=*Z2wyzTGl)fpHeC3*hpU-UDG2f&6+~)KCZ!q zO}B+3`ZY+|kF}ybA=dcO#deYH6eXPPtV@;*4CHRtwBV&nd${$+DyS#LR_wd6NMIng z0fs#Egcsk5{fo?5E@!)k*k|LWDBmWBd%ew%D7f2^6;sxe4HvZVnDbpwR2^G4f2boK zlI8==jjQlSPYY4+{li^Via~8RzfZw5Y;JayvMs{v|-QFY1})Z4&rvm5FhQ4 zc#W_DPVti<_xv)U&hiv;DL)3U+I0cHoq3e(CT;|6*CPWbJr#|avz3F)F3voTCR>iC zka)$x1irE$Yp%_Qt7nfUE7rM@mO+|WqriozPVp4mrCd3Gs!f#51rlMLst|^*AdOcN zaL^=Q?q1mfT)HlmR0bF0hfYnLf~F>}Yds9Z?nsevzwP3&;z5q`?%@R`dr5M66bXE` znQSkfjHR-nNN{j03BZHM@>}Pi#|jBDYf_MKL`Wx&++s^yJZ0dKS(c=8Su*+I{QbnB z>^{-@=Sf(PVUTM`AnCRc>&t;VaXn*8PKNTxb$?xAZn_B%uJFPY4arz}z}eF_F`^{?|9FkOIYfY9Nf3p6US`Z z4|^}2fJI_Xr~t1GNOkSSx@YePqxXV6Xw;D?yqBE?hh+^A7C%))bFK@?q=hLW$KxHG z!lwe&k2#a=?m69(fA$Ux@9BE@SYHi0#nK|F~#w zWhi^`Kg)=oIX(><*iUd%f;{b!%c4ByNk^6&Jh*DMfvC8 z&QW>Xpd0V;hkBM6-j5a~6t#hW7<+K)C@xoy6(p50aG#FhT z?Sk4mD!EIa)yUi4X=JK@4{Vs2C=3=}#CwVdkcg%f(x75M9#4LYmzG?FFY{E%*rF{b zCQ9#1u~nt5VsHb)Ix8OTNO?G)Ha~Nq|Y|esk?>4hOWorIV%*HOFnCBP zjoR%b$jtIHu+_8yZjKm_%xbmJhP6iU{HMjJYM~@v*_tCZcelfndjLvJR^#-ta=4l2 z17KlZEV`O4_V*3tp)C`|c>72J(yn?3Rbm>jqRM_4c}D@c3H6B+e+@jcC>QbIM|kj? zJaqYjgx_}GhiI7(7t$kyU$Oc@p+g?uz3r| z?hxYY4O0Z3=HKCzRZ_6BJQh}oeNuMlIEkE0)KQ@FZZzEb2MoDB1D^e~T8tfC33WjVAeD*^trVhF^K?s&i6w z*iHrxoZQU4x6p?}pH0E1Hm>FR%%-A-+k4>EUBij@rBeL;UP9g8=o3)d>jkb&G(smY z_@JDMjddp^^ilHUIpkb=jOa-C7C7Q@8=RHq3}fz2A#*p~60N*XqpOdmA=Pn)Vqdye z$fa$Ck2TcM+yP>L;1Y9`Z+TGYHot{CZ6mfnE4slo(YajGU3~Y_5dPG*g~$0$9UsT zYdD!X1~pPgL$B5vE?g0kaV!e){xE6u z-nkRnIyi{=M2%5ohb5Y`N*8^PIsgYecnLR#RKfa4Rdjv(LD;oX1HC+6CUlIJL#xmI zfRgHaMQd-^qGY%SK6Kp$2Wz~577B)F>7W6^tKEF`>9{N67D}Qg;kKyb7C>7W8I*fB z3YOj3;ymL-y0cAAva{{}EzXlyL^}^17VlgYAL(3qW1Dl##_7(?twiT3*AksqEM4oI zFSpfMXY>Z=Ed#bVi-yNLJ8nsKUb7+8`Pi8)&KpzKIuE~-ia;Qyv@*#cE<~co`<`sqc9`i9N#>2 z3XYqsfsV*15{K(LX!Uv{)H`S(da(werYZeU*Z&7jO|T?8gYMwib%$XNa~5~XYaxRh zhv4}yrf8PlXXy5=8cO;&pgo5S(3+#BsPv#dDm+sNJ)c#;gohQd=8FQlns5m6y|vK= zDn`gpQ9+78?7K4PpvcAC0rke`!$Qe;C^UEpUE~arOrj(gb%I8Qk6h8^;;-=AQWG?J zmlGPOB#qJ>V`1g@Jmhw90Vfo5qb-wOMM^B1NKElf;%C$WcV{csUoX$bgPYn&nRPu% z3|ADTyrf{EGl7LiuA@y66{KKNGBNILBf8QRb@eq-M04wGm4N71v> zd&!oH9I}4i26V(~EHpUVO>9~UNuYKyT9vmJmGC+VBO}I4*?c6_$2inccb=+2nEU95f?v z8Btt#weG{sS9n&gnA0wAK>eWsj>z-bAvjX%CM>m2M(cx*iFsnPaJ3k->Sp~6OAUWb z5-WBS6Uk+nH!I&$IWrOl+n8Z98)@QLAVFnq;}Nr$6pw{1??TlKoB0j@_zk z4=!dZRSSXf(Iyb56$^IWmBU_8j3?_0A$(6BpGXnsanXxThfm@1tw*g26ElHT#cl)`L*%ql;jTJ|A9$H%LJ zd5&%ZG$}@q(QheGIkbbHQ(!5aUDhlx)ce8*;|c`3M`;S!vwbtVoec!>x^HN!} z8$5P!LpfvhR*JPx<=7$7RZQ!x?`+80(QLu~hfMvORwgANigg@b&D`{R#oky_%@m+Y z#`M*2HdnZbX|LJD1`F+2_teoWm2Jd&nNMdg_6=et43T8V2G3{bMTp<@8cf)+uHNi) zi4LYJ#)_RaSB%q7pDO)K^HJYD~pvi>K3_op1C{-1M88+QHst^Lg(7U}Wt-}ir8?mzP9 z>HSp%zj^w8^W^;w{}X(9=|5%r^ZCE!{EvKjYkp__A>mK`Eg{kWcb`9`GJh8O-%bL* z`7fKYepYSjFFwNmzs`;S-{q)@&!?A{Sg|Z{!IA|_=FOfHwj^Z1JPCdAMDiD=%)k8o zoKt>kzTfl9Ur(j}r3`3^M9^PjC_+zWoT|F>TK8T<49|1A>udw$N}T3zoKPv&nq z{O+H>pgF@&2h5Gd0%$z-3;P`K6I{kZ@ynio~`rm}Be;vucD*HFVKhE3Mf6e`` zKgYiN$ggS`{!Mix{&60k|F_xy<=6if?`>KB&!U_5{|x_`UYmIM=Y4^?!PnsgW=!57bg7bx4-q}-}mGHU@`yWzxHqGf!YH< zo8ErtXZX+bz^=ld_x)#o*Z=VX{~y}Gzt;o5$6@cU{`0%pf4fZhOWN=FUseYHA}(GG z{EN7F{{I*8UsL|y>w&ai{q}eHf8US)@{B(}|0scs$eW)71P%4cr$q%lupWSN#H5)3%D0&Mbm1gf6sGHOkLssGjwmb%OXTO!gyY(Ok? zw#<|9yomraQ5EQIUJUl>hk<6)RguUqzj_L`{*EIA4HK{Nc`8psV>MIbbW(&#&eB)1y2K)sr?fh|(9KT|OF6*Kp;wPSsV;*&= z2}|r|2*_ez_EE$m{-+J=1W&)Y3UnS`5$ttQ5=>KAjg0cNJf?YE8%=K7Pfp_pX!NjTC_)Y^}vS8r{Cb;kd zleb-&eU>;N#ZypKuJqL>9C zeoVk7n)T~T1S2fczzc6V_U=LlR;{9*jIagjxIu zGw#bj0jsBn=+BY5jLU6JW{Z;r+js0GSnXFp4H{BRo0)E8WKKF_1CFKV=P|nnF{;`pNrY3Ir)6ctuNS1k9M;*6_WfrA0IQN8TSQyUEBGNtR=fRbU6Q8K5Te4s&v;La_^E&VXI4LF19FsO;@-|*#o@@vM zYwF{{52b2az6moU)=SWfL$zskg)-9-ah|5$-=&vCw}H2pN|yluD z?>}VpZO8LZZJom2ZqZ|VRXW+aI6XmlP%J+{vq`Y=)I0V^$2vZjnk>*d`GcRLGO8rzv21FaxHg-UVKkOF`|}9D3?yYi85uonTjHJdl8| z!H?rbK+ilHM2+$UN;QMQr(jI`xj2E~;*UV5JsS8wG6c(}F9SI7I~cKcFlbi$LXEon zn#!1ALvPrC=nB&>yhgQNI!}8iFN66=FI_GKVcpfV_YDCJT$3pIWg}?mqP@UQvymFK zDwfxEasqwN+K~QK$kHt)L3DAk6& zaA$)gn5MUbUYyy;Z-jUFrt!1bv&H57S!>N%k?1Y6cNu0C`g(ySogvWsvXGy+K$#Vx z0)Bqr2toTtdBNSUTlhw{6#v=!xq=&uR0T2SQ~1offr3v*KC#CPyjj)j^O(TrWlWyF z7OP=$iK$!qmVfY~9A9RY2m2=O78@OTpMT}!X|}GchoOheU`k$}1dUzIAjkCrh}c;O zWbXNZB{Sv0lbjoj<#=CE(7%uw7e0@Ey!i*Ms+<7i^R|PiJT0)ThPR|&Kz z=rVh=6&YJ6IcDb6B!FIB1jeg}0D~u6ft2`}yX|H%LB~ciL3g%-pzQ~M-iasR5AA$^ z7XF7zAn$c4vw3eG<7S-4z=VB_X@MOW<*dezuRZ{7%HN?I+Ga70*&;^faW6vsH8xN)iIRRVM*I-yt7%e9W>F>-W5U;)pSbgM}xY-f(GBXVT zEoRdG{xP&n$bG8+vldvUFpANdT*kchm;D~?F~bBZDm?xSt%dwKSG(BMJI(C;lULcUprh=pt}BAC!92nHt~mCd^dbI+ zrO)_dwyO%9m|WJk^cj2bg*tz#;W&1gD`czIUuBgi4P(EI>!*i(&Sj{bZY*iP$v*n# z!PfO@u)0GRF?+K2u~X%gnTxdrOz`^(V6us3?!9H0;DgbOv6~IsT&T;Q@TOP^uj63W z!)@%tC+nC?Yh3xc-p#CB;wU!cvKpH)bIzaZzdw8g{#k)PCGf{dV1{%mkfp9rPb5;P zjnng~h8sro2Yn;Dt~Q+<c_{)p1>hI$j}5 z&9JxPEp+ptX4b|~H>-!ynS`Zc0`nr#G(g$vUrk;{H4y~lXkv@zM6 z8<-rGG0gf2lNbdfmLDd)i75%53bq`Z#m?AwnLS)4U=v6TbI$%2c(Xf-nen}ty&L?Q zkvY7U@2LNt$ysH>-mfWO?C;64iV zjiFb0@#xusCA6Q~Y?@O9AhKvHSY4VxPgW5;LOKz1^-h~CUd*hm1qz$wflU_h_9m%=P)Rt6rm5wv#sG1@GOrO)(z zqOU)BOn=vqrIk-)Quam5nQobCy6pUWUiV`Y@b&l{@My3V{ibgO=oWsULPq9NcBlR5 zP%Bk>7xV_<>$7OX>8@bfn3q%mCAJOONYF9+1L>Mi{-ChoDZOb#CB?q7rU&(1ryqPz zr_Z6?g6eF4e%{XE{G0*B{ASrZtot`*zN#1C&x>hh-b+dgXOG&=r|&#x`W9#K7yAeW zai?$aBm4#n{9qwJnBT{L;up;~(vuN1m*@!+A1m_*7medLtDj*%FIdUHcU6slzWND! z;Z-Al*pr9s@d5+Z?));oM#)otW>yscKwmava@&ZxpV7rA#|>xVyVkP)54JG{l0J-0 zwjpEr!;i7+8_IZE9AaFsA)}>Z$bQIp#MA{`W_C}xMGw4SM|Cx*0xqYTPKsX2x;(F7 zW_65Z!wx?Io)?EQy-)UohO14?!AT)(nD1%!=4x+-n&v4!Ak!SIdgaaD7`%iTKU{@@ z;SzM}3^&kTy@xWnbAi73!jW!&Ye?S@ZDPuc9Kj%)0&qd=8KvUg0Llhjpj$n5(c>e! zz;lx;;6q<5qnVXX&tG+%_Fi)ZC?z}qbq`k1XNzUQ#C{!+9B2tn=-1PHUjO_WJ=#ML zNUbqN~qx^XL8h+-)yL`TwjY-%3DF5uf9KMyU z1wTzB5R7~i!ftFl$IgBm&HCJZ!#ZD7X9rDfV}di2*u{xLzUi(fp!%+pV2#dX{+$!g z*iW*7Y+%zUcH-c*00qc1TjF!Tgt=|ta?vKHRe1%t4;QkRtT|@IX(9VPt`!8_OJ{;k zSc7O=A(-~zB3L1<&$LgMU?Yc)pmhz-(2=3?tTR83x$|NKnE!Gi2->ugG5add6l))& z%Uib7X$f+y{GCW5Yw*Y8%;bJz zetvDpOaAge_>C)!{8icC;eWjS*1z!b5>me&`Pbkd+yBSn<^SLIkKg_SroS5VJN_S+ zPq)gipFsNGm(Tpa&HgXH{zrNIL>Bz+AHUoEJN%Eg-#YVWV}2tSe#ZX0^^gB@f&W>+ zpAz^}0{>+anCVqTf0$}YhYabYhQ76a?mDgU)JLasx z%bmK5ij|FVLba{b3&Ccd#7u4Kc$Oq})^juUvUnzM{NYyKcUvc(RIvo_prbXVcvPNx z;nmI)*6C7}QpVK1)DYf5WyJgTDUEmOvNz9`O66%Cy-!sLUh}>cG*KaX20Yyj#&m?I z4{LAM11w%vgA4;L=IfFGU=y~IE{)sA%x@S)(Nm20JUW`ONIgx@Uev~f1^-}%0v_vj zUy==zs03;X_n2rqJ=XO3Nig0xg)NY~3(7{80h0k8bh%X(t#H8^aM`N#UiD&T(>7b? z`jsyF^Hdd9?*3&a?5HBk^J@XM_3vq=lGpU>d?VWI9Ywp}1@ztvlj-)-hm@+@bYSf= zlx{Fn1)oQbqW4X+0WaRvP`UIQ2`m@;Eh`G8AJLx;rJQd-POFdbT1p##xS9W~#??Id_R6Y}iR9J&vQ6KX#*54lQK0z2d<7B?ipsQX@t;+mlHQtOtQwGns2U zWaz0ERQPk2?_lKf6~G*WSVp0G4l5Nukezkgo%OQHXEe5EvA1_svo|b!m`DA4*iP}j zy%#-*F)f<~6#ZxrzUUz6*LMVmRc^53BHl4G$1Df-dEc2wh4a|>GAp*wG!>+Ep9IUd z`+>GP7DS8>rK_%`)6-5kP1M{ixHa*hE2#C)q1?fX-=vw0v_VLyN zX7!l|^qr>z*bwcpZ0XV$%#j3b#&gC(ApdkeSan+g>?=J>7aB{_r*_<--G-ae4d)Mo z6H7bj8zcEZ`cwgZp>Yh57BjowI-U+1zV4vsQff4avj!!@`=~obQ-Ot@KKPO91&R+Q z)7VD`G<|iUU61(F$vWy_=Y(7^{;4Lg=#>Ue(q^=%&=$;%(4w10TG4hF_VJXo*HJ&* zwW*-R^*qG@hI*5b?R3qjRg9|yl={?c%JEVQFFQ+zw$q^~`?4g;de3rdDKnT_wo#V$ zd->MMN6LZz@ohTw&45sfi5^t3w<>jJ>HsQ#!#5|Fzz(Wf#(;izYByDNVXb3 zd0yw%6kg}1T@~UX1wV6 zu1?<`-sK${F^sxxph1=F<*57J?z|n+sk}ic6723{&7l7<0h%W7!Ms_!zzi;re!h7S zW9(~9Z9C}8=8PW5NV=V-H|fbT&W~>}y;ohC-Gk$p%{wtD_;8WQv2A45-8%<*CfT#8 z?~H-oso5ZB;URj`t~6S+>Lz`cr$nz?*vIe_#tI^b9T@ZmUmF&6K>FR zCf}t(nJDVRrM-0Q?R1`dMJ!$P$%kIFIhnp#eTY8b8baSWbeFooxKiKg@w7+O0Ps=K z8a(awp?BvTpfZm8)0wY(=*#=n>Fio7`ub=g9eVaHk24)dPhMG0JuMl?%huEePL*G& zA0E-vRr|U0rrw$KlRZ4j#nYO0`d=+}{c@`Qk9+_&&H=Vu1YTzai7c5xk@_pgz?O4QE@R zcA+*pyS5Wg6<0_*^_wJ2@r5c=J96!vHWb?FG4U<)!FE^`7K+J(;U`z3{gw|)I`p7) z<99f*u85?SRWivM6TmiWDtMc8K(XO=(!Ozwc$^wf!ZI^RgqkYRK4d}sMBc%Rjq}j( z!C#VlEEqCf(#fGu!%*ww&kaYlQ`eNF-n#d;C6Yk*0b=;xK5482a-CRI$GZmdOLZbs)@x<&- zY)p5iYqy5r%EzhPEWJYZN81?2MrE-vD(0Na?S6KdWh<>bXhM4|57ES1!x+40KHk40 zM$1x%alP*>i0o3K;jcephk-Kvur3$pY1X4{*cbSwyo-8@BFZVn<2?xS0SgQp)I^C{TfuGT z4rtq_i-US<5USXK$#>qv4`ma~Zb*S6ANerQ>ImEZ#FCAMIXKvL2=Dwn2l|(L!BaaM zZoW8$O7E4?f5T?zJG>jjgRYT*6-A`EHibykT_efq#f<54IaE0%3qhMsk?IF_aG@|6 z-Z@N$a~}Iabz&lkez%Qe33ZU3B^L-(i9siRBs)yiA+4c;+_OADHm6GwlYve~GvW#P z))@^2kAy(8Ndrb+-6Ts`OHkSG!(Fxrqqhx|*b^fntYgDFI^02NST&Cg`6hx|k8I0i(dS>meZ20rk6g9(lQZY zs`6e%kc%bKfwFG&3VOw^4coywecUE^As|5k_BlEXU=6^YPX| zE;>K!gGiIrm{R^8Vqb>S47qH?k$brAv@CuTT|gsBFJM5k5_R)@4>e}y7-5))#lFc< zQ81Gpp4x_+vxjg*qyo7kiRfL^0pryhq0ewOU01x5>I-6meD48_>y*Ta^FLw7GzYrq z&=P#C>4|3?PoTxrh0vTXiQWA3pcTX?F5_HiliEW#**E~3MAqS@%o^OiYX>;;Z7@}A z2=v;fq5YQKxYl(ssqHL8kqHy9>-z~bezXBT8w&D?ec520n=H_%8N5{FiA7iHpm1h2 zgzPj$O)qoYqH2TjCDyR+Z!w4+@`PVPSK)MvKJo6~4MH;xF&i9IU~e-AW&yJy<-HzE z53PiX?zu45hN$NMfE@pt$OOfF6U2BSC|h8O{s-=nmXK6fkf%v(ZNlp4=tk&aQ{dxxia$&Gz2tpGrIP$DW|8gfdS9im4k6?!#zj(xqmC$6x#|X zE)R3z>S=7zb1Uj~Nr79Ody|XM(%?9qA=dRLpY`fk!WotNa;=Y}S(R* zhMxa+9;?n(VP*0M6nb<63XB7(#@!*j9;i*fJ$iulHTu-@i83BNTTFHRV(|%g2-lr? zjd@%v7~DOETMaLvDBqV((vHKw%Uoz{j6N)8?9@ai=99FoF`Y7MX@y9q)@&Jlxxr|`{InY7Hd#qW2jAbVy3 zOxhd|8}f!h+O-AV90*6zXYSB8C63&0%Y)H*9q`p#7DF7@!ugcrAZ9m2!l^!--T8ow zPO2h0)@pcOrvbyu_d?)7HFT~+_}jA|TMEZGyMKmk%bv~b;OlUf%}QVmUsluCGc-As zkBhK9F_xzjWX7%X-bHPX8FLS}o#)Etr*ktzbGRE;b!_{&``jjFMc&r%KsH@*5^rQ) zH0!%5iJdE&L<=76rjD5t*uKj$^vaa)T*F=+&TCNyZN8AeJ)P0W=@rCrXMaYrNr|0w zWBps&aDEk~Nw@Khum&}qxtNBkIpc!@IX3>rI+}UzpdhC(pz0rTsaI_m25U*Ok$vAW zsrVljWGtt<+tabeKAG-69zee@TS|M5$k4QK7aF%~E0#V}rasc)^yjDNG|c2b`p`d& zR?n28dR7N$$W#&Ri7bH5r^jG{=Pk%M5Cf4v&q9frBss2qm$8354j<3+Ak|l8AjD!J z#6)#M@GVDp@>7aze!7`#+_4a__$vvz=SMU;%)n%)upn2Th;x>@!xiUQP+1mFG_?hH z?kQU+PLsk&Regy1Gt7+2?}tL|a@Zu91bF2VnRw6$!YZ#YzyFhmr>PNS;M@fAb^J^w z!S@iDSr#&SPkhLLYYADkb_N_VwkCz%!({TFElgeGBF0#@oV3*4U~J|Vf&C*XVx(vY z?Kgsn>#hhAqWs3PG0%ut=qJOu`K_R6^ob-u2$|w9MkWu70#|F!@y-*v{o6R!-O-M< z9IwE7YTQM)r^z@Vy9Lxl7%rpq5#B%Pj8Fchv9lIkVh!%5uoHf#QKb`_*rnn_-;Y(X z?|;vsF~uRAW{U#deBvtIr>lpL&7}oBt7>fAT8mWOoLheYAGbbsDC+U|6c z9UlJ%PpdI>{5vgbvLFbD!n$#n@i|NnZvwt_9DLhmhj`egMK+^Us@&JW~J zzEd6pdJ4&cX?KA)Tn<{^r9`u9F;**G2BB;r8a1;K3!WEYt#vtJWHRtge+Bk#yg}tJ z+0oVCrLa{<7=u-2qncs`&MxW)$rTx3=Ou*&^TlCU;wrd|tAh`dPBFsP>6rB77FZ@s z0_QV@U?n^s2OIKX^n(EoJ$Heb7gVvsJr4SM3P`3?Brg2u3l%%w!f*Z+7&ws$Wt+-L zlb00SNt}e26vpA^bw^;XeIsmal*7r(6tH}}3Y^}_1I9}o`n0-9>IQ4z^D2qczcZvm z<%Oln7D-4swj3szr4qGS9c1qzad`euP;+?BNB8UbWP*ed^UPx*Nng`Ms;<|9_9P>c zzDEkChNO}0DrMyF>RjfJkR;Qt)&j7B}F?+v+9)sq`(xw<;FJRnRrl__GMP!Lj$dOZ4f8VnrHN2ieQIHxWFm9?g!&bdk~ zAwgK?cAQ=}8GwpcuBf~057aCu1D{`KkZ4(8@$<82wR;p-)civ4X&gMvyaa0N*5Jwj z5&FqllQx^ZLuIK1+;(<3UXhW)Id5^5Ol`P?TLHjg8IPBut5;+#h57 z`7aG_3i^l2dKsAib|37W{vQ_qdyf~H zM$_OyNEi6u+=hP(=3%B!7Ht064_5Wg=+&|X(=QWj37iG}CsSec`8=4xoPhI>(~0`1 zJ?LvLVDj?!g3ZWg;4k2y@9#D8@x~h1_#_F!1b4G#swkZLSHpy#GlhYNyP@x?4({B2 zgvcw*hKxgjm!Ic`?SAT7N*jb%1V(_kYNTD~lmKKlC--#vKBIgAtIq6IP8*|(lG zk=o7qcgb+imWZ(_Nrkk~ubCE<&Z1YJ?ZufhL}^Y1p$c#IK|{whnzoToE8FboOso6& zVDl*Mn>-(9uX{|t+s0tspJOr zQ;1$O3+S6)PISU@d-{@=3UaS!IIC(rzS-6YzYNNW=y*FYKRp9_udOG+*@q$cYa25q z^#s~L3S7Om62xb95ua3bw6E=imea4H!_^q}M~sII*AD3a7fkBZb~agBL>L zpgs1N^tqRi1JaKetyD=&4L8K_(|*uBb_gbyc98)6@%SXc9p+gpL$t08jJ9ng*2fY^ zUE5r;duSKAsIAF-Q&xwB*h?g0ZyYhq9wViR9ps_MQ8KVB96Dpv$gB-dEKB9Y$ct;A znOT>vk)}pMTt_QOn41SV@y3)er;C_Tjm3;)+&(g5AOuo+9-DdeF#UB~o)ejB5gAJ_`xjp+@LM{zW6oHbDBiCd4{-4-vZx#P{x0?E>M432p9IOfD z@z#Qu4}hwFF7~V#N00xzjfa2gph?9RJF*4I`9YWw0B@jHE^55GC2NT9;^g$_YT59A-4fztCD67~HysHE3}#NAjpv(XSd?ug<0w$W-{c^O`cyl%Ji+sAZwhy zlQef1a&M+OaI)RRyCRXC+u=dvbAB+d_1-YAP>ozaIs@b^PQdK(?+CZ^C7F?wP1dg3 z2akUZ_iGP^+E3YN_tNg*&q)!?#SvQ+OvNG_wc$A-S%Y{UA!O$y?F^J8f`=~1G~Zg!WMSl z7-75K4&ntio4u?yoh@0|&oa}V;_=UusH?(WG&YpSkJB?@dBA0CygUpN8#w6P{~9;G zGsiveHlfi`eU!7%p))`K1N>r+ZcP*6kx~d2$lQePH&Rd-IR(S6Cgavw31};oje6lc zOnX%TXJ*gA%hoIwYqemNf`GsJVv5)D_G9=j6+A3D2yQhYuxYg}eA5qwl~*q@Q4ZU| z!g4#wKljnn(PtTSzPLo%lHEvsvO3)VX#$f&Rui$2G16JJif|8#ApU_KG1GX$+})oD zcO`F<%L~L|f6F$|;awme??j3Juj|BW@IC3R9wHjnGH~ljE;%!?0Qi%6u;P(`@ypL3 zzuzF~+}^|32!{TWy?Dg)lOC}fR<~41ctXP7Ws}a3AI#5tx0ttf2}JW>8wvI}1J5>I zWXdk=WI$Mo$cAf?85!oV#ITL&46z}`6SawZeFJmjW2a^7dne|SE&<=aA@Inol%!}k zk>0KhQn&3Y33^t{ZV}Yn+c%A)b!8lNDb%5+wGT0*=`C*XP9huZa#@TxjWXNI!KbU4 zn%&c7)k;|QiuoKirf3pvIqk%1WrVRowTozsa{}ACcnf0iMa*BZ7JrPrht=claKPUR zw=B59Hnfh>c;6~q+IyY8zliK+y$$S5IeT9@Hk{1f^=cdtxc(3H~XGO|L;|WE_QusyJSCD(VhK;ks%y zIBGW)C+{7HMFy{!$%|@`f7cZ4(q@49d%)>)BXB`$HqS>y8OFx3xgkqX25I%R;k^6wH>J2gy3Sz@>5@R`mTQ z)*G~#;La69Y6TyJeh_rr?nU;v`a}GyY|>RF2|4UdhFOxvJUnZH{iD&iaH=c37%PM= zhn+xH!3U1po#JZWE~Lw2Zc+cu)7hjgzU-X|7pdZ{f3#1*7dk%d$|p-t=*Z($+Ts2aiR;@37bo}s}*~x-c2z!tW|>QcR#{bxec^o zU{uiO5aF#~_Jb8YxJVGo_po;#hI48eJ?w*)IvV}Anx5U2PfzyDqs)s?yzg=m7etxT zzw4JndSoePIJwh=Pz8ECvyxrAYGP=wdoRAG$w{o=jw6u=Kx&r zQ-MA$SD}N87tq!B-C@9E6ZWOe!GG)HY0x@X+H5zU)}}R}L{~XJcGH3^e>2GZ5=2%S z+$2x(zA{3-qM-gnhRkm`YB_xKCvkI+B>#=wW?n8=C%vAr(7ApRahQ6F7%ZR6L|Hq* zVNJn;q8W`$x^fTG<4K8?_-7(h@sUKkpC-8qUzz@O=a?N`UzqX7Cc?Y4jl_PMCRy1O z3RAT@$?v}1WTKOxX1lIV>;p|1w&yFOexiz*y+o0mekEy{_A!--ixeRafyqpW#U4gx zmn4aCUBdLtk%d?9)R_&2R+i_lC=#)%VkSp1lSI4}XZEJ3l4ZH)7{A9inPINOGW^$1 z%gl^e;;!0Bg0;-Zn>jH=A>%${5p$dz7Pe!L_lnUUBR6n`Xe2F4zk@Ab7vY_^Z_ub~ z3R#h$&sx6g!_n+DaO63mx$}Qe`HKZ~O{*rGVr57lNeZ*aFT52jz7C*;qvmWxw;Ei@3eJXf3t>F2_yWZJHjqo=pUCTrhluY?34-ecDe(>WfL3C%_&fR}$e6%-DF;!Lzl4TR_&dCXtwb=8P(rL(aLbBx~DkNa-U*GVmsi zk&xKOTx{IRC>#$b&yGxjt(Z?f{?a1(@$-q!zC>~_>K!X@uv#!j9YXzwJLz4yTpV)l zg|s?Xsw6EBE~Opp+KEr`MecMwWz|FTLe{T6@IbewZE3Y?o;ATQbg)=#NJ(?thiv+QxACm+s) zmtBEAq6h{e&zVf+JP5un_`kyRF-zGE4{6lEZf{4}9PSS(;orelPy-gtOTi8EmI$6{ z0?&{65EB#zgL}%rH?)Z)uDr$A&YMQwxbKFDs6FI)U6G}(xFp`*{Ed9}3?U)cKauXe zjqqODm@K^an7B4fA?;Be zVMv6P3~AY*LR!~NW~{Adle|N37~{Me@?hIv%XN-H%=_r`<|IoF=cW=ptUc!mc~R)d1jgBXAEH>XP7s0 zlbG7yOUY}Df&9<)OteQ35z80UR)@7fLDYaW|96vVr%q(WzbILQ*}&A{&*&H+cieafDk=f72>>U8i>^oSsMKAwygI^AXi#XFf!iR zB(859v%A@YU!#ZQ?k|#fM9M$*`mjJIAyZFY7u&-|Qf|OLz{O*!Y@0vt`)Z z0Y5O{_b+Pj;U+!Vx`oc{KF9{k2V(KnJ9xV9GR)p9f*xM;aAxox$XT{n8+zunElLJBHt2}sw^pc>W zdScYp4Y!X~5}ueYlEc#E!>t6!z7a=srpJ>L{YFIV-algTT?{w>w!($4)bVJ?Ibv~H z6#kQGC;n}Hp!#MuMug6S5AkA{p|%HrreVmyZ2Tf%GLjrMFgfw z7A}pt`(HuSH#wNS$`~gE2%%g+6tv#?1}XwhU_p)<{5ipVQLFGsfjB7E(?1yetlz!s^sV49+e9*v#gpK}zZu5f}#Rt~In;|S(V zgO9PtNS~K5lzqssT)jdEE(SS{}L%A}45q6=T3^(t+GA-)MrLj_fG5`Jn)I2nmE*~qvJ1=9n{jE)G z;|mwMTGxPe{`Hu>IoQwsma?QLM$@R#18>^=tQn6a4a2@Ovb0D_lwR3fMUGCEz&+}6 z^oy((?ND8fj;jyi_-`}ODP|8%aV~&ods8sx*bX=o&;Xv+B1p2;(ab*rzq}Hr>s>CP zg3?Zy8Fdn6L;68HD}Yv}hEUH%Q)u4tLiCF_K*^J~xJF+X;{$dE!>Y^)c+c1hf-^(lzVC06niC7pRZl{M2cHCvXK}-a z&7hWY7>+!#hnjs7;Cc2VdA{;I(fuNUJ|-2Q*6#+j8hl7FISoy_et~LWHt<4Tkg-K= zWYWqQ@=z&@%$%e}X8Io@^ubTdKj~Ycz_Em!dmBZL{_rLW5+-0j+D0~i@qn$5CXfr- zx+Ls+8fm(m9g3)+!;fL5N*ph%gh;m<=rc!S~Kd-iUBbv(i;j8)MXsBi{ z6k0a0H0XdBnuS`V#}61YKhFxjTap+VS$l9K2YQ1uemeOVtx=Vyx9X1_D7)eOdOPU zOM>FodT^Ahg^xSGFn&7*F)#8f%$PG5qyKfdwFAh_%@OpiuR#H0iyAZT zgX1b0c<|~6ksnu1TxENShoL?xXs8l=yK&65rCAVBr4M=|`NZESj3lN{gWd1cplCu9 zeEzYY7%fmCqw=Z5?!Q4IJLnAx)v3g`?JSYd;gMtSZAkdi>x__nCbJ~$FwAzp4?7(i z$k~%h(4kaIOwwn-Qg$NeXg!4CZwJNs(QGu+vq$Vv`gU`0O^u z1iHA)fVA}!>5R;Kc*nH_FHnLn8apsLh#>1>PmON$!AUU=6+)Eo&&xU(NvXzryY`~z zvE}&Dcstq(xGx&H9cF1}C_iqZN&0y-t+mEMgIIj!C{81y`50+94&OY| zLXD*rFkP+*B;*{h*CvjbZcU+qLMt(_d<^vO_@ea1o4A@2!J27_Xrm{Ki**d~$Ff)y z4|zl|U7iL!qWCysFM7}&Aa^$QT^G7UxDby4m8TeSZd zg>!MYAikXfHch~7<|aao#Y*D+bT^FC=w<4!i{UMmM5zBT8+Oe#gQ2PdnD)^R7T-3; zw>)XMd2BWbP*s7)`%Pi*a1+efWdLg3!4UZA30V&#g!>dlcDz&~8na%3V6P|6Zy?yXogKZ8}fDjD_EH!+aZc zn*2isKP%PH7r_?PZ=x0b$Ts875KkI0dI(9)C2Ia(Hjb>!LjGDwny4p=^X#l>=EEs8 z@`XCx{i#jhxiW#CjGl~M+qU4stD5v~#v!`P-+*4bWlZY}Qn36dp-=V1DRZv~b+ad< z$lAX!XI>+W4Vt0P6u?J263EiXO4_n)HG0ZT7Vr!ac+u}A_7f$%wrmq#Efqy+H+9@p zoQ4VAb6{MCCY_z_faT$9(0BD)NIX~oNv5a4FmwZOuV3N~>pbMfiQxqiRkS#{2s@Sy z`*s2D1Mn##>7DK+51f7XGa|E-33cG&4JhT#<1A#1tjI? zLA>G=oUbMf4;djCvR?zc6};fTM}=@XafLuLb%#bFA-LXupD>RPk+PC-;;K{)k&2%9 zDry02m%0LqH{4*yQ(=57T*KXne@z3^9oXY>#_UnAsqD+8lqTN2$A%S{W5_7W3(W0g zFFMVpS;BYO=v-57*&7~L=;6&pb$GM>CpU0|h8>)ZrYbABs-D}BrOd`PPiK=a>(kq3 zbm^PzZM5&qSsaVn!P!lE!a8X!rMYxIm$=84^ZIWA=d^H?Hnd!&`&E|GUyCQuwi5?1 z^Hew9jg+Cy0WxSi^qST*PN#nV9BF$%6NZW%rMJV%P~=@XmA$(kgJd6L?WQSI?7SJ? z*Y}|VA$;m2qax@zDp2pKA8^KTTU_s0it9(6sqXt^dQ38$Dqhy6f0ORx{o6dMnI(?O z_1j>!H$qC)UGjI%8sgiS3{qbzN!G)G8CrxtOY}p^a`VlB?)BYRTwi)`x=?ll4|)aB9=^S?qz7_cJkll z9!B%?A~Lj|NA6}HB_S^9j8vX2b9zG_X}a!4_7qwZ_fTmv@3jzlJ;|TpEhu89cw{m= zCZ1t59hQ@S{%gP}(S)cz7R)2m?q^ar#Da@b5SuHbOv_A^Y2tBl`bSBK9y`znLN*ki>-0~R1pF1N7;QcE7-LL?rfFu47#B6grY}85< z8lpUnn)t}!{4;aVdgDHPpBN1<#M}g4o)wf8Rl$lPE7rm|l&uwVX7zs<(Bby^KF2i@S*rg!AOv`#Xw zuYjm)rI8-L72tkQ7?barfxg)!?2Y|SQhxYiU{ecMC^nh>+P#gP(8Xte7fP|MTEcW+ z@i?yP=?ZK=<-t>l>tKCDH_)-qqTK3@9naWQ)_#)<~@Y~coPP2^H_)^T;Q<5`=P zhpCyW2pvs{N9VW*GwAuAOw$MG49ZF zN7Iuc^mpC@+TPy;e>)ZF)F21iKV6fig?>Vdyw^A>XC9i~tU~uRj%xl)rO8KT(et%U z7`|6dFtb04hM)NlZz-oDrcc46r$jJT@;3Q4Hx@mdCP1nB9p=MqKio5#4bF${K-7ze zj(LbeOUDalFqB}eNgQdLI|lasn$SHgsF> zu>izZ#gXOt)nxYDiKP8zJY;NhhRJ`=!5Ybl#6`e>n3fp8?mM4IW_bmO3bxq7rniv9p9dwz%_|hv4P2(>6m8#dxM-{Kh{`s zLDu3lvV1@N)}lwJrkutGvnl9hF@rWh0zREA3+B8WEEqY2L2G1b@T9Hy@R$Z&XNuU{ zAVsxvO=01KLhS7-fxCruAQi8Nzdvk5o$Qn7WU500gwt`>fp&6nf|EA@`3Wpz%aquEE9jySdZJ&t9 z(?U=LTkLjHz=l2Bq028B_Ep)SeSis`I3}pcZ53dFr5C&wut&4R?I2-pEP1$B1(Y^O zGqD$fAn}DX8fD^%-l}s% z=EY(XKM+ll-G4E<;+u%)&2vz`ZzTq-nhv=;0%7vf3^IE6Cs_UT;#NzfQ+MNMbd~0L z`nhzB{%xal`JgrXmRAFfyOOy2d(mw8^G+P+k;KL%h;fUmd)bKEDco?10_#50k~7)4 zmwW%BgNg^)ar;b8(6g^UP_vvHXdfp=?Sl4M8Xt#}<8r8_&VRY$#vM1SxTWmP0 zI*!}od4*OM1=05n@l>Y&1B!05M29od^!dO$e1F~mR@~5`ic+sJWveoE%_zr3Uv8oA z5lt*9kE3}H1?XJ=AzTqPTacFxLB^cJ7$>p?4bi$WRk8OcWBDl zLl`aaTnIWaghumZsO#j0hLgJm9wc8zkU3m}7FCbH{{s(?jX#5r89Dmu!Yw@RESMD-`T*XqtKhbD8;t8| zASFi{p5riHc^WoE}cw(}(7U~Cj;PW_B zlx?30I>!>>((*^-Tck9s&b>k=_1q>>ZhVYY>c+nViLmynD3;w3@FsK*zVI939(7u> zNsdd|+$4%%YW)L`C^MMm5bi$MB5N0W=#l*_%0-p}#Mv$%!- zidm~a*EtWbuCBd>E_ANO!u(l@YD=B@PA(Z7`cbQ2X9XhkD67S!F}hZg!y!5)PH`Y?43wb;Fn zZoX_m%l}2u-0ULUs35@}t9p$~;5#Z<09`h`AMf-oqH8qlXh4k(UH5N1)x651L+y)j zkEt=$)#KBMydGNa58uS&~eLA*GwGq+uU)-;yvgzy_ksP+{Eba$tWRK zi3J^{J6x4~q zu-5M(yfBL(l{@#s%R3p&^i{?XXtV`#Vi&+yz9npLX$FVd9nh{DgQM}n@Jlj@X^9m9 zRpANXQtgW3yk|s4x(q^AY$J_nnvi$+B#Apd%!v2fVD>{d?7cG&9=UWw)-F-ty=jAu zpPq0(lv3G`GYJdFg}4FtO!li$3Jp55o!uxcg4+0nD_(hoog_Vej zSre7{bhxI5{Zrt_{Rho#p^G`|Hg=E_wv)cINu)%= z-}?1vSxXOw`q)ylC?9+ia-W`*JB;9a0-YUyZL7o+q}ovH)AuM zQZo^2o3CO)bPS3;O{A|XXVW*;wf5u2?lwfMZN*M7XFce%xG6vPKE9V$U?wSMA_8G)l`vfs@^90q|r^u&rRRNbA z3am^qlu1vA4EJ<0?({cu{_bw3osmFU`LFQn%UlS_RD&xYev#>4)NtXZgRrIaBXO&I zL1NjODSh^uIX=#e(Y{y*KlgZ(t#3rg)AwcM;=wtvQ7f8cPii7=+sc`Z z%MHo>ycpso_k?+!C;*zQ)5&1f39@mT0}0ZWA|raS%zPCuraycxIdr*)9J4x1z8>%< zFtCHDIn5(iC4aDm9RnD;udqKs-7>nxy4)xd{$DEd406BB_4_oE|U;rG4I3y?YECP7*isnT&L29QxnVgA;k2I=mc`}3{$&}=GBp(lSt9`!Y&>Yo9f9EW zUzz<~O^~?64z&N|0s1<^onM+D6}pc|wYkB1342HpSptjRpCM1m_LI}oMe*_1EQp3{ z;IimF^U1r9?0cF_98Cv^>gVZXb#F7dTpee*TS;H1 zJh;CbW7bS|VKxgiON(!fMDw~2v@IM*{{CD`j)dQ17QJX@roSv^bVi&=t3Z$Rz9ED= z`Y)4y6;ZI*mc*#Myb9xrUb3nygXqv$46Uk(q|e-A=%uy!sI4KyCc7xWUMDYshxP<^ zsB{}9cdcZ5KJR6ZHdnHn{c72|xxrMz_!z4fFvvbwMyN!PG?#PQmF8|(Mg6wf;f24c z_&_)ZeU8QmSiTqRsTnq`uIvficiD+OSXa$17^-GFtfJ`AsiinnVoViyVQ4Z>9G_z~ z-cOo<+3pQQy7ee_kuex76Yv@qAy_))C%^|~aEv-2sL85e|5F(}um21Fo3;r?Ujxhp zaSYwDOfW8FjdyL#P^?!24)_+JvGFG86JzMcAIDK*jUGCV=i&0fxmdP-x|LCiCXMN< zMrVyqf#>CTD;;4mtAvJO{!UwgHZgpj?(neSOMU&v|F*4|Z|G`f<@WjtMfaUl;&l+e zVS|L#PFu9%uc_wO7zuW7$WNrtSGDmHj#cx6W2S+j;)k(XafL_2>9Urk>)rSRJME!9LXdoEASfdq3aR_#WTq z-%2Y#*A49aSBC84KUMsy1^-!9EnHybs9eoY58nz?EmwneSp`qHK#1Ss6U>ttPUAO) z9in2=UNnO)c4;r~APlz(t*8_iNYKpR5KdBNtL{8!dt{I6$X`8hA%pwIbI z^w>XvzuS8nKT9~7&n!&li=NG<(ks@}KO5rsqmFy{AZy6K9je9u5Va6p51OF#V?*AD zhYGxBlhk-$_r~$Vp7_$SA1ZXH(V1^Avys0&tAYQnOo^AaE*$>z7ze-5gu8a4j7PuN z@dS4<|4OPJ^|>!f3wyQr1G0p_Sf!NbEmg^r&^U-mD;D9BehuC|*;IZE`<=(#*X8|+ z)B}h0r9|zM0`IG$Ca0S*%*}pO#b0wzh^8ghW7dN+yvDoVd8^xB@Ko)i`Nsk?Df`5q zHhmG}A57c9e}3Ma?>eKA59dwccSTLMGCiro>KuGSXZ|td zKg~0>3KSmU|8a4!dY0|XhRjfAx0)W|@47bM%6ii%f6d21-mDZEDzy4CT9&!+{x*N- z$;3Y7-PpL(itFas!aQ-7pRHykR5r)ztCFYHX31{8%jhOLzQC5g{cOQc@lxbJ4w}Y~ z-e1F?&~%DI+g3sUq=eU>Udmsv(wqORVxEHL4q8nmUk R2aV?_@?P-?e?z1=|9|!Q;Wz*Q literal 70112 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 z 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 Date: Thu, 27 Sep 2018 11:51:21 +0200 Subject: [PATCH 040/112] change order: first deactivate form than set requested slot to None RasaHQ/roadmap#263 --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index fbc58defa..b1f4cbfda 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -84,7 +84,7 @@ def run(self, dispatcher, tracker, domain): # there is nothing more to request, so we can submit events_from_submit = self.submit(dispatcher, temp_tracker, domain) or [] - return events + events_from_submit + [SlotSet(REQUESTED_SLOT, None), Form(None)] + return events + events_from_submit + [Form(None), SlotSet(REQUESTED_SLOT, None)] def submit(self, dispatcher, tracker, domain): raise NotImplementedError( From 858e95ced28e5f530c22279496d77aa0ea2138a7 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 27 Sep 2018 14:17:00 +0200 Subject: [PATCH 041/112] remove story without 'form: ' --- examples/formbot/stories.md | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/examples/formbot/stories.md b/examples/formbot/stories.md index 184ad7b45..45477bf3b 100644 --- a/examples/formbot/stories.md +++ b/examples/formbot/stories.md @@ -1,26 +1,3 @@ -## Generated Story 6616794937268014982 -* request_restaurant - - restaurant_form - - form{"name": "restaurant_form"} - - slot{"requested_slot": "cuisine"} -* chitchat - - utter_chitchat - - restaurant_form - - slot{"requested_slot": "cuisine"} -* inform{"cuisine": "1"} - - slot{"cuisine": "1"} - - restaurant_form - - slot{"cuisine": "1"} - - slot{"requested_slot": "num_people"} -* inform{"num_people": "1"} - - slot{"num_people": "1"} - - restaurant_form - - slot{"num_people": "1"} - - slot{"requested_slot": null} - - form{"name": null} -* thank - - utter_noworries - ## Generated Story -9155310465400161964 * request_restaurant - restaurant_form @@ -75,4 +52,3 @@ - slot{"requested_slot": null} * thank - utter_noworries - From 6339ff59673f63f0d149469ed24c29dc19d82837 Mon Sep 17 00:00:00 2001 From: akelad Date: Thu, 27 Sep 2018 17:34:37 +0200 Subject: [PATCH 042/112] remove formbot https://github.com/RasaHQ/roadmap/issues/263 --- examples/formbot/Makefile | 30 -------- examples/formbot/actions.py | 15 ---- examples/formbot/data/stories.md | 71 ------------------ examples/formbot/domain.yml | 35 --------- examples/formbot/endpoints.yml | 2 - examples/formbot/models/dialogue/domain.json | 18 ----- examples/formbot/models/dialogue/domain.yml | 43 ----------- .../fallback_policy.json | 5 -- .../featurizer.json | 1 - .../memorized_turns.json | 33 -------- .../policy_2_KerasPolicy/featurizer.json | 1 - .../policy_2_KerasPolicy/keras_model.h5 | Bin 70112 -> 0 bytes .../policy_2_KerasPolicy/keras_policy.json | 4 - .../models/dialogue/policy_metadata.json | 30 -------- examples/formbot/stories.md | 54 ------------- examples/formbot/train.py | 34 --------- rasa_core_sdk/forms.py | 8 +- 17 files changed, 4 insertions(+), 380 deletions(-) delete mode 100644 examples/formbot/Makefile delete mode 100644 examples/formbot/actions.py delete mode 100644 examples/formbot/data/stories.md delete mode 100644 examples/formbot/domain.yml delete mode 100644 examples/formbot/endpoints.yml delete mode 100644 examples/formbot/models/dialogue/domain.json delete mode 100644 examples/formbot/models/dialogue/domain.yml delete mode 100644 examples/formbot/models/dialogue/policy_0_FallbackPolicy/fallback_policy.json delete mode 100644 examples/formbot/models/dialogue/policy_1_MemoizationPolicy/featurizer.json delete mode 100644 examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json delete mode 100644 examples/formbot/models/dialogue/policy_2_KerasPolicy/featurizer.json delete mode 100644 examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_model.h5 delete mode 100644 examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_policy.json delete mode 100644 examples/formbot/models/dialogue/policy_metadata.json delete mode 100644 examples/formbot/stories.md delete mode 100644 examples/formbot/train.py diff --git a/examples/formbot/Makefile b/examples/formbot/Makefile deleted file mode 100644 index 742a1b6f2..000000000 --- a/examples/formbot/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -help: - @echo " train-nlu" - @echo " Train the natural language understanding using Rasa NLU." - @echo " train-core" - @echo " Train a dialogue model using Rasa core." - @echo " run" - @echo " Spin up a server that serves as an endpoint to receive facebook user messages." - -train-nlu: - python -m rasa_nlu.train -c nlu_model_config.yml --fixed_model_name current \ - --data ./data/nlu.md --path models/ --project nlu - -train-core: - python -m rasa_core.train -s data/stories.md -d domain.yml -o models/dialogue --epochs 1 --debug - -run-core: - python -m rasa_core.run --core models/dialogue --debug --endpoints endpoints.yml - -run-actions: - python -m rasa_core_sdk.endpoint --actions actions - -run: - make run-actions& - make run-core - -evaluate-nlu: - python -m rasa_nlu.evaluate --model models/nlu/current --data ./data/nlu.md - -visualize: - python -m rasa_core.visualize -s data/stories.md -d domain.yml -o story_graph.png diff --git a/examples/formbot/actions.py b/examples/formbot/actions.py deleted file mode 100644 index d03d8eaf7..000000000 --- a/examples/formbot/actions.py +++ /dev/null @@ -1,15 +0,0 @@ -from rasa_core_sdk.forms import FormAction - - -class RestaurantForm(FormAction): - - def name(self): - return "restaurant_form" - - @staticmethod - def required_slots(): - return ["cuisine", "num_people"] - - def submit(self, dispatcher, tracker, domain): - dispatcher.utter_message("done!") - return [] diff --git a/examples/formbot/data/stories.md b/examples/formbot/data/stories.md deleted file mode 100644 index 35bbbaeaa..000000000 --- a/examples/formbot/data/stories.md +++ /dev/null @@ -1,71 +0,0 @@ -## happy path -* request_restaurant - - restaurant_form - - form{"name": "restaurant_form"} - - form{"name": null} -* thank - - utter_noworries - - - -## chitchat once -* request_restaurant - - restaurant_form - - form{"name": "restaurant_form"} -* chitchat - - utter_chitchat - - restaurant_form - - form{"name": null} -* thank - - utter_noworries - -## unhappy path -* request_restaurant - - restaurant_form - - form{"name": "restaurant_form"} -* chitchat - - utter_chitchat - - restaurant_form -* chitchat - - utter_chitchat - - restaurant_form -* chitchat - - utter_chitchat - - restaurant_form - - form{"name": null} -* thank - - utter_noworries - -## unhappy path -* request_restaurant - - restaurant_form - - form{"name": "restaurant_form"} -* thank - - utter_chitchat - - restaurant_form - - form{"name": null} -* thank - - utter_noworries - -## Generated Story -9155310465400161964 -* request_restaurant - - restaurant_form - - form{"name": "restaurant_form"} - - slot{"requested_slot": "cuisine"} -* chitchat - - utter_chitchat - - restaurant_form - - form: slot{"requested_slot": "cuisine"} -* form: inform{"cuisine": "1"} - - form: slot{"cuisine": "1"} - - form: restaurant_form - - form: slot{"cuisine": "1"} - - form: slot{"requested_slot": "num_people"} -* form: inform{"num_people": "1"} - - form: slot{"num_people": "1"} - - form: restaurant_form - - form: slot{"num_people": "1"} - - form{"name": null} - - slot{"requested_slot": null} -* thank - - utter_noworries \ No newline at end of file diff --git a/examples/formbot/domain.yml b/examples/formbot/domain.yml deleted file mode 100644 index e13b51c1d..000000000 --- a/examples/formbot/domain.yml +++ /dev/null @@ -1,35 +0,0 @@ -intents: - - thank - - request_restaurant - - inform - - chitchat - -entities: - - cuisine - - num_people - -slots: - cuisine: - type: unfeaturized - num_people: - type: unfeaturized - requested_slot: - type: categorical - values: - - cuisine - - num_people - -templates: - utter_ask_num_people: - - text: "how many people?" - utter_ask_cuisine: - - text: "what cuisine?" - utter_noworries: - - text: "you are welcome :)" - utter_chitchat: - - text: "chitchat" - -actions: - - utter_noworries - - restaurant_form - - utter_chitchat diff --git a/examples/formbot/endpoints.yml b/examples/formbot/endpoints.yml deleted file mode 100644 index e8d74ad61..000000000 --- a/examples/formbot/endpoints.yml +++ /dev/null @@ -1,2 +0,0 @@ -action_endpoint: - url: http://localhost:5055/webhook diff --git a/examples/formbot/models/dialogue/domain.json b/examples/formbot/models/dialogue/domain.json deleted file mode 100644 index c6835f17a..000000000 --- a/examples/formbot/models/dialogue/domain.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "states": [ - "intent_chitchat", - "intent_inform", - "intent_request_restaurant", - "intent_thank", - "entity_cuisine", - "entity_num_people", - "slot_requested_slot_0", - "slot_requested_slot_1", - "prev_action_listen", - "prev_action_restart", - "prev_action_default_fallback", - "prev_utter_noworries", - "prev_restaurant_form", - "prev_utter_chitchat" - ] -} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/domain.yml b/examples/formbot/models/dialogue/domain.yml deleted file mode 100644 index 66305842b..000000000 --- a/examples/formbot/models/dialogue/domain.yml +++ /dev/null @@ -1,43 +0,0 @@ -%YAML 1.1 ---- -actions: -- utter_noworries -- restaurant_form -- utter_chitchat -config: - store_entities_as_slots: true -entities: -- cuisine -- num_people -forms: [] -intents: -- thank: - use_entities: true -- request_restaurant: - use_entities: true -- inform: - use_entities: true -- chitchat: - use_entities: true -slots: - cuisine: - initial_value: null - type: rasa_core.slots.UnfeaturizedSlot - num_people: - initial_value: null - type: rasa_core.slots.UnfeaturizedSlot - requested_slot: - initial_value: null - type: rasa_core.slots.CategoricalSlot - values: - - cuisine - - num_people -templates: - utter_ask_cuisine: - - text: what cuisine? - utter_ask_num_people: - - text: how many people? - utter_chitchat: - - text: chitchat - utter_noworries: - - text: you are welcome :) diff --git a/examples/formbot/models/dialogue/policy_0_FallbackPolicy/fallback_policy.json b/examples/formbot/models/dialogue/policy_0_FallbackPolicy/fallback_policy.json deleted file mode 100644 index 4357d9cce..000000000 --- a/examples/formbot/models/dialogue/policy_0_FallbackPolicy/fallback_policy.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "nlu_threshold": 0.0, - "core_threshold": 0.0, - "fallback_action_name": "action_default_fallback" -} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/featurizer.json b/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/featurizer.json deleted file mode 100644 index abaea9c5d..000000000 --- a/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/featurizer.json +++ /dev/null @@ -1 +0,0 @@ -{"py/object": "rasa_core.featurizers.MaxHistoryTrackerFeaturizer", "max_history": 3, "remove_duplicates": true, "state_featurizer": {"py/object": "rasa_core.featurizers.SingleStateFeaturizer", "slot_feature_len": null, "user_feature_len": null}, "use_intent_probabilities": false} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json b/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json deleted file mode 100644 index fef039f5f..000000000 --- a/examples/formbot/models/dialogue/policy_1_MemoizationPolicy/memorized_turns.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "max_history": 3, - "lookup": { - "eJyLzivNydFRgJDVtbEAMW4Fvw==": 0, - "eJyLzivNydFRqK4F4sy8ktS8kvii1MLS1GIQXVySWFqUmFdipWCoZ6CjUFCUWhafmFySmZ8Xn5NZDFQMlqiNBQCLfBlE": 4, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FrLc7Jh2tMTYkHcw0gVscCAELDRBA=": 0, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxay3OyYdrTE2JB3MNSLc6OSOzJDkjkYBv8FoXCwAfOmpG": 5, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMhxai3Py4RpTU+LBXAOwXK2OAvFWJ2dkliRnJKJYCNKcnxefkwk0OY/21pWWlKQWocvgsS8WAMZ+fKM=": 4, - "eJyljUEKgDAMBL/SB4jo1a+IhFIjFrTVdOtF/Lu1eBLx4nF3k5l21wZ2Yxq8zCQcoKNoh5wbVZdVoawDp8aMFmbUuNtFeKPr2TuabEgn9xAmj0RaY4JxTzlWeTsK9U8XAZbn8u37oL3pj+4EMRRh4w==": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUInSZ4pz8EqBRhaVA01JT4sFcA7BcrY5CNR7TsFmPpKUkIzEvG1k9yOn5efE5mUB78iCqYwEH40pa": 3, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJkFaQaFlqfFp+US6SPjAfqpkow7FprY0FAFA2Ojg=": 0, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+OSMzJLkjERCLo4FAP4oVpY=": 5, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDc+OSOzJDkjEcVQkOb8vPiczGKgEuoYWVpSklqEKlMbCwDDQl8b": 4, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocpQbCY2DbWxAFCPWrI=": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh0FyszEpoFiQ0Ga8/PiczKLgUogRsYCAGl2WrI=": 5, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUMqwaanUUKDMUpDk/Lz4nsxiohDpGlpaUpBahytTGAgBvXlqy": 4, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsniQ5vy8+JzMYqASsEStjgJlRpaWlKQWocqAzMSjA5sVtbEA8CxOMw==": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSLJGZklyRmJJVDRgqLUsvjSkpLUIlSZWh2Fajw6sFmBpKUkIzEvG1k9yHn5efE5mcVAeYjqWABix0CC": 3, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHYVqkGhZanxaflEukj4wH6qZKMOxaSXJ+JKMxLxsAs6NBQD1GVVk": 5, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSJFqYWlQHkkRVD5gqLUMqxaa3UUiDe+JCMxLxvZRJDO/Lz4nMxioDwVzCstKUktik/OyCxJzkiEuL02FgB3gly3": 4, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsniQzvy8+JzMYqA8WKJWR4EC80pLSlKL4pMzMkuSMxJL4AbiUo7N8NpYABQjSp0=": 0, - "eJyLrk5MLsksS41Pyy/KjS9KLS5JLC1KzCsB860UDPUMdBQy80pSgSIlGYl52VChgqLUsvjSkpLUovjkjMyS5IzEErBMrY5CNS7l2AzHpx7ksPy8+JzMYqA8RHUsAMQvPh4=": 3, - "eJyFyjEKgDAMAMCv9AFFdPUrIiFIxKAmkqY6iH+36CzOd93J4iQOPqHMbWiqOobNaAej5JgNi41q60NXDJ8fB2cVWDgV/93ZnQxEDzVjSu/vbyaAMj8=": 0, - "eJyLrs7MK0nNK4kvyUjMy7ZSMNQz0FEoKEoti09MLsnMz4vPySwGyoMlanUUcKouLSlJLYrPyy/PLyrKTC1GV1+UWliaWgyii0sSS4sS80oIWBULAIxrNuo=": 4, - "eJyNzMEOQDAMBuBX2QMswtWriDSLVCzo6Lo5iHdngjg4ODXt/3+tVkuCJCCdob5URZZrNTFGCCLIQG5xzBb9GW1a3X3GOaBP04sJbEje2DRiHcFg/VF+aLpGhNbx+HLnfuFfz7/oVu95Okn0": 0, - "eJyFy9EKQEAQRuFX2QeQuPUq0jZto50wq9kfF/LuRMqd6/OddhcFK3yIghAJjavLqnCz8eqNM2gxunKfbLrTUbh3QSQdvp4CJKkfJV/9Vy8Am9e0JTPh/PjuBNMuM3E=": 0, - "eJyNzUEOg0AIBdCrzAGM0a1XMQ2ZKE0nKigwumh6d3WsxkUXXRHgP6jfgQzJwF6eusqVeZG5UXCGaIYCxAuLBNS0+mTuzAtOEXWvaj6KJ7tj31hggj7oFr7oPp0RnizDzaX+i/86/ptqzxfEFlJbHK8fKyR1U8w=": 0, - "eJyLrq7VUajOzCtJzSuJL0otLE0tBtHFJYmlRYl5JVYKhnoGOgoFRall8YnJJZn5efE5mcVAxWAJorUiROPT8otyIZpjAVlOLbk=": 0, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWXxicklmfl58TmZxUDFYIlaHQXitCJE49Pyi3LRNZdkJOZlE7AqFgA0Vzxm": 3, - "eJyLrs7MK0nNK4kvSi0sTS0G0cUliaVFiXklVgqGegY6CgVFqWVIovFp+UW5YKlaHQWY5pKMxLxsZPWJySWZ+XnxOZnFQHmCqktLSlKL4vPyy/OLijJTiyHqYwFoHDfa": 0, - "eJyVi8sJgEAMBVvZAkT0aisiYZGIQc1qktWD2Ls/RK+eHsybKVdiQzaw1nNXuDzNEjcKzhDNUIDDEkQI9bq2xD2+4BRRz1XzUTzbN/a1UWDoSQ/5X/pSaIIMd1ztazw9dQ==": 0 - } -} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_2_KerasPolicy/featurizer.json b/examples/formbot/models/dialogue/policy_2_KerasPolicy/featurizer.json deleted file mode 100644 index acb94e308..000000000 --- a/examples/formbot/models/dialogue/policy_2_KerasPolicy/featurizer.json +++ /dev/null @@ -1 +0,0 @@ -{"py/object": "rasa_core.featurizers.MaxHistoryTrackerFeaturizer", "max_history": 3, "remove_duplicates": true, "state_featurizer": {"py/object": "rasa_core.featurizers.BinarySingleStateFeaturizer", "input_state_map": {"entity_cuisine": 4, "entity_num_people": 5, "intent_chitchat": 0, "intent_inform": 1, "intent_request_restaurant": 2, "intent_thank": 3, "prev_action_default_fallback": 10, "prev_action_listen": 8, "prev_action_restart": 9, "prev_restaurant_form": 12, "prev_utter_chitchat": 13, "prev_utter_noworries": 11, "slot_requested_slot_0": 6, "slot_requested_slot_1": 7}, "num_features": 14, "slot_feature_len": 2, "user_feature_len": 6}, "use_intent_probabilities": false} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_model.h5 b/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_model.h5 deleted file mode 100644 index e9f9a4e580006f43439c4efb352ec140b1c95fff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70112 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 R2aV?_@?P-?e?z1=|9|!Q;Wz*Q diff --git a/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_policy.json b/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_policy.json deleted file mode 100644 index e2ba110ec..000000000 --- a/examples/formbot/models/dialogue/policy_2_KerasPolicy/keras_policy.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "model": "keras_model.h5", - "epochs": 100 -} \ No newline at end of file diff --git a/examples/formbot/models/dialogue/policy_metadata.json b/examples/formbot/models/dialogue/policy_metadata.json deleted file mode 100644 index 009c98e3f..000000000 --- a/examples/formbot/models/dialogue/policy_metadata.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "action_fingerprints": { - "action_listen": { - "slots": [] - }, - "restaurant_form": { - "slots": [ - "requested_slot" - ] - }, - "utter_noworries": { - "slots": [] - } - }, - "rasa_core": "0.12.0a4", - "python": "3.6.5", - "max_histories": [ - null, - 3, - 3, - null - ], - "ensemble_name": "rasa_core.policies.ensemble.SimplePolicyEnsemble", - "policy_names": [ - "rasa_core.policies.fallback.FallbackPolicy", - "rasa_core.policies.memoization.MemoizationPolicy", - "rasa_core.policies.keras_policy.KerasPolicy", - "rasa_core.policies.form_policy.FormPolicy" - ] -} \ No newline at end of file diff --git a/examples/formbot/stories.md b/examples/formbot/stories.md deleted file mode 100644 index 45477bf3b..000000000 --- a/examples/formbot/stories.md +++ /dev/null @@ -1,54 +0,0 @@ -## Generated Story -9155310465400161964 -* request_restaurant - - restaurant_form - - form{"name": "restaurant_form"} - - slot{"requested_slot": "cuisine"} -* chitchat - - utter_chitchat - - restaurant_form - - form: slot{"requested_slot": "cuisine"} -* form: inform{"cuisine": "1"} - - form: slot{"cuisine": "1"} - - form: restaurant_form - - form: slot{"cuisine": "1"} - - form: slot{"requested_slot": "num_people"} -* form: inform{"num_people": "1"} - - form: slot{"num_people": "1"} - - form: restaurant_form - - form: slot{"num_people": "1"} - - form{"name": null} - - slot{"requested_slot": null} -* thank - - utter_noworries - - -## Generated Story 2392181920552359166 -* request_restaurant - - restaurant_form - - form{"name": "restaurant_form"} - - form: slot{"requested_slot": "cuisine"} -* form: inform{"cuisine": "1"} - - form: slot{"cuisine": "1"} - - form: restaurant_form - - slot{"cuisine": "1"} - - slot{"requested_slot": "num_people"} -* chitchat - - utter_chitchat - - restaurant_form - - slot{"requested_slot": "num_people"} -* chitchat - - utter_chitchat - - restaurant_form - - slot{"requested_slot": "num_people"} -* chitchat - - utter_chitchat - - restaurant_form - - form: slot{"requested_slot": "num_people"} -* form: inform{"num_people": "1"} - - form: slot{"num_people": "1"} - - form: restaurant_form - - form: slot{"num_people": "1"} - - form{"name": null} - - slot{"requested_slot": null} -* thank - - utter_noworries diff --git a/examples/formbot/train.py b/examples/formbot/train.py deleted file mode 100644 index 5130f43c3..000000000 --- a/examples/formbot/train.py +++ /dev/null @@ -1,34 +0,0 @@ -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -from rasa_core import utils -from rasa_core.agent import Agent -from rasa_core.policies.keras_policy import KerasPolicy -from rasa_core.policies.memoization import MemoizationPolicy -from rasa_core.policies.form_policy import FormPolicy -from rasa_core.policies.fallback import FallbackPolicy - -if __name__ == '__main__': - utils.configure_colored_logging(loglevel="DEBUG") - - training_data_file = 'data/stories.md' - model_path = 'models/dialogue' - - agent = Agent("domain.yml", - policies=[MemoizationPolicy(), - FormPolicy(), - FallbackPolicy()]) - - training_data = agent.load_data(training_data_file, - augmentation_factor=0) - - agent.train( - training_data, - epochs=200, - batch_size=10, - validation_split=0.2 - ) - - agent.persist(model_path) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index b1f4cbfda..004099c83 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -26,8 +26,6 @@ def name(self): raise NotImplementedError - RANDOMIZE = False - @staticmethod def required_slots(): raise NotImplementedError @@ -63,7 +61,8 @@ def activate_if_required(self, tracker): def run(self, dispatcher, tracker, domain): - if tracker.active_form == self.name() and tracker.latest_action_name == 'action_listen': + if (tracker.active_form == self.name() and + tracker.latest_action_name == 'action_listen'): events = self.validate(dispatcher, tracker, domain) else: events = [] @@ -84,7 +83,8 @@ def run(self, dispatcher, tracker, domain): # there is nothing more to request, so we can submit events_from_submit = self.submit(dispatcher, temp_tracker, domain) or [] - return events + events_from_submit + [Form(None), SlotSet(REQUESTED_SLOT, None)] + return events + events_from_submit + [Form(None), + SlotSet(REQUESTED_SLOT, None)] def submit(self, dispatcher, tracker, domain): raise NotImplementedError( From 3ef419306080a30bee7f543261e27e800d019e10 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 28 Sep 2018 12:59:42 +0200 Subject: [PATCH 043/112] refactor forms, duplicate ActionExecutionError in sdk RasaHQ/roadmap#263 --- rasa_core_sdk/__init__.py | 20 +++++-- rasa_core_sdk/endpoint.py | 2 +- rasa_core_sdk/forms.py | 119 +++++++++++++++++++++++++++----------- 3 files changed, 100 insertions(+), 41 deletions(-) diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index 03be18182..b3bdc160f 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -7,9 +7,7 @@ import logging import typing -from typing import Dict, Text, Any, Optional, Iterator -from typing import List - +from typing import Dict, Text, Any, Optional, Iterator, List logger = logging.getLogger(__name__) @@ -155,13 +153,23 @@ def name(self): # type: () -> Text """Unique identifier of this simple action.""" - raise NotImplementedError + raise NotImplementedError("An action must implement a name") def run(self, dispatcher, tracker, domain): - # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[dict] + # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] """Execute the side effects of this action.""" - raise NotImplementedError + raise NotImplementedError("An action must implement its run method") def __str__(self): return "Action('{}')".format(self.name()) + + +class ActionExecutionError(Exception): + + def __init__(self, message, action_name): + self.message = message + self.action_name = action_name + + def __str__(self): + return self.message diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index 9c81b16f1..2084d9c0f 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -11,7 +11,7 @@ from gevent.pywsgi import WSGIServer from rasa_core_sdk.executor import ActionExecutor -from rasa_core.actions.action import ActionExecutionError +from rasa_core_sdk import ActionExecutionError DEFAULT_SERVER_PORT = 5055 diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 004099c83..f766e1451 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -5,87 +5,138 @@ from __future__ import unicode_literals import logging -from typing import Text +import typing +from typing import Dict, Text, Any, List -from rasa_core_sdk import Action +from rasa_core_sdk import Action, ActionExecutionError from rasa_core_sdk.events import SlotSet, Form -from rasa_core.actions.action import ActionExecutionError logger = logging.getLogger(__name__) +if typing.TYPE_CHECKING: + from rasa_core_sdk import Tracker + from rasa_core_sdk.executor import CollectingDispatcher + # this slot is used to store information needed -# to do the form handling, needs to be part -# of the domain +# to do the form handling REQUESTED_SLOT = "requested_slot" class FormAction(Action): def name(self): # type: () -> Text - """Unique identifier of this form action.""" + """Unique identifier of the form""" - raise NotImplementedError + raise NotImplementedError("A form must implement a name") @staticmethod def required_slots(): - raise NotImplementedError + # type: () -> List[Text] + """A list of required slots that the form has to fill""" - @staticmethod - def should_request_slot(tracker, slot_name): - existing_val = tracker.get_slot(slot_name) - return existing_val is None + raise NotImplementedError("A form must implement required slots " + "that it has to fill") + # noinspection PyUnusedLocal def validate(self, dispatcher, tracker, domain): - # type: (Tracker) -> Dict[Text, Any] - """"Validate the user input.""" + # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] + """"Validate the user input else return an error""" events = [] - entities = tracker.latest_message["entities"] - - for e in entities: + for e in tracker.latest_message["entities"]: if e.get("entity") == tracker.slots[REQUESTED_SLOT]: events.append(SlotSet(e['entity'], e['value'])) + if events: return events else: - raise ActionExecutionError("Failed to validate slot {0} with " - "action {1}".format( - tracker.slots[REQUESTED_SLOT], - self.name()), self.name()) + raise ActionExecutionError("Failed to validate slot {0} " + "with action {1}" + "".format(tracker.slots[REQUESTED_SLOT], + self.name()), self.name()) + + def submit(self, dispatcher, tracker, domain): + # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] + """Define what the form has to do + after all required slots are filled""" + + raise NotImplementedError("A form must implement a submit method") + + # run helpers + def _activate_if_required(self, tracker): + # type: (Tracker) -> List[Dict] + """Return `Form` event with the name of the form + if the form was called for the first time""" - def activate_if_required(self, tracker): if tracker.active_form == self.name(): return [] else: return [Form(self.name())] - def run(self, dispatcher, tracker, domain): - + def _validate_if_required(self, dispatcher, tracker, domain): + # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] + """Return a list of events from `self.validate` + if validation is required: + - the form is active + - the form is called after `action_listen` + """ if (tracker.active_form == self.name() and tracker.latest_action_name == 'action_listen'): - events = self.validate(dispatcher, tracker, domain) + return self.validate(dispatcher, tracker, domain) else: - events = [] + return [] + + @staticmethod + def _should_request_slot(tracker, slot_name): + # type: (Tracker, Text) -> bool + """Check whether form action should request given slot""" + + return tracker.get_slot(slot_name) is None + + @staticmethod + def _deactivate(): + # type: () -> List[Dict] + """Return `Form` event with `None` as name to deactivate the form + and reset the requested slot""" + + return [Form(None), SlotSet(REQUESTED_SLOT, None)] + + def run(self, dispatcher, tracker, domain): + # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] + """Execute the side effects of this form: + - activate if needed + - validate user input if needed + - set validated slots + - utter_ask_{slot} template with the next required slot + - submit the form if all required slots are set + - deactivate the form + """ + # activate the form + events = self._activate_if_required(tracker) + # validate user input + events.extend(self._validate_if_required(dispatcher, tracker, domain)) temp_tracker = tracker.copy() for e in events: if e['event'] == 'slot': temp_tracker.slots[e["name"]] = e["value"] + + # request next slot for slot in self.required_slots(): - if self.should_request_slot(temp_tracker, slot): + if self._should_request_slot(temp_tracker, slot): dispatcher.utter_template("utter_ask_{}".format(slot), tracker) events.append(SlotSet(REQUESTED_SLOT, slot)) - return self.activate_if_required(tracker) + events + return events # there is nothing more to request, so we can submit - events_from_submit = self.submit(dispatcher, temp_tracker, domain) or [] + events.extend(self.submit(dispatcher, temp_tracker, domain)) + # deactivate the form after submission + events.extend(self._deactivate()) - return events + events_from_submit + [Form(None), - SlotSet(REQUESTED_SLOT, None)] + return events - def submit(self, dispatcher, tracker, domain): - raise NotImplementedError( - "a form action must implement a submit method") + def __str__(self): + return "Form('{}')".format(self.name()) From 7283b8b167207d75dfedd0923a01cbdbf1c5d471 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 28 Sep 2018 13:01:57 +0200 Subject: [PATCH 044/112] code style RasaHQ/roadmap#263 --- rasa_core_sdk/forms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index f766e1451..0fa8a9c10 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -53,7 +53,8 @@ def validate(self, dispatcher, tracker, domain): raise ActionExecutionError("Failed to validate slot {0} " "with action {1}" "".format(tracker.slots[REQUESTED_SLOT], - self.name()), self.name()) + self.name()), + self.name()) def submit(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] From 4aa2eac40300de6ed0d80b6e872cd8c8d6ae0d14 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 28 Sep 2018 15:14:19 +0200 Subject: [PATCH 045/112] refactor forms RasaHQ/roadmap#263 --- rasa_core_sdk/forms.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 0fa8a9c10..db98e38ae 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -6,7 +6,7 @@ import logging import typing -from typing import Dict, Text, Any, List +from typing import Dict, Text, Any, List, Union from rasa_core_sdk import Action, ActionExecutionError from rasa_core_sdk.events import SlotSet, Form @@ -37,24 +37,33 @@ def required_slots(): raise NotImplementedError("A form must implement required slots " "that it has to fill") + def slot_mapping(self): + # type: () -> Dict[Text: Union[Text, List[Text]]] + """A dictionary to map required slots to extracted entities""" + + return dict(zip(self.required_slots(), self.required_slots())) + # noinspection PyUnusedLocal def validate(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] """"Validate the user input else return an error""" + slot_to_fill = tracker.slots[REQUESTED_SLOT] - events = [] - for e in tracker.latest_message["entities"]: - if e.get("entity") == tracker.slots[REQUESTED_SLOT]: - events.append(SlotSet(e['entity'], e['value'])) + # map requested_slot to entity + required_entities = self.slot_mapping().get(slot_to_fill) - if events: - return events - else: - raise ActionExecutionError("Failed to validate slot {0} " - "with action {1}" - "".format(tracker.slots[REQUESTED_SLOT], - self.name()), - self.name()) + if required_entities: + if isinstance(required_entities, str): + required_entities = [required_entities] + + for e in tracker.latest_message["entities"]: + if e.get("entity") in required_entities: + return [SlotSet(slot_to_fill, e['value'])] + + raise ActionExecutionError("Failed to validate slot {0} " + "with action {1}" + "".format(slot_to_fill, self.name()), + self.name()) def submit(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] From 076648ad4b2fff83082a4eb82989def29e104a45 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 28 Sep 2018 16:28:33 +0200 Subject: [PATCH 046/112] print FormAction as FormAction RasaHQ/roadmap#263 --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index db98e38ae..40fc1a8b4 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -149,4 +149,4 @@ def run(self, dispatcher, tracker, domain): return events def __str__(self): - return "Form('{}')".format(self.name()) + return "FormAction('{}')".format(self.name()) From 95a4895bec852d50bcc0dce70e312ca4b7fc311c Mon Sep 17 00:00:00 2001 From: Alan Nichol Date: Wed, 3 Oct 2018 12:12:56 +0200 Subject: [PATCH 047/112] separate logic for which slot to request next https://github.com/RasaHQ/roadmap/issues/280 --- rasa_core_sdk/forms.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 40fc1a8b4..1e3d13d3a 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -111,6 +111,12 @@ def _deactivate(): return [Form(None), SlotSet(REQUESTED_SLOT, None)] + def next_slot_to_request(self, tracker): + for slot in self.required_slots(): + if self._should_request_slot(tracker, slot): + return slot + return None + def run(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] """Execute the side effects of this form: @@ -132,14 +138,12 @@ def run(self, dispatcher, tracker, domain): temp_tracker.slots[e["name"]] = e["value"] # request next slot - for slot in self.required_slots(): - if self._should_request_slot(temp_tracker, slot): - - dispatcher.utter_template("utter_ask_{}".format(slot), tracker) - - events.append(SlotSet(REQUESTED_SLOT, slot)) + slot = self.next_slot_to_request(temp_tracker) + if slot is not None: + dispatcher.utter_template("utter_ask_{}".format(slot), tracker) + events.append(SlotSet(REQUESTED_SLOT, slot)) + return events - return events # there is nothing more to request, so we can submit events.extend(self.submit(dispatcher, temp_tracker, domain)) From 12e112a1835b4b6a8cb82e254dbd98146d6f1ad9 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 4 Oct 2018 10:20:14 +0200 Subject: [PATCH 048/112] add freetext key and {intent:value} pairs RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 59 ++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 40fc1a8b4..2fd19524f 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -6,7 +6,7 @@ import logging import typing -from typing import Dict, Text, Any, List, Union +from typing import Dict, Text, Any, List, Union, Optional from rasa_core_sdk import Action, ActionExecutionError from rasa_core_sdk.events import SlotSet, Form @@ -23,6 +23,8 @@ class FormAction(Action): + FREETEXT = 'FREETEXT' + def name(self): # type: () -> Text """Unique identifier of the form""" @@ -38,32 +40,55 @@ def required_slots(): "that it has to fill") def slot_mapping(self): - # type: () -> Dict[Text: Union[Text, List[Text]]] - """A dictionary to map required slots to extracted entities""" + # type: () -> Dict[Text: Union[Text, List[Text], Dict[Text: Any]]] + """A dictionary to map required slots to extracted entities or + to intent:value pairs or free text""" return dict(zip(self.required_slots(), self.required_slots())) # noinspection PyUnusedLocal - def validate(self, dispatcher, tracker, domain): - # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] - """"Validate the user input else return an error""" + def extract(self, dispatcher, tracker, domain): + # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> Optional[List[Dict]] + """"Extract the user input else return an error""" slot_to_fill = tracker.slots[REQUESTED_SLOT] # map requested_slot to entity - required_entities = self.slot_mapping().get(slot_to_fill) + slot_mapping = self.slot_mapping().get(slot_to_fill) + + if slot_mapping: + if slot_mapping == self.FREETEXT: + return [SlotSet(slot_to_fill, + tracker.latest_message.get("text"))] + elif isinstance(slot_mapping, dict): + intent = tracker.latest_message.get("intent", {}).get("name") + if intent in slot_mapping.keys(): + return [SlotSet(slot_to_fill, slot_mapping[intent])] + else: + required_entities = slot_mapping + if not isinstance(required_entities, list): + required_entities = [required_entities] + + for e in tracker.latest_message["entities"]: + if e.get("entity") in required_entities: + return [SlotSet(slot_to_fill, e['value'])] + + return None - if required_entities: - if isinstance(required_entities, str): - required_entities = [required_entities] + # noinspection PyUnusedLocal + def validate(self, dispatcher, tracker, domain): + # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] + """"Extract the user input else return an error""" - for e in tracker.latest_message["entities"]: - if e.get("entity") in required_entities: - return [SlotSet(slot_to_fill, e['value'])] + events = self.extract(dispatcher, tracker, domain) - raise ActionExecutionError("Failed to validate slot {0} " - "with action {1}" - "".format(slot_to_fill, self.name()), - self.name()) + if events is not None: + return events + else: + raise ActionExecutionError("Failed to validate slot {0} " + "with action {1}" + "".format(tracker.slots[REQUESTED_SLOT], + self.name()), + self.name()) def submit(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] From 218edacda7a177ff87f567d7981b5fecfacd249c Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 4 Oct 2018 10:29:13 +0200 Subject: [PATCH 049/112] update slot_mapping comment RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 2fd19524f..cb846fb83 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -41,8 +41,10 @@ def required_slots(): def slot_mapping(self): # type: () -> Dict[Text: Union[Text, List[Text], Dict[Text: Any]]] - """A dictionary to map required slots to extracted entities or - to intent:value pairs or free text""" + """A dictionary to map required slots to + - an extracted entity or a list of entities + - a dictionary of intent: value pairs + - a whole message""" return dict(zip(self.required_slots(), self.required_slots())) From 64a1ee3d23f6e90539610237a3b0d0d5f101972b Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 4 Oct 2018 13:32:52 +0200 Subject: [PATCH 050/112] pick entities in the order they are present in slot_mapping list RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index cb846fb83..77e050b94 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -23,7 +23,7 @@ class FormAction(Action): - FREETEXT = 'FREETEXT' + FREETEXT = '__FREETEXT__' def name(self): # type: () -> Text @@ -58,10 +58,7 @@ def extract(self, dispatcher, tracker, domain): slot_mapping = self.slot_mapping().get(slot_to_fill) if slot_mapping: - if slot_mapping == self.FREETEXT: - return [SlotSet(slot_to_fill, - tracker.latest_message.get("text"))] - elif isinstance(slot_mapping, dict): + if isinstance(slot_mapping, dict): intent = tracker.latest_message.get("intent", {}).get("name") if intent in slot_mapping.keys(): return [SlotSet(slot_to_fill, slot_mapping[intent])] @@ -70,9 +67,15 @@ def extract(self, dispatcher, tracker, domain): if not isinstance(required_entities, list): required_entities = [required_entities] - for e in tracker.latest_message["entities"]: - if e.get("entity") in required_entities: - return [SlotSet(slot_to_fill, e['value'])] + for entity_name in required_entities: + entity_value = next(tracker.get_latest_entity_values( + entity_name), None) + if entity_value is not None: + return [SlotSet(slot_to_fill, entity_value)] + + if self.FREETEXT in required_entities: + return [SlotSet(slot_to_fill, + tracker.latest_message.get("text"))] return None From e617bc950aa45f3ee11af40f675320cffff81418 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 4 Oct 2018 14:13:12 +0200 Subject: [PATCH 051/112] allow slot_mapping to be a list of different things RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 50 +++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 77e050b94..3e7d819da 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -40,42 +40,48 @@ def required_slots(): "that it has to fill") def slot_mapping(self): - # type: () -> Dict[Text: Union[Text, List[Text], Dict[Text: Any]]] + # type: () -> Dict[Text: Union[Text, Dict, List[Text, Dict]]] """A dictionary to map required slots to - - an extracted entity or a list of entities + - an extracted entity - a dictionary of intent: value pairs - - a whole message""" + - a whole message + or a list of all of them""" return dict(zip(self.required_slots(), self.required_slots())) # noinspection PyUnusedLocal - def extract(self, dispatcher, tracker, domain): - # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> Optional[List[Dict]] + def extract(self, + dispatcher, # type: CollectingDispatcher + tracker, # type: Tracker + domain # type: Dict[Text, Any] + ): + # type: (...) -> Optional[List[Dict]] """"Extract the user input else return an error""" slot_to_fill = tracker.slots[REQUESTED_SLOT] # map requested_slot to entity - slot_mapping = self.slot_mapping().get(slot_to_fill) - - if slot_mapping: - if isinstance(slot_mapping, dict): - intent = tracker.latest_message.get("intent", {}).get("name") - if intent in slot_mapping.keys(): - return [SlotSet(slot_to_fill, slot_mapping[intent])] - else: - required_entities = slot_mapping - if not isinstance(required_entities, list): - required_entities = [required_entities] - - for entity_name in required_entities: + slot_mappings = self.slot_mapping().get(slot_to_fill) + + if slot_mappings: + if not isinstance(slot_mappings, list): + slot_mappings = [slot_mappings] + + for slot_mapping in slot_mappings: + if isinstance(slot_mapping, dict): + intent = tracker.latest_message.get("intent", + {}).get("name") + if intent in slot_mapping.keys(): + return [SlotSet(slot_to_fill, slot_mapping[intent])] + else: entity_value = next(tracker.get_latest_entity_values( - entity_name), None) + slot_mapping), None) if entity_value is not None: return [SlotSet(slot_to_fill, entity_value)] - if self.FREETEXT in required_entities: - return [SlotSet(slot_to_fill, - tracker.latest_message.get("text"))] + # the whole text can be always extracted, so it is done in the end + if self.FREETEXT in slot_mappings: + return [SlotSet(slot_to_fill, + tracker.latest_message.get("text"))] return None From b84e59b96941bf6deb7cb2015e45660fb50740d2 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 4 Oct 2018 15:02:11 +0200 Subject: [PATCH 052/112] request slot in different method RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 45 +++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 24ae95fd0..ed525ef26 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -101,6 +101,23 @@ def validate(self, dispatcher, tracker, domain): self.name()), self.name()) + # noinspection PyUnusedLocal + def next_slot_to_request(self, + dispatcher, # type: CollectingDispatcher + tracker, # type: Tracker + domain # type: Dict[Text, Any] + ): + # type: (...) -> Optional[List[Dict]] + """"Request the next slot and utter template if needed, + else return None""" + + for slot in self.required_slots(): + if self._should_request_slot(tracker, slot): + dispatcher.utter_template("utter_ask_{}".format(slot), tracker) + return [SlotSet(REQUESTED_SLOT, slot)] + + return None + def submit(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] """Define what the form has to do @@ -147,12 +164,6 @@ def _deactivate(): return [Form(None), SlotSet(REQUESTED_SLOT, None)] - def next_slot_to_request(self, tracker): - for slot in self.required_slots(): - if self._should_request_slot(tracker, slot): - return slot - return None - def run(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] """Execute the side effects of this form: @@ -173,18 +184,16 @@ def run(self, dispatcher, tracker, domain): if e['event'] == 'slot': temp_tracker.slots[e["name"]] = e["value"] - # request next slot - slot = self.next_slot_to_request(temp_tracker) - if slot is not None: - dispatcher.utter_template("utter_ask_{}".format(slot), tracker) - events.append(SlotSet(REQUESTED_SLOT, slot)) - return events - - - # there is nothing more to request, so we can submit - events.extend(self.submit(dispatcher, temp_tracker, domain)) - # deactivate the form after submission - events.extend(self._deactivate()) + next_slot_events = self.next_slot_to_request(dispatcher, temp_tracker, + domain) + if next_slot_events is not None: + # request next slot + events.extend(next_slot_events) + else: + # there is nothing more to request, so we can submit + events.extend(self.submit(dispatcher, temp_tracker, domain)) + # deactivate the form after submission + events.extend(self._deactivate()) return events From 5cc4a68a351101f0437d327b144d3f7a537d5303 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 4 Oct 2018 15:03:59 +0200 Subject: [PATCH 053/112] request slot in different methodrequest_next_slot RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index ed525ef26..3ed07c21f 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -102,7 +102,7 @@ def validate(self, dispatcher, tracker, domain): self.name()) # noinspection PyUnusedLocal - def next_slot_to_request(self, + def request_next_slot(self, dispatcher, # type: CollectingDispatcher tracker, # type: Tracker domain # type: Dict[Text, Any] @@ -184,7 +184,7 @@ def run(self, dispatcher, tracker, domain): if e['event'] == 'slot': temp_tracker.slots[e["name"]] = e["value"] - next_slot_events = self.next_slot_to_request(dispatcher, temp_tracker, + next_slot_events = self.request_next_slot(dispatcher, temp_tracker, domain) if next_slot_events is not None: # request next slot From 374ef3a332de8d47d4d7f1259b09fd15067b80d5 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 4 Oct 2018 15:05:04 +0200 Subject: [PATCH 054/112] PEP RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 3ed07c21f..61dad8bc0 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -185,7 +185,7 @@ def run(self, dispatcher, tracker, domain): temp_tracker.slots[e["name"]] = e["value"] next_slot_events = self.request_next_slot(dispatcher, temp_tracker, - domain) + domain) if next_slot_events is not None: # request next slot events.extend(next_slot_events) From 4fa247bb116b6e9825b518e156c6f881ba684df9 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 4 Oct 2018 15:32:35 +0200 Subject: [PATCH 055/112] rename ActionExecutionError to Rejected, add actionexecutionfailed event RasaHQ/roadmap#280 --- rasa_core_sdk/__init__.py | 6 +++--- rasa_core_sdk/endpoint.py | 6 ++++-- rasa_core_sdk/events.py | 12 ++++++++++++ rasa_core_sdk/forms.py | 14 +++++++------- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index b3bdc160f..beb9de77e 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -165,11 +165,11 @@ def __str__(self): return "Action('{}')".format(self.name()) -class ActionExecutionError(Exception): +class ActionExecutionRejected(Exception): - def __init__(self, message, action_name): - self.message = message + def __init__(self, action_name, message=None): self.action_name = action_name + self.message = message def __str__(self): return self.message diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index 2084d9c0f..388006d7c 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -3,6 +3,8 @@ from __future__ import print_function from __future__ import unicode_literals +from builtins import str + import argparse import logging @@ -11,7 +13,7 @@ from gevent.pywsgi import WSGIServer from rasa_core_sdk.executor import ActionExecutor -from rasa_core_sdk import ActionExecutionError +from rasa_core_sdk import ActionExecutionRejected DEFAULT_SERVER_PORT = 5055 @@ -71,7 +73,7 @@ def webhook(): action_call = request.json try: response = executor.run(action_call) - except ActionExecutionError as e: + except ActionExecutionRejected as e: logger.error(str(e)) result = {"error": str(e), "action_name": e.action_name} response = jsonify(result) diff --git a/rasa_core_sdk/events.py b/rasa_core_sdk/events.py index 6131dec85..9aab0f2c6 100644 --- a/rasa_core_sdk/events.py +++ b/rasa_core_sdk/events.py @@ -148,3 +148,15 @@ def Form(name, timestamp=None): "name": name, "timestamp": timestamp, } + + +# noinspection PyPep8Naming +def ActionExecutionFailed(action_name, policy, + policy_confidence, timestamp=None): + return { + "event": "action_execution_failed", + "action_name": action_name, + "policy": policy, + "policy_confidence": policy_confidence, + "timestamp": timestamp, + } diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 61dad8bc0..2d87dd6a3 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -8,7 +8,7 @@ import typing from typing import Dict, Text, Any, List, Union, Optional -from rasa_core_sdk import Action, ActionExecutionError +from rasa_core_sdk import Action, ActionExecutionRejected from rasa_core_sdk.events import SlotSet, Form logger = logging.getLogger(__name__) @@ -57,7 +57,7 @@ def extract(self, ): # type: (...) -> Optional[List[Dict]] """"Extract the user input else return an error""" - slot_to_fill = tracker.slots[REQUESTED_SLOT] + slot_to_fill = tracker.get_slot(REQUESTED_SLOT) # map requested_slot to entity slot_mappings = self.slot_mapping().get(slot_to_fill) @@ -89,17 +89,17 @@ def extract(self, def validate(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] """"Extract the user input else return an error""" + slot_to_fill = tracker.get_slot(REQUESTED_SLOT) events = self.extract(dispatcher, tracker, domain) if events is not None: return events else: - raise ActionExecutionError("Failed to validate slot {0} " - "with action {1}" - "".format(tracker.slots[REQUESTED_SLOT], - self.name()), - self.name()) + raise ActionExecutionRejected(self.name(), + "Failed to validate slot {0} " + "with action {1}" + "".format(slot_to_fill, self.name())) # noinspection PyUnusedLocal def request_next_slot(self, From 0a799d23066ed03df8e290613d41c97c51e9ad10 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 4 Oct 2018 15:50:16 +0200 Subject: [PATCH 056/112] add default message to ActionExecutionRejected RasaHQ/roadmap#280 --- rasa_core_sdk/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index beb9de77e..271810306 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -169,7 +169,9 @@ class ActionExecutionRejected(Exception): def __init__(self, action_name, message=None): self.action_name = action_name - self.message = message + self.message = (message or + "Custom action '{}' rejected to run" + "".format(action_name)) def __str__(self): return self.message From 25ca671b48c22b9a0c6b2a0f195043e042a8d3e9 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 4 Oct 2018 16:05:57 +0200 Subject: [PATCH 057/112] policy_confidence -> confidence, ActionExecution Exeption to ActionExecutionRejection and event to ..Rejected RasaHQ/roadmap#280 --- rasa_core_sdk/__init__.py | 2 +- rasa_core_sdk/endpoint.py | 4 ++-- rasa_core_sdk/events.py | 12 +++++++----- rasa_core_sdk/forms.py | 10 +++++----- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index 271810306..34359bcd9 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -165,7 +165,7 @@ def __str__(self): return "Action('{}')".format(self.name()) -class ActionExecutionRejected(Exception): +class ActionExecutionRejection(Exception): def __init__(self, action_name, message=None): self.action_name = action_name diff --git a/rasa_core_sdk/endpoint.py b/rasa_core_sdk/endpoint.py index 388006d7c..b3ccad153 100644 --- a/rasa_core_sdk/endpoint.py +++ b/rasa_core_sdk/endpoint.py @@ -13,7 +13,7 @@ from gevent.pywsgi import WSGIServer from rasa_core_sdk.executor import ActionExecutor -from rasa_core_sdk import ActionExecutionRejected +from rasa_core_sdk import ActionExecutionRejection DEFAULT_SERVER_PORT = 5055 @@ -73,7 +73,7 @@ def webhook(): action_call = request.json try: response = executor.run(action_call) - except ActionExecutionRejected as e: + except ActionExecutionRejection as e: logger.error(str(e)) result = {"error": str(e), "action_name": e.action_name} response = jsonify(result) diff --git a/rasa_core_sdk/events.py b/rasa_core_sdk/events.py index 9aab0f2c6..9fa0fadf3 100644 --- a/rasa_core_sdk/events.py +++ b/rasa_core_sdk/events.py @@ -123,10 +123,12 @@ def ConversationResumed(timestamp=None): # noinspection PyPep8Naming -def ActionExecuted(action_name, timestamp=None): +def ActionExecuted(action_name, policy=None, confidence=None, timestamp=None): return { "event": "action", "name": action_name, + "policy": policy, + "confidence": confidence, "timestamp": timestamp, } @@ -151,12 +153,12 @@ def Form(name, timestamp=None): # noinspection PyPep8Naming -def ActionExecutionFailed(action_name, policy, - policy_confidence, timestamp=None): +def ActionExecutionRejected(action_name, policy=None, confidence=None, + timestamp=None): return { - "event": "action_execution_failed", + "event": "action_execution_rejected", "action_name": action_name, "policy": policy, - "policy_confidence": policy_confidence, + "confidence": confidence, "timestamp": timestamp, } diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 2d87dd6a3..e26b12a7e 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -8,7 +8,7 @@ import typing from typing import Dict, Text, Any, List, Union, Optional -from rasa_core_sdk import Action, ActionExecutionRejected +from rasa_core_sdk import Action, ActionExecutionRejection from rasa_core_sdk.events import SlotSet, Form logger = logging.getLogger(__name__) @@ -96,10 +96,10 @@ def validate(self, dispatcher, tracker, domain): if events is not None: return events else: - raise ActionExecutionRejected(self.name(), - "Failed to validate slot {0} " - "with action {1}" - "".format(slot_to_fill, self.name())) + raise ActionExecutionRejection(self.name(), + "Failed to validate slot {0} " + "with action {1}" + "".format(slot_to_fill, self.name())) # noinspection PyUnusedLocal def request_next_slot(self, From ea2e7338a68e6fce67738358305d90f79df967e1 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 4 Oct 2018 17:28:34 +0200 Subject: [PATCH 058/112] PEP RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index e26b12a7e..594727752 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -103,10 +103,10 @@ def validate(self, dispatcher, tracker, domain): # noinspection PyUnusedLocal def request_next_slot(self, - dispatcher, # type: CollectingDispatcher - tracker, # type: Tracker - domain # type: Dict[Text, Any] - ): + dispatcher, # type: CollectingDispatcher + tracker, # type: Tracker + domain # type: Dict[Text, Any] + ): # type: (...) -> Optional[List[Dict]] """"Request the next slot and utter template if needed, else return None""" From e2617fa64671cda0354e0e7488a72ea8ecae4152 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 4 Oct 2018 18:20:36 +0200 Subject: [PATCH 059/112] fixes method description RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 594727752..aef50e593 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -56,7 +56,7 @@ def extract(self, domain # type: Dict[Text, Any] ): # type: (...) -> Optional[List[Dict]] - """"Extract the user input else return an error""" + """"Extract requested slot from a user input else return None""" slot_to_fill = tracker.get_slot(REQUESTED_SLOT) # map requested_slot to entity @@ -88,7 +88,7 @@ def extract(self, # noinspection PyUnusedLocal def validate(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] - """"Extract the user input else return an error""" + """"Validate extracted requested slot else raise an error""" slot_to_fill = tracker.get_slot(REQUESTED_SLOT) events = self.extract(dispatcher, tracker, domain) From 305a4d090b37058b13e528ad60cfa7bad3f12787 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 4 Oct 2018 18:34:33 +0200 Subject: [PATCH 060/112] add comment to slot_mapping RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index aef50e593..e09e329b3 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -45,7 +45,7 @@ def slot_mapping(self): - an extracted entity - a dictionary of intent: value pairs - a whole message - or a list of all of them""" + or a list of all of them, where a first match will be picked""" return dict(zip(self.required_slots(), self.required_slots())) From 2713babe00a9b1360f9664238e7d4499a024a60b Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 5 Oct 2018 10:01:50 +0200 Subject: [PATCH 061/112] add capability to set a slot to a whole message conditioned on the intent RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index e09e329b3..b19998023 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -42,9 +42,10 @@ def required_slots(): def slot_mapping(self): # type: () -> Dict[Text: Union[Text, Dict, List[Text, Dict]]] """A dictionary to map required slots to - - an extracted entity - - a dictionary of intent: value pairs - - a whole message + - an extracted entity; + - a dictionary of intent: value pairs, + if value is FREETEXT, use a whole message as value; + - a whole message; or a list of all of them, where a first match will be picked""" return dict(zip(self.required_slots(), self.required_slots())) @@ -71,7 +72,13 @@ def extract(self, intent = tracker.latest_message.get("intent", {}).get("name") if intent in slot_mapping.keys(): - return [SlotSet(slot_to_fill, slot_mapping[intent])] + if slot_mapping[intent] == self.FREETEXT: + return [SlotSet(slot_to_fill, + tracker.latest_message.get( + "text"))] + else: + return [SlotSet(slot_to_fill, + slot_mapping[intent])] else: entity_value = next(tracker.get_latest_entity_values( slot_mapping), None) From fba2dfb12a950610d686c06e5983cdc4c8e202e6 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 5 Oct 2018 10:34:43 +0200 Subject: [PATCH 062/112] PEP RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index b19998023..c46897bfb 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -106,7 +106,8 @@ def validate(self, dispatcher, tracker, domain): raise ActionExecutionRejection(self.name(), "Failed to validate slot {0} " "with action {1}" - "".format(slot_to_fill, self.name())) + "".format(slot_to_fill, + self.name())) # noinspection PyUnusedLocal def request_next_slot(self, From 0c42795ef91a43cc89fe302fcf5986df05561bbb Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 5 Oct 2018 16:08:08 +0200 Subject: [PATCH 063/112] rename action_name to name in as_dict in Rejected event RasaHQ/roadmap#280 --- rasa_core_sdk/events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/events.py b/rasa_core_sdk/events.py index 9fa0fadf3..b10573603 100644 --- a/rasa_core_sdk/events.py +++ b/rasa_core_sdk/events.py @@ -157,7 +157,7 @@ def ActionExecutionRejected(action_name, policy=None, confidence=None, timestamp=None): return { "event": "action_execution_rejected", - "action_name": action_name, + "name": action_name, "policy": policy, "confidence": confidence, "timestamp": timestamp, From 90bbb29faaea70219af0d800b134614915f8328b Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 5 Oct 2018 18:09:54 +0200 Subject: [PATCH 064/112] add helper methods to construct slot_mapping RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 69 +++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index c46897bfb..e04d40619 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -23,7 +23,6 @@ class FormAction(Action): - FREETEXT = '__FREETEXT__' def name(self): # type: () -> Text @@ -39,16 +38,27 @@ def required_slots(): raise NotImplementedError("A form must implement required slots " "that it has to fill") + @staticmethod + def from_entity(entity, intent=None): + return {"type": "from_entity", "intent": intent, "entity": entity} + + @staticmethod + def from_intent(intent, value): + return {"type": "from_intent", "intent": intent, "value": value} + + @staticmethod + def from_text(intent=None): + return {"type": "from_text", "intent": intent} + def slot_mapping(self): # type: () -> Dict[Text: Union[Text, Dict, List[Text, Dict]]] """A dictionary to map required slots to - - an extracted entity; - - a dictionary of intent: value pairs, - if value is FREETEXT, use a whole message as value; - - a whole message; + - an extracted entity + - intent: value pairs + - a whole message or a list of all of them, where a first match will be picked""" - return dict(zip(self.required_slots(), self.required_slots())) + return {slot: self.from_entity(slot) for slot in self.required_slots()} # noinspection PyUnusedLocal def extract(self, @@ -68,27 +78,32 @@ def extract(self, slot_mappings = [slot_mappings] for slot_mapping in slot_mappings: - if isinstance(slot_mapping, dict): - intent = tracker.latest_message.get("intent", - {}).get("name") - if intent in slot_mapping.keys(): - if slot_mapping[intent] == self.FREETEXT: - return [SlotSet(slot_to_fill, - tracker.latest_message.get( - "text"))] - else: - return [SlotSet(slot_to_fill, - slot_mapping[intent])] - else: - entity_value = next(tracker.get_latest_entity_values( - slot_mapping), None) - if entity_value is not None: - return [SlotSet(slot_to_fill, entity_value)] - - # the whole text can be always extracted, so it is done in the end - if self.FREETEXT in slot_mappings: - return [SlotSet(slot_to_fill, - tracker.latest_message.get("text"))] + if (not isinstance(slot_mapping, dict) or + slot_mapping.get("type") is None): + raise ValueError("Provided incompatible slot_mapping") + + mapping_intent = slot_mapping.get("intent") + intent = tracker.latest_message.get("intent", + {}).get("name") + if mapping_intent is None or mapping_intent == intent: + mapping_type = slot_mapping["type"] + + if mapping_type == "from_entity": + entity_value = next(tracker.get_latest_entity_values( + slot_mapping.get("entity")), None) + if entity_value is not None: + return [SlotSet(slot_to_fill, entity_value)] + + elif mapping_type == "from_intent": + return [SlotSet(slot_to_fill, + slot_mapping.get("value"))] + + elif mapping_type == "from_text": + return [SlotSet(slot_to_fill, + tracker.latest_message.get("text"))] + + else: + raise TypeError("slot_mapping type is not supported") return None From e2541bd24d0fac1e3bd52398819618e9e41c5b7c Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 5 Oct 2018 18:21:24 +0200 Subject: [PATCH 065/112] update comment RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index e04d40619..316dcb13e 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -56,7 +56,7 @@ def slot_mapping(self): - an extracted entity - intent: value pairs - a whole message - or a list of all of them, where a first match will be picked""" + or a list of them, where a first match will be picked""" return {slot: self.from_entity(slot) for slot in self.required_slots()} From c6a3010ef15984e3ee35ea57ead369fb4462590e Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 5 Oct 2018 18:23:30 +0200 Subject: [PATCH 066/112] update comment RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 316dcb13e..cb5eaa738 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -103,7 +103,8 @@ def extract(self, tracker.latest_message.get("text"))] else: - raise TypeError("slot_mapping type is not supported") + raise TypeError('Provided slot_mapping["type"] ' + 'is not supported') return None From 6cf6fa748ffce400571f8aa58bcfcd195896e425 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 5 Oct 2018 18:24:20 +0200 Subject: [PATCH 067/112] update comment RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index cb5eaa738..a3fdb4a13 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -80,7 +80,7 @@ def extract(self, for slot_mapping in slot_mappings: if (not isinstance(slot_mapping, dict) or slot_mapping.get("type") is None): - raise ValueError("Provided incompatible slot_mapping") + raise TypeError("Provided incompatible slot_mapping") mapping_intent = slot_mapping.get("intent") intent = tracker.latest_message.get("intent", @@ -103,8 +103,8 @@ def extract(self, tracker.latest_message.get("text"))] else: - raise TypeError('Provided slot_mapping["type"] ' - 'is not supported') + raise ValueError('Provided slot_mapping["type"] ' + 'is not supported') return None From 29996d4a308981f43b1ec227dd4dcf29af3cb369 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Mon, 8 Oct 2018 19:08:05 +0200 Subject: [PATCH 068/112] add ability learn when do not validate RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index a3fdb4a13..a705361c8 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -160,6 +160,19 @@ def _activate_if_required(self, tracker): else: return [Form(self.name())] + @staticmethod + def _predicted_no_validation(tracker): + # type: (Tracker) -> bool + """Check whether previous call to the form was rejected""" + for e in reversed(tracker.events): + if e['event'] == 'action': + if e['name'] == 'action_no_form_validation': + return True + + break + + return False + def _validate_if_required(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] """Return a list of events from `self.validate` @@ -168,7 +181,8 @@ def _validate_if_required(self, dispatcher, tracker, domain): - the form is called after `action_listen` """ if (tracker.active_form == self.name() and - tracker.latest_action_name == 'action_listen'): + tracker.latest_action_name == 'action_listen' and + not self._predicted_no_validation(tracker)): return self.validate(dispatcher, tracker, domain) else: return [] From f58998cca8c61e72a1381337ce104963b6960025 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Tue, 9 Oct 2018 13:13:26 +0200 Subject: [PATCH 069/112] change ValueError to NotImplementedError RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index a705361c8..9f6d5a1fd 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -103,8 +103,9 @@ def extract(self, tracker.latest_message.get("text"))] else: - raise ValueError('Provided slot_mapping["type"] ' - 'is not supported') + raise NotImplementedError( + 'Provided slot_mapping["type"] ' + 'is not supported') return None From fb49e2a0a43776e787353a8655e49e26a03695c4 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Wed, 24 Oct 2018 17:27:20 +0200 Subject: [PATCH 070/112] add NoFormValidation event RasaHQ/roadmap#280 --- rasa_core_sdk/events.py | 8 ++++ rasa_core_sdk/forms.py | 92 ++++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 46 deletions(-) diff --git a/rasa_core_sdk/events.py b/rasa_core_sdk/events.py index b10573603..fa31f1ed2 100644 --- a/rasa_core_sdk/events.py +++ b/rasa_core_sdk/events.py @@ -152,6 +152,14 @@ def Form(name, timestamp=None): } +# noinspection PyPep8Naming +def NoFormValidation(timestamp=None): + return { + "event": "no_form_validation", + "timestamp": timestamp, + } + + # noinspection PyPep8Naming def ActionExecutionRejected(action_name, policy=None, confidence=None, timestamp=None): diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 9f6d5a1fd..01b99ec64 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -31,8 +31,8 @@ def name(self): raise NotImplementedError("A form must implement a name") @staticmethod - def required_slots(): - # type: () -> List[Text] + def required_slots(tracker): + # type: (Tracker) -> List[Text] """A list of required slots that the form has to fill""" raise NotImplementedError("A form must implement required slots " @@ -51,14 +51,14 @@ def from_text(intent=None): return {"type": "from_text", "intent": intent} def slot_mapping(self): - # type: () -> Dict[Text: Union[Text, Dict, List[Text, Dict]]] + # type: () -> Dict[Text: Union[Dict, List[Dict]]] """A dictionary to map required slots to - - an extracted entity + - an extracted entity (default behaviour) - intent: value pairs - a whole message or a list of them, where a first match will be picked""" - return {slot: self.from_entity(slot) for slot in self.required_slots()} + return {} # noinspection PyUnusedLocal def extract(self, @@ -72,40 +72,41 @@ def extract(self, # map requested_slot to entity slot_mappings = self.slot_mapping().get(slot_to_fill) - - if slot_mappings: - if not isinstance(slot_mappings, list): - slot_mappings = [slot_mappings] - - for slot_mapping in slot_mappings: - if (not isinstance(slot_mapping, dict) or - slot_mapping.get("type") is None): - raise TypeError("Provided incompatible slot_mapping") - - mapping_intent = slot_mapping.get("intent") - intent = tracker.latest_message.get("intent", - {}).get("name") - if mapping_intent is None or mapping_intent == intent: - mapping_type = slot_mapping["type"] - - if mapping_type == "from_entity": - entity_value = next(tracker.get_latest_entity_values( - slot_mapping.get("entity")), None) - if entity_value is not None: - return [SlotSet(slot_to_fill, entity_value)] - - elif mapping_type == "from_intent": - return [SlotSet(slot_to_fill, - slot_mapping.get("value"))] - - elif mapping_type == "from_text": - return [SlotSet(slot_to_fill, - tracker.latest_message.get("text"))] - - else: - raise NotImplementedError( - 'Provided slot_mapping["type"] ' - 'is not supported') + if not slot_mappings: + slot_mappings = self.from_entity(slot_to_fill) + + if not isinstance(slot_mappings, list): + slot_mappings = [slot_mappings] + + for slot_mapping in slot_mappings: + if (not isinstance(slot_mapping, dict) or + slot_mapping.get("type") is None): + raise TypeError("Provided incompatible slot_mapping") + + mapping_intent = slot_mapping.get("intent") + intent = tracker.latest_message.get("intent", + {}).get("name") + if mapping_intent is None or mapping_intent == intent: + mapping_type = slot_mapping["type"] + + if mapping_type == "from_entity": + entity_value = next(tracker.get_latest_entity_values( + slot_mapping.get("entity")), None) + if entity_value is not None: + return [SlotSet(slot_to_fill, entity_value)] + + elif mapping_type == "from_intent": + return [SlotSet(slot_to_fill, + slot_mapping.get("value"))] + + elif mapping_type == "from_text": + return [SlotSet(slot_to_fill, + tracker.latest_message.get("text"))] + + else: + raise NotImplementedError( + 'Provided slot_mapping["type"] ' + 'is not supported') return None @@ -136,7 +137,7 @@ def request_next_slot(self, """"Request the next slot and utter template if needed, else return None""" - for slot in self.required_slots(): + for slot in self.required_slots(tracker): if self._should_request_slot(tracker, slot): dispatcher.utter_template("utter_ask_{}".format(slot), tracker) return [SlotSet(REQUESTED_SLOT, slot)] @@ -164,13 +165,12 @@ def _activate_if_required(self, tracker): @staticmethod def _predicted_no_validation(tracker): # type: (Tracker) -> bool - """Check whether previous call to the form was rejected""" + """Check whether validation should be skipped""" for e in reversed(tracker.events): - if e['event'] == 'action': - if e['name'] == 'action_no_form_validation': - return True - - break + if e['event'] == 'no_form_validation': + return True + elif e['event'] == 'action': + return False return False From 941a5e8ea221288b35df26eb0ce8c802406be3c7 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 11:46:29 +0200 Subject: [PATCH 071/112] rename slot_mapping() to slots_mappings() RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 01b99ec64..315f64968 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -50,13 +50,18 @@ def from_intent(intent, value): def from_text(intent=None): return {"type": "from_text", "intent": intent} - def slot_mapping(self): + # noinspection PyMethodMayBeStatic + def slots_mappings(self): # type: () -> Dict[Text: Union[Dict, List[Dict]]] """A dictionary to map required slots to - - an extracted entity (default behaviour) + - an extracted entity - intent: value pairs - a whole message - or a list of them, where a first match will be picked""" + or a list of them, where a first match will be picked + + Empty dict converted to extracted entity + with the same name as a slot + """ return {} @@ -71,33 +76,33 @@ def extract(self, slot_to_fill = tracker.get_slot(REQUESTED_SLOT) # map requested_slot to entity - slot_mappings = self.slot_mapping().get(slot_to_fill) - if not slot_mappings: - slot_mappings = self.from_entity(slot_to_fill) + requested_slot_mappings = self.slots_mappings().get(slot_to_fill) + if not requested_slot_mappings: + requested_slot_mappings = self.from_entity(slot_to_fill) - if not isinstance(slot_mappings, list): - slot_mappings = [slot_mappings] + if not isinstance(requested_slot_mappings, list): + requested_slot_mappings = [requested_slot_mappings] - for slot_mapping in slot_mappings: - if (not isinstance(slot_mapping, dict) or - slot_mapping.get("type") is None): + for requested_slot_mapping in requested_slot_mappings: + if (not isinstance(requested_slot_mapping, dict) or + requested_slot_mapping.get("type") is None): raise TypeError("Provided incompatible slot_mapping") - mapping_intent = slot_mapping.get("intent") + mapping_intent = requested_slot_mapping.get("intent") intent = tracker.latest_message.get("intent", {}).get("name") if mapping_intent is None or mapping_intent == intent: - mapping_type = slot_mapping["type"] + mapping_type = requested_slot_mapping["type"] if mapping_type == "from_entity": entity_value = next(tracker.get_latest_entity_values( - slot_mapping.get("entity")), None) + requested_slot_mapping.get("entity")), None) if entity_value is not None: return [SlotSet(slot_to_fill, entity_value)] elif mapping_type == "from_intent": return [SlotSet(slot_to_fill, - slot_mapping.get("value"))] + requested_slot_mapping.get("value"))] elif mapping_type == "from_text": return [SlotSet(slot_to_fill, From 759ffbca8d3f6a886d3d6e1d43132958480c1385 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 13:23:31 +0200 Subject: [PATCH 072/112] change error type, update comment RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 315f64968..8df4454e7 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -59,8 +59,8 @@ def slots_mappings(self): - a whole message or a list of them, where a first match will be picked - Empty dict converted to extracted entity - with the same name as a slot + Empty dict is converted to a mapping of + the slot to the extracted entity with the same name """ return {} @@ -109,7 +109,7 @@ def extract(self, tracker.latest_message.get("text"))] else: - raise NotImplementedError( + raise ValueError( 'Provided slot_mapping["type"] ' 'is not supported') From 2117a1bd32bec0f295a2cfea519ddb351c049190 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 13:26:52 +0200 Subject: [PATCH 073/112] slots_mappings -> slot_mappings RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 8df4454e7..642446936 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -51,7 +51,7 @@ def from_text(intent=None): return {"type": "from_text", "intent": intent} # noinspection PyMethodMayBeStatic - def slots_mappings(self): + def slot_mappings(self): # type: () -> Dict[Text: Union[Dict, List[Dict]]] """A dictionary to map required slots to - an extracted entity @@ -76,7 +76,7 @@ def extract(self, slot_to_fill = tracker.get_slot(REQUESTED_SLOT) # map requested_slot to entity - requested_slot_mappings = self.slots_mappings().get(slot_to_fill) + requested_slot_mappings = self.slot_mappings().get(slot_to_fill) if not requested_slot_mappings: requested_slot_mappings = self.from_entity(slot_to_fill) From 951b0a05cee36b4e93344885c2ac141950d47517 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 14:33:36 +0200 Subject: [PATCH 074/112] break long line RasaHQ/roadmap#280 --- rasa_core_sdk/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index e9a8238cc..b3260165b 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -33,7 +33,8 @@ def from_dict(cls, state): state.get("latest_action_name")) def __init__(self, sender_id, slots, - latest_message, events, paused, followup_action, active_form, latest_action_name): + latest_message, events, paused, followup_action, + active_form, latest_action_name): """Initialize the tracker.""" # list of previously seen events From 7299fc9f0fa856efbc22e003e49e6f5b7ab90a30 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 15:05:58 +0200 Subject: [PATCH 075/112] add logger statements to forms RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 642446936..2ca3651c1 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -74,6 +74,9 @@ def extract(self, # type: (...) -> Optional[List[Dict]] """"Extract requested slot from a user input else return None""" slot_to_fill = tracker.get_slot(REQUESTED_SLOT) + logger.debug("Trying to extract requested slot '{}' ..." + "".format(slot_to_fill)) + logger.debug("... from user input '{}'".format(tracker.latest_message)) # map requested_slot to entity requested_slot_mappings = self.slot_mappings().get(slot_to_fill) @@ -84,6 +87,8 @@ def extract(self, requested_slot_mappings = [requested_slot_mappings] for requested_slot_mapping in requested_slot_mappings: + logger.debug("Got mapping '{}'".format(requested_slot_mapping)) + if (not isinstance(requested_slot_mapping, dict) or requested_slot_mapping.get("type") is None): raise TypeError("Provided incompatible slot_mapping") @@ -95,24 +100,23 @@ def extract(self, mapping_type = requested_slot_mapping["type"] if mapping_type == "from_entity": - entity_value = next(tracker.get_latest_entity_values( - requested_slot_mapping.get("entity")), None) - if entity_value is not None: - return [SlotSet(slot_to_fill, entity_value)] - + value = next(tracker.get_latest_entity_values( + requested_slot_mapping.get("entity")), None) elif mapping_type == "from_intent": - return [SlotSet(slot_to_fill, - requested_slot_mapping.get("value"))] - + value = requested_slot_mapping.get("value") elif mapping_type == "from_text": - return [SlotSet(slot_to_fill, - tracker.latest_message.get("text"))] - + value = tracker.latest_message.get("text") else: raise ValueError( 'Provided slot_mapping["type"] ' 'is not supported') + if value is not None: + logger.debug("Successfully extracted '{}'" + "".format(value)) + return [SlotSet(slot_to_fill, value)] + + logger.debug("Failed to extract") return None # noinspection PyUnusedLocal @@ -144,9 +148,11 @@ def request_next_slot(self, for slot in self.required_slots(tracker): if self._should_request_slot(tracker, slot): + logger.debug("Request next slot '{}'".format(slot)) dispatcher.utter_template("utter_ask_{}".format(slot), tracker) return [SlotSet(REQUESTED_SLOT, slot)] + logger.debug("No slots left to request") return None def submit(self, dispatcher, tracker, domain): @@ -162,9 +168,15 @@ def _activate_if_required(self, tracker): """Return `Form` event with the name of the form if the form was called for the first time""" + if tracker.active_form is not None: + logger.debug("The form '{}' is active".format(tracker.active_form)) + else: + logger.debug("There is no active form") + if tracker.active_form == self.name(): return [] else: + logger.debug("Activate the form '{}'".format(self.name())) return [Form(self.name())] @staticmethod @@ -173,6 +185,7 @@ def _predicted_no_validation(tracker): """Check whether validation should be skipped""" for e in reversed(tracker.events): if e['event'] == 'no_form_validation': + logger.debug("'NoFormValidation' event is present") return True elif e['event'] == 'action': return False @@ -189,8 +202,10 @@ def _validate_if_required(self, dispatcher, tracker, domain): if (tracker.active_form == self.name() and tracker.latest_action_name == 'action_listen' and not self._predicted_no_validation(tracker)): + logger.debug("Validate user input") return self.validate(dispatcher, tracker, domain) else: + logger.debug("Skip validation") return [] @staticmethod @@ -205,7 +220,7 @@ def _deactivate(): # type: () -> List[Dict] """Return `Form` event with `None` as name to deactivate the form and reset the requested slot""" - + logger.debug("Deactivating the form") return [Form(None), SlotSet(REQUESTED_SLOT, None)] def run(self, dispatcher, tracker, domain): From 247ae3ebeff6ca750ec4447f7eb203ca0591c67c Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 15:45:06 +0200 Subject: [PATCH 076/112] 'extract()' returns the value of theslot and not event RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 2ca3651c1..79e739d7c 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -71,8 +71,10 @@ def extract(self, tracker, # type: Tracker domain # type: Dict[Text, Any] ): - # type: (...) -> Optional[List[Dict]] - """"Extract requested slot from a user input else return None""" + # type: (...) -> Optional[Any] + """Extract the value of requested slot from a user input + else return None + """ slot_to_fill = tracker.get_slot(REQUESTED_SLOT) logger.debug("Trying to extract requested slot '{}' ..." "".format(slot_to_fill)) @@ -114,7 +116,7 @@ def extract(self, if value is not None: logger.debug("Successfully extracted '{}'" "".format(value)) - return [SlotSet(slot_to_fill, value)] + return value logger.debug("Failed to extract") return None @@ -125,10 +127,10 @@ def validate(self, dispatcher, tracker, domain): """"Validate extracted requested slot else raise an error""" slot_to_fill = tracker.get_slot(REQUESTED_SLOT) - events = self.extract(dispatcher, tracker, domain) + extracted_value = self.extract(dispatcher, tracker, domain) - if events is not None: - return events + if extracted_value is not None: + return [SlotSet(slot_to_fill, extracted_value)] else: raise ActionExecutionRejection(self.name(), "Failed to validate slot {0} " From c463a48a1ff53dcafb122eb0ed7df6ec561229a4 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 17:00:09 +0200 Subject: [PATCH 077/112] refactor validate() RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 79e739d7c..73be9af08 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -121,23 +121,26 @@ def extract(self, logger.debug("Failed to extract") return None - # noinspection PyUnusedLocal def validate(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] """"Validate extracted requested slot else raise an error""" slot_to_fill = tracker.get_slot(REQUESTED_SLOT) extracted_value = self.extract(dispatcher, tracker, domain) - - if extracted_value is not None: - return [SlotSet(slot_to_fill, extracted_value)] - else: + if extracted_value is None: + # reject to execute the form action if nothing was extracted, + # it will allow other policies to predict another action raise ActionExecutionRejection(self.name(), "Failed to validate slot {0} " "with action {1}" "".format(slot_to_fill, self.name())) + # add custom validation logic by subclassing this method + + # validation succeed, set requested slot to extracted value + return [SlotSet(slot_to_fill, extracted_value)] + # noinspection PyUnusedLocal def request_next_slot(self, dispatcher, # type: CollectingDispatcher From 0faa382a5aa97fe0d76c8d165e56ebeb4961fa5f Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 17:04:04 +0200 Subject: [PATCH 078/112] change validate description RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 73be9af08..b75c7f921 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -123,7 +123,11 @@ def extract(self, def validate(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] - """"Validate extracted requested slot else raise an error""" + """"Validate extracted value of requested slot else raise an error + + Add custom validation and rejection logic + by subclassing this method + """ slot_to_fill = tracker.get_slot(REQUESTED_SLOT) extracted_value = self.extract(dispatcher, tracker, domain) @@ -136,8 +140,6 @@ def validate(self, dispatcher, tracker, domain): "".format(slot_to_fill, self.name())) - # add custom validation logic by subclassing this method - # validation succeed, set requested slot to extracted value return [SlotSet(slot_to_fill, extracted_value)] From 011c28c83bc7f6f6aba82e6086d649a83fe580f9 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 17:05:38 +0200 Subject: [PATCH 079/112] update comment RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index b75c7f921..59554e1af 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -80,9 +80,10 @@ def extract(self, "".format(slot_to_fill)) logger.debug("... from user input '{}'".format(tracker.latest_message)) - # map requested_slot to entity + # get mapping for requested slot requested_slot_mappings = self.slot_mappings().get(slot_to_fill) if not requested_slot_mappings: + # map requested slot to entity requested_slot_mappings = self.from_entity(slot_to_fill) if not isinstance(requested_slot_mappings, list): From 94d10bb713aedd410282b11635cd09b923630d87 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 17:07:11 +0200 Subject: [PATCH 080/112] update comment RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 59554e1af..7d174f1f3 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -124,7 +124,7 @@ def extract(self, def validate(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] - """"Validate extracted value of requested slot else raise an error + """Validate extracted value of requested slot else raise an error Add custom validation and rejection logic by subclassing this method @@ -151,7 +151,7 @@ def request_next_slot(self, domain # type: Dict[Text, Any] ): # type: (...) -> Optional[List[Dict]] - """"Request the next slot and utter template if needed, + """Request the next slot and utter template if needed, else return None""" for slot in self.required_slots(tracker): From cf754becabe80639f5e697a136c96c06f9cf195b Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 17:16:06 +0200 Subject: [PATCH 081/112] update comment RasaHQ/roadmap#280 --- rasa_core_sdk/__init__.py | 2 ++ rasa_core_sdk/forms.py | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index b3260165b..e8a42f541 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -167,6 +167,8 @@ def __str__(self): class ActionExecutionRejection(Exception): + """Raising this exception will allow other policies + to predict another action""" def __init__(self, action_name, message=None): self.action_name = action_name diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 7d174f1f3..c7dd6811d 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -124,7 +124,8 @@ def extract(self, def validate(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] - """Validate extracted value of requested slot else raise an error + """Validate extracted value of requested slo + else reject to execute the form action Add custom validation and rejection logic by subclassing this method @@ -202,10 +203,11 @@ def _predicted_no_validation(tracker): def _validate_if_required(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] - """Return a list of events from `self.validate` + """Return a list of events from `self.validate(...)` if validation is required: - the form is active - the form is called after `action_listen` + - `NoFormValidation` event is not present """ if (tracker.active_form == self.name() and tracker.latest_action_name == 'action_listen' and From 29a6b2ed5e7d76ca72b74783af308a0b74dd9a58 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 17:21:46 +0200 Subject: [PATCH 082/112] add types and descriptions to from_... helper methods RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index c7dd6811d..660f8ce8e 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -40,14 +40,26 @@ def required_slots(tracker): @staticmethod def from_entity(entity, intent=None): + # type: (Text, Optional[Text]) -> Dict[Text: Any] + """A dictionary to map required slots to + - an extracted entity + """ return {"type": "from_entity", "intent": intent, "entity": entity} @staticmethod def from_intent(intent, value): + # type: (Optional[Text], Any) -> Dict[Text: Any] + """A dictionary to map required slots to + - intent: value pair + """ return {"type": "from_intent", "intent": intent, "value": value} @staticmethod def from_text(intent=None): + # type: (Optional[Text]) -> Dict[Text: Any] + """A dictionary to map required slots to + - a whole message + """ return {"type": "from_text", "intent": intent} # noinspection PyMethodMayBeStatic From f26849768cc03554306b804778897bd44cf509c8 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 17:25:08 +0200 Subject: [PATCH 083/112] update comment RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 660f8ce8e..5f6bc1ac6 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -41,7 +41,7 @@ def required_slots(tracker): @staticmethod def from_entity(entity, intent=None): # type: (Text, Optional[Text]) -> Dict[Text: Any] - """A dictionary to map required slots to + """A dictionary for slot mapping to extract slot value from - an extracted entity """ return {"type": "from_entity", "intent": intent, "entity": entity} @@ -49,7 +49,7 @@ def from_entity(entity, intent=None): @staticmethod def from_intent(intent, value): # type: (Optional[Text], Any) -> Dict[Text: Any] - """A dictionary to map required slots to + """A dictionary for slot mapping to extract slot value from - intent: value pair """ return {"type": "from_intent", "intent": intent, "value": value} @@ -57,7 +57,7 @@ def from_intent(intent, value): @staticmethod def from_text(intent=None): # type: (Optional[Text]) -> Dict[Text: Any] - """A dictionary to map required slots to + """A dictionary for slot mapping to extract slot value from - a whole message """ return {"type": "from_text", "intent": intent} From 47942d4db090fd28713e7d20a13423e879c505ab Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 25 Oct 2018 17:44:52 +0200 Subject: [PATCH 084/112] update comment RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 5f6bc1ac6..0541c9a10 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -106,7 +106,7 @@ def extract(self, if (not isinstance(requested_slot_mapping, dict) or requested_slot_mapping.get("type") is None): - raise TypeError("Provided incompatible slot_mapping") + raise TypeError("Provided incompatible slot mapping") mapping_intent = requested_slot_mapping.get("intent") intent = tracker.latest_message.get("intent", @@ -123,7 +123,7 @@ def extract(self, value = tracker.latest_message.get("text") else: raise ValueError( - 'Provided slot_mapping["type"] ' + 'Provided slot mapping type ' 'is not supported') if value is not None: @@ -136,7 +136,7 @@ def extract(self, def validate(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] - """Validate extracted value of requested slo + """Validate extracted value of requested slot else reject to execute the form action Add custom validation and rejection logic From 8cb9941c8a353697f62d22e66335a76a1b480bd3 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Fri, 26 Oct 2018 17:06:02 +0200 Subject: [PATCH 085/112] active_form is a dict with validate and rejected info RasaHQ/roadmap#280 --- rasa_core_sdk/__init__.py | 2 +- rasa_core_sdk/events.py | 5 +++-- rasa_core_sdk/forms.py | 26 +++++++------------------- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index e8a42f541..96238702d 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -29,7 +29,7 @@ def from_dict(cls, state): state.get("events"), state.get("paused"), state.get("followup_action"), - state.get("active_form"), + state.get("active_form", {}), state.get("latest_action_name")) def __init__(self, sender_id, slots, diff --git a/rasa_core_sdk/events.py b/rasa_core_sdk/events.py index fa31f1ed2..c944cf9bd 100644 --- a/rasa_core_sdk/events.py +++ b/rasa_core_sdk/events.py @@ -153,9 +153,10 @@ def Form(name, timestamp=None): # noinspection PyPep8Naming -def NoFormValidation(timestamp=None): +def FormValidation(validate, timestamp=None): return { - "event": "no_form_validation", + "event": "form_validation", + "validate": validate, "timestamp": timestamp, } diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 0541c9a10..7a973a0d8 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -189,41 +189,29 @@ def _activate_if_required(self, tracker): """Return `Form` event with the name of the form if the form was called for the first time""" - if tracker.active_form is not None: - logger.debug("The form '{}' is active".format(tracker.active_form)) + if tracker.active_form.get('name') is not None: + logger.debug("The form '{}' is active" + "".format(tracker.active_form)) else: logger.debug("There is no active form") - if tracker.active_form == self.name(): + if tracker.active_form.get('name') == self.name(): return [] else: logger.debug("Activate the form '{}'".format(self.name())) return [Form(self.name())] - @staticmethod - def _predicted_no_validation(tracker): - # type: (Tracker) -> bool - """Check whether validation should be skipped""" - for e in reversed(tracker.events): - if e['event'] == 'no_form_validation': - logger.debug("'NoFormValidation' event is present") - return True - elif e['event'] == 'action': - return False - - return False - def _validate_if_required(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] """Return a list of events from `self.validate(...)` if validation is required: - the form is active - the form is called after `action_listen` - - `NoFormValidation` event is not present + - form validation was not cancelled """ - if (tracker.active_form == self.name() and + if (tracker.active_form.get('name') == self.name() and tracker.latest_action_name == 'action_listen' and - not self._predicted_no_validation(tracker)): + tracker.active_form.get('validate')): logger.debug("Validate user input") return self.validate(dispatcher, tracker, domain) else: From 6f8315637e07f00b86edd2a788ba86b955966827 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Tue, 30 Oct 2018 17:19:06 +0100 Subject: [PATCH 086/112] handle slot list type, fixes #24 --- rasa_core_sdk/forms.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 7a973a0d8..218a0bd7c 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -115,8 +115,11 @@ def extract(self, mapping_type = requested_slot_mapping["type"] if mapping_type == "from_entity": - value = next(tracker.get_latest_entity_values( - requested_slot_mapping.get("entity")), None) + # list is used to cover the case of list slot type + value = list(tracker.get_latest_entity_values( + requested_slot_mapping.get("entity"))) + if len(value) == 1: + value = value[0] elif mapping_type == "from_intent": value = requested_slot_mapping.get("value") elif mapping_type == "from_text": @@ -126,7 +129,7 @@ def extract(self, 'Provided slot mapping type ' 'is not supported') - if value is not None: + if value: logger.debug("Successfully extracted '{}'" "".format(value)) return value From b82dd3d98a2bd69fb4e40ee87b17d3a5750e323b Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Tue, 30 Oct 2018 17:22:36 +0100 Subject: [PATCH 087/112] style #24 --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 218a0bd7c..21471e80d 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -117,7 +117,7 @@ def extract(self, if mapping_type == "from_entity": # list is used to cover the case of list slot type value = list(tracker.get_latest_entity_values( - requested_slot_mapping.get("entity"))) + requested_slot_mapping.get("entity"))) if len(value) == 1: value = value[0] elif mapping_type == "from_intent": From 1d3cf268535319a0a572cea8618b435583f26f60 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Wed, 31 Oct 2018 17:41:36 +0100 Subject: [PATCH 088/112] add ability to validate extra slots RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 96 +++++++++++++++++++++++++++++------------- 1 file changed, 67 insertions(+), 29 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 21471e80d..1725ba593 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -78,12 +78,12 @@ def slot_mappings(self): return {} # noinspection PyUnusedLocal - def extract(self, - dispatcher, # type: CollectingDispatcher - tracker, # type: Tracker - domain # type: Dict[Text, Any] - ): - # type: (...) -> Optional[Any] + def extract_requested_slot(self, + dispatcher, # type: CollectingDispatcher + tracker, # type: Tracker + domain # type: Dict[Text, Any] + ): + # type: (...) -> Dict[Text: Any] """Extract the value of requested slot from a user input else return None """ @@ -130,35 +130,73 @@ def extract(self, 'is not supported') if value: - logger.debug("Successfully extracted '{}'" - "".format(value)) - return value + logger.debug("Successfully extracted '{}' " + "for requested slot '{}'" + "".format(value, slot_to_fill)) + return {slot_to_fill: value} - logger.debug("Failed to extract") - return None + logger.debug("Failed to extract requested slot '{}'" + "".format(slot_to_fill)) + return {} + + # noinspection PyUnusedLocal + def extract_other_slots(self, + dispatcher, # type: CollectingDispatcher + tracker, # type: Tracker + domain # type: Dict[Text, Any] + ): + # type: (...) -> Dict[Text: Any] + """Extract the values of the other slots + if they are set by corresponded entities from a user input + else return None + """ + slot_to_fill = tracker.get_slot(REQUESTED_SLOT) + + slot_values = {} + for slot in self.required_slots(tracker): + # look for other slots + if slot != slot_to_fill: + # list is used to cover the case of list slot type + value = list(tracker.get_latest_entity_values(slot)) + if len(value) == 1: + value = value[0] + + if value: + logger.debug("Extracted '{}' " + "for extra slot '{}'" + "".format(value, slot)) + slot_values[slot] = value + + return slot_values def validate(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] """Validate extracted value of requested slot else reject to execute the form action - Add custom validation and rejection logic - by subclassing this method + Subclass this method to add custom validation and rejection logic """ - slot_to_fill = tracker.get_slot(REQUESTED_SLOT) + # extract other slots that were not requested + # but set by corresponding entity + slot_values = self.extract_other_slots(dispatcher, tracker, domain) - extracted_value = self.extract(dispatcher, tracker, domain) - if extracted_value is None: - # reject to execute the form action if nothing was extracted, - # it will allow other policies to predict another action - raise ActionExecutionRejection(self.name(), - "Failed to validate slot {0} " - "with action {1}" - "".format(slot_to_fill, - self.name())) - - # validation succeed, set requested slot to extracted value - return [SlotSet(slot_to_fill, extracted_value)] + # extract requested slot + slot_to_fill = tracker.get_slot(REQUESTED_SLOT) + if slot_to_fill: + slot_values.update(self.extract_requested_slot(dispatcher, + tracker, domain)) + if not slot_values: + # reject to execute the form action + # if some slot was requested but nothing was extracted + # it will allow other policies to predict another action + raise ActionExecutionRejection(self.name(), + "Failed to validate slot {0} " + "with action {1}" + "".format(slot_to_fill, + self.name())) + + # validation succeed, set slots to extracted values + return [SlotSet(slot, value) for slot, value in slot_values.items()] # noinspection PyUnusedLocal def request_next_slot(self, @@ -212,9 +250,8 @@ def _validate_if_required(self, dispatcher, tracker, domain): - the form is called after `action_listen` - form validation was not cancelled """ - if (tracker.active_form.get('name') == self.name() and - tracker.latest_action_name == 'action_listen' and - tracker.active_form.get('validate')): + if (tracker.latest_action_name == 'action_listen' and + tracker.active_form.get('validate', True)): logger.debug("Validate user input") return self.validate(dispatcher, tracker, domain) else: @@ -251,6 +288,7 @@ def run(self, dispatcher, tracker, domain): # validate user input events.extend(self._validate_if_required(dispatcher, tracker, domain)) + # create temp tracker with populated slots from `validate` method temp_tracker = tracker.copy() for e in events: if e['event'] == 'slot': From 625a56c583c70054b6f46732678dbeb6e2da0a49 Mon Sep 17 00:00:00 2001 From: Akela Drissner-Schmid <32450038+akelad@users.noreply.github.com> Date: Thu, 1 Nov 2018 13:28:14 +0100 Subject: [PATCH 089/112] Update rasa_core_sdk/__init__.py Co-Authored-By: Ghostvv --- rasa_core_sdk/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/__init__.py b/rasa_core_sdk/__init__.py index bfc81486f..a1dcda928 100644 --- a/rasa_core_sdk/__init__.py +++ b/rasa_core_sdk/__init__.py @@ -190,7 +190,7 @@ class ActionExecutionRejection(Exception): def __init__(self, action_name, message=None): self.action_name = action_name self.message = (message or - "Custom action '{}' rejected to run" + "Custom action '{}' rejected execution of" "".format(action_name)) def __str__(self): From 28aeedb0802c9ee41db2d09eaeddb273039917ba Mon Sep 17 00:00:00 2001 From: Akela Drissner-Schmid <32450038+akelad@users.noreply.github.com> Date: Thu, 1 Nov 2018 13:28:39 +0100 Subject: [PATCH 090/112] change method description Co-Authored-By: Ghostvv --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 1725ba593..5e6f541a8 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -69,7 +69,7 @@ def slot_mappings(self): - an extracted entity - intent: value pairs - a whole message - or a list of them, where a first match will be picked + or a list of them, where the first match will be picked Empty dict is converted to a mapping of the slot to the extracted entity with the same name From 40a4f3a1511a386aa04d771c5c036ec0c6f598d5 Mon Sep 17 00:00:00 2001 From: Akela Drissner-Schmid <32450038+akelad@users.noreply.github.com> Date: Thu, 1 Nov 2018 13:28:53 +0100 Subject: [PATCH 091/112] change method description Co-Authored-By: Ghostvv --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 5e6f541a8..e74974b37 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -147,7 +147,7 @@ def extract_other_slots(self, ): # type: (...) -> Dict[Text: Any] """Extract the values of the other slots - if they are set by corresponded entities from a user input + if they are set by corresponding entities from the user input else return None """ slot_to_fill = tracker.get_slot(REQUESTED_SLOT) From 38bca19fc32cddc0694960246894bdf4580769f3 Mon Sep 17 00:00:00 2001 From: Akela Drissner-Schmid <32450038+akelad@users.noreply.github.com> Date: Thu, 1 Nov 2018 13:29:04 +0100 Subject: [PATCH 092/112] change method description Co-Authored-By: Ghostvv --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index e74974b37..c868c5c3e 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -172,7 +172,7 @@ def extract_other_slots(self, def validate(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] """Validate extracted value of requested slot - else reject to execute the form action + else reject execution of the form action Subclass this method to add custom validation and rejection logic """ From 06f6fafa9e6192cca04b6f8d210ffac1453d7ee5 Mon Sep 17 00:00:00 2001 From: Akela Drissner-Schmid <32450038+akelad@users.noreply.github.com> Date: Thu, 1 Nov 2018 13:29:17 +0100 Subject: [PATCH 093/112] fix logger statement Co-Authored-By: Ghostvv --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index c868c5c3e..d078cb73b 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -239,7 +239,7 @@ def _activate_if_required(self, tracker): if tracker.active_form.get('name') == self.name(): return [] else: - logger.debug("Activate the form '{}'".format(self.name())) + logger.debug("Activated the form '{}'".format(self.name())) return [Form(self.name())] def _validate_if_required(self, dispatcher, tracker, domain): From 9f2e1004fd98749dab0eff9f4a0cf89f79081182 Mon Sep 17 00:00:00 2001 From: Akela Drissner-Schmid <32450038+akelad@users.noreply.github.com> Date: Thu, 1 Nov 2018 13:29:35 +0100 Subject: [PATCH 094/112] fix logger statement Co-Authored-By: Ghostvv --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index d078cb73b..7e3fcb881 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -252,7 +252,7 @@ def _validate_if_required(self, dispatcher, tracker, domain): """ if (tracker.latest_action_name == 'action_listen' and tracker.active_form.get('validate', True)): - logger.debug("Validate user input") + logger.debug("Validating user input") return self.validate(dispatcher, tracker, domain) else: logger.debug("Skip validation") From 449bf9cea05baa38f4c13669fa2da31bc97b588f Mon Sep 17 00:00:00 2001 From: Akela Drissner-Schmid <32450038+akelad@users.noreply.github.com> Date: Thu, 1 Nov 2018 13:29:40 +0100 Subject: [PATCH 095/112] Update rasa_core_sdk/forms.py Co-Authored-By: Ghostvv --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 7e3fcb881..4a3a4e88b 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -255,7 +255,7 @@ def _validate_if_required(self, dispatcher, tracker, domain): logger.debug("Validating user input") return self.validate(dispatcher, tracker, domain) else: - logger.debug("Skip validation") + logger.debug("Skipping validation") return [] @staticmethod From 75e555b0a37bd4501c432c36c449a67510fa3daf Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 1 Nov 2018 13:35:00 +0100 Subject: [PATCH 096/112] add form name to deactivation logger RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 4a3a4e88b..449831b6f 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -265,12 +265,11 @@ def _should_request_slot(tracker, slot_name): return tracker.get_slot(slot_name) is None - @staticmethod - def _deactivate(): + def _deactivate(self): # type: () -> List[Dict] """Return `Form` event with `None` as name to deactivate the form and reset the requested slot""" - logger.debug("Deactivating the form") + logger.debug("Deactivating the form '{}'".format(self.name())) return [Form(None), SlotSet(REQUESTED_SLOT, None)] def run(self, dispatcher, tracker, domain): From 4e61dcd5cb77161d1a116a226c51c8ca4d4781d7 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 1 Nov 2018 13:42:32 +0100 Subject: [PATCH 097/112] add explanation to required slots RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 449831b6f..d41561554 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -33,7 +33,11 @@ def name(self): @staticmethod def required_slots(tracker): # type: (Tracker) -> List[Text] - """A list of required slots that the form has to fill""" + """A list of required slots that the form has to fill + + Use `tracker` to request different list of slots + depending on the state of the dialogue + """ raise NotImplementedError("A form must implement required slots " "that it has to fill") From 86642f049160e731b2b1ff2046b1c549d2937c84 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Mon, 5 Nov 2018 18:04:01 +0100 Subject: [PATCH 098/112] add forms tests RasaHQ/roadmap#278 --- tests/test_forms.py | 329 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 tests/test_forms.py diff --git a/tests/test_forms.py b/tests/test_forms.py new file mode 100644 index 000000000..939b60199 --- /dev/null +++ b/tests/test_forms.py @@ -0,0 +1,329 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import pytest +from rasa_core_sdk.forms import FormAction +from rasa_core_sdk import Tracker, ActionExecutionRejection +from rasa_core_sdk.executor import CollectingDispatcher +from rasa_core_sdk.events import SlotSet, Form + + +def test_extract_requested_slot_default(): + """Test default extraction of a slot value from entity with the same name + """ + form = FormAction() + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'entities': [{'entity': 'some_slot', + 'value': 'some_value'}]}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + assert slot_values == {'some_slot': 'some_value'} + + +def test_extract_requested_slot_from_entity_no_intent(): + """Test extraction of a slot value from entity with the different name + and any intent + """ + # noinspection PyAbstractClass + class CustomFormAction(FormAction): + def slot_mappings(self): + return {"some_slot": self.from_entity(entity="some_entity")} + form = CustomFormAction() + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'entities': [{'entity': 'some_entity', + 'value': 'some_value'}]}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + assert slot_values == {'some_slot': 'some_value'} + + +def test_extract_requested_slot_from_entity_with_intent(): + """Test extraction of a slot value from entity with the different name + and certain intent + """ + # noinspection PyAbstractClass + class CustomFormAction(FormAction): + def slot_mappings(self): + return {"some_slot": self.from_entity(entity="some_entity", + intent="some_intent")} + form = CustomFormAction() + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'intent': {'name': 'some_intent', 'confidence': 1.0}, + 'entities': [{'entity': 'some_entity', + 'value': 'some_value'}]}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + # check that the value was extracted for correct intent + assert slot_values == {'some_slot': 'some_value'} + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'intent': {'name': 'some_other_intent', + 'confidence': 1.0}, + 'entities': [{'entity': 'some_entity', + 'value': 'some_value'}]}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + # check that the value was not extracted for incorrect intent + assert slot_values == {} + + +def test_extract_requested_slot_from_intent(): + """Test extraction of a slot value from certain intent + """ + # noinspection PyAbstractClass + class CustomFormAction(FormAction): + def slot_mappings(self): + return {"some_slot": self.from_intent(intent="some_intent", + value="some_value")} + form = CustomFormAction() + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'intent': {'name': 'some_intent', + 'confidence': 1.0}}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + # check that the value was extracted for correct intent + assert slot_values == {'some_slot': 'some_value'} + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'intent': {'name': 'some_other_intent', + 'confidence': 1.0}}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + # check that the value was not extracted for incorrect intent + assert slot_values == {} + + +def test_extract_requested_slot_from_text_no_intent(): + """Test extraction of a slot value from text with any intent + """ + # noinspection PyAbstractClass + class CustomFormAction(FormAction): + def slot_mappings(self): + return {"some_slot": self.from_text()} + form = CustomFormAction() + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'text': 'some_text'}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + assert slot_values == {'some_slot': 'some_text'} + + +def test_extract_requested_slot_from_text_with_intent(): + """Test extraction of a slot value from text with certain intent + """ + # noinspection PyAbstractClass + class CustomFormAction(FormAction): + def slot_mappings(self): + return {"some_slot": self.from_text(intent='some_intent')} + form = CustomFormAction() + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'text': 'some_text', + 'intent': {'name': 'some_intent', + 'confidence': 1.0}}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + # check that the value was extracted for correct intent + assert slot_values == {'some_slot': 'some_text'} + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'text': 'some_text', + 'intent': {'name': 'some_other_intent', + 'confidence': 1.0}}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + # check that the value was not extracted for incorrect intent + assert slot_values == {} + + +def test_extract_other_slots(): + """Test extraction of other not requested slots values + from entities with the same names + """ + # noinspection PyAbstractClass + class CustomFormAction(FormAction): + @staticmethod + def required_slots(_tracker): + return ["some_slot", "some_other_slot"] + form = CustomFormAction() + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'entities': [{'entity': 'some_slot', + 'value': 'some_value'}]}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_other_slots(CollectingDispatcher(), + tracker, {}) + # check that the value was not extracted for requested slot + assert slot_values == {} + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'entities': [{'entity': 'some_other_slot', + 'value': 'some_other_value'}]}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_other_slots(CollectingDispatcher(), + tracker, {}) + # check that the value was extracted for non requested slot + assert slot_values == {'some_other_slot': 'some_other_value'} + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'entities': [{'entity': 'some_slot', + 'value': 'some_value'}, + {'entity': 'some_other_slot', + 'value': 'some_other_value'}]}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_other_slots(CollectingDispatcher(), + tracker, {}) + # check that the value was extracted only for non requested slot + assert slot_values == {'some_other_slot': 'some_other_value'} + + +def test_validate(): + # noinspection PyAbstractClass + class CustomFormAction(FormAction): + def name(self): + return "some_form" + + @staticmethod + def required_slots(_tracker): + return ["some_slot", "some_other_slot"] + form = CustomFormAction() + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'entities': [{'entity': 'some_slot', + 'value': 'some_value'}, + {'entity': 'some_other_slot', + 'value': 'some_other_value'}]}, + [], False, None, {}, 'action_listen') + + events = form.validate(CollectingDispatcher(), tracker, {}) + # check that validation succeed + assert events == [SlotSet('some_other_slot', 'some_other_value'), + SlotSet('some_slot', 'some_value')] + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'entities': [{'entity': 'some_other_slot', + 'value': 'some_other_value'}]}, + [], False, None, {}, 'action_listen') + + events = form.validate(CollectingDispatcher(), tracker, {}) + # check that validation succeed because other slot was extracted + assert events == [SlotSet('some_other_slot', 'some_other_value')] + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'entities': []}, + [], False, None, {}, 'action_listen') + with pytest.raises(Exception) as execinfo: + form.validate(CollectingDispatcher(), tracker, {}) + + # check that validation failed gracefully + assert execinfo.type == ActionExecutionRejection + assert ("Failed to validate slot some_slot " + "with action some_form" in str(execinfo.value)) + + +def test_activate_if_required(): + # noinspection PyAbstractClass + class CustomFormAction(FormAction): + def name(self): + return "some_form" + form = CustomFormAction() + + tracker = Tracker('default', {}, {}, + [], False, None, {}, 'action_listen') + + events = form._activate_if_required(tracker) + # check that the form was activated + assert events == [Form('some_form')] + + tracker = Tracker('default', {}, {}, [], False, None, + {'name': 'some_form', + 'validate': True, 'rejected': False}, + 'action_listen') + + events = form._activate_if_required(tracker) + # check that the form was not activated again + assert events == [] + + +def test_validate_if_required(): + # noinspection PyAbstractClass + class CustomFormAction(FormAction): + def name(self): + return "some_form" + + @staticmethod + def required_slots(_tracker): + return ["some_slot", "some_other_slot"] + form = CustomFormAction() + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'entities': [{'entity': 'some_slot', + 'value': 'some_value'}, + {'entity': 'some_other_slot', + 'value': 'some_other_value'}]}, + [], False, None, + {'name': 'some_form', + 'validate': True, 'rejected': False}, + 'action_listen') + + events = form._validate_if_required(CollectingDispatcher(), tracker, {}) + # check that validation was performed + assert events == [SlotSet('some_other_slot', 'some_other_value'), + SlotSet('some_slot', 'some_value')] + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'entities': [{'entity': 'some_slot', + 'value': 'some_value'}, + {'entity': 'some_other_slot', + 'value': 'some_other_value'}]}, + [], False, None, + {'name': 'some_form', + 'validate': False, 'rejected': False}, + 'action_listen') + + events = form._validate_if_required(CollectingDispatcher(), tracker, {}) + # check that validation was skipped because 'validate': False + assert events == [] + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'entities': [{'entity': 'some_slot', + 'value': 'some_value'}, + {'entity': 'some_other_slot', + 'value': 'some_other_value'}]}, + [], False, None, + {'name': 'some_form', + 'validate': True, 'rejected': False}, + 'some_form') + + events = form._validate_if_required(CollectingDispatcher(), tracker, {}) + # check that validation was skipped + # because previous action is not action_listen + assert events == [] From 1ed67f0165c9fd6a08ad2fd5d7092b9145d1cd1d Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Mon, 5 Nov 2018 18:28:01 +0100 Subject: [PATCH 099/112] assert lists up to a permutation RasaHQ/roadmap#278 --- tests/test_forms.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test_forms.py b/tests/test_forms.py index 939b60199..adf2e736b 100644 --- a/tests/test_forms.py +++ b/tests/test_forms.py @@ -225,8 +225,10 @@ def required_slots(_tracker): events = form.validate(CollectingDispatcher(), tracker, {}) # check that validation succeed - assert events == [SlotSet('some_other_slot', 'some_other_value'), - SlotSet('some_slot', 'some_value')] + assert (events == [SlotSet('some_other_slot', 'some_other_value'), + SlotSet('some_slot', 'some_value')] or + events == [SlotSet('some_slot', 'some_value'), + SlotSet('some_other_slot', 'some_other_value')]) tracker = Tracker('default', {'requested_slot': 'some_slot'}, {'entities': [{'entity': 'some_other_slot', @@ -296,8 +298,10 @@ def required_slots(_tracker): events = form._validate_if_required(CollectingDispatcher(), tracker, {}) # check that validation was performed - assert events == [SlotSet('some_other_slot', 'some_other_value'), - SlotSet('some_slot', 'some_value')] + assert (events == [SlotSet('some_other_slot', 'some_other_value'), + SlotSet('some_slot', 'some_value')] or + events == [SlotSet('some_slot', 'some_value'), + SlotSet('some_other_slot', 'some_other_value')]) tracker = Tracker('default', {'requested_slot': 'some_slot'}, {'entities': [{'entity': 'some_slot', From e9e062a7b190c80eeb9a19ec032e66fed7fffbbb Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Tue, 6 Nov 2018 10:28:55 +0100 Subject: [PATCH 100/112] extract other slots only if they are set to be from_entity RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 66 +++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index d41561554..14b31952d 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -81,6 +81,25 @@ def slot_mappings(self): return {} + def get_mappings_for_slot(self, slot_to_fill): + # type: (Text) -> List[Dict[Text: Any]] + """Get mappings for requested slot""" + requested_slot_mappings = self.slot_mappings().get(slot_to_fill) + if not requested_slot_mappings: + # convert empty dict to map requested slot to entity by default + requested_slot_mappings = self.from_entity(slot_to_fill) + + if not isinstance(requested_slot_mappings, list): + requested_slot_mappings = [requested_slot_mappings] + + # check provided slot mappings + for requested_slot_mapping in requested_slot_mappings: + if (not isinstance(requested_slot_mapping, dict) or + requested_slot_mapping.get("type") is None): + raise TypeError("Provided incompatible slot mapping") + + return requested_slot_mappings + # noinspection PyUnusedLocal def extract_requested_slot(self, dispatcher, # type: CollectingDispatcher @@ -97,21 +116,11 @@ def extract_requested_slot(self, logger.debug("... from user input '{}'".format(tracker.latest_message)) # get mapping for requested slot - requested_slot_mappings = self.slot_mappings().get(slot_to_fill) - if not requested_slot_mappings: - # map requested slot to entity - requested_slot_mappings = self.from_entity(slot_to_fill) - - if not isinstance(requested_slot_mappings, list): - requested_slot_mappings = [requested_slot_mappings] + requested_slot_mappings = self.get_mappings_for_slot(slot_to_fill) for requested_slot_mapping in requested_slot_mappings: logger.debug("Got mapping '{}'".format(requested_slot_mapping)) - if (not isinstance(requested_slot_mapping, dict) or - requested_slot_mapping.get("type") is None): - raise TypeError("Provided incompatible slot mapping") - mapping_intent = requested_slot_mapping.get("intent") intent = tracker.latest_message.get("intent", {}).get("name") @@ -161,15 +170,32 @@ def extract_other_slots(self, # look for other slots if slot != slot_to_fill: # list is used to cover the case of list slot type - value = list(tracker.get_latest_entity_values(slot)) - if len(value) == 1: - value = value[0] - - if value: - logger.debug("Extracted '{}' " - "for extra slot '{}'" - "".format(value, slot)) - slot_values[slot] = value + other_slot_mappings = self.get_mappings_for_slot(slot) + + for other_slot_mapping in other_slot_mappings: + intent = tracker.latest_message.get("intent", + {}).get("name") + # check whether the slot should be filled + # by entity with the same name + should_fill_slot = (other_slot_mapping == + self.from_entity(entity=slot) + or + other_slot_mapping == + self.from_entity(entity=slot, + intent=intent)) + if should_fill_slot: + # list is used to cover the case of list slot type + value = list(tracker.get_latest_entity_values(slot)) + if len(value) == 1: + value = value[0] + + if value: + logger.debug("Extracted '{}' " + "for extra slot '{}'" + "".format(value, slot)) + slot_values[slot] = value + # this slot is done, check next + break return slot_values From 8d7f492932e0eabef1ee5b0c1f968f6c64b5feb4 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Tue, 6 Nov 2018 10:39:02 +0100 Subject: [PATCH 101/112] add other slots extraction conditioned on intent tests RasaHQ/roadmap#278 --- tests/test_forms.py | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/tests/test_forms.py b/tests/test_forms.py index adf2e736b..3a411ba3a 100644 --- a/tests/test_forms.py +++ b/tests/test_forms.py @@ -161,7 +161,7 @@ def slot_mappings(self): assert slot_values == {} -def test_extract_other_slots(): +def test_extract_other_slots_no_intent(): """Test extraction of other not requested slots values from entities with the same names """ @@ -205,6 +205,49 @@ def required_slots(_tracker): assert slot_values == {'some_other_slot': 'some_other_value'} +def test_extract_other_slots_with_intent(): + """Test extraction of other not requested slots values + from entities with the same names + """ + + # noinspection PyAbstractClass + class CustomFormAction(FormAction): + @staticmethod + def required_slots(_tracker): + return ["some_slot", "some_other_slot"] + + def slot_mappings(self): + return {"some_other_slot": + self.from_entity(entity="some_other_slot", + intent="some_intent")} + + form = CustomFormAction() + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'intent': {'name': 'some_other_intent', + 'confidence': 1.0}, + 'entities': [{'entity': 'some_other_slot', + 'value': 'some_other_value'}]}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_other_slots(CollectingDispatcher(), + tracker, {}) + # check that the value was extracted for non requested slot + assert slot_values == {} + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'intent': {'name': 'some_intent', + 'confidence': 1.0}, + 'entities': [{'entity': 'some_other_slot', + 'value': 'some_other_value'}]}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_other_slots(CollectingDispatcher(), + tracker, {}) + # check that the value was extracted only for non requested slot + assert slot_values == {'some_other_slot': 'some_other_value'} + + def test_validate(): # noinspection PyAbstractClass class CustomFormAction(FormAction): From 8fa7735bd64b2d0566e1525e7e62908f66401715 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Tue, 6 Nov 2018 11:58:43 +0100 Subject: [PATCH 102/112] PEP RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 14b31952d..5e59320fe 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -177,12 +177,12 @@ def extract_other_slots(self, {}).get("name") # check whether the slot should be filled # by entity with the same name - should_fill_slot = (other_slot_mapping == - self.from_entity(entity=slot) - or - other_slot_mapping == - self.from_entity(entity=slot, - intent=intent)) + should_fill_slot = ( + other_slot_mapping == + self.from_entity(entity=slot) or + other_slot_mapping == + self.from_entity(entity=slot, intent=intent) + ) if should_fill_slot: # list is used to cover the case of list slot type value = list(tracker.get_latest_entity_values(slot)) From 2fcda122cad82c1153495a2553424ed95b2557ab Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Wed, 7 Nov 2018 09:53:15 +0100 Subject: [PATCH 103/112] take also a list of intent or not_intent RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 113 +++++++++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 33 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 5e59320fe..12abb0350 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -6,7 +6,7 @@ import logging import typing -from typing import Dict, Text, Any, List, Union, Optional +from typing import Dict, Text, Any, List, Union, Optional, Tuple from rasa_core_sdk import Action, ActionExecutionRejection from rasa_core_sdk.events import SlotSet, Form @@ -42,29 +42,46 @@ def required_slots(tracker): raise NotImplementedError("A form must implement required slots " "that it has to fill") - @staticmethod - def from_entity(entity, intent=None): - # type: (Text, Optional[Text]) -> Dict[Text: Any] + def from_entity(self, + entity, # type: Text + intent=None, # type: Optional[Union[Text, List[Text]]] + not_intent=None # type: Optional[Union[Text, List[Text]]] + ): + # type: (...) -> Dict[Text: Any] """A dictionary for slot mapping to extract slot value from - an extracted entity """ - return {"type": "from_entity", "intent": intent, "entity": entity} + intent, not_intent = self._list_intents(intent, not_intent) - @staticmethod - def from_intent(intent, value): - # type: (Optional[Text], Any) -> Dict[Text: Any] + return {"type": "from_entity", "entity": entity, + "intent": intent, "not_intent": not_intent} + + def from_intent(self, + value, # type: Any + intent=None, # type: Optional[Union[Text, List[Text]]] + not_intent=None # type: Optional[Union[Text, List[Text]]] + ): + # type: (...) -> Dict[Text: Any] """A dictionary for slot mapping to extract slot value from - intent: value pair """ - return {"type": "from_intent", "intent": intent, "value": value} + intent, not_intent = self._list_intents(intent, not_intent) - @staticmethod - def from_text(intent=None): - # type: (Optional[Text]) -> Dict[Text: Any] + return {"type": "from_intent", "value": value, + "intent": intent, "not_intent": not_intent} + + def from_text(self, + intent=None, # type: Optional[Union[Text, List[Text]]] + not_intent=None # type: Optional[Union[Text, List[Text]]] + ): + # type: (...) -> Dict[Text: Any] """A dictionary for slot mapping to extract slot value from - a whole message """ - return {"type": "from_text", "intent": intent} + intent, not_intent = self._list_intents(intent, not_intent) + + return {"type": "from_text", + "intent": intent, "not_intent": not_intent} # noinspection PyMethodMayBeStatic def slot_mappings(self): @@ -83,15 +100,13 @@ def slot_mappings(self): def get_mappings_for_slot(self, slot_to_fill): # type: (Text) -> List[Dict[Text: Any]] - """Get mappings for requested slot""" - requested_slot_mappings = self.slot_mappings().get(slot_to_fill) - if not requested_slot_mappings: - # convert empty dict to map requested slot to entity by default - requested_slot_mappings = self.from_entity(slot_to_fill) - - if not isinstance(requested_slot_mappings, list): - requested_slot_mappings = [requested_slot_mappings] - + """Get mappings for requested slot, + else map requested slot to an entity with the same name + """ + requested_slot_mappings = self._to_list( + self.slot_mappings().get(slot_to_fill, + self.from_entity(slot_to_fill)) + ) # check provided slot mappings for requested_slot_mapping in requested_slot_mappings: if (not isinstance(requested_slot_mapping, dict) or @@ -100,6 +115,15 @@ def get_mappings_for_slot(self, slot_to_fill): return requested_slot_mappings + @staticmethod + def intent_is_desired(requested_slot_mapping, tracker): + mapping_intents = requested_slot_mapping.get("intent", []) + mapping_not_intents = requested_slot_mapping.get("not_intent", []) + intent = tracker.latest_message.get("intent", + {}).get("name") + return ((not mapping_intents and intent not in mapping_not_intents) or + intent in mapping_intents) + # noinspection PyUnusedLocal def extract_requested_slot(self, dispatcher, # type: CollectingDispatcher @@ -113,7 +137,6 @@ def extract_requested_slot(self, slot_to_fill = tracker.get_slot(REQUESTED_SLOT) logger.debug("Trying to extract requested slot '{}' ..." "".format(slot_to_fill)) - logger.debug("... from user input '{}'".format(tracker.latest_message)) # get mapping for requested slot requested_slot_mappings = self.get_mappings_for_slot(slot_to_fill) @@ -121,10 +144,7 @@ def extract_requested_slot(self, for requested_slot_mapping in requested_slot_mappings: logger.debug("Got mapping '{}'".format(requested_slot_mapping)) - mapping_intent = requested_slot_mapping.get("intent") - intent = tracker.latest_message.get("intent", - {}).get("name") - if mapping_intent is None or mapping_intent == intent: + if self.intent_is_desired(requested_slot_mapping, tracker): mapping_type = requested_slot_mapping["type"] if mapping_type == "from_entity": @@ -178,10 +198,10 @@ def extract_other_slots(self, # check whether the slot should be filled # by entity with the same name should_fill_slot = ( - other_slot_mapping == - self.from_entity(entity=slot) or - other_slot_mapping == - self.from_entity(entity=slot, intent=intent) + other_slot_mapping["type"] == "from_entity" and + other_slot_mapping.get("entity") == slot and + self.intent_is_desired(other_slot_mapping, + tracker) ) if should_fill_slot: # list is used to cover the case of list slot type @@ -254,7 +274,33 @@ def submit(self, dispatcher, tracker, domain): raise NotImplementedError("A form must implement a submit method") - # run helpers + # helpers + @staticmethod + def _to_list(x): + # type: (Optional[Any]) -> List[Any] + """Convert object to a list if it is not a list, + None converted to empty list + """ + if x is None: + x = [] + elif not isinstance(x, list): + x = [x] + + return x + + def _list_intents( + self, + intent=None, # type: Optional[Union[Text, List[Text]]] + not_intent=None # type: Optional[Union[Text, List[Text]]] + ): + # type: (...) -> Tuple[List[Text], List[Text]] + """Check provided intent and not_intent""" + if intent and not_intent: + raise ValueError("Providing both intent '{}' and not_intent '{}' " + "is not supported".format(intent, not_intent)) + + return self._to_list(intent), self._to_list(not_intent) + def _activate_if_required(self, tracker): # type: (Tracker) -> List[Dict] """Return `Form` event with the name of the form @@ -282,7 +328,8 @@ def _validate_if_required(self, dispatcher, tracker, domain): """ if (tracker.latest_action_name == 'action_listen' and tracker.active_form.get('validate', True)): - logger.debug("Validating user input") + logger.debug("Validating user input '{}'" + "".format(tracker.latest_message)) return self.validate(dispatcher, tracker, domain) else: logger.debug("Skipping validation") From 4ea33e7d4e87461f76779b30819697c71fd671e0 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Wed, 7 Nov 2018 11:15:20 +0100 Subject: [PATCH 104/112] add not_intent tests RasaHQ/roadmap#278 --- tests/test_forms.py | 98 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/tests/test_forms.py b/tests/test_forms.py index 3a411ba3a..4b2d316de 100644 --- a/tests/test_forms.py +++ b/tests/test_forms.py @@ -80,6 +80,41 @@ def slot_mappings(self): assert slot_values == {} +def test_extract_requested_slot_from_entity_with_not_intent(): + """Test extraction of a slot value from entity with the different name + and certain intent + """ + # noinspection PyAbstractClass + class CustomFormAction(FormAction): + def slot_mappings(self): + return {"some_slot": self.from_entity(entity="some_entity", + not_intent="some_intent")} + form = CustomFormAction() + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'intent': {'name': 'some_intent', 'confidence': 1.0}, + 'entities': [{'entity': 'some_entity', + 'value': 'some_value'}]}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + # check that the value was extracted for correct intent + assert slot_values == {} + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'intent': {'name': 'some_other_intent', + 'confidence': 1.0}, + 'entities': [{'entity': 'some_entity', + 'value': 'some_value'}]}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + # check that the value was not extracted for incorrect intent + assert slot_values == {'some_slot': 'some_value'} + + def test_extract_requested_slot_from_intent(): """Test extraction of a slot value from certain intent """ @@ -111,6 +146,37 @@ def slot_mappings(self): assert slot_values == {} +def test_extract_requested_slot_from_not_intent(): + """Test extraction of a slot value from certain intent + """ + # noinspection PyAbstractClass + class CustomFormAction(FormAction): + def slot_mappings(self): + return {"some_slot": self.from_intent(not_intent="some_intent", + value="some_value")} + form = CustomFormAction() + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'intent': {'name': 'some_intent', + 'confidence': 1.0}}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + # check that the value was extracted for correct intent + assert slot_values == {} + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'intent': {'name': 'some_other_intent', + 'confidence': 1.0}}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + # check that the value was not extracted for incorrect intent + assert slot_values == {'some_slot': 'some_value'} + + def test_extract_requested_slot_from_text_no_intent(): """Test extraction of a slot value from text with any intent """ @@ -161,6 +227,38 @@ def slot_mappings(self): assert slot_values == {} +def test_extract_requested_slot_from_text_with_not_intent(): + """Test extraction of a slot value from text with certain intent + """ + # noinspection PyAbstractClass + class CustomFormAction(FormAction): + def slot_mappings(self): + return {"some_slot": self.from_text(not_intent='some_intent')} + form = CustomFormAction() + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'text': 'some_text', + 'intent': {'name': 'some_intent', + 'confidence': 1.0}}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + # check that the value was extracted for correct intent + assert slot_values == {} + + tracker = Tracker('default', {'requested_slot': 'some_slot'}, + {'text': 'some_text', + 'intent': {'name': 'some_other_intent', + 'confidence': 1.0}}, + [], False, None, {}, 'action_listen') + + slot_values = form.extract_requested_slot(CollectingDispatcher(), + tracker, {}) + # check that the value was not extracted for incorrect intent + assert slot_values == {'some_slot': 'some_text'} + + def test_extract_other_slots_no_intent(): """Test extraction of other not requested slots values from entities with the same names From 44345e2fb246540302bcd9807852e30cacf71da3 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Wed, 7 Nov 2018 11:33:23 +0100 Subject: [PATCH 105/112] add not_intent description RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 12abb0350..6f1f762df 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -50,6 +50,10 @@ def from_entity(self, # type: (...) -> Dict[Text: Any] """A dictionary for slot mapping to extract slot value from - an extracted entity + - conditioned on + - intent if it is not None + - not_intent if it is not None, + meaning user intent should be different """ intent, not_intent = self._list_intents(intent, not_intent) @@ -64,6 +68,10 @@ def from_intent(self, # type: (...) -> Dict[Text: Any] """A dictionary for slot mapping to extract slot value from - intent: value pair + - conditioned on + - intent if it is not None + - not_intent if it is not None, + meaning user intent should be different """ intent, not_intent = self._list_intents(intent, not_intent) @@ -77,6 +85,10 @@ def from_text(self, # type: (...) -> Dict[Text: Any] """A dictionary for slot mapping to extract slot value from - a whole message + - conditioned on + - intent if it is not None + - not_intent if it is not None, + meaning user intent should be different """ intent, not_intent = self._list_intents(intent, not_intent) From 40a02710d559d8d925741b9a310b8b8dfc697f06 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 8 Nov 2018 15:59:03 +0100 Subject: [PATCH 106/112] fix bug with value=False RasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 6f1f762df..41e74469e 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -163,7 +163,9 @@ def extract_requested_slot(self, # list is used to cover the case of list slot type value = list(tracker.get_latest_entity_values( requested_slot_mapping.get("entity"))) - if len(value) == 1: + if len(value) == 0: + value = None + elif len(value) == 1: value = value[0] elif mapping_type == "from_intent": value = requested_slot_mapping.get("value") @@ -174,7 +176,7 @@ def extract_requested_slot(self, 'Provided slot mapping type ' 'is not supported') - if value: + if value is not None: logger.debug("Successfully extracted '{}' " "for requested slot '{}'" "".format(value, slot_to_fill)) From 198550653a4a5b8a207efcecbdf3951225a9669f Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 8 Nov 2018 16:22:37 +0100 Subject: [PATCH 107/112] add changes to CHANGELOG RasaHQ/roadmap#280 --- CHANGELOG.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a4fe65df1..0b64f5375 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,10 +14,13 @@ This project adheres to `Semantic Versioning`_ starting with version 0.11.0. Added ----- - added Dockerfile for rasa_core_sdk +- add ``active_form`` and ``latest_action_name`` properties to ``Tracker`` Changed ------- +- rework forms + Removed ------- From 006ee5dee4eaa4a9b3af92a5f9f9a2a271c9b860 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 8 Nov 2018 16:52:53 +0100 Subject: [PATCH 108/112] expand changelog RasaHQ/roadmap#279 --- CHANGELOG.rst | 11 ++++- rasa_core_sdk/forms.py | 94 +++++++++++++++++++++--------------------- 2 files changed, 57 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0b64f5375..5f390c949 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,15 +15,24 @@ Added ----- - added Dockerfile for rasa_core_sdk - add ``active_form`` and ``latest_action_name`` properties to ``Tracker`` +- add ``FormAction.slot_mapping()`` method to specify the mapping between user input and requested slot in the form +- add helper methods ``FormAction.from_entity(...)``, ``FormAction.from_intent(...)`` and ``FormAction.from_text(...)`` +- add ``FormAction.validate(...)`` method to validate user input Changed ------- -- rework forms +- ``FormAction`` class was completely refactored +- ``required_fields()`` is changed to ``required_slots(tracker)`` +- moved ``FormAction.get_other_slots(...)`` functionality to ``FormAction.extract_other_slots(...)`` +- moved ``FormAction.get_requested_slot(...)`` functionality to ``FormAction.extract_requested_slot(...)`` +- logic of requesting next slot can be customized in ``FormAction.request_next_slot(...)`` method Removed ------- +- ``FormField`` class and its subclasses + Fixed ----- diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 41e74469e..05f2d99c4 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -136,6 +136,53 @@ def intent_is_desired(requested_slot_mapping, tracker): return ((not mapping_intents and intent not in mapping_not_intents) or intent in mapping_intents) + # noinspection PyUnusedLocal + def extract_other_slots(self, + dispatcher, # type: CollectingDispatcher + tracker, # type: Tracker + domain # type: Dict[Text, Any] + ): + # type: (...) -> Dict[Text: Any] + """Extract the values of the other slots + if they are set by corresponding entities from the user input + else return None + """ + slot_to_fill = tracker.get_slot(REQUESTED_SLOT) + + slot_values = {} + for slot in self.required_slots(tracker): + # look for other slots + if slot != slot_to_fill: + # list is used to cover the case of list slot type + other_slot_mappings = self.get_mappings_for_slot(slot) + + for other_slot_mapping in other_slot_mappings: + intent = tracker.latest_message.get("intent", + {}).get("name") + # check whether the slot should be filled + # by entity with the same name + should_fill_slot = ( + other_slot_mapping["type"] == "from_entity" and + other_slot_mapping.get("entity") == slot and + self.intent_is_desired(other_slot_mapping, + tracker) + ) + if should_fill_slot: + # list is used to cover the case of list slot type + value = list(tracker.get_latest_entity_values(slot)) + if len(value) == 1: + value = value[0] + + if value: + logger.debug("Extracted '{}' " + "for extra slot '{}'" + "".format(value, slot)) + slot_values[slot] = value + # this slot is done, check next + break + + return slot_values + # noinspection PyUnusedLocal def extract_requested_slot(self, dispatcher, # type: CollectingDispatcher @@ -186,53 +233,6 @@ def extract_requested_slot(self, "".format(slot_to_fill)) return {} - # noinspection PyUnusedLocal - def extract_other_slots(self, - dispatcher, # type: CollectingDispatcher - tracker, # type: Tracker - domain # type: Dict[Text, Any] - ): - # type: (...) -> Dict[Text: Any] - """Extract the values of the other slots - if they are set by corresponding entities from the user input - else return None - """ - slot_to_fill = tracker.get_slot(REQUESTED_SLOT) - - slot_values = {} - for slot in self.required_slots(tracker): - # look for other slots - if slot != slot_to_fill: - # list is used to cover the case of list slot type - other_slot_mappings = self.get_mappings_for_slot(slot) - - for other_slot_mapping in other_slot_mappings: - intent = tracker.latest_message.get("intent", - {}).get("name") - # check whether the slot should be filled - # by entity with the same name - should_fill_slot = ( - other_slot_mapping["type"] == "from_entity" and - other_slot_mapping.get("entity") == slot and - self.intent_is_desired(other_slot_mapping, - tracker) - ) - if should_fill_slot: - # list is used to cover the case of list slot type - value = list(tracker.get_latest_entity_values(slot)) - if len(value) == 1: - value = value[0] - - if value: - logger.debug("Extracted '{}' " - "for extra slot '{}'" - "".format(value, slot)) - slot_values[slot] = value - # this slot is done, check next - break - - return slot_values - def validate(self, dispatcher, tracker, domain): # type: (CollectingDispatcher, Tracker, Dict[Text, Any]) -> List[Dict] """Validate extracted value of requested slot From cdac233425ad9aeb873bdd8d29bb8b2c460edc0f Mon Sep 17 00:00:00 2001 From: Akela Drissner-Schmid <32450038+akelad@users.noreply.github.com> Date: Thu, 8 Nov 2018 17:32:28 +0100 Subject: [PATCH 109/112] Update rasa_core_sdk/forms.py Co-Authored-By: Ghostvv --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 05f2d99c4..638f00fda 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -53,7 +53,7 @@ def from_entity(self, - conditioned on - intent if it is not None - not_intent if it is not None, - meaning user intent should be different + meaning user intent should not be this intent """ intent, not_intent = self._list_intents(intent, not_intent) From e42d3be0c796b9c1b4f3f0f3fa2feac398279faf Mon Sep 17 00:00:00 2001 From: Akela Drissner-Schmid <32450038+akelad@users.noreply.github.com> Date: Thu, 8 Nov 2018 17:32:37 +0100 Subject: [PATCH 110/112] Update rasa_core_sdk/forms.py Co-Authored-By: Ghostvv --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 638f00fda..808844566 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -71,7 +71,7 @@ def from_intent(self, - conditioned on - intent if it is not None - not_intent if it is not None, - meaning user intent should be different + meaning user intent should not be this intent """ intent, not_intent = self._list_intents(intent, not_intent) From 3998de145a25dc1f90663d86ad1fe124974f82be Mon Sep 17 00:00:00 2001 From: Akela Drissner-Schmid <32450038+akelad@users.noreply.github.com> Date: Thu, 8 Nov 2018 17:32:42 +0100 Subject: [PATCH 111/112] Update rasa_core_sdk/forms.py Co-Authored-By: Ghostvv --- rasa_core_sdk/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 808844566..72e6b94c7 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -88,7 +88,7 @@ def from_text(self, - conditioned on - intent if it is not None - not_intent if it is not None, - meaning user intent should be different + meaning user intent should not be this intent """ intent, not_intent = self._list_intents(intent, not_intent) From 9d77b9e55601d817ae178ad8a620d9bf83b67f6c Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 8 Nov 2018 17:40:41 +0100 Subject: [PATCH 112/112] add method description and typesRasaHQ/roadmap#280 --- rasa_core_sdk/forms.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rasa_core_sdk/forms.py b/rasa_core_sdk/forms.py index 72e6b94c7..6bf8ae3c5 100644 --- a/rasa_core_sdk/forms.py +++ b/rasa_core_sdk/forms.py @@ -129,6 +129,10 @@ def get_mappings_for_slot(self, slot_to_fill): @staticmethod def intent_is_desired(requested_slot_mapping, tracker): + # type: (Dict[Text: Any], Tracker) -> bool + """Check whether user intent matches + intent conditions in requested_slot_mapping + """ mapping_intents = requested_slot_mapping.get("intent", []) mapping_not_intents = requested_slot_mapping.get("not_intent", []) intent = tracker.latest_message.get("intent",