diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 0975a64b87b9..000000000000 --- a/LICENSE.txt +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2022 Rasa Technologies GmbH - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md index 83ee79501a4e..832b6527b00f 100644 --- a/README.md +++ b/README.md @@ -443,8 +443,7 @@ thus validating compatibility between Rasa and Rasa X/Enterprise. Please refer to the [Rasa Product Release and Maintenance Policy](https://rasa.com/rasa-product-release-and-maintenance-policy/) page. ## License -Licensed under the Apache License, Version 2.0. -Copyright 2022 Rasa Technologies GmbH. [Copy of the license](LICENSE.txt). +Copyright 2022 Rasa Technologies GmbH. A list of the Licenses of the dependencies of the project can be found at the bottom of the diff --git a/data/graph_schemas/default_config_core_predict_schema.yml b/data/graph_schemas/default_config_core_predict_schema.yml index ce8715b6c9ed..4aa0e1903b43 100644 --- a/data/graph_schemas/default_config_core_predict_schema.yml +++ b/data/graph_schemas/default_config_core_predict_schema.yml @@ -25,7 +25,7 @@ nodes: needs: tracker: __tracker__ flows: flows_provider - uses: rasa.cdu.processor.command_processor_component.CommandProcessorComponent + uses: rasa.dialogue_understanding.processor.command_processor_component.CommandProcessorComponent constructor_name: load fn: execute_commands config: { } diff --git a/data/graph_schemas/default_config_e2e_predict_schema.yml b/data/graph_schemas/default_config_e2e_predict_schema.yml index 46d1cb69aab7..7861c3719d3e 100644 --- a/data/graph_schemas/default_config_e2e_predict_schema.yml +++ b/data/graph_schemas/default_config_e2e_predict_schema.yml @@ -247,7 +247,7 @@ nodes: needs: tracker: __tracker__ flows: flows_provider - uses: rasa.cdu.processor.command_processor_component.CommandProcessorComponent + uses: rasa.dialogue_understanding.processor.command_processor_component.CommandProcessorComponent constructor_name: load fn: execute_commands config: { } diff --git a/data/graph_schemas/default_config_predict_schema.yml b/data/graph_schemas/default_config_predict_schema.yml index 8a1908f745e7..de5790c4d31f 100644 --- a/data/graph_schemas/default_config_predict_schema.yml +++ b/data/graph_schemas/default_config_predict_schema.yml @@ -163,7 +163,7 @@ nodes: needs: tracker: __tracker__ flows: flows_provider - uses: rasa.cdu.processor.command_processor_component.CommandProcessorComponent + uses: rasa.dialogue_understanding.processor.command_processor_component.CommandProcessorComponent constructor_name: load fn: execute_commands config: { } diff --git a/data/graph_schemas/max_hist_config_predict_schema.yml b/data/graph_schemas/max_hist_config_predict_schema.yml index 9beb1771dec9..aed0dc4bdbed 100644 --- a/data/graph_schemas/max_hist_config_predict_schema.yml +++ b/data/graph_schemas/max_hist_config_predict_schema.yml @@ -25,7 +25,7 @@ nodes: needs: tracker: __tracker__ flows: flows_provider - uses: rasa.cdu.processor.command_processor_component.CommandProcessorComponent + uses: rasa.dialogue_understanding.processor.command_processor_component.CommandProcessorComponent constructor_name: load fn: execute_commands config: { } diff --git a/data/test_config/graph_config.yml b/data/test_config/graph_config.yml index 7f8b3200f5f0..a816e91ec000 100644 --- a/data/test_config/graph_config.yml +++ b/data/test_config/graph_config.yml @@ -356,7 +356,7 @@ predict_schema: needs: tracker: __tracker__ flows: flows_provider - uses: rasa.cdu.processor.command_processor_component.CommandProcessorComponent + uses: rasa.dialogue_understanding.processor.command_processor_component.CommandProcessorComponent constructor_name: load fn: execute_commands config: { } diff --git a/poetry.lock b/poetry.lock index fb73f7a5c761..bfdf7ad716ef 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4821,10 +4821,8 @@ prompt-toolkit = ">=3.0,<3.0.29" "ruamel.yaml" = ">=0.16.5,<0.18.0" sanic = ">=21.12.0,<22.0.0" Sanic-Cors = ">=2.0.0,<3.0.0" -setuptools = ">=65.5.1" typing-extensions = ">=4.1.1,<5.0.0" websockets = ">=10.0,<12.0" -wheel = ">=0.38.1" [[package]] name = "redis" @@ -7507,4 +7505,4 @@ transformers = ["sentencepiece", "transformers"] [metadata] lock-version = "2.0" python-versions = ">=3.8.1,<3.11" -content-hash = "74cb676c21adacee996bf57ed0d53a1e4d37048e3dd0352e4404e75895b4580d" +content-hash = "932867176f645c4a61a11f28b781b11d145a1b1f481e167ed830a579e8596c6c" diff --git a/pyproject.toml b/pyproject.toml index c5d0896d4bc6..16fc82f44ed1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,18 +9,17 @@ exclude = "((.eggs | .git | .pytest_cache | build | dist))" [tool.poetry] name = "rasa" -version = "3.8.0a4" +version = "3.8.0a7" 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 ",] homepage = "https://rasa.com" repository = "https://github.com/rasahq/rasa" documentation = "https://rasa.com/docs" -classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Topic :: Software Development :: Libraries",] +classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries",] keywords = [ "nlp", "machine-learning", "machine-learning-library", "bot", "bots", "botkit", "rasa conversational-agents", "conversational-ai", "chatbot", "chatbot-framework", "bot-framework",] -include = [ "LICENSE.txt", "README.md", "rasa/shared/core/training_data/visualization.html", "rasa/cli/default_config.yml", "rasa/shared/importers/*", "rasa/utils/schemas/*", "rasa/keys", "rasa/core/channels/chat.html", "rasa/cdu/classifiers/command_prompt_template.jinja2",] +include = [ "LICENSE.txt", "README.md", "rasa/shared/core/training_data/visualization.html", "rasa/cli/default_config.yml", "rasa/shared/importers/*", "rasa/utils/schemas/*", "rasa/keys", "rasa/core/channels/chat.html", "rasa/dialogue_understanding/classifiers/command_prompt_template.jinja2",] readme = "README.md" -license = "Apache-2.0" [[tool.poetry.source]] name = "internal repository mirroring psycopg binary for macos" url = "https://europe-west3-python.pkg.dev/rasa-releases/psycopg-binary/simple/" diff --git a/rasa/cdu/commands/__init__.py b/rasa/cdu/commands/__init__.py deleted file mode 100644 index c364839b8f44..000000000000 --- a/rasa/cdu/commands/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -from rasa.cdu.commands.command import Command -from rasa.cdu.commands.free_form_answer_command import FreeFormAnswerCommand -from rasa.cdu.commands.cancel_flow_command import CancelFlowCommand -from rasa.cdu.commands.knowledge_answer_command import KnowledgeAnswerCommand -from rasa.cdu.commands.chit_chat_answer_command import ChitChatAnswerCommand -from rasa.cdu.commands.can_not_handle_command import CannotHandleCommand -from rasa.cdu.commands.clarify_command import ClarifyCommand -from rasa.cdu.commands.error_command import ErrorCommand -from rasa.cdu.commands.set_slot_command import SetSlotCommand -from rasa.cdu.commands.start_flow_command import StartFlowCommand -from rasa.cdu.commands.human_handoff_command import HumanHandoffCommand -from rasa.cdu.commands.correct_slots_command import CorrectSlotsCommand, CorrectedSlot - - -__all__ = [ - "Command", - "FreeFormAnswerCommand", - "CancelFlowCommand", - "KnowledgeAnswerCommand", - "ChitChatAnswerCommand", - "CannotHandleCommand", - "ClarifyCommand", - "ErrorCommand", - "SetSlotCommand", - "StartFlowCommand", - "HumanHandoffCommand", - "CorrectSlotsCommand", - "CorrectedSlot", -] diff --git a/rasa/cdu/generator/__init__.py b/rasa/cdu/generator/__init__.py deleted file mode 100644 index 0fe0868ef1d5..000000000000 --- a/rasa/cdu/generator/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from rasa.cdu.generator.command_generator import CommandGenerator -from rasa.cdu.generator.llm_command_generator import LLMCommandGenerator - -__all__ = ["CommandGenerator", "LLMCommandGenerator"] diff --git a/rasa/cdu/stack/frames/__init__.py b/rasa/cdu/stack/frames/__init__.py deleted file mode 100644 index 2ba3db8c5730..000000000000 --- a/rasa/cdu/stack/frames/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -from rasa.cdu.stack.frames.dialogue_stack_frame import DialogueStackFrame -from rasa.cdu.stack.frames.flow_frame import UserFlowStackFrame, BaseFlowStackFrame -from rasa.cdu.stack.frames.pattern_frame import PatternFlowStackFrame -from rasa.cdu.stack.frames.search_frame import SearchStackFrame -from rasa.cdu.stack.frames.chit_chat_frame import ChitChatStackFrame - -__all__ = [ - "DialogueStackFrame", - "BaseFlowStackFrame", - "PatternFlowStackFrame", - "UserFlowStackFrame", - "SearchStackFrame", - "ChitChatStackFrame", -] diff --git a/rasa/cli/initial_project_dm2/data/nlu.yml b/rasa/cli/initial_project_dm2/data/nlu.yml deleted file mode 100644 index edbbf36d42ad..000000000000 --- a/rasa/cli/initial_project_dm2/data/nlu.yml +++ /dev/null @@ -1,11 +0,0 @@ -version: "3.1" - -nlu: - - intent: affirm - examples: | - - yes - - yup - - intent: deny - examples: | - - no - - nope \ No newline at end of file diff --git a/rasa/cdu/__init__.py b/rasa/cli/project_templates/__init__.py similarity index 100% rename from rasa/cdu/__init__.py rename to rasa/cli/project_templates/__init__.py diff --git a/rasa/cdu/patterns/__init__.py b/rasa/cli/project_templates/default/actions/__init__.py similarity index 100% rename from rasa/cdu/patterns/__init__.py rename to rasa/cli/project_templates/default/actions/__init__.py diff --git a/rasa/cli/initial_project/actions/actions.py b/rasa/cli/project_templates/default/actions/actions.py similarity index 100% rename from rasa/cli/initial_project/actions/actions.py rename to rasa/cli/project_templates/default/actions/actions.py diff --git a/rasa/cli/initial_project/config.yml b/rasa/cli/project_templates/default/config.yml similarity index 100% rename from rasa/cli/initial_project/config.yml rename to rasa/cli/project_templates/default/config.yml diff --git a/rasa/cli/initial_project/credentials.yml b/rasa/cli/project_templates/default/credentials.yml similarity index 100% rename from rasa/cli/initial_project/credentials.yml rename to rasa/cli/project_templates/default/credentials.yml diff --git a/rasa/cli/initial_project/data/nlu.yml b/rasa/cli/project_templates/default/data/nlu.yml similarity index 100% rename from rasa/cli/initial_project/data/nlu.yml rename to rasa/cli/project_templates/default/data/nlu.yml diff --git a/rasa/cli/initial_project/data/rules.yml b/rasa/cli/project_templates/default/data/rules.yml similarity index 100% rename from rasa/cli/initial_project/data/rules.yml rename to rasa/cli/project_templates/default/data/rules.yml diff --git a/rasa/cli/initial_project/data/stories.yml b/rasa/cli/project_templates/default/data/stories.yml similarity index 100% rename from rasa/cli/initial_project/data/stories.yml rename to rasa/cli/project_templates/default/data/stories.yml diff --git a/rasa/cli/initial_project/domain.yml b/rasa/cli/project_templates/default/domain.yml similarity index 100% rename from rasa/cli/initial_project/domain.yml rename to rasa/cli/project_templates/default/domain.yml diff --git a/rasa/cli/initial_project/endpoints.yml b/rasa/cli/project_templates/default/endpoints.yml similarity index 100% rename from rasa/cli/initial_project/endpoints.yml rename to rasa/cli/project_templates/default/endpoints.yml diff --git a/rasa/cli/initial_project/tests/test_stories.yml b/rasa/cli/project_templates/default/tests/test_stories.yml similarity index 100% rename from rasa/cli/initial_project/tests/test_stories.yml rename to rasa/cli/project_templates/default/tests/test_stories.yml diff --git a/rasa/cdu/processor/__init__.py b/rasa/cli/project_templates/dm2/actions/__init__.py similarity index 100% rename from rasa/cdu/processor/__init__.py rename to rasa/cli/project_templates/dm2/actions/__init__.py diff --git a/rasa/cli/initial_project_dm2/actions/actions.py b/rasa/cli/project_templates/dm2/actions/actions.py similarity index 100% rename from rasa/cli/initial_project_dm2/actions/actions.py rename to rasa/cli/project_templates/dm2/actions/actions.py diff --git a/rasa/cli/initial_project_dm2/config.yml b/rasa/cli/project_templates/dm2/config.yml similarity index 100% rename from rasa/cli/initial_project_dm2/config.yml rename to rasa/cli/project_templates/dm2/config.yml diff --git a/rasa/cli/initial_project_dm2/credentials.yml b/rasa/cli/project_templates/dm2/credentials.yml similarity index 100% rename from rasa/cli/initial_project_dm2/credentials.yml rename to rasa/cli/project_templates/dm2/credentials.yml diff --git a/rasa/cli/initial_project_dm2/data/flows.yml b/rasa/cli/project_templates/dm2/data/flows.yml similarity index 100% rename from rasa/cli/initial_project_dm2/data/flows.yml rename to rasa/cli/project_templates/dm2/data/flows.yml diff --git a/rasa/cli/initial_project_dm2/domain.yml b/rasa/cli/project_templates/dm2/domain.yml similarity index 100% rename from rasa/cli/initial_project_dm2/domain.yml rename to rasa/cli/project_templates/dm2/domain.yml diff --git a/rasa/cli/initial_project_dm2/e2e_tests/complete_request.yml b/rasa/cli/project_templates/dm2/e2e_tests/complete_request.yml similarity index 100% rename from rasa/cli/initial_project_dm2/e2e_tests/complete_request.yml rename to rasa/cli/project_templates/dm2/e2e_tests/complete_request.yml diff --git a/rasa/cli/initial_project_dm2/e2e_tests/happy.yml b/rasa/cli/project_templates/dm2/e2e_tests/happy.yml similarity index 100% rename from rasa/cli/initial_project_dm2/e2e_tests/happy.yml rename to rasa/cli/project_templates/dm2/e2e_tests/happy.yml diff --git a/rasa/cli/initial_project_dm2/endpoints.yml b/rasa/cli/project_templates/dm2/endpoints.yml similarity index 100% rename from rasa/cli/initial_project_dm2/endpoints.yml rename to rasa/cli/project_templates/dm2/endpoints.yml diff --git a/rasa/cli/initial_project_dm2/tests/test_stories.yml b/rasa/cli/project_templates/dm2/tests/test_stories.yml similarity index 100% rename from rasa/cli/initial_project_dm2/tests/test_stories.yml rename to rasa/cli/project_templates/dm2/tests/test_stories.yml diff --git a/rasa/cli/project_templates/tutorial/actions.py b/rasa/cli/project_templates/tutorial/actions.py new file mode 100644 index 000000000000..f023605fe014 --- /dev/null +++ b/rasa/cli/project_templates/tutorial/actions.py @@ -0,0 +1,22 @@ +from typing import Any, Text, Dict, List +from rasa_sdk import Action, Tracker +from rasa_sdk.executor import CollectingDispatcher +from rasa_sdk.events import SlotSet + + +class ActionSufficientFunds(Action): + def name(self) -> Text: + return "action_sufficient_funds" + + def run( + self, + dispatcher: CollectingDispatcher, + tracker: Tracker, + domain: Dict[Text, Any], + ) -> List[Dict[Text, Any]]: + # hard-coded balance for tutorial purposes. in production this + # would be retrieved from a database or an API + balance = 1000 + transfer_amount = tracker.get_slot("amount") + has_sufficient_funds = transfer_amount <= balance + return [SlotSet("has_sufficient_funds", has_sufficient_funds)] diff --git a/rasa/cli/project_templates/tutorial/config.yml b/rasa/cli/project_templates/tutorial/config.yml new file mode 100644 index 000000000000..3801f4a2c879 --- /dev/null +++ b/rasa/cli/project_templates/tutorial/config.yml @@ -0,0 +1,13 @@ +recipe: default.v1 +language: en +pipeline: + - name: LLMCommandGenerator + llm: + model_name: gpt-4 + +policies: + - name: rasa.core.policies.flow_policy.FlowPolicy +# - name: rasa_plus.ml.DocsearchPolicy +# - name: RulePolicy + +assistant_id: 20230405-114328-tranquil-mustard diff --git a/rasa/cli/project_templates/tutorial/credentials.yml b/rasa/cli/project_templates/tutorial/credentials.yml new file mode 100644 index 000000000000..e9f12911e3cf --- /dev/null +++ b/rasa/cli/project_templates/tutorial/credentials.yml @@ -0,0 +1,33 @@ +# This file contains the credentials for the voice & chat platforms +# which your bot is using. +# https://rasa.com/docs/rasa/messaging-and-voice-channels + +rest: +# # you don't need to provide anything here - this channel doesn't +# # require any credentials + + +#facebook: +# verify: "" +# secret: "" +# page-access-token: "" + +#slack: +# slack_token: "" +# slack_channel: "" +# slack_signing_secret: "" + +#socketio: +# user_message_evt: +# bot_message_evt: +# session_persistence: + +#mattermost: +# url: "https:///api/v4" +# token: "" +# webhook_url: "" + +# This entry is needed if you are using Rasa Enterprise. The entry represents credentials +# for the Rasa Enterprise "channel", i.e. Talk to your bot and Share with guest testers. +rasa: + url: "http://localhost:5002/api" diff --git a/rasa/cli/project_templates/tutorial/data/flows.yml b/rasa/cli/project_templates/tutorial/data/flows.yml new file mode 100644 index 000000000000..aa081218dc1f --- /dev/null +++ b/rasa/cli/project_templates/tutorial/data/flows.yml @@ -0,0 +1,12 @@ +flows: + transfer_money: + description: This flow lets users send money to friends and family. + steps: + - id: "ask_recipient" + collect_information: recipient + next: "ask_amount" + - id: "ask_amount" + collect_information: amount + next: "transfer_successful" + - id: "transfer_successful" + action: utter_transfer_complete diff --git a/rasa/cli/project_templates/tutorial/domain.yml b/rasa/cli/project_templates/tutorial/domain.yml new file mode 100644 index 000000000000..e06882f8f3d1 --- /dev/null +++ b/rasa/cli/project_templates/tutorial/domain.yml @@ -0,0 +1,21 @@ +version: "3.1" + +slots: + recipient: + type: text + mappings: + - type: custom + amount: + type: float + mappings: + - type: custom + +responses: + utter_ask_recipient: + - text: "Who would you like to send money to?" + + utter_ask_amount: + - text: "How much money would you like to send?" + + utter_transfer_complete: + - text: "All done. ${amount} has been sent to {recipient}." diff --git a/rasa/cli/project_templates/tutorial/endpoints.yml b/rasa/cli/project_templates/tutorial/endpoints.yml new file mode 100644 index 000000000000..5f65275b8802 --- /dev/null +++ b/rasa/cli/project_templates/tutorial/endpoints.yml @@ -0,0 +1,42 @@ +# This file contains the different endpoints your bot can use. + +# Server where the models are pulled from. +# https://rasa.com/docs/rasa/model-storage#fetching-models-from-a-server + +#models: +# url: http://my-server.com/models/default_core@latest +# wait_time_between_pulls: 10 # [optional](default: 100) + +# Server which runs your custom actions. +# https://rasa.com/docs/rasa/custom-actions + +action_endpoint: + url: "http://localhost:5055/webhook" + +# Tracker store which is used to store the conversations. +# By default the conversations are stored in memory. +# https://rasa.com/docs/rasa/tracker-stores + +#tracker_store: +# type: redis +# url: +# port: +# db: +# password: +# use_ssl: + +#tracker_store: +# type: mongod +# url: +# db: +# username: +# password: + +# Event broker which all conversation events should be streamed to. +# https://rasa.com/docs/rasa/event-brokers + +#event_broker: +# url: localhost +# username: username +# password: password +# queue: queue diff --git a/rasa/cli/scaffold.py b/rasa/cli/scaffold.py index b6715519b896..708f054e5e68 100644 --- a/rasa/cli/scaffold.py +++ b/rasa/cli/scaffold.py @@ -1,4 +1,5 @@ import argparse +from enum import Enum import os import sys from typing import List, Text @@ -16,6 +17,17 @@ ) +class ProjectTemplateName(Enum): + """Enum of the different project templates.""" + + DEFAULT = "default" + TUTORIAL = "tutorial" + DM2 = "dm2" + + def __str__(self) -> str: + return self.value + + def add_subparser( subparsers: SubParsersAction, parents: List[argparse.ArgumentParser] ) -> None: @@ -43,9 +55,11 @@ def add_subparser( help="Directory where your project should be initialized.", ) scaffold_parser.add_argument( - "--dm2", - action="store_true", - help="Temporary. Whether to create a DM2 project or a classic one", + "--template", + type=ProjectTemplateName, + choices=list(ProjectTemplateName), + default=ProjectTemplateName.DEFAULT, + help="Select the template to use for the project.", ) scaffold_parser.set_defaults(func=run) @@ -131,24 +145,27 @@ def print_run_or_instructions(args: argparse.Namespace) -> None: def init_project(args: argparse.Namespace, path: Text) -> None: """Inits project.""" os.chdir(path) - create_initial_project(".", args.dm2) + create_initial_project(".", args.template) print(f"Created project directory at '{os.getcwd()}'.") print_train_or_instructions(args) -def create_initial_project(path: Text, is_dm2: bool = False) -> None: +def create_initial_project( + path: Text, template: ProjectTemplateName = ProjectTemplateName.DEFAULT +) -> None: """Creates directory structure and templates for initial project.""" from distutils.dir_util import copy_tree - copy_tree(scaffold_path(is_dm2), path) + copy_tree(scaffold_path(template), path) -def scaffold_path(is_dm2: bool = False) -> Text: +def scaffold_path(template: ProjectTemplateName) -> Text: import pkg_resources + import rasa.cli.project_templates + + template_module = rasa.cli.project_templates.__name__ - if is_dm2: - return pkg_resources.resource_filename(__name__, "initial_project_dm2") - return pkg_resources.resource_filename(__name__, "initial_project") + return pkg_resources.resource_filename(template_module, template.value) def print_cancel() -> None: diff --git a/rasa/core/actions/action.py b/rasa/core/actions/action.py index 69424a39ccab..65f5e51cd444 100644 --- a/rasa/core/actions/action.py +++ b/rasa/core/actions/action.py @@ -97,9 +97,9 @@ def default_actions(action_endpoint: Optional[EndpointConfig] = None) -> List["Action"]: """List default actions.""" from rasa.core.actions.two_stage_fallback import TwoStageFallbackAction - from rasa.cdu.patterns.correction import ActionCorrectFlowSlot - from rasa.cdu.patterns.cancel import ActionCancelFlow - from rasa.cdu.patterns.clarify import ActionClarifyFlows + from rasa.dialogue_understanding.patterns.correction import ActionCorrectFlowSlot + from rasa.dialogue_understanding.patterns.cancel import ActionCancelFlow + from rasa.dialogue_understanding.patterns.clarify import ActionClarifyFlows return [ ActionListen(), diff --git a/rasa/core/actions/flow_trigger_action.py b/rasa/core/actions/flow_trigger_action.py index a48ff9ab6561..82ee017679bf 100644 --- a/rasa/core/actions/flow_trigger_action.py +++ b/rasa/core/actions/flow_trigger_action.py @@ -1,8 +1,11 @@ from typing import Any, Dict, Optional, Text, List import structlog -from rasa.cdu.stack.dialogue_stack import DialogueStack -from rasa.cdu.stack.frames.flow_frame import FlowStackFrameType, UserFlowStackFrame +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.frames.flow_stack_frame import ( + FlowStackFrameType, + UserFlowStackFrame, +) from rasa.core.actions import action from rasa.core.channels import OutputChannel from rasa.shared.constants import FLOW_PREFIX diff --git a/rasa/core/channels/chat.html b/rasa/core/channels/chat.html index 63691d2be525..914c0bf16684 100644 --- a/rasa/core/channels/chat.html +++ b/rasa/core/channels/chat.html @@ -316,7 +316,7 @@

Happy chatting!

function firstNonPatternFrame(stackFrames) { let frameIndex = stackFrames.length - 1; - while (frameIndex >= 0 && stackFrames[frameIndex]?.flow_id.startsWith("pattern_")) { + while (frameIndex >= 0 && stackFrames[frameIndex]?.flow_id?.startsWith("pattern_")) { frameIndex--; } return frameIndex; @@ -329,7 +329,7 @@

Happy chatting!

let frameIndex = excludePatterns ? firstNonPatternFrame(stackFrames) : stackFrames.length - 1; let activeFlow = stackFrames[frameIndex]?.flow_id; let activeStep = stackFrames[frameIndex]?.step_id; - let currentContext = stackFrames[frameIndex]?.context; + let currentContext = stackFrames[frameIndex]; let flows = await flowsData; // find flow by going through array and checking for the id field let flow = flows.find((flow) => flow.id === activeFlow); diff --git a/rasa/core/nlg/response.py b/rasa/core/nlg/response.py index b931c6b28c0e..756b243602d8 100644 --- a/rasa/core/nlg/response.py +++ b/rasa/core/nlg/response.py @@ -1,6 +1,6 @@ import copy import logging -from rasa.cdu.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack from rasa.core.constants import DEFAULT_TEMPLATE_ENGINE, TEMPLATE_ENGINE_CONFIG_KEY from rasa.shared.core.trackers import DialogueStateTracker diff --git a/rasa/core/policies/flow_policy.py b/rasa/core/policies/flow_policy.py index 66d433bc148a..97796c6ace44 100644 --- a/rasa/core/policies/flow_policy.py +++ b/rasa/core/policies/flow_policy.py @@ -1,27 +1,29 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any, Dict, Text, List, Optional, Union +from typing import Any, Dict, Text, List, Optional from jinja2 import Template from structlog.contextvars import ( bound_contextvars, ) -from rasa.cdu.stack.dialogue_stack import DialogueStack -from rasa.cdu.stack.frames import ( +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.frames import ( BaseFlowStackFrame, DialogueStackFrame, UserFlowStackFrame, ) -from rasa.cdu.patterns.collect_information import ( +from rasa.dialogue_understanding.patterns.collect_information import ( CollectInformationPatternFlowStackFrame, ) -from rasa.cdu.patterns.completed import CompletedPatternFlowStackFrame -from rasa.cdu.patterns.continue_interrupted import ( +from rasa.dialogue_understanding.patterns.completed import ( + CompletedPatternFlowStackFrame, +) +from rasa.dialogue_understanding.patterns.continue_interrupted import ( ContinueInterruptedPatternFlowStackFrame, ) -from rasa.cdu.stack.frames.flow_frame import FlowStackFrameType -from rasa.cdu.stack.utils import top_user_flow_frame +from rasa.dialogue_understanding.stack.frames.flow_stack_frame import FlowStackFrameType +from rasa.dialogue_understanding.stack.utils import top_user_flow_frame from rasa.core.constants import ( DEFAULT_POLICY_PRIORITY, @@ -295,30 +297,11 @@ def is_condition_satisfied( ) -> bool: """Evaluate a predicate condition.""" - def get_value( - initial_value: Union[Text, None] - ) -> Union[Text, float, bool, None]: - if initial_value is None or isinstance(initial_value, (bool, float)): - return initial_value - - # if this isn't a bool or float, it's something else - # the below is a best effort to convert it to something we can - # use for the predicate evaluation - initial_value = str(initial_value) # make sure it's a string - - if initial_value.lower() in ["true", "false"]: - return initial_value.lower() == "true" - - if initial_value.isnumeric(): - return float(initial_value) - - return initial_value - # attach context to the predicate evaluation to allow conditions using it context = {"context": DialogueStack.from_tracker(tracker).current_context()} document: Dict[str, Any] = context.copy() for slot in self.domain.slots: - document[slot.name] = get_value(tracker.get_slot(slot.name)) + document[slot.name] = tracker.get_slot(slot.name) p = Predicate(self.render_template_variables(predicate, context)) try: return p.evaluate(document) diff --git a/rasa/core/policies/policy.py b/rasa/core/policies/policy.py index 439e32741b97..acf17ea8661c 100644 --- a/rasa/core/policies/policy.py +++ b/rasa/core/policies/policy.py @@ -4,7 +4,7 @@ import logging from enum import Enum from pathlib import Path -from rasa.cdu.stack.dialogue_stack import ( +from rasa.dialogue_understanding.stack.dialogue_stack import ( DialogueStack, DialogueStackFrame, ) diff --git a/rasa/cdu/stack/__init__.py b/rasa/dialogue_understanding/__init__.py similarity index 100% rename from rasa/cdu/stack/__init__.py rename to rasa/dialogue_understanding/__init__.py diff --git a/rasa/dialogue_understanding/commands/__init__.py b/rasa/dialogue_understanding/commands/__init__.py new file mode 100644 index 000000000000..fd437043c2da --- /dev/null +++ b/rasa/dialogue_understanding/commands/__init__.py @@ -0,0 +1,42 @@ +from rasa.dialogue_understanding.commands.command import Command +from rasa.dialogue_understanding.commands.free_form_answer_command import ( + FreeFormAnswerCommand, +) +from rasa.dialogue_understanding.commands.cancel_flow_command import CancelFlowCommand +from rasa.dialogue_understanding.commands.knowledge_answer_command import ( + KnowledgeAnswerCommand, +) +from rasa.dialogue_understanding.commands.chit_chat_answer_command import ( + ChitChatAnswerCommand, +) +from rasa.dialogue_understanding.commands.can_not_handle_command import ( + CannotHandleCommand, +) +from rasa.dialogue_understanding.commands.clarify_command import ClarifyCommand +from rasa.dialogue_understanding.commands.error_command import ErrorCommand +from rasa.dialogue_understanding.commands.set_slot_command import SetSlotCommand +from rasa.dialogue_understanding.commands.start_flow_command import StartFlowCommand +from rasa.dialogue_understanding.commands.human_handoff_command import ( + HumanHandoffCommand, +) +from rasa.dialogue_understanding.commands.correct_slots_command import ( + CorrectSlotsCommand, + CorrectedSlot, +) + + +__all__ = [ + "Command", + "FreeFormAnswerCommand", + "CancelFlowCommand", + "KnowledgeAnswerCommand", + "ChitChatAnswerCommand", + "CannotHandleCommand", + "ClarifyCommand", + "ErrorCommand", + "SetSlotCommand", + "StartFlowCommand", + "HumanHandoffCommand", + "CorrectSlotsCommand", + "CorrectedSlot", +] diff --git a/rasa/cdu/commands/can_not_handle_command.py b/rasa/dialogue_understanding/commands/can_not_handle_command.py similarity index 95% rename from rasa/cdu/commands/can_not_handle_command.py rename to rasa/dialogue_understanding/commands/can_not_handle_command.py index 1316770b05fe..631cbe378ec4 100644 --- a/rasa/cdu/commands/can_not_handle_command.py +++ b/rasa/dialogue_understanding/commands/can_not_handle_command.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from typing import Any, Dict, List -from rasa.cdu.commands import Command +from rasa.dialogue_understanding.commands import Command from rasa.shared.core.events import Event from rasa.shared.core.flows.flow import FlowsList from rasa.shared.core.trackers import DialogueStateTracker diff --git a/rasa/cdu/commands/cancel_flow_command.py b/rasa/dialogue_understanding/commands/cancel_flow_command.py similarity index 90% rename from rasa/cdu/commands/cancel_flow_command.py rename to rasa/dialogue_understanding/commands/cancel_flow_command.py index 9d75b89c6229..9a880111e7fe 100644 --- a/rasa/cdu/commands/cancel_flow_command.py +++ b/rasa/dialogue_understanding/commands/cancel_flow_command.py @@ -5,15 +5,15 @@ import structlog -from rasa.cdu.commands import Command -from rasa.cdu.patterns.cancel import CancelPatternFlowStackFrame -from rasa.cdu.stack.dialogue_stack import DialogueStack -from rasa.cdu.stack.frames import UserFlowStackFrame +from rasa.dialogue_understanding.commands import Command +from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.frames import UserFlowStackFrame from rasa.shared.core.constants import DIALOGUE_STACK_SLOT from rasa.shared.core.events import Event, SlotSet from rasa.shared.core.flows.flow import FlowsList from rasa.shared.core.trackers import DialogueStateTracker -from rasa.cdu.stack.utils import top_user_flow_frame +from rasa.dialogue_understanding.stack.utils import top_user_flow_frame structlogger = structlog.get_logger() diff --git a/rasa/cdu/commands/chit_chat_answer_command.py b/rasa/dialogue_understanding/commands/chit_chat_answer_command.py similarity index 86% rename from rasa/cdu/commands/chit_chat_answer_command.py rename to rasa/dialogue_understanding/commands/chit_chat_answer_command.py index 74b75081a25a..38be12d210b8 100644 --- a/rasa/cdu/commands/chit_chat_answer_command.py +++ b/rasa/dialogue_understanding/commands/chit_chat_answer_command.py @@ -2,9 +2,9 @@ from dataclasses import dataclass from typing import Any, Dict, List -from rasa.cdu.commands import FreeFormAnswerCommand -from rasa.cdu.stack.dialogue_stack import DialogueStack -from rasa.cdu.stack.frames.chit_chat_frame import ChitChatStackFrame +from rasa.dialogue_understanding.commands import FreeFormAnswerCommand +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.frames.chit_chat_frame import ChitChatStackFrame from rasa.shared.core.constants import DIALOGUE_STACK_SLOT from rasa.shared.core.events import Event, SlotSet from rasa.shared.core.flows.flow import FlowsList diff --git a/rasa/cdu/commands/clarify_command.py b/rasa/dialogue_understanding/commands/clarify_command.py similarity index 92% rename from rasa/cdu/commands/clarify_command.py rename to rasa/dialogue_understanding/commands/clarify_command.py index da60ba8b878c..69a413730e0f 100644 --- a/rasa/cdu/commands/clarify_command.py +++ b/rasa/dialogue_understanding/commands/clarify_command.py @@ -4,9 +4,9 @@ from typing import Any, Dict, List import structlog -from rasa.cdu.commands import Command -from rasa.cdu.patterns.clarify import ClarifyPatternFlowStackFrame -from rasa.cdu.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.commands import Command +from rasa.dialogue_understanding.patterns.clarify import ClarifyPatternFlowStackFrame +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack from rasa.shared.core.constants import DIALOGUE_STACK_SLOT from rasa.shared.core.events import Event, SlotSet from rasa.shared.core.flows.flow import FlowsList diff --git a/rasa/cdu/commands/command.py b/rasa/dialogue_understanding/commands/command.py similarity index 100% rename from rasa/cdu/commands/command.py rename to rasa/dialogue_understanding/commands/command.py diff --git a/rasa/cdu/commands/correct_slots_command.py b/rasa/dialogue_understanding/commands/correct_slots_command.py similarity index 96% rename from rasa/cdu/commands/correct_slots_command.py rename to rasa/dialogue_understanding/commands/correct_slots_command.py index 195d86b0b23e..6c9a425ba084 100644 --- a/rasa/cdu/commands/correct_slots_command.py +++ b/rasa/dialogue_understanding/commands/correct_slots_command.py @@ -5,18 +5,21 @@ import structlog -from rasa.cdu.commands import Command -from rasa.cdu.patterns.correction import ( +from rasa.dialogue_understanding.commands import Command +from rasa.dialogue_understanding.patterns.correction import ( FLOW_PATTERN_CORRECTION_ID, CorrectionPatternFlowStackFrame, ) -from rasa.cdu.stack.dialogue_stack import DialogueStack -from rasa.cdu.stack.frames.flow_frame import BaseFlowStackFrame, UserFlowStackFrame +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.frames.flow_stack_frame import ( + BaseFlowStackFrame, + UserFlowStackFrame, +) from rasa.shared.core.constants import DIALOGUE_STACK_SLOT from rasa.shared.core.events import Event, SlotSet from rasa.shared.core.flows.flow import END_STEP, ContinueFlowStep, FlowStep, FlowsList from rasa.shared.core.trackers import DialogueStateTracker -import rasa.cdu.stack.utils as utils +import rasa.dialogue_understanding.stack.utils as utils structlogger = structlog.get_logger() diff --git a/rasa/cdu/commands/error_command.py b/rasa/dialogue_understanding/commands/error_command.py similarity index 87% rename from rasa/cdu/commands/error_command.py rename to rasa/dialogue_understanding/commands/error_command.py index a7c25a7082ac..b5e62cb90d1c 100644 --- a/rasa/cdu/commands/error_command.py +++ b/rasa/dialogue_understanding/commands/error_command.py @@ -4,9 +4,11 @@ from typing import Any, Dict, List import structlog -from rasa.cdu.commands import Command -from rasa.cdu.patterns.internal_error import InternalErrorPatternFlowStackFrame -from rasa.cdu.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.commands import Command +from rasa.dialogue_understanding.patterns.internal_error import ( + InternalErrorPatternFlowStackFrame, +) +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack from rasa.shared.core.constants import DIALOGUE_STACK_SLOT from rasa.shared.core.events import Event, SlotSet from rasa.shared.core.flows.flow import FlowsList diff --git a/rasa/cdu/commands/free_form_answer_command.py b/rasa/dialogue_understanding/commands/free_form_answer_command.py similarity index 73% rename from rasa/cdu/commands/free_form_answer_command.py rename to rasa/dialogue_understanding/commands/free_form_answer_command.py index 6dc38808c192..13bac7e5ff5c 100644 --- a/rasa/cdu/commands/free_form_answer_command.py +++ b/rasa/dialogue_understanding/commands/free_form_answer_command.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from rasa.cdu.commands import Command +from rasa.dialogue_understanding.commands import Command @dataclass diff --git a/rasa/cdu/commands/human_handoff_command.py b/rasa/dialogue_understanding/commands/human_handoff_command.py similarity index 95% rename from rasa/cdu/commands/human_handoff_command.py rename to rasa/dialogue_understanding/commands/human_handoff_command.py index 819a6e6f4c19..a91630018c50 100644 --- a/rasa/cdu/commands/human_handoff_command.py +++ b/rasa/dialogue_understanding/commands/human_handoff_command.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from typing import Any, Dict, List -from rasa.cdu.commands import Command +from rasa.dialogue_understanding.commands import Command from rasa.shared.core.events import Event from rasa.shared.core.flows.flow import FlowsList from rasa.shared.core.trackers import DialogueStateTracker diff --git a/rasa/cdu/commands/knowledge_answer_command.py b/rasa/dialogue_understanding/commands/knowledge_answer_command.py similarity index 86% rename from rasa/cdu/commands/knowledge_answer_command.py rename to rasa/dialogue_understanding/commands/knowledge_answer_command.py index fa708cb1cb5c..3077dd44a739 100644 --- a/rasa/cdu/commands/knowledge_answer_command.py +++ b/rasa/dialogue_understanding/commands/knowledge_answer_command.py @@ -2,9 +2,9 @@ from dataclasses import dataclass from typing import Any, Dict, List -from rasa.cdu.commands import FreeFormAnswerCommand -from rasa.cdu.stack.dialogue_stack import DialogueStack -from rasa.cdu.stack.frames.search_frame import SearchStackFrame +from rasa.dialogue_understanding.commands import FreeFormAnswerCommand +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.frames.search_frame import SearchStackFrame from rasa.shared.core.constants import DIALOGUE_STACK_SLOT from rasa.shared.core.events import Event, SlotSet from rasa.shared.core.flows.flow import FlowsList diff --git a/rasa/cdu/commands/set_slot_command.py b/rasa/dialogue_understanding/commands/set_slot_command.py similarity index 92% rename from rasa/cdu/commands/set_slot_command.py rename to rasa/dialogue_understanding/commands/set_slot_command.py index 136f89f60adc..c17bfea9c15f 100644 --- a/rasa/cdu/commands/set_slot_command.py +++ b/rasa/dialogue_understanding/commands/set_slot_command.py @@ -4,9 +4,9 @@ from typing import Any, Dict, List import structlog -from rasa.cdu.commands import Command -from rasa.cdu.stack.dialogue_stack import DialogueStack -from rasa.cdu.stack.utils import filled_slots_for_active_flow +from rasa.dialogue_understanding.commands import Command +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.utils import filled_slots_for_active_flow from rasa.shared.core.events import Event, SlotSet from rasa.shared.core.flows.flow import FlowsList from rasa.shared.core.trackers import DialogueStateTracker diff --git a/rasa/cdu/commands/start_flow_command.py b/rasa/dialogue_understanding/commands/start_flow_command.py similarity index 87% rename from rasa/cdu/commands/start_flow_command.py rename to rasa/dialogue_understanding/commands/start_flow_command.py index 25a7804db2b3..cb3cd5d5166a 100644 --- a/rasa/cdu/commands/start_flow_command.py +++ b/rasa/dialogue_understanding/commands/start_flow_command.py @@ -4,10 +4,16 @@ from typing import Any, Dict, List import structlog -from rasa.cdu.commands import Command -from rasa.cdu.stack.dialogue_stack import DialogueStack -from rasa.cdu.stack.frames.flow_frame import FlowStackFrameType, UserFlowStackFrame -from rasa.cdu.stack.utils import top_user_flow_frame, user_flows_on_the_stack +from rasa.dialogue_understanding.commands import Command +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.frames.flow_stack_frame import ( + FlowStackFrameType, + UserFlowStackFrame, +) +from rasa.dialogue_understanding.stack.utils import ( + top_user_flow_frame, + user_flows_on_the_stack, +) from rasa.shared.core.constants import DIALOGUE_STACK_SLOT from rasa.shared.core.events import Event, SlotSet from rasa.shared.core.flows.flow import FlowsList diff --git a/rasa/dialogue_understanding/generator/__init__.py b/rasa/dialogue_understanding/generator/__init__.py new file mode 100644 index 000000000000..8d4efbb2bc73 --- /dev/null +++ b/rasa/dialogue_understanding/generator/__init__.py @@ -0,0 +1,6 @@ +from rasa.dialogue_understanding.generator.command_generator import CommandGenerator +from rasa.dialogue_understanding.generator.llm_command_generator import ( + LLMCommandGenerator, +) + +__all__ = ["CommandGenerator", "LLMCommandGenerator"] diff --git a/rasa/cdu/generator/command_generator.py b/rasa/dialogue_understanding/generator/command_generator.py similarity index 97% rename from rasa/cdu/generator/command_generator.py rename to rasa/dialogue_understanding/generator/command_generator.py index 2a0e4b0e3e2a..3e87102061f7 100644 --- a/rasa/cdu/generator/command_generator.py +++ b/rasa/dialogue_understanding/generator/command_generator.py @@ -1,5 +1,5 @@ from typing import List, Optional -from rasa.cdu.commands import Command +from rasa.dialogue_understanding.commands import Command from rasa.shared.core.flows.flow import FlowsList from rasa.shared.core.trackers import DialogueStateTracker from rasa.shared.nlu.training_data.message import Message diff --git a/rasa/cdu/generator/command_prompt_template.jinja2 b/rasa/dialogue_understanding/generator/command_prompt_template.jinja2 similarity index 100% rename from rasa/cdu/generator/command_prompt_template.jinja2 rename to rasa/dialogue_understanding/generator/command_prompt_template.jinja2 diff --git a/rasa/cdu/generator/llm_command_generator.py b/rasa/dialogue_understanding/generator/llm_command_generator.py similarity index 96% rename from rasa/cdu/generator/llm_command_generator.py rename to rasa/dialogue_understanding/generator/llm_command_generator.py index e91d584d372a..d4c4088c9d7a 100644 --- a/rasa/cdu/generator/llm_command_generator.py +++ b/rasa/dialogue_understanding/generator/llm_command_generator.py @@ -4,9 +4,9 @@ from jinja2 import Template import structlog -from rasa.cdu.stack.utils import top_flow_frame -from rasa.cdu.generator import CommandGenerator -from rasa.cdu.commands import ( +from rasa.dialogue_understanding.stack.utils import top_flow_frame +from rasa.dialogue_understanding.generator import CommandGenerator +from rasa.dialogue_understanding.commands import ( Command, ErrorCommand, SetSlotCommand, @@ -44,7 +44,7 @@ ) DEFAULT_COMMAND_PROMPT_TEMPLATE = importlib.resources.read_text( - "rasa.cdu.generator", "command_prompt_template.jinja2" + "rasa.dialogue_understanding.generator", "command_prompt_template.jinja2" ) structlogger = structlog.get_logger() @@ -201,11 +201,14 @@ def coerce_slot_value( slot = tracker.slots[slot_name] if isinstance(slot, BooleanSlot): - return bool_from_any(nullable_value) + try: + return bool_from_any(nullable_value) + except (ValueError, TypeError): + return None elif isinstance(slot, FloatSlot): try: return float(nullable_value) - except ValueError: + except (ValueError, TypeError): return None else: return nullable_value diff --git a/rasa/cdu/generator/nlu_command_adapter.py b/rasa/dialogue_understanding/generator/nlu_command_adapter.py similarity index 100% rename from rasa/cdu/generator/nlu_command_adapter.py rename to rasa/dialogue_understanding/generator/nlu_command_adapter.py diff --git a/rasa/cli/initial_project/actions/__init__.py b/rasa/dialogue_understanding/patterns/__init__.py similarity index 100% rename from rasa/cli/initial_project/actions/__init__.py rename to rasa/dialogue_understanding/patterns/__init__.py diff --git a/rasa/cdu/patterns/cancel.py b/rasa/dialogue_understanding/patterns/cancel.py similarity index 95% rename from rasa/cdu/patterns/cancel.py rename to rasa/dialogue_understanding/patterns/cancel.py index 0a11d02c4f18..fa1eda6316c4 100644 --- a/rasa/cdu/patterns/cancel.py +++ b/rasa/dialogue_understanding/patterns/cancel.py @@ -4,10 +4,13 @@ from typing import Any, Dict, List, Optional import structlog -from rasa.cdu.stack.dialogue_stack import ( +from rasa.dialogue_understanding.stack.dialogue_stack import ( DialogueStack, ) -from rasa.cdu.stack.frames import PatternFlowStackFrame, BaseFlowStackFrame +from rasa.dialogue_understanding.stack.frames import ( + PatternFlowStackFrame, + BaseFlowStackFrame, +) from rasa.core.actions import action from rasa.core.channels.channel import OutputChannel from rasa.core.nlg.generator import NaturalLanguageGenerator diff --git a/rasa/cdu/patterns/clarify.py b/rasa/dialogue_understanding/patterns/clarify.py similarity index 96% rename from rasa/cdu/patterns/clarify.py rename to rasa/dialogue_understanding/patterns/clarify.py index ddc238e501e3..4a7b1df074f0 100644 --- a/rasa/cdu/patterns/clarify.py +++ b/rasa/dialogue_understanding/patterns/clarify.py @@ -4,10 +4,10 @@ from typing import Any, Dict, List, Optional import structlog -from rasa.cdu.stack.dialogue_stack import ( +from rasa.dialogue_understanding.stack.dialogue_stack import ( DialogueStack, ) -from rasa.cdu.stack.frames import PatternFlowStackFrame +from rasa.dialogue_understanding.stack.frames import PatternFlowStackFrame from rasa.core.actions import action from rasa.core.channels.channel import OutputChannel from rasa.core.nlg.generator import NaturalLanguageGenerator diff --git a/rasa/cdu/patterns/collect_information.py b/rasa/dialogue_understanding/patterns/collect_information.py similarity index 94% rename from rasa/cdu/patterns/collect_information.py rename to rasa/dialogue_understanding/patterns/collect_information.py index e36241ff88db..73361185e4ea 100644 --- a/rasa/cdu/patterns/collect_information.py +++ b/rasa/dialogue_understanding/patterns/collect_information.py @@ -2,9 +2,9 @@ from dataclasses import dataclass from typing import Any, Dict, List, Optional -from rasa.cdu.stack.dialogue_stack import DialogueStackFrame +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStackFrame from rasa.shared.constants import RASA_DEFAULT_FLOW_PATTERN_PREFIX -from rasa.cdu.stack.frames import PatternFlowStackFrame +from rasa.dialogue_understanding.stack.frames import PatternFlowStackFrame FLOW_PATTERN_COLLECT_INFORMATION = ( RASA_DEFAULT_FLOW_PATTERN_PREFIX + "ask_collect_information" diff --git a/rasa/cdu/patterns/completed.py b/rasa/dialogue_understanding/patterns/completed.py similarity index 94% rename from rasa/cdu/patterns/completed.py rename to rasa/dialogue_understanding/patterns/completed.py index ff6a080f3c54..5852be45c1d8 100644 --- a/rasa/cdu/patterns/completed.py +++ b/rasa/dialogue_understanding/patterns/completed.py @@ -3,7 +3,7 @@ from dataclasses import dataclass from typing import Any, Dict from rasa.shared.constants import RASA_DEFAULT_FLOW_PATTERN_PREFIX -from rasa.cdu.stack.frames import PatternFlowStackFrame +from rasa.dialogue_understanding.stack.frames import PatternFlowStackFrame FLOW_PATTERN_COMPLETED = RASA_DEFAULT_FLOW_PATTERN_PREFIX + "completed" diff --git a/rasa/cdu/patterns/continue_interrupted.py b/rasa/dialogue_understanding/patterns/continue_interrupted.py similarity index 94% rename from rasa/cdu/patterns/continue_interrupted.py rename to rasa/dialogue_understanding/patterns/continue_interrupted.py index 5e66196cab57..1137408c8383 100644 --- a/rasa/cdu/patterns/continue_interrupted.py +++ b/rasa/dialogue_understanding/patterns/continue_interrupted.py @@ -3,7 +3,7 @@ from dataclasses import dataclass from typing import Any, Dict from rasa.shared.constants import RASA_DEFAULT_FLOW_PATTERN_PREFIX -from rasa.cdu.stack.frames import PatternFlowStackFrame +from rasa.dialogue_understanding.stack.frames import PatternFlowStackFrame FLOW_PATTERN_CONTINUE_INTERRUPTED = ( diff --git a/rasa/cdu/patterns/correction.py b/rasa/dialogue_understanding/patterns/correction.py similarity index 95% rename from rasa/cdu/patterns/correction.py rename to rasa/dialogue_understanding/patterns/correction.py index a258a4e7d6a9..e8626fa6ff20 100644 --- a/rasa/cdu/patterns/correction.py +++ b/rasa/dialogue_understanding/patterns/correction.py @@ -2,10 +2,10 @@ from dataclasses import dataclass, field from typing import Any, Dict, Text, List, Optional -from rasa.cdu.patterns.collect_information import ( +from rasa.dialogue_understanding.patterns.collect_information import ( CollectInformationPatternFlowStackFrame, ) -from rasa.cdu.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack from rasa.shared.constants import RASA_DEFAULT_FLOW_PATTERN_PREFIX from rasa.shared.core.constants import ( DIALOGUE_STACK_SLOT, @@ -19,7 +19,7 @@ import structlog from rasa.core.actions import action from rasa.core.channels import OutputChannel -from rasa.cdu.stack.frames import ( +from rasa.dialogue_understanding.stack.frames import ( BaseFlowStackFrame, PatternFlowStackFrame, ) diff --git a/rasa/cdu/patterns/default_flows_for_patterns.yml b/rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml similarity index 100% rename from rasa/cdu/patterns/default_flows_for_patterns.yml rename to rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml diff --git a/rasa/cdu/patterns/internal_error.py b/rasa/dialogue_understanding/patterns/internal_error.py similarity index 93% rename from rasa/cdu/patterns/internal_error.py rename to rasa/dialogue_understanding/patterns/internal_error.py index fe57d6be9c7e..405953d9cf06 100644 --- a/rasa/cdu/patterns/internal_error.py +++ b/rasa/dialogue_understanding/patterns/internal_error.py @@ -3,7 +3,7 @@ from rasa.shared.constants import RASA_DEFAULT_FLOW_PATTERN_PREFIX from dataclasses import dataclass from typing import Any, Dict -from rasa.cdu.stack.frames import PatternFlowStackFrame +from rasa.dialogue_understanding.stack.frames import PatternFlowStackFrame FLOW_PATTERN_INTERNAL_ERROR_ID = RASA_DEFAULT_FLOW_PATTERN_PREFIX + "internal_error" diff --git a/rasa/cli/initial_project_dm2/actions/__init__.py b/rasa/dialogue_understanding/processor/__init__.py similarity index 100% rename from rasa/cli/initial_project_dm2/actions/__init__.py rename to rasa/dialogue_understanding/processor/__init__.py diff --git a/rasa/cdu/processor/command_processor.py b/rasa/dialogue_understanding/processor/command_processor.py similarity index 95% rename from rasa/cdu/processor/command_processor.py rename to rasa/dialogue_understanding/processor/command_processor.py index c258b0255a9d..c9919900b448 100644 --- a/rasa/cdu/processor/command_processor.py +++ b/rasa/dialogue_understanding/processor/command_processor.py @@ -1,7 +1,7 @@ from typing import List, Optional, Type import structlog -from rasa.cdu.commands import ( +from rasa.dialogue_understanding.commands import ( CancelFlowCommand, Command, CorrectSlotsCommand, @@ -9,17 +9,20 @@ SetSlotCommand, FreeFormAnswerCommand, ) -from rasa.cdu.patterns.collect_information import ( +from rasa.dialogue_understanding.patterns.collect_information import ( CollectInformationPatternFlowStackFrame, ) -from rasa.cdu.patterns.correction import ( +from rasa.dialogue_understanding.patterns.correction import ( CorrectionPatternFlowStackFrame, ) -from rasa.cdu.stack.dialogue_stack import DialogueStack -from rasa.cdu.stack.frames import ( +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.frames import ( BaseFlowStackFrame, ) -from rasa.cdu.stack.utils import filled_slots_for_active_flow, top_flow_frame +from rasa.dialogue_understanding.stack.utils import ( + filled_slots_for_active_flow, + top_flow_frame, +) from rasa.shared.core.events import Event, SlotSet from rasa.shared.core.flows.flow import ( FlowsList, diff --git a/rasa/cdu/processor/command_processor_component.py b/rasa/dialogue_understanding/processor/command_processor_component.py similarity index 92% rename from rasa/cdu/processor/command_processor_component.py rename to rasa/dialogue_understanding/processor/command_processor_component.py index 38ff56890f7f..e557ab2c83c8 100644 --- a/rasa/cdu/processor/command_processor_component.py +++ b/rasa/dialogue_understanding/processor/command_processor_component.py @@ -1,7 +1,7 @@ from __future__ import annotations from typing import Any, Dict, List, Text -from rasa.cdu.processor.command_processor import execute_commands +from rasa.dialogue_understanding.processor.command_processor import execute_commands from rasa.engine.graph import ExecutionContext, GraphComponent from rasa.engine.storage.resource import Resource diff --git a/rasa/dialogue_understanding/stack/__init__.py b/rasa/dialogue_understanding/stack/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/rasa/cdu/stack/dialogue_stack.py b/rasa/dialogue_understanding/stack/dialogue_stack.py similarity index 98% rename from rasa/cdu/stack/dialogue_stack.py rename to rasa/dialogue_understanding/stack/dialogue_stack.py index 9b18ed64e4cb..5059b919523d 100644 --- a/rasa/cdu/stack/dialogue_stack.py +++ b/rasa/dialogue_understanding/stack/dialogue_stack.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from typing import Any, Callable, Dict, List, Optional -from rasa.cdu.stack.frames import DialogueStackFrame +from rasa.dialogue_understanding.stack.frames import DialogueStackFrame from rasa.shared.core.constants import ( DIALOGUE_STACK_SLOT, ) diff --git a/rasa/dialogue_understanding/stack/frames/__init__.py b/rasa/dialogue_understanding/stack/frames/__init__.py new file mode 100644 index 000000000000..57bdfa9ce178 --- /dev/null +++ b/rasa/dialogue_understanding/stack/frames/__init__.py @@ -0,0 +1,19 @@ +from rasa.dialogue_understanding.stack.frames.dialogue_stack_frame import ( + DialogueStackFrame, +) +from rasa.dialogue_understanding.stack.frames.flow_stack_frame import ( + UserFlowStackFrame, + BaseFlowStackFrame, +) +from rasa.dialogue_understanding.stack.frames.pattern_frame import PatternFlowStackFrame +from rasa.dialogue_understanding.stack.frames.search_frame import SearchStackFrame +from rasa.dialogue_understanding.stack.frames.chit_chat_frame import ChitChatStackFrame + +__all__ = [ + "DialogueStackFrame", + "BaseFlowStackFrame", + "PatternFlowStackFrame", + "UserFlowStackFrame", + "SearchStackFrame", + "ChitChatStackFrame", +] diff --git a/rasa/cdu/stack/frames/chit_chat_frame.py b/rasa/dialogue_understanding/stack/frames/chit_chat_frame.py similarity index 90% rename from rasa/cdu/stack/frames/chit_chat_frame.py rename to rasa/dialogue_understanding/stack/frames/chit_chat_frame.py index 42cf4af0482a..27e05583066b 100644 --- a/rasa/cdu/stack/frames/chit_chat_frame.py +++ b/rasa/dialogue_understanding/stack/frames/chit_chat_frame.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from typing import Any, Dict -from rasa.cdu.stack.frames import DialogueStackFrame +from rasa.dialogue_understanding.stack.frames import DialogueStackFrame @dataclass diff --git a/rasa/cdu/stack/frames/dialogue_stack_frame.py b/rasa/dialogue_understanding/stack/frames/dialogue_stack_frame.py similarity index 80% rename from rasa/cdu/stack/frames/dialogue_stack_frame.py rename to rasa/dialogue_understanding/stack/frames/dialogue_stack_frame.py index a268eebc7e35..077bf7c2115f 100644 --- a/rasa/cdu/stack/frames/dialogue_stack_frame.py +++ b/rasa/dialogue_understanding/stack/frames/dialogue_stack_frame.py @@ -6,6 +6,7 @@ from typing import Any, Dict, List, Tuple import structlog +from rasa.shared.exceptions import RasaException import rasa.shared.utils.common from rasa.shared.utils.io import random_string @@ -23,6 +24,18 @@ def generate_stack_frame_id() -> str: return random_string(8) +class InvalidStackFrameType(RasaException): + """Raised if a stack frame type is invalid.""" + + def __init__(self, frame_type: str) -> None: + """Creates a `InvalidStackFrameType`. + + Args: + frame_type: The invalid frame type. + """ + super().__init__(f"Invalid stack frame type '{frame_type}'.") + + @dataclass class DialogueStackFrame: """Represents the current flow step.""" @@ -81,14 +94,15 @@ def create_typed_frame(data: Dict[str, Any]) -> DialogueStackFrame: Returns: The created `DialogueStackFrame`. """ - for cls in rasa.shared.utils.common.all_subclasses(DialogueStackFrame): + typ = data.get("type") + for clazz in rasa.shared.utils.common.all_subclasses(DialogueStackFrame): try: - if data.get("type") == cls.type(): - return cls.from_dict(data) + if typ == clazz.type(): + return clazz.from_dict(data) except NotImplementedError: # we don't want to raise an error if the frame type is not # implemented, as this is ok to be raised by an abstract class pass else: structlogger.warning("dialogue_stack.frame.unknown_type", data=data) - raise NotImplementedError + raise InvalidStackFrameType(typ) diff --git a/rasa/cdu/stack/frames/flow_frame.py b/rasa/dialogue_understanding/stack/frames/flow_stack_frame.py similarity index 77% rename from rasa/cdu/stack/frames/flow_frame.py rename to rasa/dialogue_understanding/stack/frames/flow_stack_frame.py index c168ea508b01..ceeb4d5bfe39 100644 --- a/rasa/cdu/stack/frames/flow_frame.py +++ b/rasa/dialogue_understanding/stack/frames/flow_stack_frame.py @@ -3,8 +3,21 @@ from enum import Enum from typing import Any, Dict, Optional -from rasa.cdu.stack.frames import DialogueStackFrame +from rasa.dialogue_understanding.stack.frames import DialogueStackFrame from rasa.shared.core.flows.flow import START_STEP, Flow, FlowStep, FlowsList +from rasa.shared.exceptions import RasaException + + +class InvalidFlowStackFrameType(RasaException): + """Raised if the stack frame type is invalid.""" + + def __init__(self, frame_type: Optional[str]) -> None: + """Creates a `InvalidFlowStackFrameType`. + + Args: + frame_type: The invalid stack frame type. + """ + super().__init__(f"Invalid stack frame type '{frame_type}'.") class FlowStackFrameType(str, Enum): @@ -26,7 +39,13 @@ class FlowStackFrameType(str, Enum): @staticmethod def from_str(typ: Optional[str]) -> "FlowStackFrameType": - """Creates a `StackFrameType` from a string.""" + """Creates a `FlowStackFrameType` from a string. + + Args: + typ: The string to create the `FlowStackFrameType` from. + + Returns: + The created `FlowStackFrameType`.""" if typ is None: return FlowStackFrameType.REGULAR elif typ == FlowStackFrameType.INTERRUPT.value: @@ -36,7 +55,7 @@ def from_str(typ: Optional[str]) -> "FlowStackFrameType": elif typ == FlowStackFrameType.REGULAR.value: return FlowStackFrameType.REGULAR else: - raise NotImplementedError + raise InvalidFlowStackFrameType(typ) class InvalidFlowIdException(Exception): @@ -72,7 +91,13 @@ class BaseFlowStackFrame(DialogueStackFrame): """The ID of the current step.""" def flow(self, all_flows: FlowsList) -> Flow: - """Returns the current flow.""" + """Returns the current flow. + + Args: + all_flows: All flows in the assistant. + + Returns: + The current flow.""" flow = all_flows.flow_by_id(self.flow_id) if not flow: # we shouldn't ever end up with a frame that belongs to a non @@ -81,7 +106,13 @@ def flow(self, all_flows: FlowsList) -> Flow: return flow def step(self, all_flows: FlowsList) -> FlowStep: - """Returns the current flow step.""" + """Returns the current flow step. + + Args: + all_flows: All flows in the assistant. + + Returns: + The current flow step.""" flow = self.flow(all_flows) step = flow.step_by_id(self.step_id) if not step: diff --git a/rasa/cdu/stack/frames/pattern_frame.py b/rasa/dialogue_understanding/stack/frames/pattern_frame.py similarity index 69% rename from rasa/cdu/stack/frames/pattern_frame.py rename to rasa/dialogue_understanding/stack/frames/pattern_frame.py index 3e7594b03994..54e10f2f688e 100644 --- a/rasa/cdu/stack/frames/pattern_frame.py +++ b/rasa/dialogue_understanding/stack/frames/pattern_frame.py @@ -1,6 +1,6 @@ from dataclasses import dataclass -from rasa.cdu.stack.frames import BaseFlowStackFrame +from rasa.dialogue_understanding.stack.frames import BaseFlowStackFrame @dataclass diff --git a/rasa/cdu/stack/frames/search_frame.py b/rasa/dialogue_understanding/stack/frames/search_frame.py similarity index 90% rename from rasa/cdu/stack/frames/search_frame.py rename to rasa/dialogue_understanding/stack/frames/search_frame.py index 68ea6c0c04c2..efa7aa0bd14e 100644 --- a/rasa/cdu/stack/frames/search_frame.py +++ b/rasa/dialogue_understanding/stack/frames/search_frame.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from typing import Any, Dict -from rasa.cdu.stack.frames import DialogueStackFrame +from rasa.dialogue_understanding.stack.frames import DialogueStackFrame @dataclass diff --git a/rasa/cdu/stack/utils.py b/rasa/dialogue_understanding/stack/utils.py similarity index 92% rename from rasa/cdu/stack/utils.py rename to rasa/dialogue_understanding/stack/utils.py index 744f0de08e49..144638e2b71e 100644 --- a/rasa/cdu/stack/utils.py +++ b/rasa/dialogue_understanding/stack/utils.py @@ -1,10 +1,10 @@ from typing import Optional, Set -from rasa.cdu.patterns.collect_information import ( +from rasa.dialogue_understanding.patterns.collect_information import ( CollectInformationPatternFlowStackFrame, ) -from rasa.cdu.stack.frames import BaseFlowStackFrame -from rasa.cdu.stack.dialogue_stack import DialogueStack -from rasa.cdu.stack.frames import UserFlowStackFrame +from rasa.dialogue_understanding.stack.frames import BaseFlowStackFrame +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.frames import UserFlowStackFrame from rasa.shared.core.flows.flow import FlowsList diff --git a/rasa/engine/recipes/default_components.py b/rasa/engine/recipes/default_components.py index 9d46707d9345..11c2212cc41f 100644 --- a/rasa/engine/recipes/default_components.py +++ b/rasa/engine/recipes/default_components.py @@ -1,7 +1,9 @@ from rasa.nlu.classifiers.diet_classifier import DIETClassifier from rasa.nlu.classifiers.fallback_classifier import FallbackClassifier from rasa.nlu.classifiers.keyword_intent_classifier import KeywordIntentClassifier -from rasa.cdu.generator.llm_command_generator import LLMCommandGenerator +from rasa.dialogue_understanding.generator.llm_command_generator import ( + LLMCommandGenerator, +) from rasa.nlu.classifiers.logistic_regression_classifier import ( LogisticRegressionClassifier, ) diff --git a/rasa/engine/recipes/default_recipe.py b/rasa/engine/recipes/default_recipe.py index 13153ab70a65..ca47bf658fbd 100644 --- a/rasa/engine/recipes/default_recipe.py +++ b/rasa/engine/recipes/default_recipe.py @@ -14,7 +14,9 @@ CoreFeaturizationCollector, ) from rasa.graph_components.providers.flows_provider import FlowsProvider -from rasa.cdu.processor.command_processor_component import CommandProcessorComponent +from rasa.dialogue_understanding.processor.command_processor_component import ( + CommandProcessorComponent, +) from rasa.plugin import plugin_manager from rasa.shared.exceptions import FileNotFoundException from rasa.core.policies.ensemble import DefaultPolicyPredictionEnsemble diff --git a/rasa/model_training.py b/rasa/model_training.py index 8dea1f4c7c08..59c1dc473777 100644 --- a/rasa/model_training.py +++ b/rasa/model_training.py @@ -176,9 +176,9 @@ def train( rasa.shared.utils.common.mark_as_experimental_feature("end-to-end training") training_type = TrainingType.END_TO_END - if stories.is_empty() and nlu_data.contains_no_pure_nlu_data(): + if stories.is_empty() and nlu_data.contains_no_pure_nlu_data() and flows.is_empty(): rasa.shared.utils.cli.print_error( - "No training data given. Please provide stories and NLU data in " + "No training data given. Please provide stories, flows or NLU data in " "order to train a Rasa model using the '--data' argument." ) return TrainingResult(code=1) @@ -199,9 +199,13 @@ def train( training_type = TrainingType.NLU # We will train nlu if there are any nlu example, including from e2e stories. - elif nlu_data.contains_no_pure_nlu_data() and not nlu_data.has_e2e_examples(): + elif ( + nlu_data.contains_no_pure_nlu_data() + and not nlu_data.has_e2e_examples() + and flows.is_empty() + ): rasa.shared.utils.cli.print_warning( - "No NLU data present. Just a Rasa Core model will be trained." + "No NLU data present. No NLU model will be trained." ) training_type = TrainingType.CORE diff --git a/rasa/shared/constants.py b/rasa/shared/constants.py index 6773e701929e..6aacd53811ed 100644 --- a/rasa/shared/constants.py +++ b/rasa/shared/constants.py @@ -62,6 +62,7 @@ DEFAULT_SENDER_ID = "default" UTTER_PREFIX = "utter_" +UTTER_ASK_PREFIX = "utter_ask_" FLOW_PREFIX = "flow_" ASSISTANT_ID_KEY = "assistant_id" diff --git a/rasa/shared/importers/importer.py b/rasa/shared/importers/importer.py index 09a45aff359c..a03c92298940 100644 --- a/rasa/shared/importers/importer.py +++ b/rasa/shared/importers/importer.py @@ -391,7 +391,7 @@ def load_default_pattern_flows() -> FlowsList: from rasa.shared.core.flows.yaml_flows_io import YAMLFlowsReader default_flows_file = pkg_resources.resource_filename( - "rasa.cdu.patterns", DEFAULT_PATTERN_FLOWS_FILE_NAME + "rasa.dialogue_understanding.patterns", DEFAULT_PATTERN_FLOWS_FILE_NAME ) return YAMLFlowsReader.read_from_file(default_flows_file) @@ -400,7 +400,7 @@ def load_default_pattern_flows() -> FlowsList: def load_default_pattern_flows_domain() -> Domain: """Loads the default flows from the file system.""" default_flows_file = pkg_resources.resource_filename( - "rasa.cdu.patterns", DEFAULT_PATTERN_FLOWS_FILE_NAME + "rasa.dialogue_understanding.patterns", DEFAULT_PATTERN_FLOWS_FILE_NAME ) return Domain.from_path(default_flows_file) diff --git a/rasa/validator.py b/rasa/validator.py index 97271937394e..c2d7fed6742e 100644 --- a/rasa/validator.py +++ b/rasa/validator.py @@ -3,6 +3,11 @@ from typing import Set, Text, Optional, Dict, Any, List import rasa.core.training.story_conflict +from rasa.shared.core.flows.flow import ( + ActionFlowStep, + CollectInformationFlowStep, + FlowsList, +) import rasa.shared.nlu.constants from rasa.shared.constants import ( ASSISTANT_ID_DEFAULT_VALUE, @@ -10,6 +15,7 @@ CONFIG_MANDATORY_KEYS, DOCS_URL_DOMAINS, DOCS_URL_FORMS, + UTTER_ASK_PREFIX, UTTER_PREFIX, DOCS_URL_ACTIONS, REQUIRED_SLOTS_KEY, @@ -37,6 +43,7 @@ def __init__( domain: Domain, intents: TrainingData, story_graph: StoryGraph, + flows: FlowsList, config: Optional[Dict[Text, Any]], ) -> None: """Initializes the Validator object. @@ -50,6 +57,7 @@ def __init__( self.domain = domain self.intents = intents self.story_graph = story_graph + self.flows = flows self.config = config or {} @classmethod @@ -59,8 +67,9 @@ def from_importer(cls, importer: TrainingDataImporter) -> "Validator": story_graph = importer.get_stories() intents = importer.get_nlu_data() config = importer.get_config() + flows = importer.get_flows() - return cls(domain, intents, story_graph, config) + return cls(domain, intents, story_graph, flows, config) def _non_default_intents(self) -> List[Text]: return [ @@ -172,15 +181,25 @@ def _gather_utterance_actions(self) -> Set[Text]: } return domain_responses.union(data_responses) - def verify_utterances_in_stories(self, ignore_warnings: bool = True) -> bool: - """Verifies usage of utterances in stories. - - Checks whether utterances used in the stories are valid, - and whether all valid utterances are used in stories. - """ - everything_is_alright = True + def _does_story_only_use_valid_actions( + self, used_utterances_in_stories: Set[str], utterance_actions: List[str] + ) -> bool: + """Checks if all utterances used in stories are valid.""" + has_no_warnings = True + for used_utterance in used_utterances_in_stories: + if used_utterance not in utterance_actions: + rasa.shared.utils.io.raise_warning( + f"The action '{used_utterance}' is used in the stories, " + f"but is not a valid utterance action. Please make sure " + f"the action is listed in your domain and there is a " + f"template defined with its name.", + docs=DOCS_URL_ACTIONS + "#utterance-actions", + ) + has_no_warnings = False + return has_no_warnings - utterance_actions = self._gather_utterance_actions() + def _utterances_used_in_stories(self) -> Set[str]: + """Return all utterances which are used in stories.""" stories_utterances = set() for story in self.story_graph.story_steps: @@ -199,21 +218,50 @@ def verify_utterances_in_stories(self, ignore_warnings: bool = True) -> bool: # we already processed this one before, we only want to warn once continue - if event.action_name not in utterance_actions: - rasa.shared.utils.io.raise_warning( - f"The action '{event.action_name}' is used in the stories, " - f"but is not a valid utterance action. Please make sure " - f"the action is listed in your domain and there is a " - f"template defined with its name.", - docs=DOCS_URL_ACTIONS + "#utterance-actions", - ) - everything_is_alright = ignore_warnings stories_utterances.add(event.action_name) + return stories_utterances + + def _utterances_used_in_flows(self) -> Set[str]: + """Return all utterances which are used in flows.""" + flow_utterances = set() + + for flow in self.flows.underlying_flows: + for step in flow.steps: + if isinstance(step, ActionFlowStep) and step.action.startswith( + UTTER_PREFIX + ): + flow_utterances.add(step.action) + if isinstance(step, CollectInformationFlowStep): + flow_utterances.add(UTTER_ASK_PREFIX + step.collect_information) + return flow_utterances + + def verify_utterances_in_dialogues(self, ignore_warnings: bool = True) -> bool: + """Verifies usage of utterances in stories or flows. + + Checks whether utterances used in the stories are valid, + and whether all valid utterances are used in stories. + """ + everything_is_alright = True + + utterance_actions = self._gather_utterance_actions() + + stories_utterances = self._utterances_used_in_stories() + flow_utterances = self._utterances_used_in_flows() + + all_used_utterances = flow_utterances.union(stories_utterances) + + everything_is_alright = ( + ignore_warnings + or self._does_story_only_use_valid_actions( + stories_utterances, utterance_actions + ) + ) for utterance in utterance_actions: - if utterance not in stories_utterances: + if utterance not in all_used_utterances: rasa.shared.utils.io.raise_warning( - f"The utterance '{utterance}' is not used in any story or rule." + f"The utterance '{utterance}' is not used in " + f"any story, rule or flow." ) everything_is_alright = ignore_warnings or everything_is_alright @@ -332,7 +380,7 @@ def verify_nlu(self, ignore_warnings: bool = True) -> bool: ) logger.info("Validating utterances...") - stories_are_valid = self.verify_utterances_in_stories(ignore_warnings) + stories_are_valid = self.verify_utterances_in_dialogues(ignore_warnings) return intents_are_valid and stories_are_valid and there_is_no_duplication def verify_form_slots(self) -> bool: diff --git a/rasa/version.py b/rasa/version.py index b35e57e0504a..6e999d3f7755 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__ = "3.8.0a4" +__version__ = "3.8.0a7" diff --git a/tests/cdu/commands/test_can_not_handle_command.py b/tests/cdu/commands/test_can_not_handle_command.py index 84f2df134b1d..d0aa676f020a 100644 --- a/tests/cdu/commands/test_can_not_handle_command.py +++ b/tests/cdu/commands/test_can_not_handle_command.py @@ -1,4 +1,6 @@ -from rasa.cdu.commands.can_not_handle_command import CannotHandleCommand +from rasa.dialogue_understanding.commands.can_not_handle_command import ( + CannotHandleCommand, +) from rasa.shared.core.events import UserUttered from rasa.shared.core.trackers import DialogueStateTracker diff --git a/tests/cdu/commands/test_cancel_flow_command.py b/tests/cdu/commands/test_cancel_flow_command.py index 9c16ea02d735..0aa2ea74113a 100644 --- a/tests/cdu/commands/test_cancel_flow_command.py +++ b/tests/cdu/commands/test_cancel_flow_command.py @@ -1,10 +1,10 @@ import pytest -from rasa.cdu.commands.cancel_flow_command import CancelFlowCommand -from rasa.cdu.patterns.collect_information import ( +from rasa.dialogue_understanding.commands.cancel_flow_command import CancelFlowCommand +from rasa.dialogue_understanding.patterns.collect_information import ( CollectInformationPatternFlowStackFrame, ) -from rasa.cdu.stack.dialogue_stack import DialogueStack -from rasa.cdu.stack.frames.flow_frame import UserFlowStackFrame +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.frames.flow_stack_frame import UserFlowStackFrame from rasa.shared.core.constants import DIALOGUE_STACK_SLOT from rasa.shared.core.events import SlotSet from rasa.shared.core.trackers import DialogueStateTracker diff --git a/tests/cdu/commands/test_chit_chat_answer_command.py b/tests/cdu/commands/test_chit_chat_answer_command.py index e2a42bbfc4f4..b222f91780af 100644 --- a/tests/cdu/commands/test_chit_chat_answer_command.py +++ b/tests/cdu/commands/test_chit_chat_answer_command.py @@ -1,4 +1,6 @@ -from rasa.cdu.commands.chit_chat_answer_command import ChitChatAnswerCommand +from rasa.dialogue_understanding.commands.chit_chat_answer_command import ( + ChitChatAnswerCommand, +) from rasa.shared.core.events import SlotSet, UserUttered from rasa.shared.core.trackers import DialogueStateTracker diff --git a/tests/cdu/commands/test_clarify_command.py b/tests/cdu/commands/test_clarify_command.py index 47af776f8a5a..3dba7a707d1e 100644 --- a/tests/cdu/commands/test_clarify_command.py +++ b/tests/cdu/commands/test_clarify_command.py @@ -1,5 +1,5 @@ import pytest -from rasa.cdu.commands.clarify_command import ClarifyCommand +from rasa.dialogue_understanding.commands.clarify_command import ClarifyCommand from rasa.shared.core.constants import DIALOGUE_STACK_SLOT from rasa.shared.core.events import SlotSet from rasa.shared.core.trackers import DialogueStateTracker diff --git a/tests/cdu/commands/test_command.py b/tests/cdu/commands/test_command.py index a78eb367a28a..7235e23af7ca 100644 --- a/tests/cdu/commands/test_command.py +++ b/tests/cdu/commands/test_command.py @@ -1,6 +1,6 @@ import pytest -from rasa.cdu.commands.command import Command -from rasa.cdu.commands.set_slot_command import SetSlotCommand +from rasa.dialogue_understanding.commands.command import Command +from rasa.dialogue_understanding.commands.set_slot_command import SetSlotCommand def test_command_from_json(): diff --git a/tests/cdu/commands/test_correct_slots_command.py b/tests/cdu/commands/test_correct_slots_command.py index cafbd6f35d32..a0d5e8ef130d 100644 --- a/tests/cdu/commands/test_correct_slots_command.py +++ b/tests/cdu/commands/test_correct_slots_command.py @@ -1,12 +1,17 @@ from typing import Any, Dict, List import pytest -from rasa.cdu.commands.correct_slots_command import CorrectSlotsCommand, CorrectedSlot -from rasa.cdu.patterns.collect_information import ( +from rasa.dialogue_understanding.commands.correct_slots_command import ( + CorrectSlotsCommand, + CorrectedSlot, +) +from rasa.dialogue_understanding.patterns.collect_information import ( CollectInformationPatternFlowStackFrame, ) -from rasa.cdu.patterns.correction import CorrectionPatternFlowStackFrame -from rasa.cdu.stack.dialogue_stack import DialogueStack -from rasa.cdu.stack.frames.flow_frame import UserFlowStackFrame +from rasa.dialogue_understanding.patterns.correction import ( + CorrectionPatternFlowStackFrame, +) +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.frames.flow_stack_frame import UserFlowStackFrame from rasa.shared.core.constants import DIALOGUE_STACK_SLOT from rasa.shared.core.events import SlotSet from rasa.shared.core.trackers import DialogueStateTracker diff --git a/tests/cdu/commands/test_error_command.py b/tests/cdu/commands/test_error_command.py index 10e09157d736..187b4d37e98f 100644 --- a/tests/cdu/commands/test_error_command.py +++ b/tests/cdu/commands/test_error_command.py @@ -1,4 +1,4 @@ -from rasa.cdu.commands.error_command import ErrorCommand +from rasa.dialogue_understanding.commands.error_command import ErrorCommand from rasa.shared.core.events import SlotSet, UserUttered from rasa.shared.core.trackers import DialogueStateTracker diff --git a/tests/cdu/commands/test_human_handoff_command.py b/tests/cdu/commands/test_human_handoff_command.py index 3f9f0030a5fd..df4e274fbe28 100644 --- a/tests/cdu/commands/test_human_handoff_command.py +++ b/tests/cdu/commands/test_human_handoff_command.py @@ -1,4 +1,6 @@ -from rasa.cdu.commands.human_handoff_command import HumanHandoffCommand +from rasa.dialogue_understanding.commands.human_handoff_command import ( + HumanHandoffCommand, +) from rasa.shared.core.events import UserUttered from rasa.shared.core.trackers import DialogueStateTracker diff --git a/tests/cdu/commands/test_konwledge_answer_command.py b/tests/cdu/commands/test_konwledge_answer_command.py index 0ea12650df5e..6aa407f5e35e 100644 --- a/tests/cdu/commands/test_konwledge_answer_command.py +++ b/tests/cdu/commands/test_konwledge_answer_command.py @@ -1,4 +1,6 @@ -from rasa.cdu.commands.knowledge_answer_command import KnowledgeAnswerCommand +from rasa.dialogue_understanding.commands.knowledge_answer_command import ( + KnowledgeAnswerCommand, +) from rasa.shared.core.events import SlotSet, UserUttered from rasa.shared.core.trackers import DialogueStateTracker diff --git a/tests/cdu/commands/test_set_slot_command.py b/tests/cdu/commands/test_set_slot_command.py index 00443cc06024..84e3867c2c0c 100644 --- a/tests/cdu/commands/test_set_slot_command.py +++ b/tests/cdu/commands/test_set_slot_command.py @@ -1,5 +1,5 @@ import pytest -from rasa.cdu.commands.set_slot_command import SetSlotCommand +from rasa.dialogue_understanding.commands.set_slot_command import SetSlotCommand from rasa.shared.core.constants import DIALOGUE_STACK_SLOT from rasa.shared.core.events import SlotSet from rasa.shared.core.flows.flow import FlowsList diff --git a/tests/cdu/commands/test_start_flow_command.py b/tests/cdu/commands/test_start_flow_command.py index f220421e136e..2ac2895b44d1 100644 --- a/tests/cdu/commands/test_start_flow_command.py +++ b/tests/cdu/commands/test_start_flow_command.py @@ -1,5 +1,5 @@ import pytest -from rasa.cdu.commands.start_flow_command import StartFlowCommand +from rasa.dialogue_understanding.commands.start_flow_command import StartFlowCommand from rasa.shared.core.constants import DIALOGUE_STACK_SLOT from rasa.shared.core.events import SlotSet from rasa.shared.core.trackers import DialogueStateTracker diff --git a/tests/cdu/stack/__init__.py b/tests/cdu/stack/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/cdu/stack/frames/__init__.py b/tests/cdu/stack/frames/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/cdu/stack/frames/test_chit_chat_frame.py b/tests/cdu/stack/frames/test_chit_chat_frame.py new file mode 100644 index 000000000000..7e74ad1c87ad --- /dev/null +++ b/tests/cdu/stack/frames/test_chit_chat_frame.py @@ -0,0 +1,13 @@ +from rasa.dialogue_understanding.stack.frames.chit_chat_frame import ChitChatStackFrame + + +def test_chit_chat_frame_type(): + # types should be stable as they are persisted as part of the tracker + frame = ChitChatStackFrame(frame_id="test") + assert frame.type() == "chitchat" + + +def test_chit_chat_frame_from_dict(): + frame = ChitChatStackFrame.from_dict({"frame_id": "test"}) + assert frame.frame_id == "test" + assert frame.type() == "chitchat" diff --git a/tests/cdu/stack/frames/test_dialogue_stack_frame.py b/tests/cdu/stack/frames/test_dialogue_stack_frame.py new file mode 100644 index 000000000000..155791aca2f9 --- /dev/null +++ b/tests/cdu/stack/frames/test_dialogue_stack_frame.py @@ -0,0 +1,74 @@ +from dataclasses import dataclass +from typing import Any, Dict + +import pytest +from rasa.dialogue_understanding.stack.frames.dialogue_stack_frame import ( + DialogueStackFrame, + InvalidStackFrameType, + generate_stack_frame_id, +) + + +@dataclass +class MockStackFrame(DialogueStackFrame): + @classmethod + def type(cls) -> str: + return "mock" + + +@dataclass +class MockStackFrameWithAdditionalProperty(DialogueStackFrame): + + foo: str = "" + + @classmethod + def type(cls) -> str: + return "mock_with_additional_property" + + @staticmethod + def from_dict(data: Dict[str, Any]) -> DialogueStackFrame: + return MockStackFrameWithAdditionalProperty( + frame_id=data["frame_id"], foo=data["foo"] + ) + + +def test_generate_stack_frame_id_generates_different_ids(): + assert generate_stack_frame_id() != generate_stack_frame_id() + + +def test_dialogue_stack_frame_as_dict(): + frame = MockStackFrame(frame_id="test") + + assert frame.as_dict() == {"frame_id": "test", "type": "mock"} + + +def test_dialogue_stack_frame_as_dict_contains_additional_attributes(): + frame = MockStackFrameWithAdditionalProperty(foo="foofoo", frame_id="test") + + assert frame.as_dict() == { + "frame_id": "test", + "type": "mock_with_additional_property", + "foo": "foofoo", + } + + +def test_dialogue_stack_frame_context_as_dict(): + frame = MockStackFrameWithAdditionalProperty(foo="foofoo", frame_id="test") + + assert frame.context_as_dict([]) == { + "frame_id": "test", + "type": "mock_with_additional_property", + "foo": "foofoo", + } + + +def test_create_typed_frame(): + frame = MockStackFrameWithAdditionalProperty(foo="foofoo", frame_id="test") + + assert DialogueStackFrame.create_typed_frame(frame.as_dict()) == frame + + +def test_create_typed_frame_with_unknown_type(): + + with pytest.raises(InvalidStackFrameType): + DialogueStackFrame.create_typed_frame({"type": "unknown"}) diff --git a/tests/cdu/stack/frames/test_flow_frame.py b/tests/cdu/stack/frames/test_flow_frame.py new file mode 100644 index 000000000000..3cf75fc4c96d --- /dev/null +++ b/tests/cdu/stack/frames/test_flow_frame.py @@ -0,0 +1,115 @@ +import pytest +from rasa.dialogue_understanding.stack.frames.flow_stack_frame import ( + InvalidFlowIdException, + InvalidFlowStackFrameType, + InvalidFlowStepIdException, + UserFlowStackFrame, + FlowStackFrameType, +) +from rasa.shared.core.flows.flow import ActionFlowStep, Flow, FlowLinks, FlowsList + + +def test_flow_frame_type(): + # types should be stable as they are persisted as part of the tracker + frame = UserFlowStackFrame(frame_id="test", flow_id="foo", step_id="bar") + assert frame.type() == "flow" + + +def test_flow_frame_from_dict(): + frame = UserFlowStackFrame.from_dict( + {"frame_id": "test", "flow_id": "foo", "step_id": "bar"} + ) + assert frame.frame_id == "test" + assert frame.flow_id == "foo" + assert frame.step_id == "bar" + assert frame.type() == "flow" + assert frame.frame_type == FlowStackFrameType.REGULAR + + +@pytest.mark.parametrize( + "typ,expected_type", + [ + ("regular", FlowStackFrameType.REGULAR), + ("link", FlowStackFrameType.LINK), + ("interrupt", FlowStackFrameType.INTERRUPT), + ], +) +def test_flow_stack_frame_type_from_str(typ: str, expected_type: FlowStackFrameType): + assert FlowStackFrameType.from_str(typ) == expected_type + + +def test_flow_stack_frame_type_from_str_invalid(): + with pytest.raises(InvalidFlowStackFrameType): + FlowStackFrameType.from_str("invalid") + + +def test_flow_stack_frame_type_from_str_none(): + assert FlowStackFrameType.from_str(None) == FlowStackFrameType.REGULAR + + +def test_flow_get_flow(): + frame = UserFlowStackFrame(frame_id="test", flow_id="foo", step_id="bar") + flow = Flow(id="foo", steps=[], name="foo flow", description="foo flow description") + all_flows = FlowsList(flows=[flow]) + assert frame.flow(all_flows) == flow + + +def test_flow_get_flow_non_existant_id(): + frame = UserFlowStackFrame(frame_id="test", flow_id="unknown", step_id="bar") + all_flows = FlowsList( + flows=[ + Flow( + id="foo", steps=[], name="foo flow", description="foo flow description" + ) + ] + ) + with pytest.raises(InvalidFlowIdException): + frame.flow(all_flows) + + +def test_flow_get_step(): + frame = UserFlowStackFrame(frame_id="test", flow_id="foo", step_id="my_step") + step = ActionFlowStep( + action="action_listen", + id="my_step", + description=None, + metadata={}, + next=FlowLinks(links=[]), + ) + all_flows = FlowsList( + flows=[ + Flow( + id="foo", + steps=[step], + name="foo flow", + description="foo flow description", + ) + ] + ) + assert frame.step(all_flows) == step + + +def test_flow_get_step_non_existant_id(): + frame = UserFlowStackFrame(frame_id="test", flow_id="foo", step_id="unknown") + all_flows = FlowsList( + flows=[ + Flow( + id="foo", steps=[], name="foo flow", description="foo flow description" + ) + ] + ) + with pytest.raises(InvalidFlowStepIdException): + frame.step(all_flows) + + +def test_flow_get_step_non_existant_flow_id(): + frame = UserFlowStackFrame(frame_id="test", flow_id="unknown", step_id="unknown") + all_flows = FlowsList( + flows=[ + Flow( + id="foo", steps=[], name="foo flow", description="foo flow description" + ) + ] + ) + with pytest.raises(InvalidFlowIdException): + frame.step(all_flows) diff --git a/tests/cdu/stack/frames/test_search_frame.py b/tests/cdu/stack/frames/test_search_frame.py new file mode 100644 index 000000000000..249fc7e7697e --- /dev/null +++ b/tests/cdu/stack/frames/test_search_frame.py @@ -0,0 +1,13 @@ +from rasa.dialogue_understanding.stack.frames.search_frame import SearchStackFrame + + +def test_search_frame_type(): + # types should be stable as they are persisted as part of the tracker + frame = SearchStackFrame(frame_id="test") + assert frame.type() == "search" + + +def test_search_frame_from_dict(): + frame = SearchStackFrame.from_dict({"frame_id": "test"}) + assert frame.frame_id == "test" + assert frame.type() == "search" diff --git a/tests/cdu/stack/test_dialogue_stack.py b/tests/cdu/stack/test_dialogue_stack.py new file mode 100644 index 000000000000..c5f36a3d0521 --- /dev/null +++ b/tests/cdu/stack/test_dialogue_stack.py @@ -0,0 +1,217 @@ +import dataclasses +from rasa.dialogue_understanding.patterns.collect_information import ( + CollectInformationPatternFlowStackFrame, +) +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.frames.flow_stack_frame import UserFlowStackFrame + + +def test_dialogue_stack_from_dict(): + stack = DialogueStack.from_dict( + [ + { + "type": "flow", + "flow_id": "foo", + "step_id": "first_step", + "frame_id": "some-frame-id", + }, + { + "type": "pattern_collect_information", + "collect_information": "foo", + "frame_id": "some-other-id", + "step_id": "__start__", + "flow_id": "pattern_ask_collect_information", + }, + ] + ) + + assert len(stack.frames) == 2 + + assert stack.frames[0] == UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + assert stack.frames[1] == CollectInformationPatternFlowStackFrame( + collect_information="foo", frame_id="some-other-id" + ) + + +def test_dialogue_stack_from_dict_handles_empty(): + stack = DialogueStack.from_dict([]) + assert stack.frames == [] + + +def test_dialogue_stack_as_dict(): + stack = DialogueStack( + frames=[ + UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ), + CollectInformationPatternFlowStackFrame( + collect_information="foo", frame_id="some-other-id" + ), + ] + ) + + assert stack.as_dict() == [ + { + "type": "flow", + "flow_id": "foo", + "frame_type": "regular", + "step_id": "first_step", + "frame_id": "some-frame-id", + }, + { + "type": "pattern_collect_information", + "collect_information": "foo", + "frame_id": "some-other-id", + "step_id": "__start__", + "flow_id": "pattern_ask_collect_information", + }, + ] + + +def test_dialogue_stack_as_dict_handles_empty(): + stack = DialogueStack(frames=[]) + assert stack.as_dict() == [] + + +def test_push_to_empty_stack(): + stack = DialogueStack(frames=[]) + stack.push( + UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + ) + + assert stack.frames == [ + UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + ] + + +def test_push_to_non_empty_stack(): + user_frame = UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + pattern_frame = CollectInformationPatternFlowStackFrame( + collect_information="foo", frame_id="some-other-id" + ) + + stack = DialogueStack(frames=[user_frame]) + stack.push(pattern_frame) + assert stack.top() == pattern_frame + assert stack.frames == [user_frame, pattern_frame] + + +def test_push_to_index(): + user_frame = UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + pattern_frame = CollectInformationPatternFlowStackFrame( + collect_information="foo", frame_id="some-other-id" + ) + + stack = DialogueStack(frames=[user_frame]) + stack.push(pattern_frame, index=0) + assert stack.top() == user_frame + assert stack.frames == [pattern_frame, user_frame] + + +def test_dialogue_stack_update(): + user_frame = UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + stack = DialogueStack(frames=[user_frame]) + updated_user_frame = dataclasses.replace(user_frame, step_id="second_step") + stack.update(updated_user_frame) + assert stack.top() == updated_user_frame + assert stack.frames == [updated_user_frame] + + +def test_update_empty_stack(): + stack = DialogueStack(frames=[]) + stack.update( + UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + ) + + assert stack.frames == [ + UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + ] + + +def test_pop_frame(): + user_frame = UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + pattern_frame = CollectInformationPatternFlowStackFrame( + collect_information="foo", frame_id="some-other-id" + ) + + stack = DialogueStack(frames=[]) + stack.push(user_frame) + stack.push(pattern_frame) + assert stack.pop() == pattern_frame + assert stack.frames == [user_frame] + + +def test_top_empty_stack(): + stack = DialogueStack(frames=[]) + assert stack.top() is None + + +def test_top(): + user_frame = UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + pattern_frame = CollectInformationPatternFlowStackFrame( + collect_information="foo", frame_id="some-other-id" + ) + + stack = DialogueStack(frames=[]) + stack.push(user_frame) + stack.push(pattern_frame) + assert stack.top() == pattern_frame + + +def test_get_current_context_empty_stack(): + stack = DialogueStack(frames=[]) + assert stack.current_context() == {} + + +def test_get_current_context(): + user_frame = UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + pattern_frame = CollectInformationPatternFlowStackFrame( + collect_information="foo", frame_id="some-other-id" + ) + + stack = DialogueStack(frames=[]) + stack.push(user_frame) + stack.push(pattern_frame) + assert stack.current_context() == { + "flow_id": "foo", + "frame_id": "some-frame-id", + "frame_type": "regular", + "step_id": "first_step", + "type": "flow", + "collect_information": "foo", + } + + +def test_is_empty_on_empty(): + stack = DialogueStack(frames=[]) + assert stack.is_empty() is True + + +def test_is_empty_on_non_empty(): + user_frame = UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + stack = DialogueStack(frames=[user_frame]) + assert stack.is_empty() is False diff --git a/tests/cdu/stack/test_utils.py b/tests/cdu/stack/test_utils.py new file mode 100644 index 000000000000..984be9ffc3b7 --- /dev/null +++ b/tests/cdu/stack/test_utils.py @@ -0,0 +1,201 @@ +from rasa.dialogue_understanding.patterns.collect_information import ( + CollectInformationPatternFlowStackFrame, +) +from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack +from rasa.dialogue_understanding.stack.frames.chit_chat_frame import ChitChatStackFrame +from rasa.dialogue_understanding.stack.frames.flow_stack_frame import UserFlowStackFrame +from rasa.dialogue_understanding.stack.utils import ( + filled_slots_for_active_flow, + top_flow_frame, + top_user_flow_frame, + user_flows_on_the_stack, +) +from tests.utilities import flows_from_str + + +def test_top_flow_frame_ignores_pattern(): + pattern_frame = CollectInformationPatternFlowStackFrame( + collect_information="foo", frame_id="some-other-id" + ) + user_frame = UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + stack = DialogueStack( + frames=[ + user_frame, + pattern_frame, + ] + ) + + assert top_flow_frame(stack, ignore_collect_information_pattern=True) == user_frame + + +def test_top_flow_frame_uses_pattern(): + pattern_frame = CollectInformationPatternFlowStackFrame( + collect_information="foo", frame_id="some-other-id" + ) + user_frame = UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + stack = DialogueStack(frames=[user_frame, pattern_frame]) + + assert ( + top_flow_frame(stack, ignore_collect_information_pattern=False) == pattern_frame + ) + + +def test_top_flow_frame_handles_empty(): + stack = DialogueStack(frames=[]) + assert top_flow_frame(stack) is None + + +def test_top_user_flow_frame(): + pattern_frame = CollectInformationPatternFlowStackFrame( + collect_information="foo", frame_id="some-other-id" + ) + user_frame = UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + stack = DialogueStack(frames=[user_frame, pattern_frame]) + + assert top_user_flow_frame(stack) == user_frame + + +def test_top_user_flow_frame_handles_empty(): + stack = DialogueStack(frames=[]) + assert top_user_flow_frame(stack) is None + + +def test_user_flows_on_the_stack(): + pattern_frame = CollectInformationPatternFlowStackFrame( + collect_information="foo", frame_id="some-other-id" + ) + user_frame = UserFlowStackFrame( + flow_id="foo", step_id="first_step", frame_id="some-frame-id" + ) + another_user_frame = UserFlowStackFrame( + flow_id="bar", step_id="first_step", frame_id="some-other-other-id" + ) + stack = DialogueStack(frames=[user_frame, pattern_frame, another_user_frame]) + + assert user_flows_on_the_stack(stack) == {"foo", "bar"} + + +def test_user_flows_on_the_stack_handles_empty(): + stack = DialogueStack(frames=[]) + assert user_flows_on_the_stack(stack) == set() + + +def test_filled_slots_for_active_flow(): + all_flows = flows_from_str( + """ + flows: + my_flow: + name: foo flow + steps: + - id: collect_foo + collect_information: foo + next: collect_bar + - id: collect_bar + collect_information: bar + next: collect_baz + - id: collect_baz + collect_information: baz + """ + ) + + user_frame = UserFlowStackFrame( + flow_id="my_flow", step_id="collect_bar", frame_id="some-frame-id" + ) + stack = DialogueStack(frames=[user_frame]) + + assert filled_slots_for_active_flow(stack, all_flows) == {"foo", "bar"} + + +def test_filled_slots_for_active_flow_handles_empty(): + all_flows = flows_from_str( + """ + flows: + my_flow: + name: foo flow + steps: + - id: collect_foo + collect_information: foo + next: collect_bar + - id: collect_bar + collect_information: bar + next: collect_baz + - id: collect_baz + collect_information: baz + """ + ) + + stack = DialogueStack(frames=[]) + assert filled_slots_for_active_flow(stack, all_flows) == set() + + +def test_filled_slots_for_active_flow_skips_chitchat(): + all_flows = flows_from_str( + """ + flows: + my_flow: + name: foo flow + steps: + - id: collect_foo + collect_information: foo + next: collect_bar + - id: collect_bar + collect_information: bar + next: collect_baz + - id: collect_baz + collect_information: baz + """ + ) + + user_frame = UserFlowStackFrame( + flow_id="my_flow", step_id="collect_bar", frame_id="some-frame-id" + ) + chitchat_frame = ChitChatStackFrame(frame_id="some-other-id") + stack = DialogueStack(frames=[user_frame, chitchat_frame]) + + assert filled_slots_for_active_flow(stack, all_flows) == {"foo", "bar"} + + +def test_filled_slots_for_active_flow_only_collects_till_top_most_user_flow_frame(): + all_flows = flows_from_str( + """ + flows: + my_flow: + name: foo flow + steps: + - id: collect_foo + collect_information: foo + next: collect_bar + - id: collect_bar + collect_information: bar + next: collect_baz + - id: collect_baz + collect_information: baz + my_other_flow: + name: foo flow + steps: + - id: collect_foo2 + collect_information: foo2 + next: collect_bar2 + - id: collect_bar2 + collect_information: bar2 + next: collect_baz2 + - id: collect_baz2 + collect_information: baz2 + """ + ) + + user_frame = UserFlowStackFrame( + flow_id="my_flow", step_id="collect_bar", frame_id="some-frame-id" + ) + another_user_frame = UserFlowStackFrame( + flow_id="my_other_flow", step_id="collect_bar2", frame_id="some-other-id" + ) + stack = DialogueStack(frames=[another_user_frame, user_frame]) + + assert filled_slots_for_active_flow(stack, all_flows) == {"foo", "bar"} diff --git a/tests/cli/test_rasa_data.py b/tests/cli/test_rasa_data.py index aecbc3d2f65b..e8c2f869b9ca 100644 --- a/tests/cli/test_rasa_data.py +++ b/tests/cli/test_rasa_data.py @@ -204,7 +204,7 @@ def test_data_validate_not_used_warning( for warning in [ "The intent 'goodbye' is not used in any story or rule.", - "The utterance 'utter_chatter' is not used in any story or rule.", + "The utterance 'utter_chatter' is not used in any story, rule or flow.", ]: assert warning in str(result.stderr) diff --git a/tests/cli/test_rasa_init.py b/tests/cli/test_rasa_init.py index 62932d5039e3..c53c864a1c13 100644 --- a/tests/cli/test_rasa_init.py +++ b/tests/cli/test_rasa_init.py @@ -4,6 +4,7 @@ from typing import Callable from _pytest.pytester import RunResult from _pytest.monkeypatch import MonkeyPatch +import pytest from rasa.cli import scaffold from tests.conftest import enable_cache @@ -46,7 +47,7 @@ def test_init_help(run: Callable[..., RunResult]): help_text = f"""usage: {RASA_EXE} init [-h] [-v] [-vv] [--quiet] [--logging-config-file LOGGING_CONFIG_FILE] [--no-prompt] - [--init-dir INIT_DIR] [--dm2]""" + [--init-dir INIT_DIR] [--template {{default,tutorial,dm2}}]""" lines = help_text.split("\n") # expected help text lines should appear somewhere in the output @@ -101,3 +102,18 @@ def mock_get_config(*args): scaffold.init_project(args, str(new_project_folder_path)) assert os.getcwd() == str(new_project_folder_path) assert os.path.exists(".rasa/cache") + + +@pytest.mark.parametrize("template", ["default", "tutorial", "dm2"]) +def test_train_data_non_default_template( + run_with_stdin: Callable[..., RunResult], + tmp_path: Path, + monkeypatch: MonkeyPatch, + template: str, +): + run_with_stdin( + "init", "--quiet", "--init-dir", str(tmp_path), stdin=b"N" + ) # avoid training an initial model + + # picking domain as it is present in all templates + assert (tmp_path / "domain.yml").exists() diff --git a/tests/cli/test_rasa_train.py b/tests/cli/test_rasa_train.py index 1436f6f7d13a..769a28c6b8ef 100644 --- a/tests/cli/test_rasa_train.py +++ b/tests/cli/test_rasa_train.py @@ -531,7 +531,7 @@ def test_train_validation_warnings( assert result.ret == 0 for warning in [ "The intent 'goodbye' is not used in any story or rule.", - "The utterance 'utter_chatter' is not used in any story or rule.", + "The utterance 'utter_chatter' is not used in any story, rule or flow.", ]: assert warning in str(result.stderr) diff --git a/tests/engine/recipes/test_default_recipe.py b/tests/engine/recipes/test_default_recipe.py index 6a07adf3b9f4..387709d16470 100644 --- a/tests/engine/recipes/test_default_recipe.py +++ b/tests/engine/recipes/test_default_recipe.py @@ -528,7 +528,10 @@ def test_train_core_without_nlu_pipeline(): @pytest.mark.parametrize( "config_path, expected_keys_to_configure", [ - (Path("rasa/cli/initial_project/config.yml"), {"pipeline", "policies"}), + ( + Path("rasa/cli/project_templates/default/config.yml"), + {"pipeline", "policies"}, + ), (CONFIG_FOLDER / "config_policies_empty.yml", {"policies"}), (CONFIG_FOLDER / "config_pipeline_empty.yml", {"pipeline"}), (CONFIG_FOLDER / "config_policies_missing.yml", {"policies"}), diff --git a/tests/graph_components/validators/test_default_recipe_validator.py b/tests/graph_components/validators/test_default_recipe_validator.py index 6eabab8da458..f462afeeff3a 100644 --- a/tests/graph_components/validators/test_default_recipe_validator.py +++ b/tests/graph_components/validators/test_default_recipe_validator.py @@ -1015,7 +1015,9 @@ def test_nlu_training_data_validation(): def test_no_warnings_with_default_project(tmp_path: Path): - rasa.utils.common.copy_directory(Path("rasa/cli/initial_project"), tmp_path) + rasa.utils.common.copy_directory( + Path("rasa/cli/project_templates/default/"), tmp_path + ) importer = TrainingDataImporter.load_from_config( config_path=str(tmp_path / "config.yml"), diff --git a/tests/test_validator.py b/tests/test_validator.py index 90084e4fa569..d901d73fb333 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -100,7 +100,7 @@ def test_verify_valid_responses(): ], ) validator = Validator.from_importer(importer) - assert validator.verify_utterances_in_stories() + assert validator.verify_utterances_in_dialogues() def test_verify_valid_responses_in_rules(nlu_data_path: Text): @@ -113,7 +113,7 @@ def test_verify_valid_responses_in_rules(nlu_data_path: Text): ) validator = Validator.from_importer(importer) # force validator to not ignore warnings (default is True) - assert not validator.verify_utterances_in_stories(ignore_warnings=False) + assert not validator.verify_utterances_in_dialogues(ignore_warnings=False) def test_verify_story_structure(stories_path: Text): @@ -289,9 +289,9 @@ def test_verify_logging_message_for_unused_utterance( caplog.clear() with pytest.warns(UserWarning) as record: # force validator to not ignore warnings (default is True) - validator_under_test.verify_utterances_in_stories(ignore_warnings=False) + validator_under_test.verify_utterances_in_dialogues(ignore_warnings=False) - assert "The utterance 'utter_chatter' is not used in any story or rule." in ( + assert "The utterance 'utter_chatter' is not used in any story, rule or flow." in ( m.message.args[0] for m in record ) @@ -451,7 +451,7 @@ def test_response_selector_responses_in_domain_no_errors(): ) validator = Validator.from_importer(importer) # force validator to not ignore warnings (default is True) - assert validator.verify_utterances_in_stories(ignore_warnings=False) + assert validator.verify_utterances_in_dialogues(ignore_warnings=False) def test_invalid_domain_mapping_policy(): @@ -829,9 +829,9 @@ def test_verify_utterances_does_not_error_when_no_utterance_template_provided( validator = Validator.from_importer(importer) # force validator to not ignore warnings (default is True) - assert not validator.verify_utterances_in_stories(ignore_warnings=False) + assert not validator.verify_utterances_in_dialogues(ignore_warnings=False) # test whether ignoring warnings actually works - assert validator.verify_utterances_in_stories(ignore_warnings=True) + assert validator.verify_utterances_in_dialogues(ignore_warnings=True) @pytest.mark.parametrize(