From 33b7560e378fc32216e7b4c52299de97805b06a0 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Tue, 10 Nov 2020 18:12:37 +0100 Subject: [PATCH 01/25] fix conversation starts with slots initial value --- rasa/core/policies/rule_policy.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rasa/core/policies/rule_policy.py b/rasa/core/policies/rule_policy.py index d9a9421ab0ae..49ef29b5885a 100644 --- a/rasa/core/policies/rule_policy.py +++ b/rasa/core/policies/rule_policy.py @@ -664,12 +664,15 @@ def _is_rule_applicable( return bool( # rule is shorter than current turn index turn_index >= len(reversed_rule_states) - # current rule and state turns are empty - or (not reversed_rule_states[turn_index] and not conversation_state) + # current rule and state turns are conversation starters + or ( + not reversed_rule_states[turn_index].get(PREVIOUS_ACTION) + and not conversation_state.get(PREVIOUS_ACTION) + ) # check that current rule turn features are present in current state turn or ( - reversed_rule_states[turn_index] - and conversation_state + reversed_rule_states[turn_index].get(PREVIOUS_ACTION) + and conversation_state.get(PREVIOUS_ACTION) and self._does_rule_match_state( reversed_rule_states[turn_index], conversation_state ) From b8b4fa647b438f275b6ed914795836413bf6a070 Mon Sep 17 00:00:00 2001 From: samsucik Date: Tue, 17 Nov 2020 09:51:41 +0000 Subject: [PATCH 02/25] Write tests for #7235 (conversation start rules not working with initial slot values). --- tests/core/policies/test_rule_policy.py | 142 +++++++++++++++++++++++- 1 file changed, 140 insertions(+), 2 deletions(-) diff --git a/tests/core/policies/test_rule_policy.py b/tests/core/policies/test_rule_policy.py index 32c08deda006..056deffa7e8b 100644 --- a/tests/core/policies/test_rule_policy.py +++ b/tests/core/policies/test_rule_policy.py @@ -92,6 +92,138 @@ def _form_activation_rule( ) +def test_potential_contradiction_resolved_by_conversation_start(): + """ + Two rules that contradict each other except that one of them applies only at conversation + start -> ensure that this isn't flagged as a contradiction. + """ + utter_anti_greet_action = "utter_anti_greet" + domain = Domain.from_yaml( + f""" +intents: +- {GREET_INTENT_NAME} +actions: +- {UTTER_GREET_ACTION} +- {utter_anti_greet_action} + """ + ) + policy = RulePolicy() + greet_rule_at_conversation_start = TrackerWithCachedStates.from_events( + "greet rule at conversation start", + domain=domain, + slots=domain.slots, + evts=[ + ActionExecuted(ACTION_LISTEN_NAME), + UserUttered(intent={"name": GREET_INTENT_NAME}), + ActionExecuted(UTTER_GREET_ACTION), + ], + ) + greet_rule_at_conversation_start.is_rule_tracker = True + + anti_greet_rule = TrackerWithCachedStates.from_events( + "anti greet rule", + domain=domain, + slots=domain.slots, + evts=[ + ActionExecuted(RULE_SNIPPET_ACTION_NAME), + ActionExecuted(ACTION_LISTEN_NAME), + UserUttered(intent={"name": GREET_INTENT_NAME}), + ActionExecuted(utter_anti_greet_action), + ], + ) + anti_greet_rule.is_rule_tracker = True + + policy.train( + [greet_rule_at_conversation_start, anti_greet_rule], domain, RegexInterpreter() + ) + + +def test_potential_contradiction_resolved_by_conversation_start_when_slot_initial_value(): + """ + Two rules that contradict each other except that one of them applies only at conversation + start -> ensure that this isn't flagged as a contradiction. Specifically, this checks that + the conversation-start-checking logic doesn't depend on: + 1) initial rule tracker states being empty as these can be non-empty due to initial slot values + 1.1) whether or not the initial slot value is made explicit in the initial state of the + conversation tracker + """ + utter_anti_greet_action = "utter_anti_greet" + some_slot = "slot1" + some_slot_initial_value = "slot1value" + domain = Domain.from_yaml( + f""" +intents: +- {GREET_INTENT_NAME} +actions: +- {UTTER_GREET_ACTION} +- {utter_anti_greet_action} +slots: + {some_slot}: + type: text + initial_value: {some_slot_initial_value} + """ + ) + policy = RulePolicy() + greet_rule_at_conversation_start = TrackerWithCachedStates.from_events( + "greet rule at conversation start", + domain=domain, + slots=domain.slots, + evts=[ + ActionExecuted(ACTION_LISTEN_NAME), + UserUttered(intent={"name": GREET_INTENT_NAME}), + ActionExecuted(UTTER_GREET_ACTION), + ], + ) + greet_rule_at_conversation_start.is_rule_tracker = True + + anti_greet_rule = TrackerWithCachedStates.from_events( + "anti greet rule", + domain=domain, + slots=domain.slots, + evts=[ + ActionExecuted(RULE_SNIPPET_ACTION_NAME), + ActionExecuted(ACTION_LISTEN_NAME), + UserUttered(intent={"name": GREET_INTENT_NAME}), + ActionExecuted(utter_anti_greet_action), + ], + ) + anti_greet_rule.is_rule_tracker = True + + policy.train( + [greet_rule_at_conversation_start, anti_greet_rule], domain, RegexInterpreter() + ) + + conversation_events = [ + SlotSet(some_slot, some_slot_initial_value), + ActionExecuted(ACTION_LISTEN_NAME), + UserUttered(intent={"name": GREET_INTENT_NAME}), + ] + action_probabilities_1 = policy.predict_action_probabilities( + DialogueStateTracker.from_events( + "test conversation", evts=conversation_events, slots=domain.slots + ), + domain, + RegexInterpreter(), + ) + assert_predicted_action(action_probabilities_1, domain, UTTER_GREET_ACTION) + + conversation_events_with_initial_slot_explicit = [ + SlotSet(some_slot, some_slot_initial_value), + ActionExecuted(ACTION_LISTEN_NAME), + UserUttered(intent={"name": GREET_INTENT_NAME}), + ] + action_probabilities_2 = policy.predict_action_probabilities( + DialogueStateTracker.from_events( + "test conversation with initial slot value explicitly set", + evts=conversation_events_with_initial_slot_explicit, + slots=domain.slots, + ), + domain, + RegexInterpreter(), + ) + assert_predicted_action(action_probabilities_2, domain, UTTER_GREET_ACTION) + + def test_restrict_multiple_user_inputs_in_rules(): domain = Domain.from_yaml( f""" @@ -168,7 +300,10 @@ def test_incomplete_rules_due_to_slots(): policy.train([complete_rule, incomplete_rule], domain, RegexInterpreter()) assert all( name in execinfo.value.message - for name in {some_action, incomplete_rule.sender_id,} + for name in { + some_action, + incomplete_rule.sender_id, + } ) fixed_incomplete_rule = TrackerWithCachedStates.from_events( @@ -283,7 +418,10 @@ def test_incomplete_rules_due_to_loops(): policy.train([complete_rule, incomplete_rule], domain, RegexInterpreter()) assert all( name in execinfo.value.message - for name in {some_form, incomplete_rule.sender_id,} + for name in { + some_form, + incomplete_rule.sender_id, + } ) fixed_incomplete_rule = TrackerWithCachedStates.from_events( From 89eb526110a3e5eb824ed0cf4477617d71c792eb Mon Sep 17 00:00:00 2001 From: samsucik Date: Tue, 17 Nov 2020 14:06:04 +0000 Subject: [PATCH 03/25] Include review comments. --- tests/core/policies/test_rule_policy.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/core/policies/test_rule_policy.py b/tests/core/policies/test_rule_policy.py index 056deffa7e8b..6e51e0596781 100644 --- a/tests/core/policies/test_rule_policy.py +++ b/tests/core/policies/test_rule_policy.py @@ -117,8 +117,8 @@ def test_potential_contradiction_resolved_by_conversation_start(): UserUttered(intent={"name": GREET_INTENT_NAME}), ActionExecuted(UTTER_GREET_ACTION), ], + is_rule_tracker=True, ) - greet_rule_at_conversation_start.is_rule_tracker = True anti_greet_rule = TrackerWithCachedStates.from_events( "anti greet rule", @@ -130,9 +130,11 @@ def test_potential_contradiction_resolved_by_conversation_start(): UserUttered(intent={"name": GREET_INTENT_NAME}), ActionExecuted(utter_anti_greet_action), ], + is_rule_tracker=True, ) - anti_greet_rule.is_rule_tracker = True + # Contradicting rules abort training, hence policy training here needs to succeed + # since there aren't contradicting rules in this case. policy.train( [greet_rule_at_conversation_start, anti_greet_rule], domain, RegexInterpreter() ) @@ -173,8 +175,8 @@ def test_potential_contradiction_resolved_by_conversation_start_when_slot_initia UserUttered(intent={"name": GREET_INTENT_NAME}), ActionExecuted(UTTER_GREET_ACTION), ], + is_rule_tracker=True, ) - greet_rule_at_conversation_start.is_rule_tracker = True anti_greet_rule = TrackerWithCachedStates.from_events( "anti greet rule", @@ -186,15 +188,16 @@ def test_potential_contradiction_resolved_by_conversation_start_when_slot_initia UserUttered(intent={"name": GREET_INTENT_NAME}), ActionExecuted(utter_anti_greet_action), ], + is_rule_tracker=True, ) - anti_greet_rule.is_rule_tracker = True + # Policy training needs to succeed to confirm that no contradictions have been detected policy.train( [greet_rule_at_conversation_start, anti_greet_rule], domain, RegexInterpreter() ) + # Check that the correct rule is applied when predicting next action in a story. conversation_events = [ - SlotSet(some_slot, some_slot_initial_value), ActionExecuted(ACTION_LISTEN_NAME), UserUttered(intent={"name": GREET_INTENT_NAME}), ] @@ -207,6 +210,8 @@ def test_potential_contradiction_resolved_by_conversation_start_when_slot_initia ) assert_predicted_action(action_probabilities_1, domain, UTTER_GREET_ACTION) + # A similar check, but this time the first state of the tracker will have the + # slot (which has an initial value set in the domain) explicitly set. conversation_events_with_initial_slot_explicit = [ SlotSet(some_slot, some_slot_initial_value), ActionExecuted(ACTION_LISTEN_NAME), From 0c27b802624cb1ab828a3c428e55b9b49c8ec559 Mon Sep 17 00:00:00 2001 From: samsucik Date: Wed, 18 Nov 2020 14:14:44 +0000 Subject: [PATCH 04/25] Fix the 'incomplete rule' error being thrown whenever initial_value is set for some slot + reformat code. --- rasa/core/policies/rule_policy.py | 22 ++++++++++++---------- tests/core/policies/test_rule_policy.py | 10 ++-------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/rasa/core/policies/rule_policy.py b/rasa/core/policies/rule_policy.py index 49ef29b5885a..5ad31a39c7bd 100644 --- a/rasa/core/policies/rule_policy.py +++ b/rasa/core/policies/rule_policy.py @@ -286,11 +286,11 @@ def _check_slots_fingerprint( ) -> Set[Text]: expected_slots = set(fingerprint.get(SLOTS, {})) current_slots = set(state.get(SLOTS, {}).keys()) - if expected_slots == current_slots: - # all expected slots are satisfied + if expected_slots.issubset(current_slots): + # all expected slots are found in the currently set slots return set() - return expected_slots + return expected_slots.difference(current_slots) @staticmethod def _check_active_loops_fingerprint( @@ -309,16 +309,16 @@ def _check_active_loops_fingerprint( @staticmethod def _error_messages_from_fingerprints( action_name: Text, - fingerprint_slots: Set[Text], + missing_fingerprint_slots: Set[Text], fingerprint_active_loops: Set[Text], rule_name: Text, ) -> List[Text]: error_messages = [] - if action_name and fingerprint_slots: + if action_name and missing_fingerprint_slots: error_messages.append( - f"- the action '{action_name}' in rule '{rule_name}' does not set all " - f"the slots, that it sets in other rules: " - f"'{', '.join(fingerprint_slots)}'. Please update the rule with " + f"- the action '{action_name}' in rule '{rule_name}' does not set some " + f"of the slots that it sets in other rules. Slots not set in rule '{rule_name}': " + f"'{', '.join(missing_fingerprint_slots)}'. Please update the rule with " f"an appropriate slot or if it is the last action " f"add 'wait_for_user_input: false' after this action." ) @@ -375,14 +375,16 @@ def _check_for_incomplete_rules( # for a previous action if current action is rule snippet action continue - expected_slots = self._check_slots_fingerprint(fingerprint, state) + missing_expected_slots = self._check_slots_fingerprint( + fingerprint, state + ) expected_active_loops = self._check_active_loops_fingerprint( fingerprint, state ) error_messages.extend( self._error_messages_from_fingerprints( previous_action_name, - expected_slots, + missing_expected_slots, expected_active_loops, tracker.sender_id, ) diff --git a/tests/core/policies/test_rule_policy.py b/tests/core/policies/test_rule_policy.py index 6e51e0596781..a344538ec116 100644 --- a/tests/core/policies/test_rule_policy.py +++ b/tests/core/policies/test_rule_policy.py @@ -305,10 +305,7 @@ def test_incomplete_rules_due_to_slots(): policy.train([complete_rule, incomplete_rule], domain, RegexInterpreter()) assert all( name in execinfo.value.message - for name in { - some_action, - incomplete_rule.sender_id, - } + for name in {some_action, incomplete_rule.sender_id,} ) fixed_incomplete_rule = TrackerWithCachedStates.from_events( @@ -423,10 +420,7 @@ def test_incomplete_rules_due_to_loops(): policy.train([complete_rule, incomplete_rule], domain, RegexInterpreter()) assert all( name in execinfo.value.message - for name in { - some_form, - incomplete_rule.sender_id, - } + for name in {some_form, incomplete_rule.sender_id,} ) fixed_incomplete_rule = TrackerWithCachedStates.from_events( From 613c15be9df37b86b2d7a1b239966f84e0b33884 Mon Sep 17 00:00:00 2001 From: samsucik Date: Wed, 18 Nov 2020 15:41:16 +0000 Subject: [PATCH 05/25] Add a test to check that slots set before a rule starts don't trigger incomplete rule errors. --- rasa/core/policies/rule_policy.py | 5 +- tests/core/policies/test_rule_policy.py | 109 ++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/rasa/core/policies/rule_policy.py b/rasa/core/policies/rule_policy.py index 5ad31a39c7bd..3b3a7787d7ba 100644 --- a/rasa/core/policies/rule_policy.py +++ b/rasa/core/policies/rule_policy.py @@ -286,10 +286,7 @@ def _check_slots_fingerprint( ) -> Set[Text]: expected_slots = set(fingerprint.get(SLOTS, {})) current_slots = set(state.get(SLOTS, {}).keys()) - if expected_slots.issubset(current_slots): - # all expected slots are found in the currently set slots - return set() - + # report all slots that are expected but aren't set in current slots return expected_slots.difference(current_slots) @staticmethod diff --git a/tests/core/policies/test_rule_policy.py b/tests/core/policies/test_rule_policy.py index a344538ec116..caa55249f8ed 100644 --- a/tests/core/policies/test_rule_policy.py +++ b/tests/core/policies/test_rule_policy.py @@ -140,6 +140,54 @@ def test_potential_contradiction_resolved_by_conversation_start(): ) +def test_potential_contradiction_resolved_by_conversation_start(): + """ + Two rules that contradict each other except that one of them applies only at conversation + start -> ensure that this isn't flagged as a contradiction. + """ + utter_anti_greet_action = "utter_anti_greet" + domain = Domain.from_yaml( + f""" +intents: +- {GREET_INTENT_NAME} +actions: +- {UTTER_GREET_ACTION} +- {utter_anti_greet_action} + """ + ) + policy = RulePolicy() + greet_rule_at_conversation_start = TrackerWithCachedStates.from_events( + "greet rule at conversation start", + domain=domain, + slots=domain.slots, + evts=[ + ActionExecuted(ACTION_LISTEN_NAME), + UserUttered(intent={"name": GREET_INTENT_NAME}), + ActionExecuted(UTTER_GREET_ACTION), + ], + is_rule_tracker=True, + ) + + anti_greet_rule = TrackerWithCachedStates.from_events( + "anti greet rule", + domain=domain, + slots=domain.slots, + evts=[ + ActionExecuted(RULE_SNIPPET_ACTION_NAME), + ActionExecuted(ACTION_LISTEN_NAME), + UserUttered(intent={"name": GREET_INTENT_NAME}), + ActionExecuted(utter_anti_greet_action), + ], + is_rule_tracker=True, + ) + + # Contradicting rules abort training, hence policy training here needs to succeed + # since there aren't contradicting rules in this case. + policy.train( + [greet_rule_at_conversation_start, anti_greet_rule], domain, RegexInterpreter() + ) + + def test_potential_contradiction_resolved_by_conversation_start_when_slot_initial_value(): """ Two rules that contradict each other except that one of them applies only at conversation @@ -377,6 +425,67 @@ def test_no_incomplete_rules_due_to_slots_after_listen(): ) +def test_no_incomplete_rules_due_to_additional_slots_set(): + """ + Check that rules aren't automatically flagged as incomplete just because an action + doesn't set all the slots that are set in the same context in a different rule. + There may be slots that were set by other preceding actions (or by using initial_value + for a slot), and a rule shouldn't be marked as incomplete if some of those other slots + aren't set by the action in the rule. + """ + some_action = "some_action" + some_slot = "some_slot" + some_slot_value = "value1" + some_other_slot = "some_other_slot" + some_other_slot_value = "value2" + domain = Domain.from_yaml( + f""" +intents: +- {GREET_INTENT_NAME} +actions: +- {some_action} +slots: + {some_slot}: + type: text + {some_other_slot}: + type: text + """ + ) + policy = RulePolicy() + simple_rule = TrackerWithCachedStates.from_events( + "simple rule with an action that sets 1 slot", + domain=domain, + slots=domain.slots, + evts=[ + ActionExecuted(RULE_SNIPPET_ACTION_NAME), + ActionExecuted(ACTION_LISTEN_NAME), + UserUttered(intent={"name": GREET_INTENT_NAME}), + ActionExecuted(some_action), + SlotSet(some_slot, some_slot_value), + ActionExecuted(ACTION_LISTEN_NAME), + ], + is_rule_tracker=True, + ) + simple_rule_with_slot_set = TrackerWithCachedStates.from_events( + "simple rule with an additional slot set before it starts", + domain=domain, + slots=domain.slots, + evts=[ + SlotSet(some_other_slot, some_other_slot_value), + ActionExecuted(RULE_SNIPPET_ACTION_NAME), + ActionExecuted(ACTION_LISTEN_NAME), + UserUttered(intent={"name": GREET_INTENT_NAME}), + ActionExecuted(some_action), + SlotSet(some_slot, some_slot_value), + ActionExecuted(ACTION_LISTEN_NAME), + ], + is_rule_tracker=True, + ) + + # this should finish without raising any errors about incomplete rules + policy.train([simple_rule, simple_rule_with_slot_set], domain, RegexInterpreter()) + + def test_incomplete_rules_due_to_loops(): some_form = "some_form" domain = Domain.from_yaml( From 6f621bf93a331bc72c5a515478f00a8ebc3d7fec Mon Sep 17 00:00:00 2001 From: tczekajlo Date: Thu, 19 Nov 2020 10:03:17 +0100 Subject: [PATCH 06/25] Remove the set-env command --- .../workflows/ci-model-regression-on-schedule.yml | 6 +++--- .github/workflows/ci-model-regression.yml | 12 ++++++------ .github/workflows/continous-integration.yml | 10 +++++----- .github/workflows/documentation.yml | 2 +- .github/workflows/vulnerability-scan.yml | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci-model-regression-on-schedule.yml b/.github/workflows/ci-model-regression-on-schedule.yml index badb8ee13cbe..7198d1ebc323 100644 --- a/.github/workflows/ci-model-regression-on-schedule.yml +++ b/.github/workflows/ci-model-regression-on-schedule.yml @@ -112,8 +112,8 @@ jobs: test -d dataset/$DATASET || (echo "::warning::The ${{ matrix.dataset }} dataset doesn't exist. Skipping the job." \ && echo "::set-output name=is_config_exists::false" && exit 0) - echo "::set-env name=DATASET::${DATASET}" - echo "::set-env name=CONFIG::${CONFIG}" + echo "DATASET=${DATASET}" >> $GITHUB_ENV + echo "CONFIG=${CONFIG}" >> $GITHUB_ENV - name: Set up Python 3.8 🐍 uses: actions/setup-python@v2 @@ -124,7 +124,7 @@ jobs: - name: Read Poetry Version 🔢 if: steps.set_dataset_config_vars.outputs.is_dataset_exists == 'true' && steps.set_dataset_config_vars.outputs.is_config_exists == 'true' run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 diff --git a/.github/workflows/ci-model-regression.yml b/.github/workflows/ci-model-regression.yml index 22744d1c3f02..9adf320c6e8e 100644 --- a/.github/workflows/ci-model-regression.yml +++ b/.github/workflows/ci-model-regression.yml @@ -215,8 +215,8 @@ jobs: test -d dataset/$DATASET || (echo "::warning::The ${{ matrix.dataset }} dataset doesn't exist. Skipping the job." \ && echo "::set-output name=is_config_exists::false" && exit 0) - echo "::set-env name=DATASET::${DATASET}" - echo "::set-env name=CONFIG::${CONFIG}" + echo "DATASET=${DATASET}" >> $GITHUB_ENV + echo "CONFIG=${CONFIG}" >> $GITHUB_ENV - name: Set up Python 3.8 🐍 uses: actions/setup-python@v2 @@ -227,7 +227,7 @@ jobs: - name: Read Poetry Version 🔢 if: steps.set_dataset_config_vars.outputs.is_dataset_exists == 'true' && steps.set_dataset_config_vars.outputs.is_config_exists == 'true' run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 @@ -360,8 +360,8 @@ jobs: test -d dataset/$DATASET || (echo "::warning::The ${{ matrix.dataset }} dataset doesn't exist. Skipping the job." \ && echo "::set-output name=is_config_exists::false" && exit 0) - echo "::set-env name=DATASET::${DATASET}" - echo "::set-env name=CONFIG::${CONFIG}" + echo "DATASET=${DATASET}" >> $GITHUB_ENV + echo "CONFIG=${CONFIG}" >> $GITHUB_ENV - name: Set up Python 3.8 🐍 uses: actions/setup-python@v2 @@ -372,7 +372,7 @@ jobs: - name: Read Poetry Version 🔢 if: steps.set_dataset_config_vars.outputs.is_dataset_exists == 'true' && steps.set_dataset_config_vars.outputs.is_config_exists == 'true' run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 diff --git a/.github/workflows/continous-integration.yml b/.github/workflows/continous-integration.yml index d4a69f18cac3..2750e7eb39bc 100644 --- a/.github/workflows/continous-integration.yml +++ b/.github/workflows/continous-integration.yml @@ -69,7 +69,7 @@ jobs: - name: Read Poetry Version 🔢 run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 @@ -145,7 +145,7 @@ jobs: - name: Read Poetry Version 🔢 run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 @@ -288,7 +288,7 @@ jobs: - name: Read Poetry Version 🔢 run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 @@ -346,9 +346,9 @@ jobs: shell: bash run: | if [ -z "${{ secrets.RASABOT_AUTOMERGE_GITHUB_TOKEN }}" ]; then - echo ::set-env name=MERGE_TOKEN::${{ secrets.GITHUB_TOKEN }} + echo "MERGE_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV else - echo ::set-env name=MERGE_TOKEN::${{ secrets.RASABOT_AUTOMERGE_GITHUB_TOKEN }} + echo "MERGE_TOKEN=${{ secrets.RASABOT_AUTOMERGE_GITHUB_TOKEN }}" >> $GITHUB_ENV fi - uses: rasahq/merge-pal-action@master with: diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 495376ada13e..71871e39f6c7 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -38,7 +38,7 @@ jobs: - name: Read Poetry Version 🔢 run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 diff --git a/.github/workflows/vulnerability-scan.yml b/.github/workflows/vulnerability-scan.yml index 83fec17ad037..7a2944073dca 100644 --- a/.github/workflows/vulnerability-scan.yml +++ b/.github/workflows/vulnerability-scan.yml @@ -32,7 +32,7 @@ jobs: IMAGE_NAME=rasa/rasa:latest-scanned docker build -f $DOCKERFILE -t $IMAGE_NAME . - echo "::set-env name=IMAGE_WITH_POETRY_LOCK::$IMAGE_NAME" + echo "IMAGE_WITH_POETRY_LOCK=$IMAGE_NAME" >> $GITHUB_ENV - name: Scan image 🕵️‍♀️🕵️‍♂️ uses: wochinge/gitrivy@6bf026b From b7bb76b2621face18bf666c7fe47dc2dddb0df13 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 19 Nov 2020 15:03:36 +0100 Subject: [PATCH 07/25] fix core featurizers when core was trained separately --- .../featurizers/single_state_featurizer.py | 30 +++++++++++++++++-- rasa/core/featurizers/tracker_featurizers.py | 2 +- tests/core/test_featurizer.py | 23 +++++++++++++- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/rasa/core/featurizers/single_state_featurizer.py b/rasa/core/featurizers/single_state_featurizer.py index 6831bca01efa..aa7e2d7b7bda 100644 --- a/rasa/core/featurizers/single_state_featurizer.py +++ b/rasa/core/featurizers/single_state_featurizer.py @@ -6,7 +6,7 @@ import rasa.shared.utils.io from rasa.shared.core.domain import SubState, State, Domain -from rasa.shared.nlu.interpreter import NaturalLanguageInterpreter +from rasa.shared.nlu.interpreter import NaturalLanguageInterpreter, RegexInterpreter from rasa.shared.core.constants import PREVIOUS_ACTION, ACTIVE_LOOP, USER, SLOTS from rasa.shared.constants import DOCS_URL_MIGRATION_GUIDE from rasa.shared.core.trackers import is_prev_action_listen_in_state @@ -34,15 +34,27 @@ class SingleStateFeaturizer: """ def __init__(self) -> None: + # rasa core can be trained separately, therefore interpreter during training + # will be `RegexInterpreter` but during prediction it'll be + # interpreter based on some trained nlu model + self._use_regex_interpreter = False self._default_feature_states = {} self.action_texts = [] - def prepare_from_domain(self, domain: Domain) -> None: + def prepare_for_training( + self, domain: Domain, interpreter: NaturalLanguageInterpreter + ) -> None: """Gets necessary information for featurization from domain. Args: domain: An instance of :class:`rasa.shared.core.domain.Domain`. + interpreter: The interpreter used to encode the state """ + if isinstance(interpreter, RegexInterpreter): + # this method is called during training, + # RegexInterpreter means that core was trained separately + self._use_regex_interpreter = True + # store feature states for each attribute in order to create binary features def convert_to_dict(feature_states: List[Text]) -> Dict[Text, int]: return { @@ -159,6 +171,16 @@ def _extract_state_features( interpreter: NaturalLanguageInterpreter, sparse: bool = False, ) -> Dict[Text, List["Features"]]: + # this method is called during both prediction and training, + # `self._use_regex_interpreter == True` means that core was trained + # separately, therefore substitute interpreter based on some trained + # nlu model with default RegexInterpreter to make sure + # that prediction and train time features are the same + # if ( + # self._use_regex_interpreter + # and not isinstance(interpreter, RegexInterpreter) + # ): + # interpreter = RegexInterpreter() message = Message(data=sub_state) # remove entities from possible attributes @@ -168,7 +190,9 @@ def _extract_state_features( parsed_message = interpreter.featurize_message(message) output = self._get_features_from_parsed_message(parsed_message, attributes) - + print(sub_state) + print(interpreter) + print(output) # check that name attributes have features name_attribute = self._get_name_attribute(attributes) if name_attribute and name_attribute not in output: diff --git a/rasa/core/featurizers/tracker_featurizers.py b/rasa/core/featurizers/tracker_featurizers.py index 7d42e9f5ea25..0aa04584fef9 100644 --- a/rasa/core/featurizers/tracker_featurizers.py +++ b/rasa/core/featurizers/tracker_featurizers.py @@ -131,7 +131,7 @@ def featurize_trackers( f"to get numerical features for trackers." ) - self.state_featurizer.prepare_from_domain(domain) + self.state_featurizer.prepare_for_training(domain, interpreter) trackers_as_states, trackers_as_actions = self.training_states_and_actions( trackers, domain diff --git a/tests/core/test_featurizer.py b/tests/core/test_featurizer.py index 1faa2a51071e..c2fcc4f91cf1 100644 --- a/tests/core/test_featurizer.py +++ b/tests/core/test_featurizer.py @@ -122,7 +122,7 @@ def test_single_state_featurizer_creates_encoded_all_actions(): action_names=["a", "b", "c", "d"], ) f = SingleStateFeaturizer() - f.prepare_from_domain(domain) + f.prepare_for_training(domain, RegexInterpreter()) encoded_actions = f.encode_all_actions(domain, RegexInterpreter()) assert len(encoded_actions) == len(domain.action_names) assert all( @@ -255,3 +255,24 @@ def test_single_state_featurizer_with_interpreter_state_with_no_action_name( assert ( encoded[ACTIVE_LOOP][0].features != scipy.sparse.coo_matrix([[0, 0, 0, 1]]) ).nnz == 0 + + +def test_single_state_featurizer_uses_regex_interpreter(unpacked_trained_moodbot_path: Text): + from rasa.core.agent import Agent + + domain = Domain( + intents=[], + entities=[], + slots=[], + templates={}, + forms=[], + action_names=["a", "b", "c", "d"], + ) + f = SingleStateFeaturizer() + # simulate that core was trained separately by passing + # RegexInterpreter to prepare_for_training + f.prepare_for_training(domain, RegexInterpreter()) + # simulate that nlu and core models were manually combined for prediction + # by passing trained interpreter to encode_all_actions + interpreter = Agent.load(unpacked_trained_moodbot_path).interpreter + encoded_actions = f.encode_all_actions(domain, interpreter) From ae0b289b574398584af7b2453e6f50a95f4ec84e Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 19 Nov 2020 16:33:10 +0100 Subject: [PATCH 08/25] create test for separate core training --- rasa/core/featurizers/single_state_featurizer.py | 13 +++++-------- tests/core/test_featurizer.py | 16 ++++++++-------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/rasa/core/featurizers/single_state_featurizer.py b/rasa/core/featurizers/single_state_featurizer.py index aa7e2d7b7bda..6abdea1ae2e2 100644 --- a/rasa/core/featurizers/single_state_featurizer.py +++ b/rasa/core/featurizers/single_state_featurizer.py @@ -176,11 +176,10 @@ def _extract_state_features( # separately, therefore substitute interpreter based on some trained # nlu model with default RegexInterpreter to make sure # that prediction and train time features are the same - # if ( - # self._use_regex_interpreter - # and not isinstance(interpreter, RegexInterpreter) - # ): - # interpreter = RegexInterpreter() + if self._use_regex_interpreter and not isinstance( + interpreter, RegexInterpreter + ): + interpreter = RegexInterpreter() message = Message(data=sub_state) # remove entities from possible attributes @@ -190,9 +189,7 @@ def _extract_state_features( parsed_message = interpreter.featurize_message(message) output = self._get_features_from_parsed_message(parsed_message, attributes) - print(sub_state) - print(interpreter) - print(output) + # check that name attributes have features name_attribute = self._get_name_attribute(attributes) if name_attribute and name_attribute not in output: diff --git a/tests/core/test_featurizer.py b/tests/core/test_featurizer.py index c2fcc4f91cf1..4334928059b9 100644 --- a/tests/core/test_featurizer.py +++ b/tests/core/test_featurizer.py @@ -257,16 +257,13 @@ def test_single_state_featurizer_with_interpreter_state_with_no_action_name( ).nnz == 0 -def test_single_state_featurizer_uses_regex_interpreter(unpacked_trained_moodbot_path: Text): +def test_single_state_featurizer_uses_regex_interpreter( + unpacked_trained_moodbot_path: Text, +): from rasa.core.agent import Agent domain = Domain( - intents=[], - entities=[], - slots=[], - templates={}, - forms=[], - action_names=["a", "b", "c", "d"], + intents=[], entities=[], slots=[], templates={}, forms=[], action_names=[], ) f = SingleStateFeaturizer() # simulate that core was trained separately by passing @@ -275,4 +272,7 @@ def test_single_state_featurizer_uses_regex_interpreter(unpacked_trained_moodbot # simulate that nlu and core models were manually combined for prediction # by passing trained interpreter to encode_all_actions interpreter = Agent.load(unpacked_trained_moodbot_path).interpreter - encoded_actions = f.encode_all_actions(domain, interpreter) + features = f._extract_state_features({TEXT: "some text"}, interpreter) + # RegexInterpreter cannot create features for text, therefore since featurizer + # was trained without nlu, features for text should be empty + assert not features From db36f20b88709421b9a59c31d2092385e565fb2e Mon Sep 17 00:00:00 2001 From: Federico Tedin Date: Thu, 19 Nov 2020 17:03:25 +0100 Subject: [PATCH 09/25] Simplify is_url function --- rasa/core/run.py | 3 ++- rasa/nlu/utils/__init__.py | 27 +++++++++++++-------------- tests/nlu/test_utils.py | 11 ++++++++++- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/rasa/core/run.py b/rasa/core/run.py index fe438f790ed9..4635f9ad3c89 100644 --- a/rasa/core/run.py +++ b/rasa/core/run.py @@ -271,7 +271,8 @@ async def load_agent_on_start( ) except Exception as e: rasa.shared.utils.io.raise_warning( - f"The model at '{model_path}' could not be loaded. " f"Error: {e}" + f"The model at '{model_path}' could not be loaded. " + f"Error: {type(e)}: {e}" ) app.agent = None diff --git a/rasa/nlu/utils/__init__.py b/rasa/nlu/utils/__init__.py index e68a3839c675..92eb78732760 100644 --- a/rasa/nlu/utils/__init__.py +++ b/rasa/nlu/utils/__init__.py @@ -1,5 +1,4 @@ import os -import re from typing import Any, Optional, Text from pathlib import Path @@ -49,23 +48,23 @@ def is_model_dir(model_dir: Text) -> bool: def is_url(resource_name: Text) -> bool: """Check whether the url specified is a well formed one. - Regex adapted from https://stackoverflow.com/a/7160778/3001665 - Args: resource_name: Remote URL to validate - Returns: `True` if valid, otherwise `False`. + Returns: + `True` if valid, otherwise `False`. """ - URL_REGEX = re.compile( - r"^(?:http|ftp|file)s?://" # http:// or https:// or file:// - r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|" # domain - r"localhost|" # localhost - r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" # or ip - r"(?::\d+)?" # optional port - r"(?:/?|[/?]\S+)$", - re.IGNORECASE, - ) - return URL_REGEX.match(resource_name) is not None + from urllib.parse import urlparse + + try: + result = urlparse(resource_name) + except Exception: + return False + + if result.scheme == "file": + return bool(result.path) + + return bool(result.scheme in ["http", "https", "ftp", "ftps"] and result.netloc) def remove_model(model_dir: Text) -> bool: diff --git a/tests/nlu/test_utils.py b/tests/nlu/test_utils.py index ec8258146606..6c0c67330de3 100644 --- a/tests/nlu/test_utils.py +++ b/tests/nlu/test_utils.py @@ -8,6 +8,10 @@ from rasa.shared.exceptions import RasaException import rasa.shared.nlu.training_data.message +from rasa.nlu.tokenizers.convert_tokenizer import ( + ORIGINAL_TF_HUB_MODULE_URL, + RESTRICTED_ACCESS_URL, +) import rasa.shared.utils.io import rasa.utils.io as io_utils from rasa.nlu import utils @@ -115,7 +119,12 @@ def test_remove_model_invalid(empty_model_dir): ("https://www.google.com", True), ("http://google.com", True), ("http://www.google.com", True), - ("http://a/b/c", False), + ("http://a/b/c", True), + ("http://localhost:5002/api/projects/default/models/tags/production", True), + ("http://rasa-x:5002/api/projects/default/models/tags/production", True), + (ORIGINAL_TF_HUB_MODULE_URL, True), + (RESTRICTED_ACCESS_URL, True), + ("file:///some/path/file", True), ], ) def test_is_url(url: Text, result: bool): From 7131c55f5bd305a4c38d65b0371a3dab7097260f Mon Sep 17 00:00:00 2001 From: Vladimir Vlasov Date: Thu, 19 Nov 2020 17:31:53 +0100 Subject: [PATCH 10/25] Update rasa/core/featurizers/single_state_featurizer.py Co-authored-by: Tanja --- rasa/core/featurizers/single_state_featurizer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rasa/core/featurizers/single_state_featurizer.py b/rasa/core/featurizers/single_state_featurizer.py index 6abdea1ae2e2..dfa709b287d3 100644 --- a/rasa/core/featurizers/single_state_featurizer.py +++ b/rasa/core/featurizers/single_state_featurizer.py @@ -35,8 +35,9 @@ class SingleStateFeaturizer: def __init__(self) -> None: # rasa core can be trained separately, therefore interpreter during training - # will be `RegexInterpreter` but during prediction it'll be - # interpreter based on some trained nlu model + # will be `RegexInterpreter`. If the model is combined with a rasa nlu model + # during prediction the interpreter might be different. If that is the case, we need + # to make sure to "reset" the interpreter. self._use_regex_interpreter = False self._default_feature_states = {} self.action_texts = [] From e3c161bf7778004f2e57c8806f859c5347fd1fbf Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Thu, 19 Nov 2020 17:40:44 +0100 Subject: [PATCH 11/25] add changelog --- changelog/7316.bugfix.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelog/7316.bugfix.md diff --git a/changelog/7316.bugfix.md b/changelog/7316.bugfix.md new file mode 100644 index 000000000000..aaa0a64b2ab8 --- /dev/null +++ b/changelog/7316.bugfix.md @@ -0,0 +1,2 @@ +`SingleStateFeaturizer` checks whether it was trained with `RegexInterpreter` as +nlu interpreter. If that is the case, `RegexInterpreter` is used during prediction. From c46abfc7b77cd288b10037fbc1bb63fc7d129645 Mon Sep 17 00:00:00 2001 From: samsucik Date: Mon, 23 Nov 2020 11:37:42 +0000 Subject: [PATCH 12/25] Include review comments: make code & tests more readable + separate 2 tests. --- changelog/7235.bugfix.md | 3 + rasa/core/policies/rule_policy.py | 56 +++++++++------ tests/core/policies/test_rule_policy.py | 91 +++++++++++++------------ 3 files changed, 85 insertions(+), 65 deletions(-) create mode 100644 changelog/7235.bugfix.md diff --git a/changelog/7235.bugfix.md b/changelog/7235.bugfix.md new file mode 100644 index 000000000000..35227cb0c9e2 --- /dev/null +++ b/changelog/7235.bugfix.md @@ -0,0 +1,3 @@ +Slots that use `initial_value` won't cause rule contradiction errors when `conversation_start: true` is used. Previously, two rules that differed only in their use of `conversation_start` would be flagged as contradicting when a slot used `initial_value`. + +In checking for incomplete rules, an action will be required to have set _only_ those slots that the same action has set in another rule. Previously, an action was expected to have set also slots which, despite being present after this action in another rule, were not actually set by this action. \ No newline at end of file diff --git a/rasa/core/policies/rule_policy.py b/rasa/core/policies/rule_policy.py index 3b3a7787d7ba..f7f5fc350afe 100644 --- a/rasa/core/policies/rule_policy.py +++ b/rasa/core/policies/rule_policy.py @@ -281,7 +281,7 @@ def _check_rule_restriction( ) @staticmethod - def _check_slots_fingerprint( + def _expected_but_missing_slots( fingerprint: Dict[Text, List[Text]], state: State ) -> Set[Text]: expected_slots = set(fingerprint.get(SLOTS, {})) @@ -314,9 +314,9 @@ def _error_messages_from_fingerprints( if action_name and missing_fingerprint_slots: error_messages.append( f"- the action '{action_name}' in rule '{rule_name}' does not set some " - f"of the slots that it sets in other rules. Slots not set in rule '{rule_name}': " - f"'{', '.join(missing_fingerprint_slots)}'. Please update the rule with " - f"an appropriate slot or if it is the last action " + f"of the slots that it sets in other rules. Slots not set in rule " + f"'{rule_name}': '{', '.join(missing_fingerprint_slots)}'. Please " + f"update the rule with an appropriate slot or if it is the last action " f"add 'wait_for_user_input: false' after this action." ) if action_name and fingerprint_active_loops: @@ -372,7 +372,7 @@ def _check_for_incomplete_rules( # for a previous action if current action is rule snippet action continue - missing_expected_slots = self._check_slots_fingerprint( + missing_expected_slots = self._expected_but_missing_slots( fingerprint, state ) expected_active_loops = self._check_active_loops_fingerprint( @@ -660,22 +660,34 @@ def _is_rule_applicable( # turn_index goes back in time reversed_rule_states = list(reversed(self._rule_key_to_state(rule_key))) - return bool( - # rule is shorter than current turn index - turn_index >= len(reversed_rule_states) - # current rule and state turns are conversation starters - or ( - not reversed_rule_states[turn_index].get(PREVIOUS_ACTION) - and not conversation_state.get(PREVIOUS_ACTION) - ) - # check that current rule turn features are present in current state turn - or ( - reversed_rule_states[turn_index].get(PREVIOUS_ACTION) - and conversation_state.get(PREVIOUS_ACTION) - and self._does_rule_match_state( - reversed_rule_states[turn_index], conversation_state - ) - ) + # the rule must be applicable because we got (without any applicability issues) + # further in the conversation history than the rule's length + if turn_index >= len(reversed_rule_states): + return True + + # a state has previous action if and only if it is not a conversation start + # state + current_previous_action = conversation_state.get(PREVIOUS_ACTION) + rule_previous_action = reversed_rule_states[turn_index].get(PREVIOUS_ACTION) + + # current rule state is a conversation starter (due to conversation_start: true) + # but current conversation state is not. + # or + # current conversation state is a starter (due to initial_value set for a slot) + # but current rule state is not a starter + if current_previous_action and not rule_previous_action: + return False + + # current conversation state and rule state are conversation starters. + # any slots with initial_value set will necessarily be in both states and don't + # need to be checked. + if not current_previous_action: + # Ignore other states for conversation starts + return True + + # check: current rule state features are present in current conversation state + return self._does_rule_match_state( + reversed_rule_states[turn_index], conversation_state ) def _get_possible_keys( @@ -744,6 +756,8 @@ def _find_action_from_rules( ) -> Tuple[Optional[Text], Optional[Text]]: tracker_as_states = self.featurizer.prediction_states([tracker], domain) states = tracker_as_states[0] + for s in states: + print(f":::::{s}") logger.debug(f"Current tracker state: {states}") diff --git a/tests/core/policies/test_rule_policy.py b/tests/core/policies/test_rule_policy.py index caa55249f8ed..a7f6782741e3 100644 --- a/tests/core/policies/test_rule_policy.py +++ b/tests/core/policies/test_rule_policy.py @@ -93,10 +93,9 @@ def _form_activation_rule( def test_potential_contradiction_resolved_by_conversation_start(): - """ - Two rules that contradict each other except that one of them applies only at conversation - start -> ensure that this isn't flagged as a contradiction. - """ + # Two rules that contradict each other except that one of them applies only at + # conversation start -> ensure that this isn't flagged as a contradiction. + utter_anti_greet_action = "utter_anti_greet" domain = Domain.from_yaml( f""" @@ -140,12 +139,16 @@ def test_potential_contradiction_resolved_by_conversation_start(): ) -def test_potential_contradiction_resolved_by_conversation_start(): - """ - Two rules that contradict each other except that one of them applies only at conversation - start -> ensure that this isn't flagged as a contradiction. - """ +def test_potential_contradiction_resolved_by_conversation_start_when_slot_initial_value(): + # Two rules that contradict each other except that one of them applies only at + # conversation start -> ensure that this isn't flagged as a contradiction. + # Specifically, this checks that the conversation-start-checking logic doesn't + # depend on initial rule tracker states being empty as these can be non-empty due to + # initial slot values + utter_anti_greet_action = "utter_anti_greet" + some_slot = "slot1" + some_slot_initial_value = "slot1value" domain = Domain.from_yaml( f""" intents: @@ -153,6 +156,10 @@ def test_potential_contradiction_resolved_by_conversation_start(): actions: - {UTTER_GREET_ACTION} - {utter_anti_greet_action} +slots: + {some_slot}: + type: text + initial_value: {some_slot_initial_value} """ ) policy = RulePolicy() @@ -181,22 +188,34 @@ def test_potential_contradiction_resolved_by_conversation_start(): is_rule_tracker=True, ) - # Contradicting rules abort training, hence policy training here needs to succeed - # since there aren't contradicting rules in this case. + # Policy training needs to succeed to confirm that no contradictions have been + # detected policy.train( [greet_rule_at_conversation_start, anti_greet_rule], domain, RegexInterpreter() ) + # Check that the correct rule is applied when predicting next action in a story. + conversation_events = [ + ActionExecuted(ACTION_LISTEN_NAME), + UserUttered(intent={"name": GREET_INTENT_NAME}), + ] + action_probabilities_1 = policy.predict_action_probabilities( + DialogueStateTracker.from_events( + "test conversation", evts=conversation_events, slots=domain.slots + ), + domain, + RegexInterpreter(), + ) + assert_predicted_action(action_probabilities_1, domain, UTTER_GREET_ACTION) + + +def test_potential_contradiction_resolved_by_conversation_start_when_slot_initial_value_explicit(): + # Two rules that contradict each other except that one of them applies only at + # conversation start -> ensure that this isn't flagged as a contradiction. + # Specifically, this checks that the conversation-start-checking logic doesn't + # depend on whether or not the initial slot value is made explicit in the initial + # state of the conversation tracker -def test_potential_contradiction_resolved_by_conversation_start_when_slot_initial_value(): - """ - Two rules that contradict each other except that one of them applies only at conversation - start -> ensure that this isn't flagged as a contradiction. Specifically, this checks that - the conversation-start-checking logic doesn't depend on: - 1) initial rule tracker states being empty as these can be non-empty due to initial slot values - 1.1) whether or not the initial slot value is made explicit in the initial state of the - conversation tracker - """ utter_anti_greet_action = "utter_anti_greet" some_slot = "slot1" some_slot_initial_value = "slot1value" @@ -239,27 +258,12 @@ def test_potential_contradiction_resolved_by_conversation_start_when_slot_initia is_rule_tracker=True, ) - # Policy training needs to succeed to confirm that no contradictions have been detected + # Policy training needs to succeed to confirm that no contradictions have been + # detected policy.train( [greet_rule_at_conversation_start, anti_greet_rule], domain, RegexInterpreter() ) - # Check that the correct rule is applied when predicting next action in a story. - conversation_events = [ - ActionExecuted(ACTION_LISTEN_NAME), - UserUttered(intent={"name": GREET_INTENT_NAME}), - ] - action_probabilities_1 = policy.predict_action_probabilities( - DialogueStateTracker.from_events( - "test conversation", evts=conversation_events, slots=domain.slots - ), - domain, - RegexInterpreter(), - ) - assert_predicted_action(action_probabilities_1, domain, UTTER_GREET_ACTION) - - # A similar check, but this time the first state of the tracker will have the - # slot (which has an initial value set in the domain) explicitly set. conversation_events_with_initial_slot_explicit = [ SlotSet(some_slot, some_slot_initial_value), ActionExecuted(ACTION_LISTEN_NAME), @@ -426,13 +430,12 @@ def test_no_incomplete_rules_due_to_slots_after_listen(): def test_no_incomplete_rules_due_to_additional_slots_set(): - """ - Check that rules aren't automatically flagged as incomplete just because an action - doesn't set all the slots that are set in the same context in a different rule. - There may be slots that were set by other preceding actions (or by using initial_value - for a slot), and a rule shouldn't be marked as incomplete if some of those other slots - aren't set by the action in the rule. - """ + # Check that rules aren't automatically flagged as incomplete just because an action + # doesn't set all the slots that are set in the same context in a different rule. + # There may be slots that were set by other preceding actions (or by using + # initial_value for a slot), and a rule shouldn't be marked as incomplete if some of + # those other slots aren't set by the action in the rule. + some_action = "some_action" some_slot = "some_slot" some_slot_value = "value1" From 59e1aa28c74703cdeb8d62393a9e7c5e4768d6aa Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Mon, 23 Nov 2020 13:48:01 +0100 Subject: [PATCH 13/25] make sure explicit code is equivalent to bool --- rasa/core/policies/rule_policy.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/rasa/core/policies/rule_policy.py b/rasa/core/policies/rule_policy.py index f7f5fc350afe..9b8920a5562a 100644 --- a/rasa/core/policies/rule_policy.py +++ b/rasa/core/policies/rule_policy.py @@ -670,21 +670,20 @@ def _is_rule_applicable( current_previous_action = conversation_state.get(PREVIOUS_ACTION) rule_previous_action = reversed_rule_states[turn_index].get(PREVIOUS_ACTION) - # current rule state is a conversation starter (due to conversation_start: true) - # but current conversation state is not. - # or - # current conversation state is a starter (due to initial_value set for a slot) - # but current rule state is not a starter - if current_previous_action and not rule_previous_action: - return False - # current conversation state and rule state are conversation starters. # any slots with initial_value set will necessarily be in both states and don't # need to be checked. - if not current_previous_action: - # Ignore other states for conversation starts + if not rule_previous_action and not current_previous_action: return True + # current rule state is a conversation starter (due to conversation_start: true) + # but current conversation state is not. + # or + # current conversation state is a starter + # but current rule state is not. + if not rule_previous_action or not current_previous_action: + return False + # check: current rule state features are present in current conversation state return self._does_rule_match_state( reversed_rule_states[turn_index], conversation_state From 3a993be51354a66453131acee84d50597fddf642 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Mon, 23 Nov 2020 14:19:45 +0100 Subject: [PATCH 14/25] fix docstring --- rasa/core/policies/rule_policy.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/rasa/core/policies/rule_policy.py b/rasa/core/policies/rule_policy.py index 9b8920a5562a..8fce2790f78e 100644 --- a/rasa/core/policies/rule_policy.py +++ b/rasa/core/policies/rule_policy.py @@ -655,7 +655,16 @@ def _rule_key_to_state(rule_key: Text) -> List[State]: def _is_rule_applicable( self, rule_key: Text, turn_index: int, conversation_state: State ) -> bool: - """Check if rule is satisfied with current state at turn.""" + """Check if rule is satisfied with current state at turn. + + Args: + rule_key: the textual representation of learned rule + turn_index: index of a current dialogue turn + conversation_state: the state that corresponds to turn_index + + Returns: + a boolean that says whether the rule is applicable to current state + """ # turn_index goes back in time reversed_rule_states = list(reversed(self._rule_key_to_state(rule_key))) @@ -755,8 +764,6 @@ def _find_action_from_rules( ) -> Tuple[Optional[Text], Optional[Text]]: tracker_as_states = self.featurizer.prediction_states([tracker], domain) states = tracker_as_states[0] - for s in states: - print(f":::::{s}") logger.debug(f"Current tracker state: {states}") From 2e907229bba019458dd571e7ecd251f6c441779d Mon Sep 17 00:00:00 2001 From: Federico Tedin Date: Mon, 23 Nov 2020 15:27:15 +0100 Subject: [PATCH 15/25] PR feedback --- rasa/nlu/utils/__init__.py | 4 ++-- tests/nlu/test_utils.py | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/rasa/nlu/utils/__init__.py b/rasa/nlu/utils/__init__.py index 92eb78732760..cb1fffe81023 100644 --- a/rasa/nlu/utils/__init__.py +++ b/rasa/nlu/utils/__init__.py @@ -54,10 +54,10 @@ def is_url(resource_name: Text) -> bool: Returns: `True` if valid, otherwise `False`. """ - from urllib.parse import urlparse + from urllib import parse try: - result = urlparse(resource_name) + result = parse.urlparse(resource_name) except Exception: return False diff --git a/tests/nlu/test_utils.py b/tests/nlu/test_utils.py index 6c0c67330de3..e33ce4c98113 100644 --- a/tests/nlu/test_utils.py +++ b/tests/nlu/test_utils.py @@ -119,9 +119,14 @@ def test_remove_model_invalid(empty_model_dir): ("https://www.google.com", True), ("http://google.com", True), ("http://www.google.com", True), + ("http://www.google.com?foo=bar", True), ("http://a/b/c", True), ("http://localhost:5002/api/projects/default/models/tags/production", True), ("http://rasa-x:5002/api/projects/default/models/tags/production", True), + ( + "http://rasa-x:5002/api/projects/default/models/tags/production?foo=bar", + True, + ), (ORIGINAL_TF_HUB_MODULE_URL, True), (RESTRICTED_ACCESS_URL, True), ("file:///some/path/file", True), From fd08792483d8708e2c5e82a0208802af7ffb9df3 Mon Sep 17 00:00:00 2001 From: Federico Tedin Date: Mon, 23 Nov 2020 15:29:21 +0100 Subject: [PATCH 16/25] Changelog --- changelog/7317.bugfix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/7317.bugfix.md diff --git a/changelog/7317.bugfix.md b/changelog/7317.bugfix.md new file mode 100644 index 000000000000..a150badf33a5 --- /dev/null +++ b/changelog/7317.bugfix.md @@ -0,0 +1 @@ +Fixed Rasa Open Source not being able to fetch models from certain URLs. From 6531e14cc639a67c4e67061b1156fa9ec8062d21 Mon Sep 17 00:00:00 2001 From: tczekajlo Date: Thu, 19 Nov 2020 10:03:17 +0100 Subject: [PATCH 17/25] Remove the set-env command --- .../workflows/ci-model-regression-on-schedule.yml | 6 +++--- .github/workflows/ci-model-regression.yml | 12 ++++++------ .github/workflows/continous-integration.yml | 10 +++++----- .github/workflows/documentation.yml | 2 +- .github/workflows/vulnerability-scan.yml | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci-model-regression-on-schedule.yml b/.github/workflows/ci-model-regression-on-schedule.yml index badb8ee13cbe..7198d1ebc323 100644 --- a/.github/workflows/ci-model-regression-on-schedule.yml +++ b/.github/workflows/ci-model-regression-on-schedule.yml @@ -112,8 +112,8 @@ jobs: test -d dataset/$DATASET || (echo "::warning::The ${{ matrix.dataset }} dataset doesn't exist. Skipping the job." \ && echo "::set-output name=is_config_exists::false" && exit 0) - echo "::set-env name=DATASET::${DATASET}" - echo "::set-env name=CONFIG::${CONFIG}" + echo "DATASET=${DATASET}" >> $GITHUB_ENV + echo "CONFIG=${CONFIG}" >> $GITHUB_ENV - name: Set up Python 3.8 🐍 uses: actions/setup-python@v2 @@ -124,7 +124,7 @@ jobs: - name: Read Poetry Version 🔢 if: steps.set_dataset_config_vars.outputs.is_dataset_exists == 'true' && steps.set_dataset_config_vars.outputs.is_config_exists == 'true' run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 diff --git a/.github/workflows/ci-model-regression.yml b/.github/workflows/ci-model-regression.yml index 22744d1c3f02..9adf320c6e8e 100644 --- a/.github/workflows/ci-model-regression.yml +++ b/.github/workflows/ci-model-regression.yml @@ -215,8 +215,8 @@ jobs: test -d dataset/$DATASET || (echo "::warning::The ${{ matrix.dataset }} dataset doesn't exist. Skipping the job." \ && echo "::set-output name=is_config_exists::false" && exit 0) - echo "::set-env name=DATASET::${DATASET}" - echo "::set-env name=CONFIG::${CONFIG}" + echo "DATASET=${DATASET}" >> $GITHUB_ENV + echo "CONFIG=${CONFIG}" >> $GITHUB_ENV - name: Set up Python 3.8 🐍 uses: actions/setup-python@v2 @@ -227,7 +227,7 @@ jobs: - name: Read Poetry Version 🔢 if: steps.set_dataset_config_vars.outputs.is_dataset_exists == 'true' && steps.set_dataset_config_vars.outputs.is_config_exists == 'true' run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 @@ -360,8 +360,8 @@ jobs: test -d dataset/$DATASET || (echo "::warning::The ${{ matrix.dataset }} dataset doesn't exist. Skipping the job." \ && echo "::set-output name=is_config_exists::false" && exit 0) - echo "::set-env name=DATASET::${DATASET}" - echo "::set-env name=CONFIG::${CONFIG}" + echo "DATASET=${DATASET}" >> $GITHUB_ENV + echo "CONFIG=${CONFIG}" >> $GITHUB_ENV - name: Set up Python 3.8 🐍 uses: actions/setup-python@v2 @@ -372,7 +372,7 @@ jobs: - name: Read Poetry Version 🔢 if: steps.set_dataset_config_vars.outputs.is_dataset_exists == 'true' && steps.set_dataset_config_vars.outputs.is_config_exists == 'true' run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 diff --git a/.github/workflows/continous-integration.yml b/.github/workflows/continous-integration.yml index d4a69f18cac3..2750e7eb39bc 100644 --- a/.github/workflows/continous-integration.yml +++ b/.github/workflows/continous-integration.yml @@ -69,7 +69,7 @@ jobs: - name: Read Poetry Version 🔢 run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 @@ -145,7 +145,7 @@ jobs: - name: Read Poetry Version 🔢 run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 @@ -288,7 +288,7 @@ jobs: - name: Read Poetry Version 🔢 run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 @@ -346,9 +346,9 @@ jobs: shell: bash run: | if [ -z "${{ secrets.RASABOT_AUTOMERGE_GITHUB_TOKEN }}" ]; then - echo ::set-env name=MERGE_TOKEN::${{ secrets.GITHUB_TOKEN }} + echo "MERGE_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV else - echo ::set-env name=MERGE_TOKEN::${{ secrets.RASABOT_AUTOMERGE_GITHUB_TOKEN }} + echo "MERGE_TOKEN=${{ secrets.RASABOT_AUTOMERGE_GITHUB_TOKEN }}" >> $GITHUB_ENV fi - uses: rasahq/merge-pal-action@master with: diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 495376ada13e..71871e39f6c7 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -38,7 +38,7 @@ jobs: - name: Read Poetry Version 🔢 run: | - echo "::set-env name=POETRY_VERSION::$(scripts/poetry-version.sh)" + echo "POETRY_VERSION=$(scripts/poetry-version.sh)" >> $GITHUB_ENV shell: bash - name: Install poetry 🦄 diff --git a/.github/workflows/vulnerability-scan.yml b/.github/workflows/vulnerability-scan.yml index 83fec17ad037..7a2944073dca 100644 --- a/.github/workflows/vulnerability-scan.yml +++ b/.github/workflows/vulnerability-scan.yml @@ -32,7 +32,7 @@ jobs: IMAGE_NAME=rasa/rasa:latest-scanned docker build -f $DOCKERFILE -t $IMAGE_NAME . - echo "::set-env name=IMAGE_WITH_POETRY_LOCK::$IMAGE_NAME" + echo "IMAGE_WITH_POETRY_LOCK=$IMAGE_NAME" >> $GITHUB_ENV - name: Scan image 🕵️‍♀️🕵️‍♂️ uses: wochinge/gitrivy@6bf026b From 566040ed6f9baea27ca09e6813f299c6108621ae Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Mon, 23 Nov 2020 17:25:32 +0100 Subject: [PATCH 18/25] remove blank lines after docstrings --- rasa/core/policies/rule_policy.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/rasa/core/policies/rule_policy.py b/rasa/core/policies/rule_policy.py index 8fce2790f78e..ec8bd81ae64c 100644 --- a/rasa/core/policies/rule_policy.py +++ b/rasa/core/policies/rule_policy.py @@ -125,7 +125,6 @@ def __init__( enable_fallback_prediction: If `True` `core_fallback_action_name` is predicted in case no rule matched. """ - self._core_fallback_threshold = core_fallback_threshold self._fallback_action_name = core_fallback_action_name self._enable_fallback_prediction = enable_fallback_prediction @@ -194,7 +193,6 @@ def _states_for_unhappy_loop_predictions(states: List[State]) -> List[State]: Returns: modified states """ - # leave only last 2 dialogue turns to # - capture previous meaningful action before action_listen # - ignore previous intent @@ -226,7 +224,6 @@ def _create_loop_unhappy_lookup_from_states( Returns: lookup dictionary """ - lookup = {} for states, actions in zip(trackers_as_states, trackers_as_actions): action = actions[0] @@ -665,7 +662,6 @@ def _is_rule_applicable( Returns: a boolean that says whether the rule is applicable to current state """ - # turn_index goes back in time reversed_rule_states = list(reversed(self._rule_key_to_state(rule_key))) From 26ac74458a057fb5131596cc25abc8d3b1b42e7a Mon Sep 17 00:00:00 2001 From: Ella Date: Tue, 24 Nov 2020 14:46:57 +0100 Subject: [PATCH 19/25] prepared release of version 2.0.7 --- CHANGELOG.mdx | 8 ++++++++ changelog/5974.bugfix.md | 1 - changelog/7317.bugfix.md | 1 - pyproject.toml | 2 +- rasa/version.py | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) delete mode 100644 changelog/5974.bugfix.md delete mode 100644 changelog/7317.bugfix.md diff --git a/CHANGELOG.mdx b/CHANGELOG.mdx index d9081835b8c0..d099078f3137 100644 --- a/CHANGELOG.mdx +++ b/CHANGELOG.mdx @@ -16,6 +16,14 @@ https://github.com/RasaHQ/rasa/tree/master/changelog/ . --> +## [2.0.7] - 2020-11-24 + + +### Bugfixes +- [#5974](https://github.com/rasahq/rasa/issues/5974): `ActionRestart` will now trigger `ActionSessionStart` as a followup action. +- [#7317](https://github.com/rasahq/rasa/issues/7317): Fixed Rasa Open Source not being able to fetch models from certain URLs. + + ## [2.0.6] - 2020-11-10 diff --git a/changelog/5974.bugfix.md b/changelog/5974.bugfix.md deleted file mode 100644 index 4da157c583b1..000000000000 --- a/changelog/5974.bugfix.md +++ /dev/null @@ -1 +0,0 @@ -`ActionRestart` will now trigger `ActionSessionStart` as a followup action. diff --git a/changelog/7317.bugfix.md b/changelog/7317.bugfix.md deleted file mode 100644 index a150badf33a5..000000000000 --- a/changelog/7317.bugfix.md +++ /dev/null @@ -1 +0,0 @@ -Fixed Rasa Open Source not being able to fetch models from certain URLs. diff --git a/pyproject.toml b/pyproject.toml index c2ef490fac46..1ed40ca0d93d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ exclude = "((.eggs | .git | .pytype | .pytest_cache | build | dist))" [tool.poetry] name = "rasa" -version = "2.0.6" +version = "2.0.7" description = "Open source machine learning framework to automate text- and voice-based conversations: NLU, dialogue management, connect to Slack, Facebook, and more - Create chatbots and voice assistants" authors = [ "Rasa Technologies GmbH ",] maintainers = [ "Tom Bocklisch ",] diff --git a/rasa/version.py b/rasa/version.py index 8f9a3c980039..b7b438d51804 100644 --- a/rasa/version.py +++ b/rasa/version.py @@ -1,3 +1,3 @@ # this file will automatically be changed, # do not add anything but the version number here! -__version__ = "2.0.6" +__version__ = "2.0.7" From 79877c3f8387f715c1ebc22602df0d5d36f256f4 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Tue, 24 Nov 2020 15:09:45 +0100 Subject: [PATCH 20/25] add docstring --- rasa/core/featurizers/single_state_featurizer.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rasa/core/featurizers/single_state_featurizer.py b/rasa/core/featurizers/single_state_featurizer.py index dfa709b287d3..712ae097dac0 100644 --- a/rasa/core/featurizers/single_state_featurizer.py +++ b/rasa/core/featurizers/single_state_featurizer.py @@ -34,10 +34,11 @@ class SingleStateFeaturizer: """ def __init__(self) -> None: + """Initialize the single state featurizer.""" # rasa core can be trained separately, therefore interpreter during training # will be `RegexInterpreter`. If the model is combined with a rasa nlu model - # during prediction the interpreter might be different. If that is the case, we need - # to make sure to "reset" the interpreter. + # during prediction the interpreter might be different. + # If that is the case, we need to make sure to "reset" the interpreter. self._use_regex_interpreter = False self._default_feature_states = {} self.action_texts = [] From 3f3da59d72d7f48db489586af313b7dce378a16f Mon Sep 17 00:00:00 2001 From: Ella Rohm-Ensing Date: Tue, 24 Nov 2020 15:30:38 +0100 Subject: [PATCH 21/25] Update CHANGELOG.mdx --- CHANGELOG.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.mdx b/CHANGELOG.mdx index d099078f3137..54113e0b8ab7 100644 --- a/CHANGELOG.mdx +++ b/CHANGELOG.mdx @@ -23,6 +23,8 @@ https://github.com/RasaHQ/rasa/tree/master/changelog/ . --> - [#5974](https://github.com/rasahq/rasa/issues/5974): `ActionRestart` will now trigger `ActionSessionStart` as a followup action. - [#7317](https://github.com/rasahq/rasa/issues/7317): Fixed Rasa Open Source not being able to fetch models from certain URLs. + This addresses an issue introduced in 2.0.3 where `rasa-production` could not use the models from `rasa-x` in Rasa X server mode. + ## [2.0.6] - 2020-11-10 From 24df469cd4dfcc2d549e8f78080f6c3382879f6b Mon Sep 17 00:00:00 2001 From: Ella Date: Tue, 24 Nov 2020 16:38:52 +0100 Subject: [PATCH 22/25] move changelog entry --- CHANGELOG.mdx | 2 ++ changelog/7316.bugfix.md | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 changelog/7316.bugfix.md diff --git a/CHANGELOG.mdx b/CHANGELOG.mdx index 54113e0b8ab7..4cc0823520bf 100644 --- a/CHANGELOG.mdx +++ b/CHANGELOG.mdx @@ -24,6 +24,8 @@ https://github.com/RasaHQ/rasa/tree/master/changelog/ . --> - [#7317](https://github.com/rasahq/rasa/issues/7317): Fixed Rasa Open Source not being able to fetch models from certain URLs. This addresses an issue introduced in 2.0.3 where `rasa-production` could not use the models from `rasa-x` in Rasa X server mode. +- [#7316](https://github.com/rasahq/rasa/issues/7316): `SingleStateFeaturizer` checks whether it was trained with `RegexInterpreter` as + NLU interpreter. If that is the case, `RegexInterpreter` is used during prediction. ## [2.0.6] - 2020-11-10 diff --git a/changelog/7316.bugfix.md b/changelog/7316.bugfix.md deleted file mode 100644 index aaa0a64b2ab8..000000000000 --- a/changelog/7316.bugfix.md +++ /dev/null @@ -1,2 +0,0 @@ -`SingleStateFeaturizer` checks whether it was trained with `RegexInterpreter` as -nlu interpreter. If that is the case, `RegexInterpreter` is used during prediction. From 0a3faa16480548467764052f4ae56615a4558775 Mon Sep 17 00:00:00 2001 From: Vova Vv Date: Wed, 25 Nov 2020 16:41:06 +0100 Subject: [PATCH 23/25] emulate loop rejection only of it is active --- rasa/core/policies/rule_policy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rasa/core/policies/rule_policy.py b/rasa/core/policies/rule_policy.py index ec8bd81ae64c..57a701ddfb29 100644 --- a/rasa/core/policies/rule_policy.py +++ b/rasa/core/policies/rule_policy.py @@ -423,10 +423,12 @@ def _check_prediction( ) -> Optional[Text]: predicted_action_name = self._predict_next_action(tracker, domain, interpreter) + # if there is an active_loop, # RulePolicy will always predict active_loop first, # but inside loop unhappy path there might be another action if ( - predicted_action_name != gold_action_name + tracker.active_loop_name + and predicted_action_name != gold_action_name and predicted_action_name == tracker.active_loop_name ): rasa.core.test.emulate_loop_rejection(tracker) From 588646a8c03548c8b268bce38743fad413e0d0fd Mon Sep 17 00:00:00 2001 From: samsucik Date: Thu, 26 Nov 2020 10:26:15 +0000 Subject: [PATCH 24/25] prepared release of version 2.0.8 --- CHANGELOG.mdx | 9 +++++++++ changelog/7235.bugfix.md | 3 --- pyproject.toml | 2 +- rasa/version.py | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) delete mode 100644 changelog/7235.bugfix.md diff --git a/CHANGELOG.mdx b/CHANGELOG.mdx index 4cc0823520bf..5f1f1bb5857c 100644 --- a/CHANGELOG.mdx +++ b/CHANGELOG.mdx @@ -16,6 +16,15 @@ https://github.com/RasaHQ/rasa/tree/master/changelog/ . --> +## [2.0.8] - 2020-11-26 + + +### Bugfixes +- [#7235](https://github.com/rasahq/rasa/issues/7235): Slots that use `initial_value` won't cause rule contradiction errors when `conversation_start: true` is used. Previously, two rules that differed only in their use of `conversation_start` would be flagged as contradicting when a slot used `initial_value`. + + In checking for incomplete rules, an action will be required to have set _only_ those slots that the same action has set in another rule. Previously, an action was expected to have set also slots which, despite being present after this action in another rule, were not actually set by this action. + + ## [2.0.7] - 2020-11-24 diff --git a/changelog/7235.bugfix.md b/changelog/7235.bugfix.md deleted file mode 100644 index 35227cb0c9e2..000000000000 --- a/changelog/7235.bugfix.md +++ /dev/null @@ -1,3 +0,0 @@ -Slots that use `initial_value` won't cause rule contradiction errors when `conversation_start: true` is used. Previously, two rules that differed only in their use of `conversation_start` would be flagged as contradicting when a slot used `initial_value`. - -In checking for incomplete rules, an action will be required to have set _only_ those slots that the same action has set in another rule. Previously, an action was expected to have set also slots which, despite being present after this action in another rule, were not actually set by this action. \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 1ed40ca0d93d..6fefe730d3f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ exclude = "((.eggs | .git | .pytype | .pytest_cache | build | dist))" [tool.poetry] name = "rasa" -version = "2.0.7" +version = "2.0.8" description = "Open source machine learning framework to automate text- and voice-based conversations: NLU, dialogue management, connect to Slack, Facebook, and more - Create chatbots and voice assistants" authors = [ "Rasa Technologies GmbH ",] maintainers = [ "Tom Bocklisch ",] diff --git a/rasa/version.py b/rasa/version.py index b7b438d51804..d9d44c6fe9fe 100644 --- a/rasa/version.py +++ b/rasa/version.py @@ -1,3 +1,3 @@ # this file will automatically be changed, # do not add anything but the version number here! -__version__ = "2.0.7" +__version__ = "2.0.8" From 1ad62f3255da99105f64bd97f966f4511a95c0d2 Mon Sep 17 00:00:00 2001 From: samsucik Date: Fri, 27 Nov 2020 09:21:48 +0000 Subject: [PATCH 25/25] Delete deprecated urls from tests/nlu/test_utils. --- tests/nlu/test_utils.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/nlu/test_utils.py b/tests/nlu/test_utils.py index e33ce4c98113..5897064d2be6 100644 --- a/tests/nlu/test_utils.py +++ b/tests/nlu/test_utils.py @@ -8,10 +8,6 @@ from rasa.shared.exceptions import RasaException import rasa.shared.nlu.training_data.message -from rasa.nlu.tokenizers.convert_tokenizer import ( - ORIGINAL_TF_HUB_MODULE_URL, - RESTRICTED_ACCESS_URL, -) import rasa.shared.utils.io import rasa.utils.io as io_utils from rasa.nlu import utils @@ -127,8 +123,6 @@ def test_remove_model_invalid(empty_model_dir): "http://rasa-x:5002/api/projects/default/models/tags/production?foo=bar", True, ), - (ORIGINAL_TF_HUB_MODULE_URL, True), - (RESTRICTED_ACCESS_URL, True), ("file:///some/path/file", True), ], )