rasa-pro 3.11.0__py3-none-any.whl → 3.11.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rasa-pro might be problematic. Click here for more details.
- README.md +396 -17
- rasa/__main__.py +15 -31
- rasa/api.py +1 -5
- rasa/cli/arguments/default_arguments.py +2 -1
- rasa/cli/arguments/shell.py +1 -5
- rasa/cli/arguments/train.py +0 -14
- rasa/cli/e2e_test.py +1 -1
- rasa/cli/evaluate.py +8 -8
- rasa/cli/inspect.py +7 -15
- rasa/cli/interactive.py +0 -1
- rasa/cli/llm_fine_tuning.py +1 -1
- rasa/cli/project_templates/calm/config.yml +7 -5
- rasa/cli/project_templates/calm/endpoints.yml +2 -15
- rasa/cli/project_templates/tutorial/config.yml +5 -8
- rasa/cli/project_templates/tutorial/data/flows.yml +1 -1
- rasa/cli/project_templates/tutorial/data/patterns.yml +0 -5
- rasa/cli/project_templates/tutorial/domain.yml +0 -14
- rasa/cli/project_templates/tutorial/endpoints.yml +0 -5
- rasa/cli/run.py +1 -1
- rasa/cli/scaffold.py +2 -4
- rasa/cli/studio/studio.py +8 -18
- rasa/cli/studio/upload.py +15 -0
- rasa/cli/train.py +0 -3
- rasa/cli/utils.py +1 -6
- rasa/cli/x.py +8 -8
- rasa/constants.py +1 -3
- rasa/core/actions/action.py +33 -75
- rasa/core/actions/e2e_stub_custom_action_executor.py +1 -5
- rasa/core/actions/http_custom_action_executor.py +0 -4
- rasa/core/channels/__init__.py +0 -2
- rasa/core/channels/channel.py +0 -20
- rasa/core/channels/development_inspector.py +3 -10
- rasa/core/channels/inspector/dist/assets/{arc-bc141fb2.js → arc-86942a71.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-be2db283.js → c4Diagram-d0fbc5ce-b0290676.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-55366915.js → classDiagram-936ed81e-f6405f6e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-bb529518.js → classDiagram-v2-c3cb15f1-ef61ac77.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{createText-62fc7601-b0ec81d6.js → createText-62fc7601-f0411e58.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-6166330c.js → edges-f2ad444c-7dcc4f3b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-5ccc6a8e.js → erDiagram-9d236eb7-e0c092d7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-fca3bfe4.js → flowDb-1972c806-fba2e3ce.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-4739080f.js → flowDiagram-7ea5b25a-7a70b71a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-24a5f41a.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-7c1b0e0f.js → flowchart-elk-definition-abe16c3d-00a59b68.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-772fd050.js → ganttDiagram-9b5ea136-293c91fa.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-8eae1dc9.js → gitGraphDiagram-99d0ae7c-07b2d68c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-f55afcdf.js → index-2c4b9a3b-bc959fbd.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-e7cef9de.js → index-3a8a5a28.js} +143 -143
- rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-124d4a14.js → infoDiagram-736b4530-4a350f72.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-7c4fae44.js → journeyDiagram-df861f2b-af464fb7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-b9885fb6.js → layout-0071f036.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-7c59abb6.js → line-2f73cc83.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-4776f780.js → linear-f014b4cc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-2332c46c.js → mindmap-definition-beec6740-d2426fb6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-8fb39303.js → pieDiagram-dbbf0591-776f01a2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-3c7180a2.js → quadrantDiagram-4d7f4fd6-82e00b57.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-e910bcb8.js → requirementDiagram-6fc4c22a-ea13c6bb.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-ead16c89.js → sankeyDiagram-8f13d901-1feca7e9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-29a02a19.js → sequenceDiagram-b655622a-070c61d2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-042b3137.js → stateDiagram-59f0c015-24f46263.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-2178c0f3.js → stateDiagram-v2-2b26beab-c9056051.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-080da4f6-23ffa4fc.js → styles-080da4f6-08abc34a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-94f59763.js → styles-3dcbcfbf-bc74c25a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9c745c82-78a6bebc.js → styles-9c745c82-4e5d66de.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-eae2a6f6.js → svgDrawCommon-4835440b-849c4517.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-5c968d92.js → timeline-definition-5b62e21b-d0fb1598.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-fd3db0d5.js → xychartDiagram-2b33534f-04d115e2.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/src/App.tsx +1 -1
- rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +3 -6
- rasa/core/channels/socketio.py +2 -7
- rasa/core/channels/telegram.py +1 -1
- rasa/core/channels/twilio.py +1 -1
- rasa/core/channels/voice_ready/audiocodes.py +4 -15
- rasa/core/channels/voice_ready/jambonz.py +4 -15
- rasa/core/channels/voice_ready/twilio_voice.py +21 -6
- rasa/core/channels/voice_ready/utils.py +5 -6
- rasa/core/channels/voice_stream/asr/asr_engine.py +1 -19
- rasa/core/channels/voice_stream/asr/asr_event.py +0 -5
- rasa/core/channels/voice_stream/asr/deepgram.py +15 -28
- rasa/core/channels/voice_stream/audio_bytes.py +0 -1
- rasa/core/channels/voice_stream/tts/azure.py +3 -9
- rasa/core/channels/voice_stream/tts/cartesia.py +8 -12
- rasa/core/channels/voice_stream/tts/tts_engine.py +1 -11
- rasa/core/channels/voice_stream/twilio_media_streams.py +19 -28
- rasa/core/channels/voice_stream/util.py +4 -4
- rasa/core/channels/voice_stream/voice_channel.py +42 -222
- rasa/core/featurizers/single_state_featurizer.py +1 -22
- rasa/core/featurizers/tracker_featurizers.py +18 -115
- rasa/core/information_retrieval/qdrant.py +0 -1
- rasa/core/nlg/contextual_response_rephraser.py +25 -44
- rasa/core/persistor.py +34 -191
- rasa/core/policies/enterprise_search_policy.py +60 -119
- rasa/core/policies/flows/flow_executor.py +4 -7
- rasa/core/policies/intentless_policy.py +22 -82
- rasa/core/policies/ted_policy.py +33 -58
- rasa/core/policies/unexpected_intent_policy.py +7 -15
- rasa/core/processor.py +13 -89
- rasa/core/run.py +2 -2
- rasa/core/training/interactive.py +35 -34
- rasa/core/utils.py +22 -58
- rasa/dialogue_understanding/coexistence/llm_based_router.py +12 -39
- rasa/dialogue_understanding/commands/__init__.py +0 -4
- rasa/dialogue_understanding/commands/change_flow_command.py +0 -6
- rasa/dialogue_understanding/commands/utils.py +0 -5
- rasa/dialogue_understanding/generator/constants.py +0 -2
- rasa/dialogue_understanding/generator/flow_retrieval.py +4 -49
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +23 -37
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +10 -57
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +1 -19
- rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2 +0 -3
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +10 -90
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +0 -53
- rasa/dialogue_understanding/processor/command_processor.py +1 -21
- rasa/e2e_test/assertions.py +16 -133
- rasa/e2e_test/assertions_schema.yml +0 -23
- rasa/e2e_test/e2e_test_case.py +6 -85
- rasa/e2e_test/e2e_test_runner.py +4 -6
- rasa/e2e_test/utils/io.py +1 -3
- rasa/engine/loader.py +0 -12
- rasa/engine/validation.py +11 -541
- rasa/keys +1 -0
- rasa/llm_fine_tuning/notebooks/unsloth_finetuning.ipynb +407 -0
- rasa/model_training.py +7 -29
- rasa/nlu/classifiers/diet_classifier.py +25 -38
- rasa/nlu/classifiers/logistic_regression_classifier.py +9 -22
- rasa/nlu/classifiers/sklearn_intent_classifier.py +16 -37
- rasa/nlu/extractors/crf_entity_extractor.py +50 -93
- rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +16 -45
- rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +17 -52
- rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +3 -5
- rasa/nlu/tokenizers/whitespace_tokenizer.py +14 -3
- rasa/server.py +1 -3
- rasa/shared/constants.py +0 -61
- rasa/shared/core/constants.py +0 -9
- rasa/shared/core/domain.py +5 -8
- rasa/shared/core/flows/flow.py +0 -5
- rasa/shared/core/flows/flows_list.py +1 -5
- rasa/shared/core/flows/flows_yaml_schema.json +0 -10
- rasa/shared/core/flows/validation.py +0 -96
- rasa/shared/core/flows/yaml_flows_io.py +4 -13
- rasa/shared/core/slots.py +0 -5
- rasa/shared/importers/importer.py +2 -19
- rasa/shared/importers/rasa.py +1 -5
- rasa/shared/nlu/training_data/features.py +2 -120
- rasa/shared/nlu/training_data/formats/rasa_yaml.py +3 -18
- rasa/shared/providers/_configs/azure_openai_client_config.py +3 -5
- rasa/shared/providers/_configs/openai_client_config.py +1 -1
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +0 -1
- rasa/shared/providers/_configs/utils.py +0 -16
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +29 -18
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +21 -54
- rasa/shared/providers/embedding/default_litellm_embedding_client.py +0 -24
- rasa/shared/providers/llm/_base_litellm_client.py +31 -63
- rasa/shared/providers/llm/azure_openai_llm_client.py +29 -50
- rasa/shared/providers/llm/default_litellm_llm_client.py +0 -24
- rasa/shared/providers/llm/self_hosted_llm_client.py +29 -17
- rasa/shared/providers/mappings.py +0 -19
- rasa/shared/utils/common.py +2 -37
- rasa/shared/utils/io.py +6 -28
- rasa/shared/utils/llm.py +46 -353
- rasa/shared/utils/yaml.py +82 -181
- rasa/studio/auth.py +5 -3
- rasa/studio/config.py +4 -13
- rasa/studio/constants.py +0 -1
- rasa/studio/data_handler.py +4 -13
- rasa/studio/upload.py +80 -175
- rasa/telemetry.py +17 -94
- rasa/tracing/config.py +1 -3
- rasa/tracing/instrumentation/attribute_extractors.py +17 -94
- rasa/tracing/instrumentation/instrumentation.py +0 -121
- rasa/utils/common.py +0 -5
- rasa/utils/endpoints.py +1 -27
- rasa/utils/io.py +81 -7
- rasa/utils/log_utils.py +2 -9
- rasa/utils/tensorflow/model_data.py +193 -2
- rasa/validator.py +4 -110
- rasa/version.py +1 -1
- rasa_pro-3.11.0a1.dist-info/METADATA +576 -0
- {rasa_pro-3.11.0.dist-info → rasa_pro-3.11.0a1.dist-info}/RECORD +182 -216
- rasa/core/actions/action_repeat_bot_messages.py +0 -89
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-736177bf.js +0 -1
- rasa/core/channels/inspector/src/helpers/audiostream.ts +0 -165
- rasa/core/channels/voice_stream/asr/azure.py +0 -129
- rasa/core/channels/voice_stream/browser_audio.py +0 -107
- rasa/core/channels/voice_stream/call_state.py +0 -23
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +0 -60
- rasa/dialogue_understanding/commands/user_silence_command.py +0 -59
- rasa/dialogue_understanding/patterns/repeat.py +0 -37
- rasa/dialogue_understanding/patterns/user_silence.py +0 -37
- rasa/model_manager/__init__.py +0 -0
- rasa/model_manager/config.py +0 -40
- rasa/model_manager/model_api.py +0 -559
- rasa/model_manager/runner_service.py +0 -286
- rasa/model_manager/socket_bridge.py +0 -146
- rasa/model_manager/studio_jwt_auth.py +0 -86
- rasa/model_manager/trainer_service.py +0 -325
- rasa/model_manager/utils.py +0 -87
- rasa/model_manager/warm_rasa_process.py +0 -187
- rasa/model_service.py +0 -112
- rasa/shared/core/flows/utils.py +0 -39
- rasa/shared/providers/_configs/litellm_router_client_config.py +0 -220
- rasa/shared/providers/_configs/model_group_config.py +0 -167
- rasa/shared/providers/_configs/rasa_llm_client_config.py +0 -73
- rasa/shared/providers/_utils.py +0 -79
- rasa/shared/providers/embedding/litellm_router_embedding_client.py +0 -135
- rasa/shared/providers/llm/litellm_router_llm_client.py +0 -182
- rasa/shared/providers/llm/rasa_llm_client.py +0 -112
- rasa/shared/providers/router/__init__.py +0 -0
- rasa/shared/providers/router/_base_litellm_router_client.py +0 -183
- rasa/shared/providers/router/router_client.py +0 -73
- rasa/shared/utils/health_check/__init__.py +0 -0
- rasa/shared/utils/health_check/embeddings_health_check_mixin.py +0 -31
- rasa/shared/utils/health_check/health_check.py +0 -258
- rasa/shared/utils/health_check/llm_health_check_mixin.py +0 -31
- rasa/utils/sanic_error_handler.py +0 -32
- rasa/utils/tensorflow/feature_array.py +0 -366
- rasa_pro-3.11.0.dist-info/METADATA +0 -198
- {rasa_pro-3.11.0.dist-info → rasa_pro-3.11.0a1.dist-info}/NOTICE +0 -0
- {rasa_pro-3.11.0.dist-info → rasa_pro-3.11.0a1.dist-info}/WHEEL +0 -0
- {rasa_pro-3.11.0.dist-info → rasa_pro-3.11.0a1.dist-info}/entry_points.txt +0 -0
rasa/e2e_test/assertions.py
CHANGED
|
@@ -71,7 +71,6 @@ class AssertionType(Enum):
|
|
|
71
71
|
SLOT_WAS_SET = "slot_was_set"
|
|
72
72
|
SLOT_WAS_NOT_SET = "slot_was_not_set"
|
|
73
73
|
BOT_UTTERED = "bot_uttered"
|
|
74
|
-
BOT_DID_NOT_UTTER = "bot_did_not_utter"
|
|
75
74
|
GENERATIVE_RESPONSE_IS_RELEVANT = "generative_response_is_relevant"
|
|
76
75
|
GENERATIVE_RESPONSE_IS_GROUNDED = "generative_response_is_grounded"
|
|
77
76
|
|
|
@@ -723,7 +722,6 @@ class BotUtteredAssertion(Assertion):
|
|
|
723
722
|
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
724
723
|
"""Run the bot_uttered assertion on the given events for that user turn."""
|
|
725
724
|
matching_event = None
|
|
726
|
-
error_messages = []
|
|
727
725
|
|
|
728
726
|
if self.utter_name is not None:
|
|
729
727
|
try:
|
|
@@ -734,8 +732,11 @@ class BotUtteredAssertion(Assertion):
|
|
|
734
732
|
and event.metadata.get("utter_action") == self.utter_name
|
|
735
733
|
)
|
|
736
734
|
except StopIteration:
|
|
737
|
-
|
|
738
|
-
|
|
735
|
+
error_message = f"Bot did not utter '{self.utter_name}' response."
|
|
736
|
+
error_message += assertion_order_error_message
|
|
737
|
+
|
|
738
|
+
return self._generate_assertion_failure(
|
|
739
|
+
error_message, prior_events, turn_events, self.line
|
|
739
740
|
)
|
|
740
741
|
|
|
741
742
|
if self.text_matches is not None:
|
|
@@ -747,11 +748,16 @@ class BotUtteredAssertion(Assertion):
|
|
|
747
748
|
if isinstance(event, BotUttered) and pattern.search(event.text)
|
|
748
749
|
)
|
|
749
750
|
except StopIteration:
|
|
750
|
-
|
|
751
|
+
error_message = (
|
|
751
752
|
f"Bot did not utter any response which "
|
|
752
753
|
f"matches the provided text pattern "
|
|
753
754
|
f"'{self.text_matches}'."
|
|
754
755
|
)
|
|
756
|
+
error_message += assertion_order_error_message
|
|
757
|
+
|
|
758
|
+
return self._generate_assertion_failure(
|
|
759
|
+
error_message, prior_events, turn_events, self.line
|
|
760
|
+
)
|
|
755
761
|
|
|
756
762
|
if self.buttons:
|
|
757
763
|
try:
|
|
@@ -761,16 +767,13 @@ class BotUtteredAssertion(Assertion):
|
|
|
761
767
|
if isinstance(event, BotUttered) and self._buttons_match(event)
|
|
762
768
|
)
|
|
763
769
|
except StopIteration:
|
|
764
|
-
|
|
770
|
+
error_message = (
|
|
765
771
|
"Bot did not utter any response with the expected buttons."
|
|
766
772
|
)
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
return self._generate_assertion_failure(
|
|
772
|
-
error_message, prior_events, turn_events, self.line
|
|
773
|
-
)
|
|
773
|
+
error_message += assertion_order_error_message
|
|
774
|
+
return self._generate_assertion_failure(
|
|
775
|
+
error_message, prior_events, turn_events, self.line
|
|
776
|
+
)
|
|
774
777
|
|
|
775
778
|
return None, matching_event
|
|
776
779
|
|
|
@@ -800,126 +803,6 @@ class BotUtteredAssertion(Assertion):
|
|
|
800
803
|
return hash(json.dumps(self.as_dict()))
|
|
801
804
|
|
|
802
805
|
|
|
803
|
-
@dataclass
|
|
804
|
-
class BotDidNotUtterAssertion(Assertion):
|
|
805
|
-
"""Class for the 'bot_did_not_utter' assertion."""
|
|
806
|
-
|
|
807
|
-
utter_name: Optional[str] = None
|
|
808
|
-
text_matches: Optional[str] = None
|
|
809
|
-
buttons: Optional[List[AssertedButton]] = None
|
|
810
|
-
line: Optional[int] = None
|
|
811
|
-
|
|
812
|
-
@classmethod
|
|
813
|
-
def type(cls) -> str:
|
|
814
|
-
return AssertionType.BOT_DID_NOT_UTTER.value
|
|
815
|
-
|
|
816
|
-
@staticmethod
|
|
817
|
-
def from_dict(assertion_dict: Dict[Text, Any]) -> BotDidNotUtterAssertion:
|
|
818
|
-
"""Creates a BotDidNotUtterAssertion from a dictionary."""
|
|
819
|
-
assertion_dict = assertion_dict.get(AssertionType.BOT_DID_NOT_UTTER.value, {})
|
|
820
|
-
utter_name = assertion_dict.get("utter_name")
|
|
821
|
-
text_matches = assertion_dict.get("text_matches")
|
|
822
|
-
buttons = [
|
|
823
|
-
AssertedButton.from_dict(button)
|
|
824
|
-
for button in assertion_dict.get("buttons", [])
|
|
825
|
-
]
|
|
826
|
-
|
|
827
|
-
if not utter_name and not text_matches and not buttons:
|
|
828
|
-
raise RasaException(
|
|
829
|
-
"A 'bot_did_not_utter' assertion is empty. "
|
|
830
|
-
"It should contain at least one of the allowed properties: "
|
|
831
|
-
"'utter_name', 'text_matches', or 'buttons'."
|
|
832
|
-
)
|
|
833
|
-
|
|
834
|
-
return BotDidNotUtterAssertion(
|
|
835
|
-
utter_name=utter_name,
|
|
836
|
-
text_matches=text_matches,
|
|
837
|
-
buttons=buttons,
|
|
838
|
-
line=assertion_dict.lc.line + 1 if hasattr(assertion_dict, "lc") else None,
|
|
839
|
-
)
|
|
840
|
-
|
|
841
|
-
def run(
|
|
842
|
-
self,
|
|
843
|
-
turn_events: List[Event],
|
|
844
|
-
prior_events: List[Event],
|
|
845
|
-
assertion_order_error_message: str = "",
|
|
846
|
-
**kwargs: Any,
|
|
847
|
-
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
848
|
-
"""Checks that the bot did not utter the specified messages or buttons."""
|
|
849
|
-
for event in turn_events:
|
|
850
|
-
if isinstance(event, BotUttered):
|
|
851
|
-
error_messages = []
|
|
852
|
-
if self._utter_name_matches(event):
|
|
853
|
-
error_messages.append(
|
|
854
|
-
f"Bot uttered a forbidden utterance '{self.utter_name}'."
|
|
855
|
-
)
|
|
856
|
-
if self._text_matches(event):
|
|
857
|
-
error_messages.append(
|
|
858
|
-
f"Bot uttered a forbidden message matching "
|
|
859
|
-
f"the pattern '{self.text_matches}'."
|
|
860
|
-
)
|
|
861
|
-
if self._buttons_match(event):
|
|
862
|
-
error_messages.append(
|
|
863
|
-
"Bot uttered a forbidden response with specified buttons."
|
|
864
|
-
)
|
|
865
|
-
|
|
866
|
-
if error_messages:
|
|
867
|
-
error_message = " ".join(error_messages)
|
|
868
|
-
error_message += assertion_order_error_message
|
|
869
|
-
return self._generate_assertion_failure(
|
|
870
|
-
error_message, prior_events, turn_events, self.line
|
|
871
|
-
)
|
|
872
|
-
return None, None
|
|
873
|
-
|
|
874
|
-
def _utter_name_matches(self, event: BotUttered) -> bool:
|
|
875
|
-
if self.utter_name is not None:
|
|
876
|
-
if event.metadata.get("utter_action") == self.utter_name:
|
|
877
|
-
return True
|
|
878
|
-
return False
|
|
879
|
-
|
|
880
|
-
def _text_matches(self, event: BotUttered) -> bool:
|
|
881
|
-
if self.text_matches is not None:
|
|
882
|
-
pattern = re.compile(self.text_matches)
|
|
883
|
-
if pattern.search(event.text):
|
|
884
|
-
return True
|
|
885
|
-
return False
|
|
886
|
-
|
|
887
|
-
def _buttons_match(self, event: BotUttered) -> bool:
|
|
888
|
-
"""Check if the bot response contains any of the forbidden buttons."""
|
|
889
|
-
if self.buttons is None:
|
|
890
|
-
return False
|
|
891
|
-
|
|
892
|
-
actual_buttons = event.data.get("buttons", [])
|
|
893
|
-
if not actual_buttons:
|
|
894
|
-
return False
|
|
895
|
-
|
|
896
|
-
for actual_button in actual_buttons:
|
|
897
|
-
if any(
|
|
898
|
-
self._is_forbidden_button(actual_button, forbidden_button)
|
|
899
|
-
for forbidden_button in self.buttons
|
|
900
|
-
):
|
|
901
|
-
return True
|
|
902
|
-
return False
|
|
903
|
-
|
|
904
|
-
@staticmethod
|
|
905
|
-
def _is_forbidden_button(
|
|
906
|
-
actual_button: Dict[str, Any], forbidden_button: AssertedButton
|
|
907
|
-
) -> bool:
|
|
908
|
-
"""Check if the button matches any of the forbidden buttons."""
|
|
909
|
-
actual_title = actual_button.get("title")
|
|
910
|
-
actual_payload = actual_button.get("payload")
|
|
911
|
-
|
|
912
|
-
title_matches = forbidden_button.title == actual_title
|
|
913
|
-
payload_matches = forbidden_button.payload == actual_payload
|
|
914
|
-
if title_matches and payload_matches:
|
|
915
|
-
return True
|
|
916
|
-
return False
|
|
917
|
-
|
|
918
|
-
def __hash__(self) -> int:
|
|
919
|
-
"""Hash method to ensure the assertion is hashable."""
|
|
920
|
-
return hash(json.dumps(self.as_dict()))
|
|
921
|
-
|
|
922
|
-
|
|
923
806
|
@dataclass
|
|
924
807
|
class GenerativeResponseMixin(Assertion):
|
|
925
808
|
"""Mixin class for storing generative response assertions."""
|
|
@@ -83,29 +83,6 @@ schema;assertions:
|
|
|
83
83
|
text_matches:
|
|
84
84
|
type: str
|
|
85
85
|
nullable: false
|
|
86
|
-
bot_did_not_utter:
|
|
87
|
-
type: map
|
|
88
|
-
nullable: false
|
|
89
|
-
mapping:
|
|
90
|
-
utter_name:
|
|
91
|
-
type: str
|
|
92
|
-
nullable: false
|
|
93
|
-
buttons:
|
|
94
|
-
type: seq
|
|
95
|
-
nullable: false
|
|
96
|
-
matching: "all"
|
|
97
|
-
sequence:
|
|
98
|
-
- type: map
|
|
99
|
-
mapping:
|
|
100
|
-
title:
|
|
101
|
-
type: str
|
|
102
|
-
nullable: false
|
|
103
|
-
payload:
|
|
104
|
-
type: str
|
|
105
|
-
nullable: false
|
|
106
|
-
text_matches:
|
|
107
|
-
type: str
|
|
108
|
-
nullable: false
|
|
109
86
|
generative_response_is_relevant:
|
|
110
87
|
type: map
|
|
111
88
|
mapping:
|
rasa/e2e_test/e2e_test_case.py
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from collections import OrderedDict
|
|
2
|
-
from collections import defaultdict
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from typing import Any, Dict, List, Optional, Text, Union
|
|
5
5
|
|
|
6
|
-
import structlog
|
|
7
|
-
|
|
8
6
|
from rasa.e2e_test.assertions import Assertion
|
|
9
7
|
from rasa.e2e_test.constants import (
|
|
10
8
|
KEY_ASSERTIONS,
|
|
@@ -22,11 +20,10 @@ from rasa.e2e_test.constants import (
|
|
|
22
20
|
KEY_USER_INPUT,
|
|
23
21
|
)
|
|
24
22
|
from rasa.e2e_test.stub_custom_action import StubCustomAction
|
|
25
|
-
from rasa.shared.constants import DOCS_BASE_URL
|
|
26
23
|
from rasa.shared.core.events import BotUttered, SlotSet, UserUttered
|
|
27
24
|
from rasa.shared.exceptions import RasaException
|
|
28
25
|
|
|
29
|
-
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
30
27
|
|
|
31
28
|
|
|
32
29
|
@dataclass(frozen=True)
|
|
@@ -346,10 +343,9 @@ class ActualStepOutput:
|
|
|
346
343
|
try:
|
|
347
344
|
return self.user_uttered_events[0]
|
|
348
345
|
except IndexError:
|
|
349
|
-
|
|
350
|
-
"
|
|
351
|
-
|
|
352
|
-
f"ActualStepOutput: {self}",
|
|
346
|
+
logger.debug(
|
|
347
|
+
f"Could not find `UserUttered` event in the ActualStepOutput: "
|
|
348
|
+
f"{self}"
|
|
353
349
|
)
|
|
354
350
|
return None
|
|
355
351
|
return None
|
|
@@ -399,7 +395,7 @@ class TestCase:
|
|
|
399
395
|
else:
|
|
400
396
|
steps.append(TestStep.from_dict(step))
|
|
401
397
|
|
|
402
|
-
|
|
398
|
+
return TestCase(
|
|
403
399
|
name=input_test_case.get(KEY_TEST_CASE, "default"),
|
|
404
400
|
steps=steps,
|
|
405
401
|
file=file,
|
|
@@ -409,81 +405,6 @@ class TestCase:
|
|
|
409
405
|
fixture_names=input_test_case.get(KEY_FIXTURES),
|
|
410
406
|
metadata_name=input_test_case.get(KEY_METADATA),
|
|
411
407
|
)
|
|
412
|
-
test_case.validate()
|
|
413
|
-
return test_case
|
|
414
|
-
|
|
415
|
-
def validate(self) -> None:
|
|
416
|
-
"""Validates the test case.
|
|
417
|
-
|
|
418
|
-
This method calls all validation methods required for the test case.
|
|
419
|
-
"""
|
|
420
|
-
if self.uses_assertions():
|
|
421
|
-
self.validate_duplicate_user_messages_metadata()
|
|
422
|
-
|
|
423
|
-
def validate_duplicate_user_messages_metadata(self) -> None:
|
|
424
|
-
"""Validates that duplicate user messages use metadata correctly.
|
|
425
|
-
|
|
426
|
-
Ensures that each duplicate user message uses unique metadata.
|
|
427
|
-
|
|
428
|
-
Raises warnings if any issues are found.
|
|
429
|
-
"""
|
|
430
|
-
docs_link = (
|
|
431
|
-
f"{DOCS_BASE_URL}/testing/e2e-testing-assertions/assertions-how-to-guide/"
|
|
432
|
-
"#how-to-handle-duplicate-user-text-messages-in-the-same-test-case"
|
|
433
|
-
)
|
|
434
|
-
no_metadata_event_info = (
|
|
435
|
-
"Test case '{name}' has duplicate user steps with text '{text}', "
|
|
436
|
-
"and user step at line {line} lacks metadata. When using "
|
|
437
|
-
"duplicate user messages, metadata should be set on each step to ensure "
|
|
438
|
-
f"correct processing. Please refer to the documentation: {docs_link}"
|
|
439
|
-
)
|
|
440
|
-
non_unique_metadata_event_info = (
|
|
441
|
-
"Test case '{name}' has duplicate user steps with text '{text}', "
|
|
442
|
-
"and user step at line {line} has duplicate metadata "
|
|
443
|
-
"name '{metadata_name}'. Metadata names should be unique for each user "
|
|
444
|
-
"step among duplicates. This may cause issues in processing "
|
|
445
|
-
f"user messages. Please refer to the documentation: {docs_link}"
|
|
446
|
-
)
|
|
447
|
-
|
|
448
|
-
# Use dict[str, list] structure to group steps by user message text to easily
|
|
449
|
-
# identify and validate instances with duplicate messages and their metadata.
|
|
450
|
-
message_steps = defaultdict(list)
|
|
451
|
-
|
|
452
|
-
# Collect user steps by text
|
|
453
|
-
for step in self.steps:
|
|
454
|
-
if step.actor == KEY_USER_INPUT and step.text:
|
|
455
|
-
message_steps[step.text].append(step)
|
|
456
|
-
|
|
457
|
-
# Check for duplicate messages
|
|
458
|
-
for text, steps in message_steps.items():
|
|
459
|
-
if len(steps) <= 1:
|
|
460
|
-
continue
|
|
461
|
-
|
|
462
|
-
metadata_names_used = set()
|
|
463
|
-
for step in steps:
|
|
464
|
-
if not step.metadata_name:
|
|
465
|
-
structlogger.warning(
|
|
466
|
-
"e2e_test_case.validate_duplicate_user_messages_metadata.no_metadata",
|
|
467
|
-
event_info=no_metadata_event_info.format(
|
|
468
|
-
name=self.name,
|
|
469
|
-
text=text,
|
|
470
|
-
line=step.line,
|
|
471
|
-
),
|
|
472
|
-
)
|
|
473
|
-
break
|
|
474
|
-
elif step.metadata_name in metadata_names_used:
|
|
475
|
-
structlogger.warning(
|
|
476
|
-
"e2e_test_case.validate_duplicate_user_messages_metadata.non_unique_metadata",
|
|
477
|
-
event_info=non_unique_metadata_event_info.format(
|
|
478
|
-
name=self.name,
|
|
479
|
-
text=text,
|
|
480
|
-
line=step.line,
|
|
481
|
-
metadata_name=step.metadata_name,
|
|
482
|
-
),
|
|
483
|
-
)
|
|
484
|
-
break
|
|
485
|
-
else:
|
|
486
|
-
metadata_names_used.add(step.metadata_name)
|
|
487
408
|
|
|
488
409
|
def as_dict(self) -> Dict[Text, Any]:
|
|
489
410
|
"""Returns the test case as a dictionary."""
|
rasa/e2e_test/e2e_test_runner.py
CHANGED
|
@@ -136,7 +136,7 @@ class E2ETestRunner:
|
|
|
136
136
|
return turns
|
|
137
137
|
|
|
138
138
|
tracker = await self.agent.processor.fetch_tracker_with_initial_session(
|
|
139
|
-
sender_id
|
|
139
|
+
sender_id
|
|
140
140
|
)
|
|
141
141
|
# turn -1 i used to contain events that happen during
|
|
142
142
|
# the start of the session and before the first user message
|
|
@@ -190,11 +190,11 @@ class E2ETestRunner:
|
|
|
190
190
|
error=f"Message handling timed out for user message '{step.text}'.",
|
|
191
191
|
exc_info=True,
|
|
192
192
|
)
|
|
193
|
-
except Exception
|
|
193
|
+
except Exception:
|
|
194
194
|
structlogger.error(
|
|
195
195
|
"e2e_test_runner.run_prediction_loop",
|
|
196
196
|
error=f"An exception occurred while handling "
|
|
197
|
-
f"user message '{step.text}'.
|
|
197
|
+
f"user message '{step.text}'.",
|
|
198
198
|
)
|
|
199
199
|
tracker = await self.agent.tracker_store.retrieve(sender_id) # type: ignore[assignment]
|
|
200
200
|
turns[position], event_cursor = self.get_actual_step_output(
|
|
@@ -826,7 +826,7 @@ class E2ETestRunner:
|
|
|
826
826
|
return
|
|
827
827
|
|
|
828
828
|
tracker = await self.agent.processor.fetch_tracker_with_initial_session(
|
|
829
|
-
sender_id
|
|
829
|
+
sender_id
|
|
830
830
|
)
|
|
831
831
|
|
|
832
832
|
for fixture in fixtures:
|
|
@@ -1155,8 +1155,6 @@ class E2ETestRunner:
|
|
|
1155
1155
|
flow_paths_stack
|
|
1156
1156
|
and self.agent.domain
|
|
1157
1157
|
and self.agent.domain.is_custom_action(event.action_name)
|
|
1158
|
-
and STEP_ID_METADATA_KEY in event.metadata
|
|
1159
|
-
and ACTIVE_FLOW_METADATA_KEY in event.metadata
|
|
1160
1158
|
):
|
|
1161
1159
|
flow_paths_stack[-1].nodes.append(self._create_path_node(event))
|
|
1162
1160
|
|
rasa/e2e_test/utils/io.py
CHANGED
|
@@ -346,7 +346,7 @@ def read_test_cases(path: str) -> TestSuite:
|
|
|
346
346
|
beta_flag_verified = False
|
|
347
347
|
|
|
348
348
|
for test_file in test_files:
|
|
349
|
-
test_file_content = parse_raw_yaml(Path(test_file).read_text(
|
|
349
|
+
test_file_content = parse_raw_yaml(Path(test_file).read_text())
|
|
350
350
|
|
|
351
351
|
validate_yaml_data_using_schema_with_assertions(
|
|
352
352
|
yaml_data=test_file_content, schema_content=e2e_test_schema
|
|
@@ -506,8 +506,6 @@ def transform_results_output_to_yaml(yaml_string: str) -> str:
|
|
|
506
506
|
result.append(s)
|
|
507
507
|
elif s.startswith("\n"):
|
|
508
508
|
result.append(s.strip())
|
|
509
|
-
elif s.strip().startswith("#"):
|
|
510
|
-
continue
|
|
511
509
|
else:
|
|
512
510
|
result.append(s)
|
|
513
511
|
return "".join(result)
|
rasa/engine/loader.py
CHANGED
|
@@ -4,10 +4,6 @@ from typing import Tuple, Type
|
|
|
4
4
|
from rasa.engine.graph import ExecutionContext
|
|
5
5
|
from rasa.engine.runner.interface import GraphRunner
|
|
6
6
|
from rasa.engine.storage.storage import ModelMetadata, ModelStorage
|
|
7
|
-
from rasa.engine.validation import (
|
|
8
|
-
validate_model_client_configuration_setup_during_inference_time,
|
|
9
|
-
validate_model_group_configuration_setup,
|
|
10
|
-
)
|
|
11
7
|
|
|
12
8
|
|
|
13
9
|
def load_predict_graph_runner(
|
|
@@ -30,14 +26,6 @@ def load_predict_graph_runner(
|
|
|
30
26
|
model_storage, model_metadata = model_storage_class.from_model_archive(
|
|
31
27
|
storage_path=storage_path, model_archive_path=model_archive_path
|
|
32
28
|
)
|
|
33
|
-
|
|
34
|
-
# Components using LLMs or embeddings can reference model groups defined in
|
|
35
|
-
# the endpoints.yml file for their client configurations. To ensure they will work
|
|
36
|
-
# properly validate model group references before loading
|
|
37
|
-
# the components.
|
|
38
|
-
validate_model_group_configuration_setup()
|
|
39
|
-
validate_model_client_configuration_setup_during_inference_time(model_metadata)
|
|
40
|
-
|
|
41
29
|
runner = graph_runner_class.create(
|
|
42
30
|
graph_schema=model_metadata.predict_schema,
|
|
43
31
|
model_storage=model_storage,
|