rasa-pro 3.11.5__py3-none-any.whl → 3.12.0__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 +10 -13
- rasa/__main__.py +7 -7
- rasa/anonymization/anonymisation_rule_yaml_reader.py +1 -1
- rasa/anonymization/anonymization_pipeline.py +3 -3
- rasa/anonymization/anonymization_rule_executor.py +17 -11
- rasa/anonymization/anonymization_rule_orchestrator.py +2 -3
- rasa/cli/arguments/data.py +2 -2
- rasa/cli/arguments/default_arguments.py +1 -1
- rasa/cli/arguments/evaluate.py +2 -1
- rasa/cli/arguments/interactive.py +1 -1
- rasa/cli/arguments/run.py +1 -1
- rasa/cli/arguments/test.py +7 -5
- rasa/cli/arguments/train.py +3 -3
- rasa/cli/arguments/visualize.py +2 -2
- rasa/cli/arguments/x.py +1 -0
- rasa/cli/data.py +20 -3
- rasa/cli/dialogue_understanding_test.py +386 -0
- rasa/cli/evaluate.py +1 -1
- rasa/cli/export.py +6 -6
- rasa/cli/inspect.py +20 -1
- rasa/cli/interactive.py +4 -5
- rasa/cli/llm_fine_tuning.py +51 -16
- rasa/cli/markers.py +1 -2
- rasa/cli/project_templates/calm/actions/add_contact.py +1 -1
- rasa/cli/project_templates/calm/config.yml +2 -2
- rasa/cli/project_templates/calm/domain/list_contacts.yml +1 -2
- rasa/cli/project_templates/calm/domain/remove_contact.yml +1 -2
- rasa/cli/project_templates/calm/domain/shared.yml +1 -4
- rasa/cli/project_templates/calm/endpoints.yml +2 -2
- rasa/cli/project_templates/tutorial/actions/actions.py +3 -2
- rasa/cli/shell.py +5 -6
- rasa/cli/studio/download.py +1 -2
- rasa/cli/studio/studio.py +2 -3
- rasa/cli/studio/train.py +0 -1
- rasa/cli/telemetry.py +2 -2
- rasa/cli/test.py +11 -11
- rasa/cli/train.py +3 -0
- rasa/cli/utils.py +25 -5
- rasa/constants.py +0 -1
- rasa/core/__init__.py +0 -1
- rasa/core/actions/action.py +135 -208
- rasa/core/actions/action_handle_digressions.py +164 -0
- rasa/core/actions/action_hangup.py +1 -1
- rasa/core/actions/action_repeat_bot_messages.py +2 -2
- rasa/core/actions/action_run_slot_rejections.py +18 -6
- rasa/core/actions/action_trigger_chitchat.py +1 -1
- rasa/core/actions/action_trigger_flow.py +5 -5
- rasa/core/actions/action_trigger_search.py +1 -1
- rasa/core/actions/custom_action_executor.py +1 -1
- rasa/core/actions/direct_custom_actions_executor.py +1 -0
- rasa/core/actions/forms.py +22 -15
- rasa/core/actions/http_custom_action_executor.py +8 -1
- rasa/core/actions/loops.py +3 -3
- rasa/core/actions/two_stage_fallback.py +13 -13
- rasa/core/auth_retry_tracker_store.py +1 -2
- rasa/core/brokers/broker.py +2 -1
- rasa/core/brokers/file.py +1 -1
- rasa/core/brokers/kafka.py +8 -8
- rasa/core/brokers/pika.py +8 -9
- rasa/core/brokers/sql.py +4 -3
- rasa/core/channels/__init__.py +7 -0
- rasa/core/channels/botframework.py +2 -2
- rasa/core/channels/callback.py +4 -4
- rasa/core/channels/channel.py +11 -11
- rasa/core/channels/console.py +0 -1
- rasa/core/channels/development_inspector.py +80 -24
- rasa/core/channels/facebook.py +5 -5
- rasa/core/channels/hangouts.py +7 -8
- rasa/core/channels/inspector/dist/assets/{arc-f0f8bd46.js → arc-9f1365dc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-7162c77d.js → blockDiagram-38ab4fdb-e0f81b12.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-b1d0d098.js → c4Diagram-3d4e48cf-9deaee1c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-44956714.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-807a1b27.js → classDiagram-70f12bd4-20450a96.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-5238dcdb.js → classDiagram-v2-f2320105-749d2abf.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-a9475142.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-75dfaa67.js → createText-2e5e7dd3-bef0b38c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-df20501d.js → edges-e0da2a9e-943801a7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-13cf4797.js → erDiagram-9861fffd-d523a948.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-a4991264.js → flowDb-956e92f1-54e4cf19.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-ccecf773.js → flowDiagram-66a62f08-48bfbbe8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-43fa749a.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-b5801783.js → flowchart-elk-definition-4a651766-17c30827.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-161e079a.js → ganttDiagram-c361ad54-43086f2d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-f38e86a4.js → gitGraphDiagram-72cf32ee-5c8b693e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-be6ef5d8.js → graph-41a90d26.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3862675e-d9ce8994.js → index-3862675e-b43eeae9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-7794b245.js → index-e8affe45.js} +155 -155
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-5000a3dc.js → infoDiagram-f8f76790-0b20676b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-8ef0a17a.js → journeyDiagram-49397b02-39bce7b5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-d649bc98.js → layout-dc8eeea4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-95add810.js → line-c4d2e756.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-f6025094.js → linear-86f6f2d9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-2e8531c4.js → mindmap-definition-fc14e90a-4216f771.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-918adfdb.js → pieDiagram-8a3498a8-1a0cfa96.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-cbd01797.js → quadrantDiagram-120e2f19-f91e67cf.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-6a8b877b.js → requirementDiagram-deff3bca-d4046bed.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-c377c3fe.js → sankeyDiagram-04a897e0-2cf6d1d7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-ab9e9b7f.js → sequenceDiagram-704730f1-751ac4f5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-5e6ae67d.js → stateDiagram-587899a1-f734f4d4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-40643476.js → stateDiagram-v2-d93cdb3a-91c65710.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-afb8d108.js → styles-6aaf32cf-e0cff7be.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-7edc9423.js → styles-9a916d00-c8029e5d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-c1d8f7e9.js → styles-c10674c1-114f312a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-f494b2ef.js → svgDrawCommon-08f97a94-b7b9dc00.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-11c7cdd0.js → timeline-definition-85554ec2-9536d189.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-3f191ec1.js → xychartDiagram-e933f94c-bf3b0f36.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/package.json +1 -0
- rasa/core/channels/inspector/src/App.tsx +15 -2
- rasa/core/channels/inspector/src/components/RasaLogo.tsx +31 -0
- rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +68 -0
- rasa/core/channels/inspector/src/components/Welcome.tsx +19 -13
- rasa/core/channels/inspector/yarn.lock +5 -0
- rasa/core/channels/mattermost.py +4 -4
- rasa/core/channels/rasa_chat.py +4 -4
- rasa/core/channels/rest.py +11 -12
- rasa/core/channels/rocketchat.py +4 -3
- rasa/core/channels/slack.py +6 -5
- rasa/core/channels/socketio.py +8 -28
- rasa/core/channels/studio_chat.py +212 -0
- rasa/core/channels/telegram.py +105 -55
- rasa/core/channels/twilio.py +3 -3
- rasa/core/channels/vier_cvg.py +2 -2
- rasa/core/channels/voice_ready/audiocodes.py +9 -9
- rasa/core/channels/voice_ready/jambonz.py +5 -5
- rasa/core/channels/voice_ready/jambonz_protocol.py +3 -4
- rasa/core/channels/voice_ready/twilio_voice.py +9 -8
- rasa/core/channels/voice_ready/utils.py +2 -2
- rasa/core/channels/voice_stream/asr/asr_engine.py +12 -6
- rasa/core/channels/voice_stream/asr/asr_event.py +5 -0
- rasa/core/channels/voice_stream/asr/azure.py +16 -3
- rasa/core/channels/voice_stream/asr/deepgram.py +76 -19
- rasa/core/channels/voice_stream/audiocodes.py +292 -0
- rasa/core/channels/voice_stream/browser_audio.py +14 -7
- rasa/core/channels/voice_stream/call_state.py +6 -2
- rasa/core/channels/voice_stream/genesys.py +320 -0
- rasa/core/channels/voice_stream/tts/azure.py +13 -5
- rasa/core/channels/voice_stream/tts/cartesia.py +34 -14
- rasa/core/channels/voice_stream/tts/tts_cache.py +3 -2
- rasa/core/channels/voice_stream/tts/tts_engine.py +1 -1
- rasa/core/channels/voice_stream/twilio_media_streams.py +12 -8
- rasa/core/channels/voice_stream/util.py +1 -1
- rasa/core/channels/voice_stream/voice_channel.py +100 -56
- rasa/core/channels/webexteams.py +3 -4
- rasa/core/constants.py +2 -0
- rasa/core/evaluation/marker.py +7 -6
- rasa/core/evaluation/marker_base.py +15 -16
- rasa/core/evaluation/marker_stats.py +3 -4
- rasa/core/evaluation/marker_tracker_loader.py +5 -4
- rasa/core/exporter.py +4 -4
- rasa/core/featurizers/precomputation.py +8 -8
- rasa/core/featurizers/single_state_featurizer.py +7 -7
- rasa/core/featurizers/tracker_featurizers.py +13 -13
- rasa/core/http_interpreter.py +3 -4
- rasa/core/information_retrieval/__init__.py +1 -1
- rasa/core/information_retrieval/faiss.py +4 -4
- rasa/core/information_retrieval/information_retrieval.py +2 -2
- rasa/core/information_retrieval/milvus.py +3 -3
- rasa/core/information_retrieval/qdrant.py +3 -3
- rasa/core/jobs.py +1 -0
- rasa/core/lock.py +2 -3
- rasa/core/lock_store.py +3 -3
- rasa/core/migrate.py +12 -9
- rasa/core/nlg/__init__.py +1 -1
- rasa/core/nlg/callback.py +2 -3
- rasa/core/nlg/contextual_response_rephraser.py +82 -14
- rasa/core/nlg/generator.py +85 -17
- rasa/core/nlg/interpolator.py +4 -3
- rasa/core/nlg/response.py +9 -7
- rasa/core/nlg/summarize.py +1 -0
- rasa/core/nlg/translate.py +55 -0
- rasa/core/persistor.py +3 -3
- rasa/core/policies/ensemble.py +10 -9
- rasa/core/policies/enterprise_search_policy.py +87 -21
- rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +1 -1
- rasa/core/policies/flow_policy.py +13 -14
- rasa/core/policies/flows/flow_executor.py +85 -55
- rasa/core/policies/intentless_policy.py +6 -7
- rasa/core/policies/memoization.py +22 -20
- rasa/core/policies/policy.py +24 -22
- rasa/core/policies/rule_policy.py +37 -36
- rasa/core/policies/ted_policy.py +87 -85
- rasa/core/policies/unexpected_intent_policy.py +77 -75
- rasa/core/processor.py +167 -74
- rasa/core/run.py +5 -4
- rasa/core/secrets_manager/endpoints.py +2 -3
- rasa/core/secrets_manager/factory.py +2 -3
- rasa/core/secrets_manager/secret_manager.py +2 -3
- rasa/core/secrets_manager/vault.py +2 -2
- rasa/core/test.py +30 -30
- rasa/core/tracker_store.py +138 -49
- rasa/core/train.py +1 -1
- rasa/core/training/__init__.py +2 -2
- rasa/core/training/converters/responses_prefix_converter.py +1 -2
- rasa/core/training/interactive.py +13 -13
- rasa/core/training/story_conflict.py +4 -5
- rasa/core/training/training.py +3 -5
- rasa/core/utils.py +5 -5
- rasa/core/visualize.py +1 -1
- rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -2
- rasa/dialogue_understanding/coexistence/llm_based_router.py +5 -5
- rasa/dialogue_understanding/commands/__init__.py +22 -22
- rasa/dialogue_understanding/commands/can_not_handle_command.py +38 -1
- rasa/dialogue_understanding/commands/cancel_flow_command.py +96 -9
- rasa/dialogue_understanding/commands/change_flow_command.py +36 -2
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +36 -4
- rasa/dialogue_understanding/commands/clarify_command.py +46 -4
- rasa/dialogue_understanding/commands/command.py +3 -2
- rasa/dialogue_understanding/commands/command_syntax_manager.py +55 -0
- rasa/dialogue_understanding/commands/correct_slots_command.py +14 -5
- rasa/dialogue_understanding/commands/error_command.py +1 -1
- rasa/dialogue_understanding/commands/free_form_answer_command.py +2 -1
- rasa/dialogue_understanding/commands/handle_code_change_command.py +2 -2
- rasa/dialogue_understanding/commands/handle_digressions_command.py +144 -0
- rasa/dialogue_understanding/commands/human_handoff_command.py +34 -4
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +36 -4
- rasa/dialogue_understanding/commands/noop_command.py +2 -1
- rasa/dialogue_understanding/commands/prompt_command.py +94 -0
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +34 -4
- rasa/dialogue_understanding/commands/restart_command.py +2 -5
- rasa/dialogue_understanding/commands/session_end_command.py +3 -5
- rasa/dialogue_understanding/commands/session_start_command.py +3 -5
- rasa/dialogue_understanding/commands/set_slot_command.py +55 -16
- rasa/dialogue_understanding/commands/skip_question_command.py +34 -4
- rasa/dialogue_understanding/commands/start_flow_command.py +78 -2
- rasa/dialogue_understanding/commands/user_silence_command.py +3 -5
- rasa/dialogue_understanding/commands/utils.py +126 -43
- rasa/dialogue_understanding/constants.py +2 -0
- rasa/dialogue_understanding/generator/__init__.py +2 -0
- rasa/dialogue_understanding/generator/command_generator.py +120 -79
- rasa/dialogue_understanding/generator/command_parser.py +245 -0
- rasa/dialogue_understanding/generator/constants.py +12 -4
- rasa/dialogue_understanding/generator/flow_retrieval.py +7 -7
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +187 -59
- rasa/dialogue_understanding/generator/llm_command_generator.py +6 -3
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +106 -110
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +53 -11
- rasa/dialogue_understanding/generator/prompt_templates/__init__.py +0 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +58 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +57 -0
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +574 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +41 -386
- rasa/dialogue_understanding/generator/utils.py +76 -0
- rasa/dialogue_understanding/patterns/cancel.py +2 -1
- rasa/dialogue_understanding/patterns/cannot_handle.py +1 -0
- rasa/dialogue_understanding/patterns/chitchat.py +1 -1
- rasa/dialogue_understanding/patterns/clarify.py +2 -1
- rasa/dialogue_understanding/patterns/code_change.py +2 -0
- rasa/dialogue_understanding/patterns/collect_information.py +7 -4
- rasa/dialogue_understanding/patterns/completed.py +1 -1
- rasa/dialogue_understanding/patterns/continue_interrupted.py +1 -1
- rasa/dialogue_understanding/patterns/correction.py +17 -3
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +78 -2
- rasa/dialogue_understanding/patterns/handle_digressions.py +81 -0
- rasa/dialogue_understanding/patterns/human_handoff.py +1 -1
- rasa/dialogue_understanding/patterns/internal_error.py +1 -0
- rasa/dialogue_understanding/patterns/search.py +1 -1
- rasa/dialogue_understanding/patterns/session_start.py +1 -1
- rasa/dialogue_understanding/patterns/skip_question.py +1 -0
- rasa/dialogue_understanding/patterns/user_silence.py +1 -1
- rasa/dialogue_understanding/patterns/validate_slot.py +65 -0
- rasa/dialogue_understanding/processor/command_processor.py +193 -43
- rasa/dialogue_understanding/processor/command_processor_component.py +1 -1
- rasa/dialogue_understanding/stack/dialogue_stack.py +4 -3
- rasa/dialogue_understanding/stack/frames/__init__.py +2 -2
- rasa/dialogue_understanding/stack/frames/chit_chat_frame.py +4 -1
- rasa/dialogue_understanding/stack/frames/dialogue_stack_frame.py +2 -3
- rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +5 -2
- rasa/dialogue_understanding/stack/frames/search_frame.py +4 -1
- rasa/dialogue_understanding/stack/utils.py +56 -10
- rasa/dialogue_understanding/utils.py +164 -0
- rasa/dialogue_understanding_test/README.md +429 -0
- rasa/dialogue_understanding_test/__init__.py +0 -0
- rasa/dialogue_understanding_test/command_comparison.py +60 -0
- rasa/dialogue_understanding_test/command_metric_calculation.py +122 -0
- rasa/dialogue_understanding_test/constants.py +22 -0
- rasa/dialogue_understanding_test/du_test_case.py +448 -0
- rasa/dialogue_understanding_test/du_test_result.py +390 -0
- rasa/dialogue_understanding_test/du_test_runner.py +322 -0
- rasa/dialogue_understanding_test/du_test_schema.yml +161 -0
- rasa/dialogue_understanding_test/io.py +443 -0
- rasa/dialogue_understanding_test/test_case_simulation/__init__.py +0 -0
- rasa/dialogue_understanding_test/test_case_simulation/exception.py +28 -0
- rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +336 -0
- rasa/dialogue_understanding_test/utils.py +70 -0
- rasa/dialogue_understanding_test/validation.py +77 -0
- rasa/e2e_test/aggregate_test_stats_calculator.py +1 -1
- rasa/e2e_test/assertions.py +202 -175
- rasa/e2e_test/assertions_schema.yml +6 -0
- rasa/e2e_test/constants.py +16 -1
- rasa/e2e_test/e2e_config.py +102 -41
- rasa/e2e_test/e2e_config_schema.yml +28 -10
- rasa/e2e_test/e2e_test_case.py +5 -5
- rasa/e2e_test/e2e_test_converter.py +2 -3
- rasa/e2e_test/e2e_test_coverage_report.py +6 -6
- rasa/e2e_test/e2e_test_result.py +1 -1
- rasa/e2e_test/e2e_test_runner.py +143 -38
- rasa/e2e_test/llm_judge_prompts/answer_relevance_prompt_template.jinja2 +93 -0
- rasa/e2e_test/llm_judge_prompts/groundedness_prompt_template.jinja2 +169 -0
- rasa/e2e_test/stub_custom_action.py +1 -1
- rasa/e2e_test/utils/generative_assertions.py +243 -0
- rasa/e2e_test/utils/io.py +123 -93
- rasa/e2e_test/utils/validation.py +101 -3
- rasa/engine/caching.py +5 -7
- rasa/engine/constants.py +1 -1
- rasa/engine/graph.py +3 -2
- rasa/engine/language.py +182 -0
- rasa/engine/recipes/config_files/default_config.yml +4 -0
- rasa/engine/recipes/default_components.py +13 -15
- rasa/engine/recipes/default_recipe.py +65 -49
- rasa/engine/recipes/graph_recipe.py +10 -7
- rasa/engine/recipes/recipe.py +2 -2
- rasa/engine/runner/dask.py +2 -2
- rasa/engine/runner/interface.py +1 -0
- rasa/engine/storage/local_model_storage.py +6 -4
- rasa/engine/storage/resource.py +2 -1
- rasa/engine/storage/storage.py +8 -3
- rasa/engine/training/components.py +2 -1
- rasa/engine/training/fingerprinting.py +4 -2
- rasa/engine/training/graph_trainer.py +4 -4
- rasa/engine/training/hooks.py +2 -2
- rasa/engine/validation.py +36 -33
- rasa/exceptions.py +3 -2
- rasa/graph_components/converters/nlu_message_converter.py +3 -3
- rasa/graph_components/providers/domain_for_core_training_provider.py +3 -3
- rasa/graph_components/providers/domain_provider.py +3 -2
- rasa/graph_components/providers/flows_provider.py +2 -3
- rasa/graph_components/providers/forms_provider.py +4 -4
- rasa/graph_components/providers/nlu_training_data_provider.py +5 -3
- rasa/graph_components/providers/responses_provider.py +4 -4
- rasa/graph_components/providers/rule_only_provider.py +3 -2
- rasa/graph_components/providers/story_graph_provider.py +8 -8
- rasa/graph_components/providers/training_tracker_provider.py +3 -2
- rasa/graph_components/validators/default_recipe_validator.py +16 -16
- rasa/graph_components/validators/finetuning_validator.py +10 -8
- rasa/hooks.py +19 -14
- rasa/jupyter.py +2 -2
- rasa/llm_fine_tuning/annotation_module.py +4 -4
- rasa/llm_fine_tuning/conversations.py +5 -33
- rasa/llm_fine_tuning/llm_data_preparation_module.py +6 -4
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +4 -4
- rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +18 -13
- rasa/llm_fine_tuning/paraphrasing_module.py +6 -2
- rasa/llm_fine_tuning/storage.py +3 -3
- rasa/llm_fine_tuning/train_test_split_module.py +27 -27
- rasa/llm_fine_tuning/utils.py +7 -0
- rasa/markers/marker.py +2 -3
- rasa/markers/marker_base.py +1 -2
- rasa/markers/upload.py +2 -2
- rasa/markers/validate.py +2 -3
- rasa/model.py +3 -5
- rasa/model_manager/config.py +1 -1
- rasa/model_manager/model_api.py +5 -4
- rasa/model_manager/runner_service.py +13 -10
- rasa/model_manager/socket_bridge.py +15 -9
- rasa/model_manager/studio_jwt_auth.py +1 -0
- rasa/model_manager/trainer_service.py +9 -7
- rasa/model_manager/utils.py +1 -1
- rasa/model_manager/warm_rasa_process.py +14 -9
- rasa/model_service.py +5 -6
- rasa/model_testing.py +13 -15
- rasa/model_training.py +29 -29
- rasa/nlu/classifiers/diet_classifier.py +72 -73
- rasa/nlu/classifiers/fallback_classifier.py +9 -8
- rasa/nlu/classifiers/keyword_intent_classifier.py +7 -6
- rasa/nlu/classifiers/logistic_regression_classifier.py +3 -3
- rasa/nlu/classifiers/mitie_intent_classifier.py +5 -4
- rasa/nlu/classifiers/regex_message_handler.py +3 -2
- rasa/nlu/classifiers/sklearn_intent_classifier.py +2 -2
- rasa/nlu/convert.py +2 -2
- rasa/nlu/emulators/dialogflow.py +3 -3
- rasa/nlu/emulators/luis.py +5 -5
- rasa/nlu/emulators/no_emulator.py +1 -0
- rasa/nlu/emulators/wit.py +4 -4
- rasa/nlu/extractors/crf_entity_extractor.py +11 -11
- rasa/nlu/extractors/duckling_entity_extractor.py +7 -6
- rasa/nlu/extractors/entity_synonyms.py +10 -9
- rasa/nlu/extractors/extractor.py +16 -16
- rasa/nlu/extractors/mitie_entity_extractor.py +10 -9
- rasa/nlu/extractors/regex_entity_extractor.py +11 -10
- rasa/nlu/extractors/spacy_entity_extractor.py +2 -2
- rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +15 -14
- rasa/nlu/featurizers/dense_featurizer/dense_featurizer.py +2 -1
- rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +10 -9
- rasa/nlu/featurizers/dense_featurizer/mitie_featurizer.py +9 -7
- rasa/nlu/featurizers/dense_featurizer/spacy_featurizer.py +13 -12
- rasa/nlu/featurizers/featurizer.py +5 -4
- rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +6 -6
- rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +4 -4
- rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +4 -4
- rasa/nlu/featurizers/sparse_featurizer/sparse_featurizer.py +2 -0
- rasa/nlu/model.py +0 -1
- rasa/nlu/selectors/response_selector.py +67 -68
- rasa/nlu/test.py +38 -38
- rasa/nlu/tokenizers/jieba_tokenizer.py +1 -2
- rasa/nlu/tokenizers/mitie_tokenizer.py +2 -2
- rasa/nlu/tokenizers/spacy_tokenizer.py +3 -3
- rasa/nlu/tokenizers/tokenizer.py +6 -7
- rasa/nlu/tokenizers/whitespace_tokenizer.py +1 -1
- rasa/nlu/utils/bilou_utils.py +7 -7
- rasa/nlu/utils/hugging_face/registry.py +22 -22
- rasa/nlu/utils/hugging_face/transformers_pre_post_processors.py +2 -1
- rasa/nlu/utils/mitie_utils.py +2 -1
- rasa/nlu/utils/pattern_utils.py +1 -1
- rasa/nlu/utils/spacy_utils.py +3 -3
- rasa/plugin.py +12 -1
- rasa/server.py +3 -2
- rasa/shared/constants.py +45 -18
- rasa/shared/core/command_payload_reader.py +15 -7
- rasa/shared/core/constants.py +34 -4
- rasa/shared/core/conversation.py +1 -2
- rasa/shared/core/domain.py +19 -20
- rasa/shared/core/events.py +60 -39
- rasa/shared/core/flows/__init__.py +0 -1
- rasa/shared/core/flows/constants.py +11 -0
- rasa/shared/core/flows/flow.py +107 -26
- rasa/shared/core/flows/flow_step.py +4 -3
- rasa/shared/core/flows/flow_step_links.py +1 -2
- rasa/shared/core/flows/flow_step_sequence.py +1 -1
- rasa/shared/core/flows/flows_list.py +3 -3
- rasa/shared/core/flows/flows_yaml_schema.json +69 -3
- rasa/shared/core/flows/nlu_trigger.py +1 -1
- rasa/shared/core/flows/steps/__init__.py +2 -2
- rasa/shared/core/flows/steps/action.py +1 -1
- rasa/shared/core/flows/steps/call.py +1 -1
- rasa/shared/core/flows/steps/collect.py +22 -40
- rasa/shared/core/flows/steps/internal.py +1 -1
- rasa/shared/core/flows/steps/link.py +1 -1
- rasa/shared/core/flows/steps/no_operation.py +2 -2
- rasa/shared/core/flows/steps/set_slots.py +1 -1
- rasa/shared/core/flows/utils.py +44 -4
- rasa/shared/core/flows/validation.py +4 -6
- rasa/shared/core/generator.py +20 -21
- rasa/shared/core/slot_mappings.py +360 -121
- rasa/shared/core/slots.py +163 -6
- rasa/shared/core/trackers.py +108 -33
- rasa/shared/core/training_data/loading.py +1 -1
- rasa/shared/core/training_data/story_reader/story_reader.py +3 -3
- rasa/shared/core/training_data/story_reader/story_step_builder.py +4 -4
- rasa/shared/core/training_data/story_reader/yaml_story_reader.py +29 -31
- rasa/shared/core/training_data/story_writer/yaml_story_writer.py +22 -24
- rasa/shared/core/training_data/structures.py +11 -12
- rasa/shared/core/training_data/visualization.py +10 -10
- rasa/shared/data.py +6 -6
- rasa/shared/engine/caching.py +0 -1
- rasa/shared/exceptions.py +2 -2
- rasa/shared/importers/importer.py +58 -2
- rasa/shared/importers/rasa.py +5 -6
- rasa/shared/importers/utils.py +1 -1
- rasa/shared/nlu/constants.py +9 -0
- rasa/shared/nlu/training_data/entities_parser.py +6 -6
- rasa/shared/nlu/training_data/features.py +3 -3
- rasa/shared/nlu/training_data/formats/__init__.py +1 -1
- rasa/shared/nlu/training_data/formats/dialogflow.py +4 -5
- rasa/shared/nlu/training_data/formats/luis.py +7 -8
- rasa/shared/nlu/training_data/formats/rasa.py +4 -5
- rasa/shared/nlu/training_data/formats/rasa_yaml.py +17 -16
- rasa/shared/nlu/training_data/formats/readerwriter.py +8 -11
- rasa/shared/nlu/training_data/formats/wit.py +3 -4
- rasa/shared/nlu/training_data/loading.py +4 -4
- rasa/shared/nlu/training_data/lookup_tables_parser.py +1 -1
- rasa/shared/nlu/training_data/message.py +13 -14
- rasa/shared/nlu/training_data/schemas/data_schema.py +1 -1
- rasa/shared/nlu/training_data/schemas/responses.yml +19 -11
- rasa/shared/nlu/training_data/synonyms_parser.py +3 -3
- rasa/shared/nlu/training_data/training_data.py +12 -13
- rasa/shared/nlu/training_data/util.py +11 -10
- rasa/shared/providers/_configs/azure_entra_id_config.py +541 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +150 -15
- rasa/shared/providers/_configs/client_config.py +3 -1
- rasa/shared/providers/_configs/default_litellm_client_config.py +9 -7
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +13 -11
- rasa/shared/providers/_configs/litellm_router_client_config.py +12 -10
- rasa/shared/providers/_configs/model_group_config.py +8 -5
- rasa/shared/providers/_configs/oauth_config.py +33 -0
- rasa/shared/providers/_configs/openai_client_config.py +14 -12
- rasa/shared/providers/_configs/rasa_llm_client_config.py +5 -3
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +12 -11
- rasa/shared/providers/_configs/utils.py +1 -0
- rasa/shared/providers/_ssl_verification_utils.py +5 -6
- rasa/shared/providers/_utils.py +5 -5
- rasa/shared/providers/constants.py +6 -0
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +1 -1
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +32 -7
- rasa/shared/providers/embedding/embedding_client.py +1 -1
- rasa/shared/providers/embedding/litellm_router_embedding_client.py +5 -2
- rasa/shared/providers/llm/_base_litellm_client.py +43 -18
- rasa/shared/providers/llm/azure_openai_llm_client.py +90 -34
- rasa/shared/providers/llm/default_litellm_llm_client.py +4 -2
- rasa/shared/providers/llm/litellm_router_llm_client.py +32 -9
- rasa/shared/providers/llm/llm_client.py +24 -8
- rasa/shared/providers/llm/llm_response.py +61 -2
- rasa/shared/providers/llm/openai_llm_client.py +11 -5
- rasa/shared/providers/llm/rasa_llm_client.py +17 -14
- rasa/shared/providers/llm/self_hosted_llm_client.py +35 -15
- rasa/shared/providers/mappings.py +18 -19
- rasa/shared/providers/router/_base_litellm_router_client.py +48 -15
- rasa/shared/providers/router/router_client.py +3 -1
- rasa/shared/utils/cli.py +1 -1
- rasa/shared/utils/common.py +15 -1
- rasa/shared/utils/constants.py +3 -0
- rasa/shared/utils/health_check/embeddings_health_check_mixin.py +1 -1
- rasa/shared/utils/health_check/health_check.py +3 -3
- rasa/shared/utils/health_check/llm_health_check_mixin.py +1 -1
- rasa/shared/utils/io.py +1 -1
- rasa/shared/utils/llm.py +100 -18
- rasa/shared/utils/pykwalify_extensions.py +25 -1
- rasa/shared/utils/schemas/domain.yml +26 -1
- rasa/shared/utils/schemas/events.py +1 -1
- rasa/shared/utils/yaml.py +24 -20
- rasa/studio/auth.py +3 -3
- rasa/studio/config.py +1 -2
- rasa/studio/data_handler.py +3 -3
- rasa/studio/download.py +1 -1
- rasa/studio/results_logger.py +3 -3
- rasa/studio/upload.py +21 -5
- rasa/telemetry.py +127 -48
- rasa/tracing/config.py +5 -3
- rasa/tracing/constants.py +12 -0
- rasa/tracing/instrumentation/attribute_extractors.py +92 -14
- rasa/tracing/instrumentation/instrumentation.py +61 -5
- rasa/tracing/instrumentation/intentless_policy_instrumentation.py +1 -1
- rasa/tracing/instrumentation/metrics.py +52 -11
- rasa/tracing/metric_instrument_provider.py +54 -14
- rasa/utils/common.py +12 -24
- rasa/utils/endpoints.py +1 -1
- rasa/utils/io.py +7 -7
- rasa/utils/licensing.py +3 -4
- rasa/utils/log_utils.py +7 -6
- rasa/utils/ml_utils.py +1 -0
- rasa/utils/plotting.py +3 -3
- rasa/utils/sanic_error_handler.py +1 -1
- rasa/utils/tensorflow/callback.py +2 -2
- rasa/utils/tensorflow/crf.py +2 -2
- rasa/utils/tensorflow/data_generator.py +5 -5
- rasa/utils/tensorflow/environment.py +3 -3
- rasa/utils/tensorflow/feature_array.py +2 -3
- rasa/utils/tensorflow/layers.py +18 -12
- rasa/utils/tensorflow/layers_utils.py +2 -1
- rasa/utils/tensorflow/metrics.py +2 -2
- rasa/utils/tensorflow/model_data.py +7 -7
- rasa/utils/tensorflow/model_data_utils.py +10 -9
- rasa/utils/tensorflow/models.py +31 -32
- rasa/utils/tensorflow/rasa_layers.py +20 -19
- rasa/utils/tensorflow/types.py +2 -1
- rasa/utils/train_utils.py +23 -21
- rasa/utils/url_tools.py +1 -1
- rasa/validator.py +594 -115
- rasa/version.py +1 -1
- {rasa_pro-3.11.5.dist-info → rasa_pro-3.12.0.dist-info}/METADATA +23 -26
- rasa_pro-3.12.0.dist-info/RECORD +829 -0
- rasa/core/channels/inspector/dist/assets/channel-e265ea59.js +0 -1
- rasa/core/channels/inspector/dist/assets/clone-21f8a43d.js +0 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-5c8ce12d.js +0 -1
- rasa_pro-3.11.5.dist-info/RECORD +0 -785
- /rasa/dialogue_understanding/generator/{single_step → prompt_templates}/command_prompt_template.jinja2 +0 -0
- {rasa_pro-3.11.5.dist-info → rasa_pro-3.12.0.dist-info}/NOTICE +0 -0
- {rasa_pro-3.11.5.dist-info → rasa_pro-3.12.0.dist-info}/WHEEL +0 -0
- {rasa_pro-3.11.5.dist-info → rasa_pro-3.12.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
from typing import Any, Dict, Generator, List, Optional, Text
|
|
3
|
+
|
|
4
|
+
from rasa.dialogue_understanding.commands import Command
|
|
5
|
+
from rasa.dialogue_understanding.constants import (
|
|
6
|
+
RASA_RECORD_COMMANDS_AND_PROMPTS_ENV_VAR_NAME,
|
|
7
|
+
)
|
|
8
|
+
from rasa.shared.constants import ROUTE_TO_CALM_SLOT
|
|
9
|
+
from rasa.shared.core.trackers import DialogueStateTracker
|
|
10
|
+
from rasa.shared.nlu.constants import (
|
|
11
|
+
COMMANDS,
|
|
12
|
+
KEY_COMPONENT_NAME,
|
|
13
|
+
KEY_LLM_RESPONSE_METADATA,
|
|
14
|
+
KEY_PROMPT_NAME,
|
|
15
|
+
KEY_SYSTEM_PROMPT,
|
|
16
|
+
KEY_USER_PROMPT,
|
|
17
|
+
PREDICTED_COMMANDS,
|
|
18
|
+
PROMPTS,
|
|
19
|
+
SET_SLOT_COMMAND,
|
|
20
|
+
)
|
|
21
|
+
from rasa.shared.nlu.training_data.message import Message
|
|
22
|
+
from rasa.shared.providers.llm.llm_response import LLMResponse
|
|
23
|
+
from rasa.utils.common import get_bool_env_variable
|
|
24
|
+
|
|
25
|
+
record_commands_and_prompts = get_bool_env_variable(
|
|
26
|
+
RASA_RECORD_COMMANDS_AND_PROMPTS_ENV_VAR_NAME, False
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@contextmanager
|
|
31
|
+
def set_record_commands_and_prompts() -> Generator:
|
|
32
|
+
global record_commands_and_prompts
|
|
33
|
+
record_commands_and_prompts = True
|
|
34
|
+
try:
|
|
35
|
+
yield
|
|
36
|
+
finally:
|
|
37
|
+
record_commands_and_prompts = False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def add_commands_to_message_parse_data(
|
|
41
|
+
message: Message, component_name: str, commands: List[Command]
|
|
42
|
+
) -> None:
|
|
43
|
+
"""Add commands to the message parse data.
|
|
44
|
+
|
|
45
|
+
Commands are only added in case the flag 'record_commands_and_prompts' is set.
|
|
46
|
+
Example of predicted commands in the message parse data:
|
|
47
|
+
Message(data={
|
|
48
|
+
PREDICTED_COMMANDS: {
|
|
49
|
+
"MultiStepLLMCommandGenerator": [
|
|
50
|
+
{"command": "set_slot", "name": "slot_name", "value": "slot_value"},
|
|
51
|
+
],
|
|
52
|
+
"NLUCommandAdapter": [
|
|
53
|
+
{"command": "start_flow", "name": "test_flow"},
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
"""
|
|
58
|
+
# only set commands if the flag "record_commands_and_prompts" is set to True
|
|
59
|
+
if not record_commands_and_prompts:
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
commands_as_dict = [command.as_dict() for command in commands]
|
|
63
|
+
|
|
64
|
+
if message.get(PREDICTED_COMMANDS) is not None:
|
|
65
|
+
predicted_commands = message.get(PREDICTED_COMMANDS)
|
|
66
|
+
if component_name in predicted_commands:
|
|
67
|
+
predicted_commands[component_name].extend(commands_as_dict)
|
|
68
|
+
else:
|
|
69
|
+
predicted_commands[component_name] = commands_as_dict
|
|
70
|
+
else:
|
|
71
|
+
predicted_commands = {component_name: commands_as_dict}
|
|
72
|
+
|
|
73
|
+
message.set(
|
|
74
|
+
PREDICTED_COMMANDS,
|
|
75
|
+
predicted_commands,
|
|
76
|
+
add_to_output=True,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def add_prompt_to_message_parse_data(
|
|
81
|
+
message: Message,
|
|
82
|
+
component_name: str,
|
|
83
|
+
prompt_name: str,
|
|
84
|
+
user_prompt: str,
|
|
85
|
+
system_prompt: Optional[str] = None,
|
|
86
|
+
llm_response: Optional[LLMResponse] = None,
|
|
87
|
+
) -> None:
|
|
88
|
+
"""Add prompt to the message parse data.
|
|
89
|
+
|
|
90
|
+
Prompt is only added in case the flag 'record_commands_and_prompts' is set.
|
|
91
|
+
Example of prompts in the message parse data:
|
|
92
|
+
Message(data={
|
|
93
|
+
PROMPTS: [
|
|
94
|
+
{
|
|
95
|
+
"component_name": "MultiStepLLMCommandGenerator",
|
|
96
|
+
"prompt_name": "fill_slots_prompt",
|
|
97
|
+
"user_prompt": "...",
|
|
98
|
+
"system_prompt": "...",
|
|
99
|
+
"llm_response_metadata": { ... }
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"component_name": "MultiStepLLMCommandGenerator",
|
|
103
|
+
"prompt_name": "handle_flows_prompt",
|
|
104
|
+
"user_prompt": "...",
|
|
105
|
+
"system_prompt": "...",
|
|
106
|
+
"llm_response_metadata": { ... }
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"component_name": "SingleStepLLMCommandGenerator",
|
|
110
|
+
"prompt_name": "prompt_template",
|
|
111
|
+
"user_prompt": "...",
|
|
112
|
+
"system_prompt": "...",
|
|
113
|
+
"llm_response_metadata": { ... }
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
"""
|
|
119
|
+
# Only set prompt if the flag "record_commands_and_prompts" is set to True.
|
|
120
|
+
if not record_commands_and_prompts:
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
# Construct the dictionary with prompt details.
|
|
124
|
+
prompt_data: Dict[Text, Any] = {
|
|
125
|
+
KEY_COMPONENT_NAME: component_name,
|
|
126
|
+
KEY_PROMPT_NAME: prompt_name,
|
|
127
|
+
KEY_USER_PROMPT: user_prompt,
|
|
128
|
+
KEY_LLM_RESPONSE_METADATA: llm_response.to_dict() if llm_response else None,
|
|
129
|
+
**({KEY_SYSTEM_PROMPT: system_prompt} if system_prompt else {}),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# Get or create a top-level "prompts" list.
|
|
133
|
+
prompts = message.get(PROMPTS) or []
|
|
134
|
+
prompts.append(prompt_data)
|
|
135
|
+
|
|
136
|
+
# Update the message with the new prompts list.
|
|
137
|
+
message.set(PROMPTS, prompts, add_to_output=True)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def _handle_via_nlu_in_coexistence(
|
|
141
|
+
tracker: Optional[DialogueStateTracker], message: Message
|
|
142
|
+
) -> bool:
|
|
143
|
+
"""Check if the message should be handled by the NLU subsystem in coexistence mode.""" # noqa: E501
|
|
144
|
+
if not tracker:
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
if not tracker.has_coexistence_routing_slot:
|
|
148
|
+
return False
|
|
149
|
+
|
|
150
|
+
value = tracker.get_slot(ROUTE_TO_CALM_SLOT)
|
|
151
|
+
if value is not None:
|
|
152
|
+
return not value
|
|
153
|
+
|
|
154
|
+
# routing slot has been reset so we need to check
|
|
155
|
+
# the command issued by the Router component
|
|
156
|
+
if message.get(COMMANDS):
|
|
157
|
+
for command in message.get(COMMANDS):
|
|
158
|
+
if (
|
|
159
|
+
command.get("command") == SET_SLOT_COMMAND
|
|
160
|
+
and command.get("name") == ROUTE_TO_CALM_SLOT
|
|
161
|
+
):
|
|
162
|
+
return not command.get("value")
|
|
163
|
+
|
|
164
|
+
return False
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# Dialogue Understanding Tests
|
|
2
|
+
|
|
3
|
+
Dialogue Understanding Tests (DUT) are designed to evaluate the command prediction accuracy of a
|
|
4
|
+
chatbot's [dialogue understanding](https://rasa.com/docs/rasa-pro/concepts/dialogue-understanding) module.
|
|
5
|
+
Rather than merely assessing whether a chatbot behaves as expected in
|
|
6
|
+
[end-to-end (E2E) tests](https://rasa.com/docs/rasa-pro/production/testing-your-assistant#end-to-end-testing), these
|
|
7
|
+
tests delve deeper into understanding *why* a chatbot may not be performing as anticipated.
|
|
8
|
+
They aim to identify discrepancies between the expected and predicted
|
|
9
|
+
[commands](https://rasa.com/docs/rasa-pro/concepts/dialogue-understanding#command-reference) during a conversation,
|
|
10
|
+
providing insights into potential pitfalls in the
|
|
11
|
+
[command generator](https://rasa.com/docs/rasa-pro/concepts/dialogue-understanding#commandgenerator)'s operation.
|
|
12
|
+
|
|
13
|
+
The primary focus of Dialogue Understanding Tests is the command generator,
|
|
14
|
+
a core component responsible for interpreting user input and orchestrating the chatbot's subsequent actions.
|
|
15
|
+
When updates are made to the command generator — such as switching to a different LLM or tweaking the prompt —
|
|
16
|
+
Dialogue Understanding Tests offer a structured approach to evaluate how accurately these changes affect command
|
|
17
|
+
predictions.
|
|
18
|
+
|
|
19
|
+
*Note*: Dialogue Understanding Tests only work for [CALM-based assistants](https://rasa.com/docs/rasa-pro/calm)!
|
|
20
|
+
|
|
21
|
+
## How Dialogue Understanding Tests work
|
|
22
|
+
|
|
23
|
+
Dialogue Understanding Tests are designed to evaluate a chatbot's dialogue understanding capabilities
|
|
24
|
+
within a given conversational context.
|
|
25
|
+
In order to run Dialogue Understanding Tests, you first need to write test cases.
|
|
26
|
+
Each test case consists of a sequence of interactions that simulate conversations with the chatbot.
|
|
27
|
+
These interactions are broken down into user inputs, expected commands, and bot responses.
|
|
28
|
+
Each step of a test case is evaluated independently.
|
|
29
|
+
The predicted commands of the dialogue understanding module are compared with the expected commands
|
|
30
|
+
defined in the test case.
|
|
31
|
+
The Dialogue Understanding Test framework is able to evaluate each step of a test case, also if
|
|
32
|
+
a previous test step failed.
|
|
33
|
+
This allows for a more detailed analysis of the chatbot's performance.
|
|
34
|
+
After all test cases have been executed, a detailed report is generated, including metrics such as accuracy,
|
|
35
|
+
precision, recall, and f1-score for all commands.
|
|
36
|
+
|
|
37
|
+
## Running Dialogue Understanding Tests
|
|
38
|
+
|
|
39
|
+
Dialogue Understanding Tests are hidden behind a feature flag. To enable the feature, set the
|
|
40
|
+
environment variable `RASA_PRO_BETA_DIALOGUE_UNDERSTANDING_TEST` to `true`.
|
|
41
|
+
|
|
42
|
+
To run Dialogue Understanding Tests, execute the following command:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
rasa test du <path-to-test-cases>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
By default, the test cases are expected to be located in the `dialogue_understanding_tests` directory.
|
|
49
|
+
Execute `rasa test du --help` to see the available options for running Dialogue Understanding Tests.
|
|
50
|
+
|
|
51
|
+
In order to execute any custom action that is needed by the Dialogue Understanding Tests, you need to
|
|
52
|
+
either start the action server in the background before running the tests via `rasa run actions` or use
|
|
53
|
+
[Stubbing Custom Actions](https://rasa.com/docs/rasa-pro/production/testing-your-assistant#stubbing-custom-actions).
|
|
54
|
+
|
|
55
|
+
## Defining a Dialogue Understanding Test Case
|
|
56
|
+
|
|
57
|
+
A test case is structured as a sequence of interactions between a user and the chatbot, specifying both inputs and
|
|
58
|
+
expected outcomes. Each test case is composed of multiple steps, and each step consists of:
|
|
59
|
+
|
|
60
|
+
- **User Utterance**: The input message from the user.
|
|
61
|
+
- **Commands**: All expected commands that are generated in response to the user's message.
|
|
62
|
+
- **Bot Response(s)**: All expected responses from the bot, which can be either a direct textual response or a reference
|
|
63
|
+
to
|
|
64
|
+
a predefined bot response template.
|
|
65
|
+
|
|
66
|
+
Here is a sample test case:
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
test_cases:
|
|
70
|
+
- test_case: user_adds_contact_to_their_list
|
|
71
|
+
steps:
|
|
72
|
+
- user: I want to add someone to my contact list
|
|
73
|
+
commands:
|
|
74
|
+
- StartFlow(add_contact)
|
|
75
|
+
- utter: utter_ask_add_contact_handle
|
|
76
|
+
- user: it's @barts
|
|
77
|
+
commands:
|
|
78
|
+
- SetSlot(handle, @barts)
|
|
79
|
+
- bot: "What is the name of the contact you want to add?"
|
|
80
|
+
- user: just Bart
|
|
81
|
+
commands:
|
|
82
|
+
- SetSlot(name, Bart)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
*Note*: The list of commands and the list of bot responses need to be complete!
|
|
86
|
+
|
|
87
|
+
[Fixtures](https://rasa.com/docs/rasa-pro/production/testing-your-assistant#fixtures-for-pre-filled-slots),
|
|
88
|
+
[Metadata](https://rasa.com/docs/rasa-pro/production/testing-your-assistant#metadata-on-user-messages),
|
|
89
|
+
and [Stubbing Custom Actions](https://rasa.com/docs/rasa-pro/production/testing-your-assistant#stubbing-custom-actions)
|
|
90
|
+
known from end-to-end (E2E) tests are supported as well.
|
|
91
|
+
They behave the same as in E2E tests.
|
|
92
|
+
|
|
93
|
+
### Explanation of the Commands
|
|
94
|
+
|
|
95
|
+
[Commands](https://rasa.com/docs/rasa-pro/concepts/dialogue-understanding#command-reference)
|
|
96
|
+
are the core components that direct the chatbot's actions in response to a user's input.
|
|
97
|
+
Each command represents an operation or decision the bot should make.
|
|
98
|
+
Here are the default commands that can be used in a test case:
|
|
99
|
+
|
|
100
|
+
- **StartFlow(flow_name)**: Starts a new flow with the specified flow name.
|
|
101
|
+
- **CancelFlow()**: Cancels the current flow.
|
|
102
|
+
- **SetSlot(slot_name, slot_value)**: Assigns a value to a specific slot.
|
|
103
|
+
- **Clarify(options)**: Seeks clarification by presenting options to the user.
|
|
104
|
+
The options are optional in Dialogue Understanding Tests.
|
|
105
|
+
- **ChitChat()**: Initiates a casual conversation or response.
|
|
106
|
+
- **SearchAndReply()**: Performs a search operation and generates a reply.
|
|
107
|
+
- **HumanHandoff()**: Transfers the conversation to a human agent.
|
|
108
|
+
- **SkipQuestion()**: The user asked to skip a certain step.
|
|
109
|
+
- **RepeatLastBotMessages()**: Repeats the last bot message(s) to the user.
|
|
110
|
+
|
|
111
|
+
The syntax of the commands matches the domain specific language (DSL) used in the prompt templates of the
|
|
112
|
+
command generators.
|
|
113
|
+
|
|
114
|
+
*Note*: It is also possible to use custom commands in the test cases
|
|
115
|
+
(see section "Evaluation of Custom Commands").
|
|
116
|
+
|
|
117
|
+
### Explanation of `placeholder_generated_answer`
|
|
118
|
+
|
|
119
|
+
The `placeholder_generated_answer` is used in scenarios where a bot response is dynamically generated, such as
|
|
120
|
+
when the bot retrieves information from an external knowledge base.
|
|
121
|
+
In such cases, you may not know the exact wording of the bot's response ahead of time.
|
|
122
|
+
This placeholder should be used in the test case where a specific bot response is expected but may vary due
|
|
123
|
+
to external dynamic content or search results.
|
|
124
|
+
It signals that the exact bot utterance is not fixed, yet the test case recognizes and accepts a dynamically
|
|
125
|
+
generated response in its place.
|
|
126
|
+
|
|
127
|
+
Here is an example test case that uses `placeholder_generated_answer`:
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
test_cases:
|
|
131
|
+
- test_case: user asks a knowledge question during flow
|
|
132
|
+
steps:
|
|
133
|
+
- user: I want to send some money to Tre
|
|
134
|
+
commands:
|
|
135
|
+
- StartFlow(transfer_money)
|
|
136
|
+
- SetSlot(transfer_money_recipient, Tre)
|
|
137
|
+
- utter: utter_ask_transfer_money_amount_of_money
|
|
138
|
+
- user: btw, are these transfers free of charge?
|
|
139
|
+
commands:
|
|
140
|
+
- SearchAndReply()
|
|
141
|
+
- utter: placeholder_generated_answer
|
|
142
|
+
- utter: utter_ask_transfer_money_amount_of_money
|
|
143
|
+
- user: great, 50$ then
|
|
144
|
+
commands:
|
|
145
|
+
- SetSlot(transfer_money_amount_of_money, 50)
|
|
146
|
+
- utter: utter_ask_transfer_money_final_confirmation
|
|
147
|
+
- user: yes
|
|
148
|
+
commands:
|
|
149
|
+
- SetSlot(transfer_money_final_confirmation, True)
|
|
150
|
+
- utter: utter_transfer_complete
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Evaluation of Custom Commands
|
|
154
|
+
|
|
155
|
+
### Defining new Custom Commands
|
|
156
|
+
|
|
157
|
+
To evaluate custom commands in Dialogue Understanding Tests, you need to define the custom command
|
|
158
|
+
as a subclass of `Command` and implement the `to_dsl`, `from_dsl`, and `regex_pattern` methods.
|
|
159
|
+
|
|
160
|
+
Here is an example of a custom command:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from rasa.dialogue_understanding.commands import Command
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class TestCommand(Command):
|
|
167
|
+
...
|
|
168
|
+
|
|
169
|
+
def to_dsl(self) -> str:
|
|
170
|
+
"""Converts the command to a DSL string."""
|
|
171
|
+
return "Test()"
|
|
172
|
+
|
|
173
|
+
@classmethod
|
|
174
|
+
def from_dsl(cls, dsl: str) -> "TestCommand":
|
|
175
|
+
# Parse the DSL string and create a CustomCommand object
|
|
176
|
+
return TestCommand()
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def regex_pattern() -> str:
|
|
180
|
+
# Define the regex pattern that matches the DSL string
|
|
181
|
+
return r"Test\(\)"
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
After defining the custom command, you can instruct the command parser to parse this new custom command
|
|
185
|
+
from your custom command generator by passing the custom command as an additional command in your
|
|
186
|
+
`parse_commands` method:
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
@classmethod
|
|
190
|
+
def parse_commands(
|
|
191
|
+
cls, actions: Optional[str], tracker: DialogueStateTracker, flows: FlowsList
|
|
192
|
+
) -> List[Command]:
|
|
193
|
+
"""Parse the actions returned by the llm into intent and entities.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
actions: The actions returned by the llm.
|
|
197
|
+
tracker: The tracker containing the current state of the conversation.
|
|
198
|
+
flows: the list of flows
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
The parsed commands.
|
|
202
|
+
"""
|
|
203
|
+
from rasa.dialogue_understanding.generator.command_parser import (
|
|
204
|
+
parse_commands as parse_commands_using_command_parsers,
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
return parse_commands_using_command_parsers(actions, flows, additional_commands=[TestCommand])
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The additional commands are passed as a list of custom command classes.
|
|
211
|
+
|
|
212
|
+
When running the Dialogue Understanding Tests, you can pass the custom command as a cli argument:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
rasa test du <path-to-test-cases> --additional-commands my_module.TestCommand
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The `--additional-commands` argument takes a list of custom command classes separated by spaces.
|
|
219
|
+
|
|
220
|
+
### Updating Default Commands
|
|
221
|
+
|
|
222
|
+
If you want to update the default commands, you can subclass the default command and override any of the methods.
|
|
223
|
+
|
|
224
|
+
In this example, we update the regex of the `CancelFlow` command to match the DSL string `Cancel()` instead of
|
|
225
|
+
`CancelFlow()`:
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
from rasa.dialogue_understanding.commands import CancelFlow
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class CustomCancelFlow(CancelFlow):
|
|
232
|
+
def to_dsl(self) -> str:
|
|
233
|
+
"""Converts the command to a DSL string."""
|
|
234
|
+
return "Cancel()"
|
|
235
|
+
|
|
236
|
+
@staticmethod
|
|
237
|
+
def regex_pattern() -> str:
|
|
238
|
+
# Define the regex pattern that matches the DSL string
|
|
239
|
+
return r"Cancel\(\)"
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
After defining the custom command, you can instruct the command parser to parse this new custom command
|
|
243
|
+
from your command generator by passing the updated default command as an additional command and
|
|
244
|
+
removing the default command:
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
@classmethod
|
|
248
|
+
def parse_commands(
|
|
249
|
+
cls, actions: Optional[str], tracker: DialogueStateTracker, flows: FlowsList
|
|
250
|
+
) -> List[Command]:
|
|
251
|
+
"""Parse the actions returned by the llm into intent and entities.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
actions: The actions returned by the llm.
|
|
255
|
+
tracker: The tracker containing the current state of the conversation.
|
|
256
|
+
flows: the list of flows
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
The parsed commands.
|
|
260
|
+
"""
|
|
261
|
+
from rasa.dialogue_understanding.generator.command_parser import (
|
|
262
|
+
parse_commands as parse_commands_using_command_parsers,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
return parse_commands_using_command_parsers(actions, flows, additional_commands=[CustomCancelFlow],
|
|
266
|
+
remove_default_commands=[CancelFlow])
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
When running the Dialogue Understanding Tests, you can pass the updated default command as a cli argument:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
rasa test du <path-to-test-cases> --additional-commands my_module.CustomCancelFlow --remove-default-commands CancelFlow
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Like the `--additional-commands` arg, the `--remove-default-commands` arg takes a list of default command classes
|
|
276
|
+
separated by spaces.
|
|
277
|
+
|
|
278
|
+
**Note**: The class name alone is sufficient for the `--remove-default-commands` argument because the default commands
|
|
279
|
+
are
|
|
280
|
+
already known by the Dialogue Understanding Test framework.
|
|
281
|
+
|
|
282
|
+
## Criteria for Test Case Success
|
|
283
|
+
|
|
284
|
+
A test case is considered to have passed if all the expected commands match the predicted commands at
|
|
285
|
+
each step. The expected and predicted commands are considered identical if their types and arguments
|
|
286
|
+
exactly match, with the order of the commands being irrelevant.
|
|
287
|
+
To compare two commands we use the `__eq__` method of the commands.
|
|
288
|
+
There's an exception for the `Clarify` command.
|
|
289
|
+
|
|
290
|
+
**Clarify Command**
|
|
291
|
+
|
|
292
|
+
When defining a `Clarify` command in a Dialogue Understanding Test, you have the option to leave the
|
|
293
|
+
command's options empty or specify a list of options; the options are optional.
|
|
294
|
+
If you provide a list of options, the predicted `Clarify` command must include the exact same list
|
|
295
|
+
to match the expected command. If you leave the options list empty, the predicted `Clarify` command
|
|
296
|
+
can have any list of options.
|
|
297
|
+
|
|
298
|
+
## Dialogue Understanding Test Output
|
|
299
|
+
|
|
300
|
+
The output of Dialogue Understanding Tests provides a comprehensive view of the chatbot's performance in
|
|
301
|
+
predicting and generating commands.
|
|
302
|
+
It includes detailed information that helps users pinpoint areas of improvement in the command generation process.
|
|
303
|
+
The output is logged to the console and saved in a detailed report file in a structured format for later analysis or
|
|
304
|
+
record-keeping.
|
|
305
|
+
|
|
306
|
+
The following information is present in the output:
|
|
307
|
+
|
|
308
|
+
- Number of passed and failed test cases.
|
|
309
|
+
- Number of passed and failed user utterances.
|
|
310
|
+
- Test case names of failed and passed test cases.
|
|
311
|
+
- A detailed diff of expected vs. predicted commands for each failed user message in a failed test case.
|
|
312
|
+
A test case can have multiple failed user messages. The command generators listed in the output are the ones that
|
|
313
|
+
generated the predicted commands.
|
|
314
|
+
Example of a failed test case diff:
|
|
315
|
+
```diff
|
|
316
|
+
------------- test_case: <file-path>::user_adds_contact_to_their_list -------------
|
|
317
|
+
|
|
318
|
+
Number of failed steps: 1
|
|
319
|
+
|
|
320
|
+
== failure starting at user message 'it's @barts'.
|
|
321
|
+
|
|
322
|
+
-- COMMAND GENERATOR(s) --
|
|
323
|
+
SingleStepLLMCommandGenerator
|
|
324
|
+
|
|
325
|
+
-- CONVERSATION --
|
|
326
|
+
user: I want to add someone to my contact list
|
|
327
|
+
bot: What's the handle of the user you want to add?
|
|
328
|
+
user: it's @barts
|
|
329
|
+
-- EXPECTED -- | -- PREDICTED --
|
|
330
|
+
SetSlot(handle, @barts) | SetSlot(name, @barts)
|
|
331
|
+
```
|
|
332
|
+
- Command metrics for each command type, including the total count, true positives (tp), false positives (fp),
|
|
333
|
+
false negatives (fn), precision, recall, and f1-score.
|
|
334
|
+
Example of command metrics:
|
|
335
|
+
```diff
|
|
336
|
+
start flow (total count: 10):
|
|
337
|
+
tp: 10 fp: 0 fn: 0
|
|
338
|
+
precision: 1.00
|
|
339
|
+
recall : 1.00
|
|
340
|
+
f1 : 1.00
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
If you start the dialogue understanding tests with the `--output-prompt` flag, you will also see the prompt that
|
|
344
|
+
returned the predicted commands.
|
|
345
|
+
|
|
346
|
+
## Converting end-to-end (E2E) Tests to Dialogue Understanding Tests
|
|
347
|
+
|
|
348
|
+
To convert end-to-end (E2E) tests into Dialogue Understanding Tests you can use a standalone Python script:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
python convert_e2e_tests_to_du_tests.py <path-to-e2e-tests>
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
The script has the following parameters:
|
|
355
|
+
|
|
356
|
+
- `<path-to-e2e-tests>`: The path to your existing E2E test cases (can be a single file or a directory).
|
|
357
|
+
- `--output-folder <output-folder>`: The path where the converted test cases will be saved. The default is
|
|
358
|
+
`dialogue_understanding_tests`.
|
|
359
|
+
|
|
360
|
+
After running the script, the output folder structure will look like this:
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
<output-folder>
|
|
364
|
+
|-- ready
|
|
365
|
+
| |-- test_case_a.yml
|
|
366
|
+
| |-- test_case_b.yml
|
|
367
|
+
| |-- ...
|
|
368
|
+
|-- to_review
|
|
369
|
+
| |-- test_case_c.yml
|
|
370
|
+
| |-- test_case_d.yml
|
|
371
|
+
| |-- ...
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Test cases that end up in **ready** are converted from E2E test cases that passed.
|
|
375
|
+
No further action is needed for these cases.
|
|
376
|
+
Test cases in **to_review** may require manual intervention because the E2E test failed.
|
|
377
|
+
Review these cases to ensure that the converted test cases are correct and the list of commands and
|
|
378
|
+
bot responses is complete.
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
## Converting DUT test from one DSL to a another DSL
|
|
382
|
+
|
|
383
|
+
If you need to transform your commands from one DSL format to another
|
|
384
|
+
(for instance, updating `StartFlow(flow_name)` to `start flow_name` or `SetSlot(slot_name, slot_value)` to `set slot_name slot_value`),
|
|
385
|
+
you can use a standalone Python script:
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
python convert_dut_dsl.py --dut-tests-dir <path> --output-dir <path> --dsl-mappings <path>
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
The script has the following required parameters:
|
|
392
|
+
|
|
393
|
+
- `--dut-tests-dir <path>`: The directory (relative or absolute) containing your
|
|
394
|
+
existing Dialogue Understanding Tests (DUT). The script will look for `.yaml` or
|
|
395
|
+
`.yml` files within this folder (and subfolders).
|
|
396
|
+
- `--output-dir <path>`: The directory where transformed files will be saved. The folder
|
|
397
|
+
structure from your `dut-tests-dir` is preserved.
|
|
398
|
+
- `--dsl-mappings <path>`: The YAML file defining your DSL mapping rules.
|
|
399
|
+
|
|
400
|
+
The YAML file containing the mappings must adhere to the following format:
|
|
401
|
+
```yaml
|
|
402
|
+
mappings:
|
|
403
|
+
|
|
404
|
+
- from_dsl_regex: "^StartFlow\\(([^)]*)\\)$"
|
|
405
|
+
to_dsl_pattern: "start {1}"
|
|
406
|
+
|
|
407
|
+
- from_dsl_regex: "^SetSlot\\(([^,]+),\\s*(.*)\\)$"
|
|
408
|
+
to_dsl_pattern: "set {1} {2}"
|
|
409
|
+
|
|
410
|
+
- from_dsl_regex: "Clarify\(([\"\'a-zA-Z0-9_, ]*)\)"
|
|
411
|
+
to_dsl_pattern: "clarify {1}"
|
|
412
|
+
input_separators:
|
|
413
|
+
- ","
|
|
414
|
+
- " "
|
|
415
|
+
output_separator: " "
|
|
416
|
+
|
|
417
|
+
# ... add more mappings here
|
|
418
|
+
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
- `from_dsl_regex`: A regular expression (string) used to match the old DSL command.
|
|
422
|
+
Must include any necessary anchors (like ^ and $) and capturing groups ( ... ) for
|
|
423
|
+
dynamic parts.
|
|
424
|
+
- `to_dsl_pattern`: A string that contains placeholders like `{1}`, `{2}`, etc. Each
|
|
425
|
+
placeholder corresponds to a capturing group in from_dsl_regex, in order of
|
|
426
|
+
appearance.
|
|
427
|
+
- `input_separators`: Optional list of separators of the captured groups that can be replaced
|
|
428
|
+
with the `output_separator`
|
|
429
|
+
- `output_separator`: Output separator to replace separators from the list of `input_separators` in the captured group.
|
|
File without changes
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from rasa.dialogue_understanding.commands import ClarifyCommand, Command
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def are_command_lists_equal(
|
|
7
|
+
command_list_1: List[Command], command_list_2: List[Command]
|
|
8
|
+
) -> bool:
|
|
9
|
+
"""Check if the command lists are equal."""
|
|
10
|
+
if len(command_list_1) != len(command_list_2):
|
|
11
|
+
return False
|
|
12
|
+
|
|
13
|
+
for command in command_list_1:
|
|
14
|
+
if not is_command_present_in_list(command, command_list_2):
|
|
15
|
+
return False
|
|
16
|
+
|
|
17
|
+
return True
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def is_command_present_in_list(
|
|
21
|
+
command: Command,
|
|
22
|
+
list_of_commands: List[Command],
|
|
23
|
+
) -> bool:
|
|
24
|
+
"""Check if the command is present in the list of commands."""
|
|
25
|
+
for command_in_list in list_of_commands:
|
|
26
|
+
if are_commands_equal(command, command_in_list):
|
|
27
|
+
return True
|
|
28
|
+
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def are_commands_equal(
|
|
33
|
+
command_1: Command,
|
|
34
|
+
command_2: Command,
|
|
35
|
+
) -> bool:
|
|
36
|
+
"""Compare the commands.
|
|
37
|
+
|
|
38
|
+
Clarify commands are compared separately as options might be optional.
|
|
39
|
+
"""
|
|
40
|
+
# as options are optional for clarify commands,
|
|
41
|
+
# we need to check them separately
|
|
42
|
+
if isinstance(command_1, ClarifyCommand) and isinstance(command_2, ClarifyCommand):
|
|
43
|
+
return _are_clarify_commands_equal(command_1, command_2)
|
|
44
|
+
|
|
45
|
+
# an exact match is required for all other commands
|
|
46
|
+
return command_1 == command_2
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _are_clarify_commands_equal(
|
|
50
|
+
expected_command: ClarifyCommand,
|
|
51
|
+
predicted_command: ClarifyCommand,
|
|
52
|
+
) -> bool:
|
|
53
|
+
# if the expected command contains options,
|
|
54
|
+
# the predicted command should have the same options
|
|
55
|
+
if expected_command.options:
|
|
56
|
+
return sorted(expected_command.options) == sorted(predicted_command.options)
|
|
57
|
+
|
|
58
|
+
# if the expected command does not contain options,
|
|
59
|
+
# it does not matter whether the predicted command has options or not
|
|
60
|
+
return True
|