rasa-pro 3.12.0.dev13__py3-none-any.whl → 3.12.0rc2__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/anonymization/anonymization_rule_executor.py +16 -10
- rasa/cli/data.py +16 -0
- 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/utils.py +12 -0
- rasa/core/actions/action.py +84 -191
- rasa/core/actions/action_handle_digressions.py +35 -13
- rasa/core/actions/action_run_slot_rejections.py +16 -4
- rasa/core/channels/__init__.py +2 -0
- rasa/core/channels/studio_chat.py +19 -0
- rasa/core/channels/telegram.py +42 -24
- 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 +4 -1
- rasa/core/channels/voice_stream/call_state.py +3 -0
- rasa/core/channels/voice_stream/genesys.py +6 -2
- rasa/core/channels/voice_stream/tts/azure.py +9 -1
- rasa/core/channels/voice_stream/tts/cartesia.py +14 -8
- rasa/core/channels/voice_stream/voice_channel.py +23 -2
- rasa/core/constants.py +2 -0
- 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 +19 -7
- rasa/core/processor.py +71 -9
- rasa/dialogue_understanding/commands/can_not_handle_command.py +20 -2
- rasa/dialogue_understanding/commands/cancel_flow_command.py +24 -6
- 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/handle_digressions_command.py +1 -7
- 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 +24 -2
- rasa/dialogue_understanding/commands/skip_question_command.py +20 -2
- rasa/dialogue_understanding/commands/start_flow_command.py +22 -2
- rasa/dialogue_understanding/commands/utils.py +71 -4
- rasa/dialogue_understanding/generator/__init__.py +2 -0
- rasa/dialogue_understanding/generator/command_parser.py +15 -12
- rasa/dialogue_understanding/generator/constants.py +3 -0
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +12 -5
- rasa/dialogue_understanding/generator/llm_command_generator.py +5 -3
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +17 -3
- rasa/dialogue_understanding/generator/prompt_templates/__init__.py +0 -0
- rasa/dialogue_understanding/generator/{single_step → prompt_templates}/command_prompt_template.jinja2 +2 -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/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +84 -0
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +522 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +12 -310
- rasa/dialogue_understanding/patterns/collect_information.py +1 -1
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +16 -0
- rasa/dialogue_understanding/patterns/validate_slot.py +65 -0
- rasa/dialogue_understanding/processor/command_processor.py +39 -0
- rasa/dialogue_understanding/stack/utils.py +38 -0
- rasa/dialogue_understanding_test/du_test_case.py +58 -18
- rasa/dialogue_understanding_test/du_test_result.py +14 -10
- rasa/dialogue_understanding_test/io.py +14 -0
- rasa/e2e_test/assertions.py +6 -8
- rasa/e2e_test/llm_judge_prompts/answer_relevance_prompt_template.jinja2 +5 -1
- rasa/e2e_test/llm_judge_prompts/groundedness_prompt_template.jinja2 +4 -0
- 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/llm_fine_tuning/conversations.py +1 -1
- rasa/model_manager/runner_service.py +7 -4
- rasa/model_manager/socket_bridge.py +7 -6
- rasa/shared/constants.py +15 -13
- rasa/shared/core/constants.py +2 -0
- rasa/shared/core/flows/constants.py +11 -0
- rasa/shared/core/flows/flow.py +83 -19
- rasa/shared/core/flows/flows_yaml_schema.json +31 -3
- rasa/shared/core/flows/steps/collect.py +1 -36
- rasa/shared/core/flows/utils.py +28 -4
- rasa/shared/core/flows/validation.py +1 -1
- rasa/shared/core/slot_mappings.py +208 -5
- rasa/shared/core/slots.py +137 -1
- rasa/shared/core/trackers.py +74 -1
- rasa/shared/importers/importer.py +50 -2
- 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 +33 -7
- rasa/shared/utils/pykwalify_extensions.py +24 -0
- rasa/shared/utils/schemas/domain.yml +26 -0
- rasa/telemetry.py +2 -1
- 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/validator.py +372 -7
- rasa/version.py +1 -1
- {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/METADATA +13 -14
- {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/RECORD +139 -124
- {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/NOTICE +0 -0
- {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/WHEEL +0 -0
- {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/entry_points.txt +0 -0
README.md
CHANGED
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
-
[](https://github.com/RasaHQ/rasa-private/actions)
|
|
6
5
|
[](https://sonarcloud.io/summary/new_code?id=RasaHQ_rasa)
|
|
7
|
-
[](https://rasa.com/docs/
|
|
6
|
+
[](https://rasa.com/docs/docs/pro/intro)
|
|
7
|
+

|
|
8
8
|
|
|
9
9
|
</div>
|
|
10
10
|
|
|
11
11
|
<hr />
|
|
12
12
|
|
|
13
|
-
|
|
14
13
|
Rasa Pro is a framework for building scalable, dynamic conversational AI assistants that integrate large language models (LLMs) to enable more contextually aware and agentic interactions. Whether you’re new to conversational AI or an experienced developer, Rasa Pro offers enhanced flexibility, control, and performance for mission-critical applications.
|
|
15
14
|
|
|
16
15
|
Building on the foundation of Rasa Open Source, Rasa Pro adds advanced features like CALM (Conversational AI with Language Models) and Dialogue Understanding (DU), which enable developers to shift from traditional intent-driven systems to LLM-based agents. This allows for more robust, responsive interactions that adhere strictly to business logic, while reducing risks like prompt injection and minimizing hallucinations.
|
|
@@ -23,19 +22,17 @@ Building on the foundation of Rasa Open Source, Rasa Pro adds advanced features
|
|
|
23
22
|
- **Robustness and Control:** Maintain strict adherence to business logic, preventing unwanted behaviors like prompt injection and hallucinations, leading to more reliable responses and secure interactions.
|
|
24
23
|
- **Built-in Security:** Safeguard sensitive data, control access, and ensure secure deployment, essential for production environments that demand high levels of security and compliance.
|
|
25
24
|
|
|
25
|
+
A [free developer license](https://rasa.com/docs/pro/intro/#who-rasa-pro-is-for) is available so you can explore and get to know Rasa Pro. It allows you to take your assistant live in production a limited capacity. A paid license is required for larger-scale production use, but all code is visible and can be customized as needed.
|
|
26
26
|
|
|
27
|
+
To get started right now, you can
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
To get started right now, you can
|
|
31
|
-
|
|
32
|
-
`pip install rasa-pro`
|
|
29
|
+
`pip install rasa-pro`
|
|
33
30
|
|
|
34
|
-
Check out our
|
|
31
|
+
Check out our
|
|
35
32
|
|
|
36
|
-
- [Rasa-pro Quickstart](https://rasa.com/docs/
|
|
37
|
-
- [Conversational AI with Language Models (CALM) conceptual rundown](https://rasa.com/docs/
|
|
38
|
-
- [Rasa Pro / CALM tutorial](https://rasa.com/docs/
|
|
39
|
-
- [Rasa pro changelog](https://rasa.com/docs/
|
|
33
|
+
- [Rasa-pro Quickstart](https://rasa.com/docs/learn/quickstart/pro),
|
|
34
|
+
- [Conversational AI with Language Models (CALM) conceptual rundown](https://rasa.com/docs/learn/concepts/calm),
|
|
35
|
+
- [Rasa Pro / CALM tutorial](https://rasa.com/docs/pro/tutorial), and
|
|
36
|
+
- [Rasa pro changelog](https://rasa.com/docs/reference/changelogs/rasa-pro-changelog)
|
|
40
37
|
|
|
41
38
|
for more. Also feel free to reach out to us on the [Rasa forum](https://forum.rasa.com/).
|
|
@@ -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/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
69
|
REQUESTED_SLOT,
|
|
77
70
|
USER_INTENT_OUT_OF_SCOPE,
|
|
78
71
|
SetSlotExtractor,
|
|
79
|
-
SlotMappingType,
|
|
80
72
|
)
|
|
81
73
|
from rasa.shared.core.domain import Domain
|
|
82
74
|
from rasa.shared.core.events import (
|
|
@@ -92,26 +84,23 @@ 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:
|
|
111
101
|
from rasa.core.channels.channel import OutputChannel
|
|
112
102
|
from rasa.core.nlg import NaturalLanguageGenerator
|
|
113
103
|
from rasa.shared.core.events import IntentPrediction
|
|
114
|
-
from rasa.shared.core.slot_mappings import SlotMapping
|
|
115
104
|
|
|
116
105
|
logger = logging.getLogger(__name__)
|
|
117
106
|
|
|
@@ -268,25 +257,36 @@ def action_for_name_or_text(
|
|
|
268
257
|
return RemoteAction(action_name_or_text, action_endpoint)
|
|
269
258
|
|
|
270
259
|
|
|
271
|
-
def create_bot_utterance(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
CUSTOM: message.pop(CUSTOM, None),
|
|
286
|
-
},
|
|
287
|
-
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,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
buttons = get_translated_buttons(
|
|
273
|
+
buttons=message_copy.pop(BUTTONS, None), language=language
|
|
288
274
|
)
|
|
289
|
-
|
|
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)
|
|
290
290
|
|
|
291
291
|
|
|
292
292
|
class Action:
|
|
@@ -399,7 +399,7 @@ class ActionBotResponse(Action):
|
|
|
399
399
|
message = add_bot_utterance_metadata(
|
|
400
400
|
message, self.utter_action, nlg, domain, tracker
|
|
401
401
|
)
|
|
402
|
-
return [create_bot_utterance(message)]
|
|
402
|
+
return [create_bot_utterance(message, tracker.current_language)]
|
|
403
403
|
|
|
404
404
|
def name(self) -> Text:
|
|
405
405
|
"""Returns action name."""
|
|
@@ -433,7 +433,7 @@ class ActionEndToEndResponse(Action):
|
|
|
433
433
|
) -> List[Event]:
|
|
434
434
|
"""Runs action (see parent class for full docstring)."""
|
|
435
435
|
message = {"text": self.action_text}
|
|
436
|
-
return [create_bot_utterance(message)]
|
|
436
|
+
return [create_bot_utterance(message, tracker.current_language)]
|
|
437
437
|
|
|
438
438
|
def event_for_successful_execution(
|
|
439
439
|
self,
|
|
@@ -917,7 +917,7 @@ class RemoteAction(Action):
|
|
|
917
917
|
# Avoid overwriting `draft` values with empty values
|
|
918
918
|
response = {k: v for k, v in response.items() if v}
|
|
919
919
|
draft.update(response)
|
|
920
|
-
bot_messages.append(create_bot_utterance(draft))
|
|
920
|
+
bot_messages.append(create_bot_utterance(draft, tracker.current_language))
|
|
921
921
|
|
|
922
922
|
return bot_messages
|
|
923
923
|
|
|
@@ -1125,7 +1125,7 @@ class ActionDefaultAskAffirmation(Action):
|
|
|
1125
1125
|
"utter_action": self.name(),
|
|
1126
1126
|
}
|
|
1127
1127
|
|
|
1128
|
-
return [create_bot_utterance(message)]
|
|
1128
|
+
return [create_bot_utterance(message, tracker.current_language)]
|
|
1129
1129
|
|
|
1130
1130
|
|
|
1131
1131
|
class ActionDefaultAskRephrase(ActionBotResponse):
|
|
@@ -1158,7 +1158,7 @@ class ActionSendText(Action):
|
|
|
1158
1158
|
fallback = {"text": ""}
|
|
1159
1159
|
metadata_copy = copy.deepcopy(metadata) if metadata else {}
|
|
1160
1160
|
message = metadata_copy.get("message", fallback)
|
|
1161
|
-
return [create_bot_utterance(message)]
|
|
1161
|
+
return [create_bot_utterance(message, tracker.current_language)]
|
|
1162
1162
|
|
|
1163
1163
|
|
|
1164
1164
|
class ActionExtractSlots(Action):
|
|
@@ -1179,113 +1179,6 @@ class ActionExtractSlots(Action):
|
|
|
1179
1179
|
"""Returns action_extract_slots name."""
|
|
1180
1180
|
return ACTION_EXTRACT_SLOTS
|
|
1181
1181
|
|
|
1182
|
-
async def _run_custom_action(
|
|
1183
|
-
self,
|
|
1184
|
-
custom_action: Text,
|
|
1185
|
-
output_channel: "OutputChannel",
|
|
1186
|
-
nlg: "NaturalLanguageGenerator",
|
|
1187
|
-
tracker: "DialogueStateTracker",
|
|
1188
|
-
domain: "Domain",
|
|
1189
|
-
) -> List[Event]:
|
|
1190
|
-
slot_events: List[Event] = []
|
|
1191
|
-
remote_action = RemoteAction(custom_action, self._action_endpoint)
|
|
1192
|
-
disallowed_types = set()
|
|
1193
|
-
|
|
1194
|
-
try:
|
|
1195
|
-
custom_events = await remote_action.run(
|
|
1196
|
-
output_channel, nlg, tracker, domain
|
|
1197
|
-
)
|
|
1198
|
-
for event in custom_events:
|
|
1199
|
-
if isinstance(event, SlotSet):
|
|
1200
|
-
slot_events.append(event)
|
|
1201
|
-
elif isinstance(event, BotUttered):
|
|
1202
|
-
slot_events.append(event)
|
|
1203
|
-
else:
|
|
1204
|
-
disallowed_types.add(event.type_name)
|
|
1205
|
-
except (RasaException, ClientResponseError) as e:
|
|
1206
|
-
logger.warning(
|
|
1207
|
-
f"Failed to execute custom action '{custom_action}' "
|
|
1208
|
-
f"as a result of error '{e!s}'. The default action "
|
|
1209
|
-
f"'{self.name()}' failed to fill slots with custom "
|
|
1210
|
-
f"mappings."
|
|
1211
|
-
)
|
|
1212
|
-
|
|
1213
|
-
for type_name in disallowed_types:
|
|
1214
|
-
logger.info(
|
|
1215
|
-
f"Running custom action '{custom_action}' has resulted "
|
|
1216
|
-
f"in an event of type '{type_name}'. This is "
|
|
1217
|
-
f"disallowed and the tracker will not be "
|
|
1218
|
-
f"updated with this event."
|
|
1219
|
-
)
|
|
1220
|
-
|
|
1221
|
-
return slot_events
|
|
1222
|
-
|
|
1223
|
-
async def _execute_custom_action(
|
|
1224
|
-
self,
|
|
1225
|
-
mapping: "SlotMapping",
|
|
1226
|
-
executed_custom_actions: Set[Text],
|
|
1227
|
-
output_channel: "OutputChannel",
|
|
1228
|
-
nlg: "NaturalLanguageGenerator",
|
|
1229
|
-
tracker: "DialogueStateTracker",
|
|
1230
|
-
domain: "Domain",
|
|
1231
|
-
calm_custom_action_names: Optional[Set[str]] = None,
|
|
1232
|
-
) -> Tuple[List[Event], Set[Text]]:
|
|
1233
|
-
custom_action = mapping.run_action_every_turn
|
|
1234
|
-
|
|
1235
|
-
if not custom_action or custom_action in executed_custom_actions:
|
|
1236
|
-
return [], executed_custom_actions
|
|
1237
|
-
|
|
1238
|
-
if (
|
|
1239
|
-
calm_custom_action_names is not None
|
|
1240
|
-
and custom_action in calm_custom_action_names
|
|
1241
|
-
):
|
|
1242
|
-
return [], executed_custom_actions
|
|
1243
|
-
|
|
1244
|
-
slot_events = await self._run_custom_action(
|
|
1245
|
-
custom_action, output_channel, nlg, tracker, domain
|
|
1246
|
-
)
|
|
1247
|
-
|
|
1248
|
-
executed_custom_actions.add(custom_action)
|
|
1249
|
-
|
|
1250
|
-
return slot_events, executed_custom_actions
|
|
1251
|
-
|
|
1252
|
-
async def _execute_validation_action(
|
|
1253
|
-
self,
|
|
1254
|
-
extraction_events: List[Event],
|
|
1255
|
-
output_channel: "OutputChannel",
|
|
1256
|
-
nlg: "NaturalLanguageGenerator",
|
|
1257
|
-
tracker: "DialogueStateTracker",
|
|
1258
|
-
domain: "Domain",
|
|
1259
|
-
) -> List[Event]:
|
|
1260
|
-
slot_events: List[SlotSet] = [
|
|
1261
|
-
event for event in extraction_events if isinstance(event, SlotSet)
|
|
1262
|
-
]
|
|
1263
|
-
|
|
1264
|
-
slot_candidates = "\n".join([e.key for e in slot_events])
|
|
1265
|
-
logger.debug(f"Validating extracted slots: {slot_candidates}")
|
|
1266
|
-
|
|
1267
|
-
if ACTION_VALIDATE_SLOT_MAPPINGS not in domain.user_actions:
|
|
1268
|
-
return cast(List[Event], slot_events)
|
|
1269
|
-
|
|
1270
|
-
_tracker = DialogueStateTracker.from_events(
|
|
1271
|
-
tracker.sender_id,
|
|
1272
|
-
tracker.events_after_latest_restart() + cast(List[Event], slot_events),
|
|
1273
|
-
slots=domain.slots,
|
|
1274
|
-
)
|
|
1275
|
-
validate_events = await self._run_custom_action(
|
|
1276
|
-
ACTION_VALIDATE_SLOT_MAPPINGS, output_channel, nlg, _tracker, domain
|
|
1277
|
-
)
|
|
1278
|
-
validated_slot_names = [
|
|
1279
|
-
event.key for event in validate_events if isinstance(event, SlotSet)
|
|
1280
|
-
]
|
|
1281
|
-
|
|
1282
|
-
# If the custom action doesn't return a SlotSet event for an extracted slot
|
|
1283
|
-
# candidate we assume that it was valid. The custom action has to return a
|
|
1284
|
-
# SlotSet(slot_name, None) event to mark a Slot as invalid.
|
|
1285
|
-
return validate_events + [
|
|
1286
|
-
event for event in slot_events if event.key not in validated_slot_names
|
|
1287
|
-
]
|
|
1288
|
-
|
|
1289
1182
|
async def run(
|
|
1290
1183
|
self,
|
|
1291
1184
|
output_channel: "OutputChannel",
|
|
@@ -1296,59 +1189,59 @@ class ActionExtractSlots(Action):
|
|
|
1296
1189
|
) -> List[Event]:
|
|
1297
1190
|
"""Runs action. Please see parent class for the full docstring."""
|
|
1298
1191
|
slot_events: List[Event] = []
|
|
1299
|
-
executed_custom_actions: Set[Text] = set()
|
|
1300
|
-
|
|
1301
1192
|
user_slots = [
|
|
1302
1193
|
slot
|
|
1303
1194
|
for slot in domain.slots
|
|
1304
1195
|
if slot.name not in DEFAULT_SLOT_NAMES | KNOWLEDGE_BASE_SLOT_NAMES
|
|
1305
1196
|
]
|
|
1306
1197
|
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
if flows is not None:
|
|
1315
|
-
flows = FlowsList.from_json(flows)
|
|
1316
|
-
calm_slot_names = flows.available_slot_names()
|
|
1317
|
-
calm_custom_action_names = flows.available_custom_actions()
|
|
1318
|
-
|
|
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
|
+
)
|
|
1319
1205
|
slot_filling_manager = SlotFillingManager(
|
|
1320
|
-
domain,
|
|
1206
|
+
domain,
|
|
1207
|
+
tracker,
|
|
1208
|
+
action_endpoint=self._action_endpoint,
|
|
1321
1209
|
)
|
|
1322
1210
|
|
|
1323
1211
|
for slot in user_slots:
|
|
1324
1212
|
# allows the action to set slots that are shared between
|
|
1325
1213
|
# the NLU-based system and CALM system in a coexistence bot
|
|
1326
|
-
if
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
for mapping in slot.mappings:
|
|
1334
|
-
should_fill_controlled_slot = mapping.type == SlotMappingType.CONTROLLED
|
|
1335
|
-
|
|
1336
|
-
if should_fill_controlled_slot:
|
|
1337
|
-
(
|
|
1338
|
-
custom_evts,
|
|
1339
|
-
executed_custom_actions,
|
|
1340
|
-
) = await self._execute_custom_action(
|
|
1341
|
-
mapping,
|
|
1342
|
-
executed_custom_actions,
|
|
1343
|
-
output_channel,
|
|
1344
|
-
nlg,
|
|
1345
|
-
tracker,
|
|
1346
|
-
domain,
|
|
1347
|
-
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,
|
|
1348
1220
|
)
|
|
1349
|
-
|
|
1221
|
+
)
|
|
1222
|
+
if not should_fill_slot_in_coexistence:
|
|
1223
|
+
continue
|
|
1350
1224
|
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
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
|