rasa-pro 3.12.0.dev12__py3-none-any.whl → 3.12.0rc1__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.
- rasa/anonymization/anonymization_rule_executor.py +16 -10
- rasa/cli/data.py +16 -0
- rasa/cli/inspect.py +20 -1
- rasa/cli/project_templates/calm/config.yml +2 -2
- rasa/cli/project_templates/calm/endpoints.yml +2 -2
- rasa/cli/shell.py +3 -3
- rasa/cli/utils.py +12 -0
- rasa/core/actions/action.py +99 -193
- rasa/core/actions/action_handle_digressions.py +142 -0
- rasa/core/actions/action_run_slot_rejections.py +16 -4
- rasa/core/actions/forms.py +10 -5
- rasa/core/channels/__init__.py +4 -0
- rasa/core/channels/studio_chat.py +19 -0
- rasa/core/channels/telegram.py +42 -24
- rasa/core/channels/voice_ready/audiocodes.py +42 -23
- rasa/core/channels/voice_ready/utils.py +1 -1
- rasa/core/channels/voice_stream/asr/asr_engine.py +10 -4
- rasa/core/channels/voice_stream/asr/azure.py +14 -1
- rasa/core/channels/voice_stream/asr/deepgram.py +20 -4
- rasa/core/channels/voice_stream/audiocodes.py +264 -0
- rasa/core/channels/voice_stream/browser_audio.py +5 -1
- rasa/core/channels/voice_stream/call_state.py +10 -1
- rasa/core/channels/voice_stream/genesys.py +335 -0
- rasa/core/channels/voice_stream/tts/azure.py +11 -2
- rasa/core/channels/voice_stream/tts/cartesia.py +29 -10
- rasa/core/channels/voice_stream/twilio_media_streams.py +2 -1
- rasa/core/channels/voice_stream/voice_channel.py +25 -3
- rasa/core/constants.py +2 -0
- rasa/core/migrate.py +2 -2
- rasa/core/nlg/contextual_response_rephraser.py +18 -1
- rasa/core/nlg/generator.py +83 -15
- rasa/core/nlg/response.py +6 -3
- rasa/core/nlg/translate.py +55 -0
- rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +1 -1
- rasa/core/policies/flows/flow_executor.py +47 -46
- rasa/core/processor.py +72 -9
- rasa/core/run.py +4 -3
- rasa/dialogue_understanding/commands/can_not_handle_command.py +20 -2
- rasa/dialogue_understanding/commands/cancel_flow_command.py +80 -4
- rasa/dialogue_understanding/commands/change_flow_command.py +20 -2
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +20 -2
- rasa/dialogue_understanding/commands/clarify_command.py +29 -3
- rasa/dialogue_understanding/commands/command.py +1 -16
- rasa/dialogue_understanding/commands/command_syntax_manager.py +55 -0
- rasa/dialogue_understanding/commands/correct_slots_command.py +11 -2
- rasa/dialogue_understanding/commands/handle_digressions_command.py +150 -0
- rasa/dialogue_understanding/commands/human_handoff_command.py +20 -2
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +20 -2
- rasa/dialogue_understanding/commands/prompt_command.py +94 -0
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +20 -2
- rasa/dialogue_understanding/commands/set_slot_command.py +29 -15
- rasa/dialogue_understanding/commands/skip_question_command.py +20 -2
- rasa/dialogue_understanding/commands/start_flow_command.py +61 -2
- rasa/dialogue_understanding/commands/utils.py +98 -4
- rasa/dialogue_understanding/constants.py +1 -0
- rasa/dialogue_understanding/generator/__init__.py +2 -0
- rasa/dialogue_understanding/generator/command_generator.py +110 -73
- rasa/dialogue_understanding/generator/command_parser.py +16 -13
- rasa/dialogue_understanding/generator/constants.py +3 -0
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +170 -5
- rasa/dialogue_understanding/generator/llm_command_generator.py +5 -3
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +26 -4
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +44 -3
- rasa/dialogue_understanding/generator/prompt_templates/__init__.py +0 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +60 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +77 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_default.jinja2 +68 -0
- rasa/dialogue_understanding/generator/{single_step/command_prompt_template.jinja2 → prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2} +1 -1
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +460 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +12 -318
- rasa/dialogue_understanding/generator/utils.py +32 -1
- rasa/dialogue_understanding/patterns/collect_information.py +1 -1
- rasa/dialogue_understanding/patterns/correction.py +13 -1
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +78 -2
- rasa/dialogue_understanding/patterns/handle_digressions.py +81 -0
- rasa/dialogue_understanding/patterns/validate_slot.py +65 -0
- rasa/dialogue_understanding/processor/command_processor.py +154 -28
- rasa/dialogue_understanding/utils.py +31 -0
- rasa/dialogue_understanding_test/README.md +50 -0
- rasa/dialogue_understanding_test/du_test_case.py +28 -8
- rasa/dialogue_understanding_test/du_test_result.py +13 -9
- rasa/dialogue_understanding_test/io.py +14 -0
- rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +3 -3
- rasa/e2e_test/utils/io.py +0 -37
- rasa/engine/graph.py +1 -0
- rasa/engine/language.py +140 -0
- rasa/engine/recipes/config_files/default_config.yml +4 -0
- rasa/engine/recipes/default_recipe.py +2 -0
- rasa/engine/recipes/graph_recipe.py +2 -0
- rasa/engine/storage/local_model_storage.py +1 -0
- rasa/engine/storage/storage.py +4 -1
- rasa/model_manager/runner_service.py +7 -4
- rasa/model_manager/socket_bridge.py +7 -6
- rasa/model_manager/warm_rasa_process.py +0 -1
- rasa/model_training.py +24 -27
- rasa/shared/constants.py +15 -13
- rasa/shared/core/constants.py +30 -3
- rasa/shared/core/domain.py +13 -20
- rasa/shared/core/events.py +13 -2
- rasa/shared/core/flows/constants.py +11 -0
- rasa/shared/core/flows/flow.py +100 -19
- rasa/shared/core/flows/flows_yaml_schema.json +69 -3
- rasa/shared/core/flows/steps/collect.py +19 -37
- rasa/shared/core/flows/utils.py +43 -4
- rasa/shared/core/flows/validation.py +1 -1
- rasa/shared/core/slot_mappings.py +350 -111
- rasa/shared/core/slots.py +154 -3
- rasa/shared/core/trackers.py +77 -2
- rasa/shared/importers/importer.py +50 -2
- rasa/shared/nlu/constants.py +1 -0
- rasa/shared/nlu/training_data/schemas/responses.yml +19 -12
- rasa/shared/providers/_configs/azure_entra_id_config.py +541 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +138 -3
- rasa/shared/providers/_configs/client_config.py +3 -1
- rasa/shared/providers/_configs/default_litellm_client_config.py +3 -1
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +3 -1
- rasa/shared/providers/_configs/litellm_router_client_config.py +3 -1
- rasa/shared/providers/_configs/model_group_config.py +4 -2
- rasa/shared/providers/_configs/oauth_config.py +33 -0
- rasa/shared/providers/_configs/openai_client_config.py +3 -1
- rasa/shared/providers/_configs/rasa_llm_client_config.py +3 -1
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +3 -1
- rasa/shared/providers/constants.py +6 -0
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +28 -3
- rasa/shared/providers/embedding/litellm_router_embedding_client.py +3 -1
- rasa/shared/providers/llm/_base_litellm_client.py +42 -17
- rasa/shared/providers/llm/azure_openai_llm_client.py +81 -25
- rasa/shared/providers/llm/default_litellm_llm_client.py +3 -1
- rasa/shared/providers/llm/litellm_router_llm_client.py +29 -8
- rasa/shared/providers/llm/llm_client.py +23 -7
- rasa/shared/providers/llm/openai_llm_client.py +9 -3
- rasa/shared/providers/llm/rasa_llm_client.py +11 -2
- rasa/shared/providers/llm/self_hosted_llm_client.py +30 -11
- rasa/shared/providers/router/_base_litellm_router_client.py +3 -1
- rasa/shared/providers/router/router_client.py +3 -1
- rasa/shared/utils/constants.py +3 -0
- rasa/shared/utils/llm.py +31 -8
- rasa/shared/utils/pykwalify_extensions.py +24 -0
- rasa/shared/utils/schemas/domain.yml +26 -1
- rasa/telemetry.py +45 -14
- rasa/tracing/config.py +2 -0
- rasa/tracing/constants.py +12 -0
- rasa/tracing/instrumentation/instrumentation.py +36 -0
- rasa/tracing/instrumentation/metrics.py +41 -0
- rasa/tracing/metric_instrument_provider.py +40 -0
- rasa/utils/common.py +0 -1
- rasa/validator.py +561 -89
- rasa/version.py +1 -1
- {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/METADATA +2 -1
- {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/RECORD +153 -134
- {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/NOTICE +0 -0
- {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/WHEEL +0 -0
- {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/entry_points.txt +0 -0
|
@@ -4,20 +4,14 @@ from dataclasses import dataclass
|
|
|
4
4
|
from typing import Any, Dict, List, Optional, Text, Union
|
|
5
5
|
|
|
6
6
|
from faker import Faker
|
|
7
|
-
from presidio_analyzer import AnalyzerEngine
|
|
8
|
-
from presidio_analyzer.nlp_engine import (
|
|
9
|
-
SpacyNlpEngine,
|
|
10
|
-
StanzaNlpEngine,
|
|
11
|
-
TransformersNlpEngine,
|
|
12
|
-
)
|
|
13
|
-
from presidio_anonymizer import AnonymizerEngine
|
|
14
|
-
from presidio_anonymizer.entities import OperatorConfig
|
|
15
7
|
|
|
16
8
|
from rasa.shared.exceptions import RasaException
|
|
17
9
|
from rasa.utils.singleton import Singleton
|
|
18
10
|
|
|
19
11
|
if typing.TYPE_CHECKING:
|
|
12
|
+
from presidio_analyzer import AnalyzerEngine
|
|
20
13
|
from presidio_analyzer.nlp_engine.nlp_engine import NlpEngine
|
|
14
|
+
from presidio_anonymizer.entities import OperatorConfig
|
|
21
15
|
|
|
22
16
|
DEFAULT_PRESIDIO_LANG_CODE = "en"
|
|
23
17
|
DEFAULT_PRESIDIO_MODEL_NAME = "en_core_web_lg"
|
|
@@ -61,8 +55,10 @@ class AnonymizationAnalyzer(metaclass=Singleton):
|
|
|
61
55
|
@staticmethod
|
|
62
56
|
def _get_analyzer_engine(
|
|
63
57
|
anonymization_rule_list: AnonymizationRuleList,
|
|
64
|
-
) -> AnalyzerEngine:
|
|
58
|
+
) -> "AnalyzerEngine":
|
|
65
59
|
"""Returns an analyzer engine for all the anonymization rule lists."""
|
|
60
|
+
from presidio_analyzer import AnalyzerEngine
|
|
61
|
+
|
|
66
62
|
try:
|
|
67
63
|
nlp_engine = AnonymizationAnalyzer._build_presidio_nlp_engine(
|
|
68
64
|
anonymization_rule_list
|
|
@@ -84,6 +80,12 @@ class AnonymizationAnalyzer(metaclass=Singleton):
|
|
|
84
80
|
anonymization_rule_list: AnonymizationRuleList,
|
|
85
81
|
) -> "NlpEngine":
|
|
86
82
|
"""Creates an instance of the Presidio nlp engine."""
|
|
83
|
+
from presidio_analyzer.nlp_engine import (
|
|
84
|
+
SpacyNlpEngine,
|
|
85
|
+
StanzaNlpEngine,
|
|
86
|
+
TransformersNlpEngine,
|
|
87
|
+
)
|
|
88
|
+
|
|
87
89
|
if anonymization_rule_list.model_provider == "transformers":
|
|
88
90
|
nlp_engine = TransformersNlpEngine(
|
|
89
91
|
models={
|
|
@@ -111,6 +113,8 @@ class AnonymizationRuleExecutor:
|
|
|
111
113
|
|
|
112
114
|
def __init__(self, anonymization_rule_list: AnonymizationRuleList):
|
|
113
115
|
"""Initialize the anonymization rule executor."""
|
|
116
|
+
from presidio_anonymizer import AnonymizerEngine
|
|
117
|
+
|
|
114
118
|
self.anonymization_rule_list = anonymization_rule_list
|
|
115
119
|
|
|
116
120
|
is_valid_rule_list = self._validate_anonymization_rule_list(
|
|
@@ -243,8 +247,10 @@ class AnonymizationRuleExecutor:
|
|
|
243
247
|
|
|
244
248
|
return func
|
|
245
249
|
|
|
246
|
-
def get_operators(self) -> Dict[Text, OperatorConfig]:
|
|
250
|
+
def get_operators(self) -> Dict[Text, "OperatorConfig"]:
|
|
247
251
|
"""Returns a dictionary of operators for the given anonymization rule list."""
|
|
252
|
+
from presidio_anonymizer.entities import OperatorConfig
|
|
253
|
+
|
|
248
254
|
operators = {}
|
|
249
255
|
|
|
250
256
|
for rule in self.anonymization_rule_list.rule_list:
|
rasa/cli/data.py
CHANGED
|
@@ -178,6 +178,22 @@ def _add_data_validate_parsers(
|
|
|
178
178
|
)
|
|
179
179
|
arguments.set_validator_arguments(flows_structure_parser)
|
|
180
180
|
|
|
181
|
+
translations_structure_parser = validate_subparsers.add_parser(
|
|
182
|
+
"translations",
|
|
183
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
184
|
+
parents=parents,
|
|
185
|
+
help="Checks for inconsistencies of the flow and response translation.",
|
|
186
|
+
)
|
|
187
|
+
translations_structure_parser.set_defaults(
|
|
188
|
+
func=lambda args: rasa.cli.utils.validate_files(
|
|
189
|
+
args.fail_on_warnings,
|
|
190
|
+
args.max_history,
|
|
191
|
+
_build_training_data_importer(args),
|
|
192
|
+
translations_only=True,
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
arguments.set_validator_arguments(translations_structure_parser)
|
|
196
|
+
|
|
181
197
|
|
|
182
198
|
def _build_training_data_importer(args: argparse.Namespace) -> "TrainingDataImporter":
|
|
183
199
|
config = rasa.cli.utils.get_validated_path(
|
rasa/cli/inspect.py
CHANGED
|
@@ -9,6 +9,10 @@ from rasa import telemetry
|
|
|
9
9
|
from rasa.cli import SubParsersAction
|
|
10
10
|
from rasa.cli.arguments import shell as arguments
|
|
11
11
|
from rasa.core import constants
|
|
12
|
+
from rasa.engine.storage.local_model_storage import LocalModelStorage
|
|
13
|
+
from rasa.exceptions import ModelNotFound
|
|
14
|
+
from rasa.model import get_local_model
|
|
15
|
+
from rasa.shared.utils.cli import print_error
|
|
12
16
|
from rasa.utils.cli import remove_argument_from_parser
|
|
13
17
|
|
|
14
18
|
|
|
@@ -55,6 +59,8 @@ async def open_inspector_in_browser(server_url: Text, voice: bool = False) -> No
|
|
|
55
59
|
def inspect(args: argparse.Namespace) -> None:
|
|
56
60
|
"""Inspect the bot using the most recent model."""
|
|
57
61
|
import rasa.cli.run
|
|
62
|
+
from rasa.cli.utils import get_validated_path
|
|
63
|
+
from rasa.shared.constants import DEFAULT_MODELS_PATH
|
|
58
64
|
|
|
59
65
|
async def after_start_hook_open_inspector(_: Sanic, __: AbstractEventLoop) -> None:
|
|
60
66
|
"""Hook to open the browser on server start."""
|
|
@@ -71,5 +77,18 @@ def inspect(args: argparse.Namespace) -> None:
|
|
|
71
77
|
args.credentials = None
|
|
72
78
|
args.server_listeners = [(after_start_hook_open_inspector, "after_server_start")]
|
|
73
79
|
|
|
74
|
-
|
|
80
|
+
model = get_validated_path(args.model, "model", DEFAULT_MODELS_PATH)
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
model = get_local_model(model)
|
|
84
|
+
except ModelNotFound:
|
|
85
|
+
print_error(
|
|
86
|
+
"No model found. Train a model before running the "
|
|
87
|
+
"server using `rasa train`."
|
|
88
|
+
)
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
metadata = LocalModelStorage.metadata_from_archive(model)
|
|
92
|
+
|
|
93
|
+
telemetry.track_inspect_started(args.connector, metadata.assistant_id)
|
|
75
94
|
rasa.cli.run.run(args)
|
rasa/cli/shell.py
CHANGED
|
@@ -95,7 +95,7 @@ def shell_nlu(args: argparse.Namespace) -> None:
|
|
|
95
95
|
)
|
|
96
96
|
return
|
|
97
97
|
|
|
98
|
-
telemetry.track_shell_started("nlu")
|
|
98
|
+
telemetry.track_shell_started("nlu", metadata.assistant_id)
|
|
99
99
|
rasa.nlu.run.run_cmdline(model)
|
|
100
100
|
|
|
101
101
|
|
|
@@ -129,12 +129,12 @@ def shell(args: argparse.Namespace) -> None:
|
|
|
129
129
|
if metadata.training_type == TrainingType.NLU:
|
|
130
130
|
import rasa.nlu.run
|
|
131
131
|
|
|
132
|
-
telemetry.track_shell_started("nlu")
|
|
132
|
+
telemetry.track_shell_started("nlu", metadata.assistant_id)
|
|
133
133
|
|
|
134
134
|
rasa.nlu.run.run_cmdline(model)
|
|
135
135
|
else:
|
|
136
136
|
import rasa.cli.run
|
|
137
137
|
|
|
138
|
-
telemetry.track_shell_started("rasa")
|
|
138
|
+
telemetry.track_shell_started("rasa", metadata.assistant_id)
|
|
139
139
|
|
|
140
140
|
rasa.cli.run.run(args)
|
rasa/cli/utils.py
CHANGED
|
@@ -248,6 +248,7 @@ def validate_files(
|
|
|
248
248
|
importer: TrainingDataImporter,
|
|
249
249
|
stories_only: bool = False,
|
|
250
250
|
flows_only: bool = False,
|
|
251
|
+
translations_only: bool = False,
|
|
251
252
|
) -> None:
|
|
252
253
|
"""Validates either the story structure or the entire project.
|
|
253
254
|
|
|
@@ -257,6 +258,7 @@ def validate_files(
|
|
|
257
258
|
importer: The `TrainingDataImporter` to use to load the training data.
|
|
258
259
|
stories_only: If `True`, only the story structure is validated.
|
|
259
260
|
flows_only: If `True`, only the flows are validated.
|
|
261
|
+
translations_only: If `True`, only the translations data is validated.
|
|
260
262
|
"""
|
|
261
263
|
from rasa.validator import Validator
|
|
262
264
|
|
|
@@ -266,6 +268,8 @@ def validate_files(
|
|
|
266
268
|
all_good = _validate_story_structure(validator, max_history, fail_on_warnings)
|
|
267
269
|
elif flows_only:
|
|
268
270
|
all_good = validator.verify_flows()
|
|
271
|
+
elif translations_only:
|
|
272
|
+
all_good = validator.verify_translations()
|
|
269
273
|
else:
|
|
270
274
|
if importer.get_domain().is_empty():
|
|
271
275
|
structlogger.error(
|
|
@@ -280,6 +284,7 @@ def validate_files(
|
|
|
280
284
|
validator, max_history, fail_on_warnings
|
|
281
285
|
)
|
|
282
286
|
valid_flows = validator.verify_flows()
|
|
287
|
+
valid_translations = validator.verify_translations(summary_mode=True)
|
|
283
288
|
valid_CALM_slot_mappings = validator.validate_CALM_slot_mappings()
|
|
284
289
|
|
|
285
290
|
all_good = (
|
|
@@ -287,6 +292,7 @@ def validate_files(
|
|
|
287
292
|
and valid_nlu
|
|
288
293
|
and valid_stories
|
|
289
294
|
and valid_flows
|
|
295
|
+
and valid_translations
|
|
290
296
|
and valid_CALM_slot_mappings
|
|
291
297
|
)
|
|
292
298
|
|
|
@@ -309,6 +315,10 @@ def _validate_domain(validator: "Validator") -> bool:
|
|
|
309
315
|
valid_slot_mappings = validator.verify_slot_mappings()
|
|
310
316
|
valid_responses = validator.check_for_no_empty_parenthesis_in_responses()
|
|
311
317
|
valid_buttons = validator.validate_button_payloads()
|
|
318
|
+
valid_slot_validation = validator.verify_slot_validation()
|
|
319
|
+
valid_conditional_responses = (
|
|
320
|
+
validator.validate_conditional_response_variation_predicates()
|
|
321
|
+
)
|
|
312
322
|
return (
|
|
313
323
|
valid_domain_validity
|
|
314
324
|
and valid_actions_in_stories_rules
|
|
@@ -317,6 +327,8 @@ def _validate_domain(validator: "Validator") -> bool:
|
|
|
317
327
|
and valid_slot_mappings
|
|
318
328
|
and valid_responses
|
|
319
329
|
and valid_buttons
|
|
330
|
+
and valid_slot_validation
|
|
331
|
+
and valid_conditional_responses
|
|
320
332
|
)
|
|
321
333
|
|
|
322
334
|
|
rasa/core/actions/action.py
CHANGED
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
import logging
|
|
3
3
|
from functools import lru_cache
|
|
4
|
-
from typing import
|
|
5
|
-
TYPE_CHECKING,
|
|
6
|
-
Any,
|
|
7
|
-
Dict,
|
|
8
|
-
List,
|
|
9
|
-
Optional,
|
|
10
|
-
Set,
|
|
11
|
-
Text,
|
|
12
|
-
Tuple,
|
|
13
|
-
cast,
|
|
14
|
-
)
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Text, cast
|
|
15
5
|
|
|
16
6
|
from jsonschema import Draft202012Validator
|
|
17
7
|
|
|
@@ -29,11 +19,15 @@ from rasa.core.actions.e2e_stub_custom_action_executor import (
|
|
|
29
19
|
from rasa.core.actions.grpc_custom_action_executor import GRPCCustomActionExecutor
|
|
30
20
|
from rasa.core.actions.http_custom_action_executor import HTTPCustomActionExecutor
|
|
31
21
|
from rasa.core.constants import (
|
|
22
|
+
KEY_IS_CALM_SYSTEM,
|
|
23
|
+
KEY_IS_COEXISTENCE_ASSISTANT,
|
|
32
24
|
UTTER_SOURCE_METADATA_KEY,
|
|
33
25
|
)
|
|
26
|
+
from rasa.core.nlg.translate import get_translated_buttons, get_translated_text
|
|
34
27
|
from rasa.core.policies.policy import PolicyPrediction
|
|
35
28
|
from rasa.core.utils import add_bot_utterance_metadata
|
|
36
29
|
from rasa.e2e_test.constants import KEY_STUB_CUSTOM_ACTIONS
|
|
30
|
+
from rasa.engine.language import Language
|
|
37
31
|
from rasa.nlu.constants import (
|
|
38
32
|
RESPONSE_SELECTOR_DEFAULT_INTENT,
|
|
39
33
|
RESPONSE_SELECTOR_PREDICTION_KEY,
|
|
@@ -70,13 +64,11 @@ from rasa.shared.core.constants import (
|
|
|
70
64
|
ACTION_SEND_TEXT_NAME,
|
|
71
65
|
ACTION_SESSION_START_NAME,
|
|
72
66
|
ACTION_UNLIKELY_INTENT_NAME,
|
|
73
|
-
ACTION_VALIDATE_SLOT_MAPPINGS,
|
|
74
67
|
DEFAULT_SLOT_NAMES,
|
|
75
68
|
KNOWLEDGE_BASE_SLOT_NAMES,
|
|
76
|
-
MAPPING_TYPE,
|
|
77
69
|
REQUESTED_SLOT,
|
|
78
70
|
USER_INTENT_OUT_OF_SCOPE,
|
|
79
|
-
|
|
71
|
+
SetSlotExtractor,
|
|
80
72
|
)
|
|
81
73
|
from rasa.shared.core.domain import Domain
|
|
82
74
|
from rasa.shared.core.events import (
|
|
@@ -92,19 +84,17 @@ from rasa.shared.core.events import (
|
|
|
92
84
|
UserUttered,
|
|
93
85
|
)
|
|
94
86
|
from rasa.shared.core.flows import FlowsList
|
|
87
|
+
from rasa.shared.core.flows.constants import KEY_TRANSLATION
|
|
95
88
|
from rasa.shared.core.slot_mappings import (
|
|
96
89
|
SlotFillingManager,
|
|
97
90
|
extract_slot_value,
|
|
98
91
|
)
|
|
99
92
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
100
93
|
from rasa.shared.exceptions import RasaException
|
|
101
|
-
from rasa.shared.nlu.constants import
|
|
102
|
-
INTENT_NAME_KEY,
|
|
103
|
-
INTENT_RANKING_KEY,
|
|
104
|
-
)
|
|
94
|
+
from rasa.shared.nlu.constants import INTENT_NAME_KEY, INTENT_RANKING_KEY
|
|
105
95
|
from rasa.shared.utils.io import raise_warning
|
|
106
96
|
from rasa.shared.utils.schemas.events import EVENTS_SCHEMA
|
|
107
|
-
from rasa.utils.endpoints import
|
|
97
|
+
from rasa.utils.endpoints import EndpointConfig
|
|
108
98
|
from rasa.utils.url_tools import UrlSchema, get_url_schema
|
|
109
99
|
|
|
110
100
|
if TYPE_CHECKING:
|
|
@@ -118,6 +108,10 @@ logger = logging.getLogger(__name__)
|
|
|
118
108
|
def default_actions(action_endpoint: Optional[EndpointConfig] = None) -> List["Action"]:
|
|
119
109
|
"""List default actions."""
|
|
120
110
|
from rasa.core.actions.action_clean_stack import ActionCleanStack
|
|
111
|
+
from rasa.core.actions.action_handle_digressions import (
|
|
112
|
+
ActionBlockDigressions,
|
|
113
|
+
ActionContinueDigression,
|
|
114
|
+
)
|
|
121
115
|
from rasa.core.actions.action_hangup import ActionHangup
|
|
122
116
|
from rasa.core.actions.action_repeat_bot_messages import ActionRepeatBotMessages
|
|
123
117
|
from rasa.core.actions.action_run_slot_rejections import ActionRunSlotRejections
|
|
@@ -152,6 +146,8 @@ def default_actions(action_endpoint: Optional[EndpointConfig] = None) -> List["A
|
|
|
152
146
|
ActionResetRouting(),
|
|
153
147
|
ActionHangup(),
|
|
154
148
|
ActionRepeatBotMessages(),
|
|
149
|
+
ActionBlockDigressions(),
|
|
150
|
+
ActionContinueDigression(),
|
|
155
151
|
]
|
|
156
152
|
|
|
157
153
|
|
|
@@ -261,25 +257,36 @@ def action_for_name_or_text(
|
|
|
261
257
|
return RemoteAction(action_name_or_text, action_endpoint)
|
|
262
258
|
|
|
263
259
|
|
|
264
|
-
def create_bot_utterance(
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
# `.get` is intentional - no `pop` as we still need the image`
|
|
275
|
-
# property to set it in the following line)
|
|
276
|
-
ATTACHMENT: message.pop(ATTACHMENT, None) or message.get(IMAGE, None),
|
|
277
|
-
IMAGE: message.pop(IMAGE, None),
|
|
278
|
-
CUSTOM: message.pop(CUSTOM, None),
|
|
279
|
-
},
|
|
280
|
-
metadata=message,
|
|
260
|
+
def create_bot_utterance(
|
|
261
|
+
message: Dict[Text, Any], language: Optional[Language] = None
|
|
262
|
+
) -> BotUttered:
|
|
263
|
+
"""Create BotUttered event from message with translation support."""
|
|
264
|
+
message_copy = copy.deepcopy(message)
|
|
265
|
+
|
|
266
|
+
text = get_translated_text(
|
|
267
|
+
text=message_copy.pop(TEXT, None),
|
|
268
|
+
translation=message_copy.pop(KEY_TRANSLATION, {}),
|
|
269
|
+
language=language,
|
|
281
270
|
)
|
|
282
|
-
|
|
271
|
+
|
|
272
|
+
buttons = get_translated_buttons(
|
|
273
|
+
buttons=message_copy.pop(BUTTONS, None), language=language
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
data = {
|
|
277
|
+
ELEMENTS: message_copy.pop(ELEMENTS, None),
|
|
278
|
+
QUICK_REPLIES: message_copy.pop(QUICK_REPLIES, None),
|
|
279
|
+
BUTTONS: buttons,
|
|
280
|
+
# for legacy / compatibility reasons we need to set the image
|
|
281
|
+
# to be the attachment if there is no other attachment (the
|
|
282
|
+
# `.get` is intentional - no `pop` as we still need the image`
|
|
283
|
+
# property to set it in the following line)
|
|
284
|
+
ATTACHMENT: message_copy.pop(ATTACHMENT, None) or message_copy.get(IMAGE, None),
|
|
285
|
+
IMAGE: message_copy.pop(IMAGE, None),
|
|
286
|
+
CUSTOM: message_copy.pop(CUSTOM, None),
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return BotUttered(text=text, data=data, metadata=message_copy)
|
|
283
290
|
|
|
284
291
|
|
|
285
292
|
class Action:
|
|
@@ -392,7 +399,7 @@ class ActionBotResponse(Action):
|
|
|
392
399
|
message = add_bot_utterance_metadata(
|
|
393
400
|
message, self.utter_action, nlg, domain, tracker
|
|
394
401
|
)
|
|
395
|
-
return [create_bot_utterance(message)]
|
|
402
|
+
return [create_bot_utterance(message, tracker.current_language)]
|
|
396
403
|
|
|
397
404
|
def name(self) -> Text:
|
|
398
405
|
"""Returns action name."""
|
|
@@ -426,7 +433,7 @@ class ActionEndToEndResponse(Action):
|
|
|
426
433
|
) -> List[Event]:
|
|
427
434
|
"""Runs action (see parent class for full docstring)."""
|
|
428
435
|
message = {"text": self.action_text}
|
|
429
|
-
return [create_bot_utterance(message)]
|
|
436
|
+
return [create_bot_utterance(message, tracker.current_language)]
|
|
430
437
|
|
|
431
438
|
def event_for_successful_execution(
|
|
432
439
|
self,
|
|
@@ -910,7 +917,7 @@ class RemoteAction(Action):
|
|
|
910
917
|
# Avoid overwriting `draft` values with empty values
|
|
911
918
|
response = {k: v for k, v in response.items() if v}
|
|
912
919
|
draft.update(response)
|
|
913
|
-
bot_messages.append(create_bot_utterance(draft))
|
|
920
|
+
bot_messages.append(create_bot_utterance(draft, tracker.current_language))
|
|
914
921
|
|
|
915
922
|
return bot_messages
|
|
916
923
|
|
|
@@ -940,7 +947,14 @@ class RemoteAction(Action):
|
|
|
940
947
|
)
|
|
941
948
|
|
|
942
949
|
events = rasa.shared.core.events.deserialise_events(events_json)
|
|
943
|
-
|
|
950
|
+
|
|
951
|
+
processed_events = []
|
|
952
|
+
for event in events:
|
|
953
|
+
if isinstance(event, SlotSet) and event.filled_by is None:
|
|
954
|
+
event.filled_by = SetSlotExtractor.CUSTOM.value
|
|
955
|
+
processed_events.append(event)
|
|
956
|
+
|
|
957
|
+
return cast(List[Event], bot_messages) + processed_events
|
|
944
958
|
|
|
945
959
|
def name(self) -> Text:
|
|
946
960
|
return self._name
|
|
@@ -1111,7 +1125,7 @@ class ActionDefaultAskAffirmation(Action):
|
|
|
1111
1125
|
"utter_action": self.name(),
|
|
1112
1126
|
}
|
|
1113
1127
|
|
|
1114
|
-
return [create_bot_utterance(message)]
|
|
1128
|
+
return [create_bot_utterance(message, tracker.current_language)]
|
|
1115
1129
|
|
|
1116
1130
|
|
|
1117
1131
|
class ActionDefaultAskRephrase(ActionBotResponse):
|
|
@@ -1144,7 +1158,7 @@ class ActionSendText(Action):
|
|
|
1144
1158
|
fallback = {"text": ""}
|
|
1145
1159
|
metadata_copy = copy.deepcopy(metadata) if metadata else {}
|
|
1146
1160
|
message = metadata_copy.get("message", fallback)
|
|
1147
|
-
return [create_bot_utterance(message)]
|
|
1161
|
+
return [create_bot_utterance(message, tracker.current_language)]
|
|
1148
1162
|
|
|
1149
1163
|
|
|
1150
1164
|
class ActionExtractSlots(Action):
|
|
@@ -1165,113 +1179,6 @@ class ActionExtractSlots(Action):
|
|
|
1165
1179
|
"""Returns action_extract_slots name."""
|
|
1166
1180
|
return ACTION_EXTRACT_SLOTS
|
|
1167
1181
|
|
|
1168
|
-
async def _run_custom_action(
|
|
1169
|
-
self,
|
|
1170
|
-
custom_action: Text,
|
|
1171
|
-
output_channel: "OutputChannel",
|
|
1172
|
-
nlg: "NaturalLanguageGenerator",
|
|
1173
|
-
tracker: "DialogueStateTracker",
|
|
1174
|
-
domain: "Domain",
|
|
1175
|
-
) -> List[Event]:
|
|
1176
|
-
slot_events: List[Event] = []
|
|
1177
|
-
remote_action = RemoteAction(custom_action, self._action_endpoint)
|
|
1178
|
-
disallowed_types = set()
|
|
1179
|
-
|
|
1180
|
-
try:
|
|
1181
|
-
custom_events = await remote_action.run(
|
|
1182
|
-
output_channel, nlg, tracker, domain
|
|
1183
|
-
)
|
|
1184
|
-
for event in custom_events:
|
|
1185
|
-
if isinstance(event, SlotSet):
|
|
1186
|
-
slot_events.append(event)
|
|
1187
|
-
elif isinstance(event, BotUttered):
|
|
1188
|
-
slot_events.append(event)
|
|
1189
|
-
else:
|
|
1190
|
-
disallowed_types.add(event.type_name)
|
|
1191
|
-
except (RasaException, ClientResponseError) as e:
|
|
1192
|
-
logger.warning(
|
|
1193
|
-
f"Failed to execute custom action '{custom_action}' "
|
|
1194
|
-
f"as a result of error '{e!s}'. The default action "
|
|
1195
|
-
f"'{self.name()}' failed to fill slots with custom "
|
|
1196
|
-
f"mappings."
|
|
1197
|
-
)
|
|
1198
|
-
|
|
1199
|
-
for type_name in disallowed_types:
|
|
1200
|
-
logger.info(
|
|
1201
|
-
f"Running custom action '{custom_action}' has resulted "
|
|
1202
|
-
f"in an event of type '{type_name}'. This is "
|
|
1203
|
-
f"disallowed and the tracker will not be "
|
|
1204
|
-
f"updated with this event."
|
|
1205
|
-
)
|
|
1206
|
-
|
|
1207
|
-
return slot_events
|
|
1208
|
-
|
|
1209
|
-
async def _execute_custom_action(
|
|
1210
|
-
self,
|
|
1211
|
-
mapping: Dict[Text, Any],
|
|
1212
|
-
executed_custom_actions: Set[Text],
|
|
1213
|
-
output_channel: "OutputChannel",
|
|
1214
|
-
nlg: "NaturalLanguageGenerator",
|
|
1215
|
-
tracker: "DialogueStateTracker",
|
|
1216
|
-
domain: "Domain",
|
|
1217
|
-
calm_custom_action_names: Optional[Set[str]] = None,
|
|
1218
|
-
) -> Tuple[List[Event], Set[Text]]:
|
|
1219
|
-
custom_action = mapping.get("action")
|
|
1220
|
-
|
|
1221
|
-
if not custom_action or custom_action in executed_custom_actions:
|
|
1222
|
-
return [], executed_custom_actions
|
|
1223
|
-
|
|
1224
|
-
if (
|
|
1225
|
-
calm_custom_action_names is not None
|
|
1226
|
-
and custom_action in calm_custom_action_names
|
|
1227
|
-
):
|
|
1228
|
-
return [], executed_custom_actions
|
|
1229
|
-
|
|
1230
|
-
slot_events = await self._run_custom_action(
|
|
1231
|
-
custom_action, output_channel, nlg, tracker, domain
|
|
1232
|
-
)
|
|
1233
|
-
|
|
1234
|
-
executed_custom_actions.add(custom_action)
|
|
1235
|
-
|
|
1236
|
-
return slot_events, executed_custom_actions
|
|
1237
|
-
|
|
1238
|
-
async def _execute_validation_action(
|
|
1239
|
-
self,
|
|
1240
|
-
extraction_events: List[Event],
|
|
1241
|
-
output_channel: "OutputChannel",
|
|
1242
|
-
nlg: "NaturalLanguageGenerator",
|
|
1243
|
-
tracker: "DialogueStateTracker",
|
|
1244
|
-
domain: "Domain",
|
|
1245
|
-
) -> List[Event]:
|
|
1246
|
-
slot_events: List[SlotSet] = [
|
|
1247
|
-
event for event in extraction_events if isinstance(event, SlotSet)
|
|
1248
|
-
]
|
|
1249
|
-
|
|
1250
|
-
slot_candidates = "\n".join([e.key for e in slot_events])
|
|
1251
|
-
logger.debug(f"Validating extracted slots: {slot_candidates}")
|
|
1252
|
-
|
|
1253
|
-
if ACTION_VALIDATE_SLOT_MAPPINGS not in domain.user_actions:
|
|
1254
|
-
return cast(List[Event], slot_events)
|
|
1255
|
-
|
|
1256
|
-
_tracker = DialogueStateTracker.from_events(
|
|
1257
|
-
tracker.sender_id,
|
|
1258
|
-
tracker.events_after_latest_restart() + cast(List[Event], slot_events),
|
|
1259
|
-
slots=domain.slots,
|
|
1260
|
-
)
|
|
1261
|
-
validate_events = await self._run_custom_action(
|
|
1262
|
-
ACTION_VALIDATE_SLOT_MAPPINGS, output_channel, nlg, _tracker, domain
|
|
1263
|
-
)
|
|
1264
|
-
validated_slot_names = [
|
|
1265
|
-
event.key for event in validate_events if isinstance(event, SlotSet)
|
|
1266
|
-
]
|
|
1267
|
-
|
|
1268
|
-
# If the custom action doesn't return a SlotSet event for an extracted slot
|
|
1269
|
-
# candidate we assume that it was valid. The custom action has to return a
|
|
1270
|
-
# SlotSet(slot_name, None) event to mark a Slot as invalid.
|
|
1271
|
-
return validate_events + [
|
|
1272
|
-
event for event in slot_events if event.key not in validated_slot_names
|
|
1273
|
-
]
|
|
1274
|
-
|
|
1275
1182
|
async def run(
|
|
1276
1183
|
self,
|
|
1277
1184
|
output_channel: "OutputChannel",
|
|
@@ -1282,60 +1189,59 @@ class ActionExtractSlots(Action):
|
|
|
1282
1189
|
) -> List[Event]:
|
|
1283
1190
|
"""Runs action. Please see parent class for the full docstring."""
|
|
1284
1191
|
slot_events: List[Event] = []
|
|
1285
|
-
executed_custom_actions: Set[Text] = set()
|
|
1286
|
-
|
|
1287
1192
|
user_slots = [
|
|
1288
1193
|
slot
|
|
1289
1194
|
for slot in domain.slots
|
|
1290
1195
|
if slot.name not in DEFAULT_SLOT_NAMES | KNOWLEDGE_BASE_SLOT_NAMES
|
|
1291
1196
|
]
|
|
1292
1197
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
if flows is not None:
|
|
1301
|
-
flows = FlowsList.from_json(flows)
|
|
1302
|
-
calm_slot_names = flows.available_slot_names()
|
|
1303
|
-
calm_custom_action_names = flows.available_custom_actions()
|
|
1304
|
-
|
|
1198
|
+
all_flows = metadata.get("all_flows") if metadata else None
|
|
1199
|
+
flows = FlowsList.from_json(all_flows) if all_flows else None
|
|
1200
|
+
calm_slot_names = flows.available_slot_names() if flows else set()
|
|
1201
|
+
is_calm_system = metadata.get(KEY_IS_CALM_SYSTEM) if metadata else False
|
|
1202
|
+
is_coexistence_bot = (
|
|
1203
|
+
metadata.get(KEY_IS_COEXISTENCE_ASSISTANT) if metadata else False
|
|
1204
|
+
)
|
|
1305
1205
|
slot_filling_manager = SlotFillingManager(
|
|
1306
|
-
domain,
|
|
1206
|
+
domain,
|
|
1207
|
+
tracker,
|
|
1208
|
+
action_endpoint=self._action_endpoint,
|
|
1307
1209
|
)
|
|
1308
1210
|
|
|
1309
1211
|
for slot in user_slots:
|
|
1310
1212
|
# allows the action to set slots that are shared between
|
|
1311
1213
|
# the NLU-based system and CALM system in a coexistence bot
|
|
1312
|
-
if
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
for mapping in slot.mappings:
|
|
1320
|
-
mapping_type = SlotMappingType(mapping.get(MAPPING_TYPE))
|
|
1321
|
-
should_fill_custom_slot = mapping_type == SlotMappingType.CUSTOM
|
|
1322
|
-
|
|
1323
|
-
if should_fill_custom_slot:
|
|
1324
|
-
(
|
|
1325
|
-
custom_evts,
|
|
1326
|
-
executed_custom_actions,
|
|
1327
|
-
) = await self._execute_custom_action(
|
|
1328
|
-
mapping,
|
|
1329
|
-
executed_custom_actions,
|
|
1330
|
-
output_channel,
|
|
1331
|
-
nlg,
|
|
1332
|
-
tracker,
|
|
1333
|
-
domain,
|
|
1334
|
-
calm_custom_action_names,
|
|
1214
|
+
if is_coexistence_bot:
|
|
1215
|
+
should_fill_slot_in_coexistence = (
|
|
1216
|
+
slot_filling_manager.should_fill_slot_in_coexistence(
|
|
1217
|
+
is_calm_system=is_calm_system,
|
|
1218
|
+
slot=slot,
|
|
1219
|
+
calm_slot_names=calm_slot_names,
|
|
1335
1220
|
)
|
|
1336
|
-
|
|
1221
|
+
)
|
|
1222
|
+
if not should_fill_slot_in_coexistence:
|
|
1223
|
+
continue
|
|
1337
1224
|
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1225
|
+
if not is_calm_system:
|
|
1226
|
+
slot_value, is_extracted = extract_slot_value(
|
|
1227
|
+
slot, slot_filling_manager
|
|
1228
|
+
)
|
|
1229
|
+
if is_extracted:
|
|
1230
|
+
slot_events.append(SlotSet(slot.name, slot_value))
|
|
1231
|
+
|
|
1232
|
+
custom_events = await slot_filling_manager.run_action_at_every_turn(
|
|
1233
|
+
slot,
|
|
1234
|
+
output_channel,
|
|
1235
|
+
nlg,
|
|
1236
|
+
)
|
|
1237
|
+
slot_events.extend(custom_events)
|
|
1238
|
+
|
|
1239
|
+
if not is_calm_system:
|
|
1240
|
+
validated_events = await slot_filling_manager.execute_validation_action(
|
|
1241
|
+
slot_events,
|
|
1242
|
+
output_channel,
|
|
1243
|
+
nlg,
|
|
1244
|
+
)
|
|
1245
|
+
return validated_events
|
|
1246
|
+
|
|
1247
|
+
return slot_events
|