rasa-pro 3.8.18__py3-none-any.whl → 3.9.15__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 +6 -42
- rasa/__main__.py +14 -9
- rasa/anonymization/anonymization_pipeline.py +0 -1
- rasa/anonymization/anonymization_rule_executor.py +3 -3
- rasa/anonymization/utils.py +4 -3
- rasa/api.py +2 -2
- rasa/cli/arguments/default_arguments.py +1 -1
- rasa/cli/arguments/run.py +2 -2
- rasa/cli/arguments/test.py +1 -1
- rasa/cli/arguments/train.py +10 -10
- rasa/cli/e2e_test.py +27 -7
- rasa/cli/export.py +0 -1
- rasa/cli/license.py +3 -3
- rasa/cli/project_templates/calm/actions/action_template.py +1 -1
- rasa/cli/project_templates/calm/config.yml +1 -1
- rasa/cli/project_templates/calm/credentials.yml +1 -1
- rasa/cli/project_templates/calm/data/flows/add_contact.yml +1 -1
- rasa/cli/project_templates/calm/data/flows/remove_contact.yml +1 -1
- rasa/cli/project_templates/calm/domain/add_contact.yml +8 -2
- rasa/cli/project_templates/calm/domain/list_contacts.yml +3 -0
- rasa/cli/project_templates/calm/domain/remove_contact.yml +9 -2
- rasa/cli/project_templates/calm/domain/shared.yml +5 -0
- rasa/cli/project_templates/calm/endpoints.yml +4 -4
- rasa/cli/project_templates/default/actions/actions.py +1 -1
- rasa/cli/project_templates/default/config.yml +5 -5
- rasa/cli/project_templates/default/credentials.yml +1 -1
- rasa/cli/project_templates/default/endpoints.yml +4 -4
- rasa/cli/project_templates/default/tests/test_stories.yml +1 -1
- rasa/cli/project_templates/tutorial/config.yml +1 -1
- rasa/cli/project_templates/tutorial/credentials.yml +1 -1
- rasa/cli/project_templates/tutorial/data/patterns.yml +6 -0
- rasa/cli/project_templates/tutorial/domain.yml +4 -0
- rasa/cli/project_templates/tutorial/endpoints.yml +6 -6
- rasa/cli/run.py +0 -1
- rasa/cli/scaffold.py +3 -2
- rasa/cli/studio/download.py +11 -0
- rasa/cli/studio/studio.py +180 -24
- rasa/cli/studio/upload.py +0 -8
- rasa/cli/telemetry.py +18 -6
- rasa/cli/utils.py +21 -10
- rasa/cli/x.py +3 -2
- rasa/constants.py +1 -1
- rasa/core/actions/action.py +90 -315
- rasa/core/actions/action_exceptions.py +24 -0
- rasa/core/actions/constants.py +3 -0
- rasa/core/actions/custom_action_executor.py +188 -0
- rasa/core/actions/forms.py +11 -7
- rasa/core/actions/grpc_custom_action_executor.py +251 -0
- rasa/core/actions/http_custom_action_executor.py +140 -0
- rasa/core/actions/loops.py +3 -0
- rasa/core/actions/two_stage_fallback.py +1 -1
- rasa/core/agent.py +2 -4
- rasa/core/brokers/pika.py +1 -2
- rasa/core/channels/audiocodes.py +1 -1
- rasa/core/channels/botframework.py +0 -1
- rasa/core/channels/callback.py +0 -1
- rasa/core/channels/console.py +6 -8
- rasa/core/channels/development_inspector.py +1 -1
- rasa/core/channels/facebook.py +0 -3
- rasa/core/channels/hangouts.py +0 -6
- rasa/core/channels/inspector/dist/assets/{arc-5623b6dc.js → arc-b6e548fe.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-685c106a.js → c4Diagram-d0fbc5ce-fa03ac9e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-8cbed007.js → classDiagram-936ed81e-ee67392a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-5889cf12.js → classDiagram-v2-c3cb15f1-9b283fae.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{createText-62fc7601-24c249d7.js → createText-62fc7601-8b6fcc2a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-7dd06a75.js → edges-f2ad444c-22e77f4f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-62c1e54c.js → erDiagram-9d236eb7-60ffc87f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-ce49b86f.js → flowDb-1972c806-9dd802e4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-4067e48f.js → flowDiagram-7ea5b25a-5fa1912f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-1844e5a5.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-59fe4051.js → flowchart-elk-definition-abe16c3d-622a1fd2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-47e3a43b.js → ganttDiagram-9b5ea136-e285a63a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-5a2ac0d9.js → gitGraphDiagram-99d0ae7c-f237bdca.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-dfb8efc4.js → index-2c4b9a3b-4b03d70e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-268a75c0.js → index-a5d3e69d.js} +4 -4
- rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-b0c470f2.js → infoDiagram-736b4530-72a0fa5f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-2edb829a.js → journeyDiagram-df861f2b-82218c41.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-b6873d69.js → layout-78cff630.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-1efc5781.js → line-5038b469.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-661e9b94.js → linear-c4fc4098.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-2d2e727f.js → mindmap-definition-beec6740-c33c8ea6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-9d3ea93d.js → pieDiagram-dbbf0591-a8d03059.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-06a178a2.js → quadrantDiagram-4d7f4fd6-6a0e56b2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-0bfedffc.js → requirementDiagram-6fc4c22a-2dc7c7bd.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-d76d0a04.js → sankeyDiagram-8f13d901-2360fe39.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-37bb4341.js → sequenceDiagram-b655622a-41b9f9ad.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-f52f7f57.js → stateDiagram-59f0c015-0aad326f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-4a986a20.js → stateDiagram-v2-2b26beab-9847d984.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-080da4f6-7dd9ae12.js → styles-080da4f6-564d890e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-46e1ca14.js → styles-3dcbcfbf-38957613.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9c745c82-4a97439a.js → styles-9c745c82-f0fc6921.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-823917a3.js → svgDrawCommon-4835440b-ef3c5a77.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-9ea72896.js → timeline-definition-5b62e21b-bf3e91c1.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-b631a8b6.js → xychartDiagram-2b33534f-4d4026c0.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/src/components/DiagramFlow.tsx +10 -0
- rasa/core/channels/inspector/src/helpers/formatters.test.ts +4 -7
- rasa/core/channels/inspector/src/helpers/formatters.ts +3 -2
- rasa/core/channels/rest.py +36 -21
- rasa/core/channels/rocketchat.py +0 -1
- rasa/core/channels/socketio.py +1 -1
- rasa/core/channels/telegram.py +3 -3
- rasa/core/channels/webexteams.py +0 -1
- rasa/core/concurrent_lock_store.py +1 -1
- rasa/core/evaluation/marker_base.py +1 -3
- rasa/core/evaluation/marker_stats.py +1 -2
- rasa/core/featurizers/single_state_featurizer.py +3 -26
- rasa/core/featurizers/tracker_featurizers.py +18 -122
- rasa/core/information_retrieval/__init__.py +7 -0
- rasa/core/information_retrieval/faiss.py +9 -4
- rasa/core/information_retrieval/information_retrieval.py +64 -7
- rasa/core/information_retrieval/milvus.py +7 -14
- rasa/core/information_retrieval/qdrant.py +8 -15
- rasa/core/lock_store.py +0 -1
- rasa/core/migrate.py +1 -2
- rasa/core/nlg/callback.py +3 -4
- rasa/core/policies/enterprise_search_policy.py +86 -22
- rasa/core/policies/enterprise_search_prompt_template.jinja2 +4 -41
- rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +60 -0
- rasa/core/policies/flows/flow_executor.py +104 -2
- rasa/core/policies/intentless_policy.py +7 -9
- rasa/core/policies/memoization.py +3 -3
- rasa/core/policies/policy.py +18 -9
- rasa/core/policies/rule_policy.py +8 -11
- rasa/core/policies/ted_policy.py +61 -88
- rasa/core/policies/unexpected_intent_policy.py +8 -17
- rasa/core/processor.py +136 -47
- rasa/core/run.py +41 -25
- rasa/core/secrets_manager/endpoints.py +2 -2
- rasa/core/secrets_manager/vault.py +6 -8
- rasa/core/test.py +3 -5
- rasa/core/tracker_store.py +49 -14
- rasa/core/train.py +1 -3
- rasa/core/training/interactive.py +9 -6
- rasa/core/utils.py +5 -10
- rasa/dialogue_understanding/coexistence/intent_based_router.py +11 -4
- rasa/dialogue_understanding/coexistence/llm_based_router.py +2 -3
- rasa/dialogue_understanding/commands/__init__.py +4 -0
- rasa/dialogue_understanding/commands/can_not_handle_command.py +9 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +9 -0
- rasa/dialogue_understanding/commands/change_flow_command.py +38 -0
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +9 -0
- rasa/dialogue_understanding/commands/clarify_command.py +9 -0
- rasa/dialogue_understanding/commands/correct_slots_command.py +9 -0
- rasa/dialogue_understanding/commands/error_command.py +12 -0
- rasa/dialogue_understanding/commands/handle_code_change_command.py +9 -0
- rasa/dialogue_understanding/commands/human_handoff_command.py +9 -0
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +9 -0
- rasa/dialogue_understanding/commands/noop_command.py +9 -0
- rasa/dialogue_understanding/commands/set_slot_command.py +38 -3
- rasa/dialogue_understanding/commands/skip_question_command.py +9 -0
- rasa/dialogue_understanding/commands/start_flow_command.py +9 -0
- rasa/dialogue_understanding/generator/__init__.py +16 -1
- rasa/dialogue_understanding/generator/command_generator.py +92 -6
- rasa/dialogue_understanding/generator/constants.py +18 -0
- rasa/dialogue_understanding/generator/flow_retrieval.py +7 -5
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +467 -0
- rasa/dialogue_understanding/generator/llm_command_generator.py +39 -609
- rasa/dialogue_understanding/generator/multi_step/__init__.py +0 -0
- rasa/dialogue_understanding/generator/multi_step/fill_slots_prompt.jinja2 +62 -0
- rasa/dialogue_understanding/generator/multi_step/handle_flows_prompt.jinja2 +38 -0
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +827 -0
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +69 -8
- rasa/dialogue_understanding/generator/single_step/__init__.py +0 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +345 -0
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +36 -31
- rasa/dialogue_understanding/processor/command_processor.py +112 -3
- rasa/e2e_test/constants.py +1 -0
- rasa/e2e_test/e2e_test_case.py +44 -0
- rasa/e2e_test/e2e_test_runner.py +114 -11
- rasa/e2e_test/e2e_test_schema.yml +18 -0
- rasa/engine/caching.py +0 -1
- rasa/engine/graph.py +18 -6
- rasa/engine/recipes/config_files/default_config.yml +3 -3
- rasa/engine/recipes/default_components.py +1 -1
- rasa/engine/recipes/default_recipe.py +4 -5
- rasa/engine/recipes/recipe.py +1 -1
- rasa/engine/runner/dask.py +3 -9
- rasa/engine/storage/local_model_storage.py +0 -2
- rasa/engine/validation.py +179 -145
- rasa/exceptions.py +2 -2
- rasa/graph_components/validators/default_recipe_validator.py +3 -5
- rasa/hooks.py +0 -1
- rasa/model.py +1 -1
- rasa/model_training.py +1 -0
- rasa/nlu/classifiers/diet_classifier.py +33 -52
- rasa/nlu/classifiers/logistic_regression_classifier.py +9 -22
- rasa/nlu/classifiers/sklearn_intent_classifier.py +16 -37
- rasa/nlu/extractors/crf_entity_extractor.py +54 -97
- rasa/nlu/extractors/duckling_entity_extractor.py +1 -1
- rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +1 -5
- rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +0 -4
- rasa/nlu/featurizers/featurizer.py +1 -1
- rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +18 -49
- rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +26 -64
- rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +3 -5
- rasa/nlu/persistor.py +68 -26
- rasa/nlu/selectors/response_selector.py +7 -10
- rasa/nlu/test.py +0 -3
- rasa/nlu/utils/hugging_face/registry.py +1 -1
- rasa/nlu/utils/spacy_utils.py +1 -3
- rasa/server.py +22 -7
- rasa/shared/constants.py +12 -1
- rasa/shared/core/command_payload_reader.py +109 -0
- rasa/shared/core/constants.py +4 -5
- rasa/shared/core/domain.py +57 -56
- rasa/shared/core/events.py +4 -7
- rasa/shared/core/flows/flow.py +9 -0
- rasa/shared/core/flows/flows_list.py +12 -0
- rasa/shared/core/flows/steps/action.py +7 -2
- rasa/shared/core/generator.py +12 -11
- rasa/shared/core/slot_mappings.py +315 -24
- rasa/shared/core/slots.py +4 -2
- rasa/shared/core/trackers.py +32 -14
- rasa/shared/core/training_data/loading.py +0 -1
- rasa/shared/core/training_data/story_reader/story_reader.py +3 -3
- rasa/shared/core/training_data/story_reader/yaml_story_reader.py +11 -11
- rasa/shared/core/training_data/story_writer/yaml_story_writer.py +5 -3
- rasa/shared/core/training_data/structures.py +1 -1
- rasa/shared/core/training_data/visualization.py +1 -1
- rasa/shared/data.py +58 -1
- rasa/shared/exceptions.py +36 -2
- rasa/shared/importers/importer.py +1 -2
- rasa/shared/importers/rasa.py +0 -1
- rasa/shared/nlu/constants.py +2 -0
- rasa/shared/nlu/training_data/entities_parser.py +1 -2
- rasa/shared/nlu/training_data/features.py +2 -120
- rasa/shared/nlu/training_data/formats/dialogflow.py +3 -2
- rasa/shared/nlu/training_data/formats/rasa_yaml.py +3 -5
- rasa/shared/nlu/training_data/formats/readerwriter.py +0 -1
- rasa/shared/nlu/training_data/message.py +13 -0
- rasa/shared/nlu/training_data/training_data.py +0 -2
- rasa/shared/providers/openai/session_handler.py +2 -2
- rasa/shared/utils/constants.py +3 -0
- rasa/shared/utils/io.py +11 -1
- rasa/shared/utils/llm.py +1 -2
- rasa/shared/utils/pykwalify_extensions.py +1 -0
- rasa/shared/utils/schemas/domain.yml +3 -0
- rasa/shared/utils/yaml.py +44 -35
- rasa/studio/auth.py +26 -10
- rasa/studio/constants.py +2 -0
- rasa/studio/data_handler.py +114 -107
- rasa/studio/download.py +160 -27
- rasa/studio/results_logger.py +137 -0
- rasa/studio/train.py +6 -7
- rasa/studio/upload.py +159 -134
- rasa/telemetry.py +188 -34
- rasa/tracing/config.py +18 -3
- rasa/tracing/constants.py +26 -2
- rasa/tracing/instrumentation/attribute_extractors.py +50 -41
- rasa/tracing/instrumentation/instrumentation.py +290 -44
- rasa/tracing/instrumentation/intentless_policy_instrumentation.py +7 -5
- rasa/tracing/instrumentation/metrics.py +109 -21
- rasa/tracing/metric_instrument_provider.py +83 -3
- rasa/utils/cli.py +2 -1
- rasa/utils/common.py +1 -1
- rasa/utils/endpoints.py +1 -2
- rasa/utils/io.py +72 -6
- rasa/utils/licensing.py +246 -31
- rasa/utils/ml_utils.py +1 -1
- rasa/utils/tensorflow/data_generator.py +1 -1
- rasa/utils/tensorflow/environment.py +1 -1
- rasa/utils/tensorflow/model_data.py +201 -12
- rasa/utils/tensorflow/model_data_utils.py +499 -500
- rasa/utils/tensorflow/models.py +5 -6
- rasa/utils/tensorflow/rasa_layers.py +15 -15
- rasa/utils/train_utils.py +1 -1
- rasa/utils/url_tools.py +53 -0
- rasa/validator.py +305 -3
- rasa/version.py +1 -1
- {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/METADATA +25 -61
- {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/RECORD +276 -259
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-85583a23.js +0 -1
- rasa/utils/tensorflow/feature_array.py +0 -370
- /rasa/dialogue_understanding/generator/{command_prompt_template.jinja2 → single_step/command_prompt_template.jinja2} +0 -0
- {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/NOTICE +0 -0
- {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/WHEEL +0 -0
- {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, Text
|
|
4
|
+
|
|
5
|
+
import rasa
|
|
6
|
+
from rasa.core.actions.action_exceptions import DomainNotFound
|
|
7
|
+
from rasa.core.actions.constants import DEFAULT_SELECTIVE_DOMAIN, SELECTIVE_DOMAIN
|
|
8
|
+
from rasa.shared.constants import DOCS_BASE_URL
|
|
9
|
+
from rasa.shared.exceptions import RasaException
|
|
10
|
+
from rasa.utils.endpoints import EndpointConfig
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from rasa.shared.core.domain import Domain
|
|
14
|
+
from rasa.shared.core.trackers import DialogueStateTracker
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CustomActionExecutor(abc.ABC):
|
|
21
|
+
"""Interface for custom action executors.
|
|
22
|
+
|
|
23
|
+
Provides an abstraction layer for executing custom actions
|
|
24
|
+
regardless of the communication protocol.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
@abc.abstractmethod
|
|
28
|
+
async def run(
|
|
29
|
+
self,
|
|
30
|
+
tracker: "DialogueStateTracker",
|
|
31
|
+
domain: "Domain",
|
|
32
|
+
include_domain: bool = False,
|
|
33
|
+
) -> Dict[Text, Any]:
|
|
34
|
+
"""Executes the custom action.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
tracker: The current state of the dialogue.
|
|
38
|
+
domain: The domain object containing domain-specific information.
|
|
39
|
+
include_domain: If True, the domain information is included in the request.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
The response from the execution of the custom action.
|
|
43
|
+
"""
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class NoEndpointCustomActionExecutor(CustomActionExecutor):
|
|
48
|
+
"""Implementation of a custom action executor when endpoint is not set.
|
|
49
|
+
|
|
50
|
+
Used to handle the case where no endpoint is configured.
|
|
51
|
+
|
|
52
|
+
Raises RasaException when executed.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, action_name: str) -> None:
|
|
56
|
+
"""Initializes the custom action executor.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
action_name: The name of the custom action.
|
|
60
|
+
"""
|
|
61
|
+
self.action_name = action_name
|
|
62
|
+
|
|
63
|
+
async def run(
|
|
64
|
+
self,
|
|
65
|
+
tracker: "DialogueStateTracker",
|
|
66
|
+
domain: "Domain",
|
|
67
|
+
include_domain: bool = False,
|
|
68
|
+
) -> Dict[Text, Any]:
|
|
69
|
+
"""Executes the custom action.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
tracker: The current state of the dialogue.
|
|
73
|
+
domain: The domain object containing domain-specific information.
|
|
74
|
+
include_domain: If True, the domain information
|
|
75
|
+
is included in the request.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
The response from the execution of the custom action.
|
|
79
|
+
"""
|
|
80
|
+
raise RasaException(
|
|
81
|
+
f"Failed to execute custom action '{self.action_name}' "
|
|
82
|
+
f"because no endpoint is configured to run this "
|
|
83
|
+
f"custom action. Please take a look at "
|
|
84
|
+
f"the docs and set an endpoint configuration via the "
|
|
85
|
+
f"--endpoints flag. "
|
|
86
|
+
f"{DOCS_BASE_URL}/custom-actions"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class CustomActionRequestWriter:
|
|
91
|
+
"""Writes the request payload for a custom action."""
|
|
92
|
+
|
|
93
|
+
def __init__(self, action_name: str, action_endpoint: EndpointConfig) -> None:
|
|
94
|
+
"""Initializes the request writer.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
action_name: The name of the custom action.
|
|
98
|
+
action_endpoint: The endpoint configuration for the action server.
|
|
99
|
+
"""
|
|
100
|
+
self.action_name = action_name
|
|
101
|
+
self.action_endpoint = action_endpoint
|
|
102
|
+
|
|
103
|
+
def _is_selective_domain_enabled(self) -> bool:
|
|
104
|
+
"""Check if selective domain handling is enabled.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
True if selective domain handling is enabled, otherwise False.
|
|
108
|
+
"""
|
|
109
|
+
if self.action_endpoint is None:
|
|
110
|
+
return False
|
|
111
|
+
return bool(
|
|
112
|
+
self.action_endpoint.kwargs.get(SELECTIVE_DOMAIN, DEFAULT_SELECTIVE_DOMAIN)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def create(
|
|
116
|
+
self,
|
|
117
|
+
tracker: "DialogueStateTracker",
|
|
118
|
+
domain: "Domain",
|
|
119
|
+
include_domain: bool = False,
|
|
120
|
+
) -> Dict[str, Any]:
|
|
121
|
+
"""Create the JSON payload for the action server request.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
tracker: The current state of the dialogue.
|
|
125
|
+
domain: The domain object containing domain-specific information.
|
|
126
|
+
include_domain: If True, the domain information is included in the request.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
A JSON payload to be sent to the action server.
|
|
130
|
+
"""
|
|
131
|
+
from rasa.shared.core.trackers import EventVerbosity
|
|
132
|
+
|
|
133
|
+
tracker_state = tracker.current_state(EventVerbosity.ALL)
|
|
134
|
+
|
|
135
|
+
result = {
|
|
136
|
+
"next_action": self.action_name,
|
|
137
|
+
"sender_id": tracker.sender_id,
|
|
138
|
+
"tracker": tracker_state,
|
|
139
|
+
"version": rasa.__version__,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if include_domain and (
|
|
143
|
+
not self._is_selective_domain_enabled()
|
|
144
|
+
or domain.does_custom_action_explicitly_need_domain(self.action_name)
|
|
145
|
+
):
|
|
146
|
+
result["domain"] = domain.as_dict()
|
|
147
|
+
|
|
148
|
+
result["domain_digest"] = domain.fingerprint()
|
|
149
|
+
|
|
150
|
+
return result
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class RetryCustomActionExecutor(CustomActionExecutor):
|
|
154
|
+
"""Retries the execution of a custom action."""
|
|
155
|
+
|
|
156
|
+
def __init__(self, custom_action_executor: CustomActionExecutor) -> None:
|
|
157
|
+
self._custom_action_executor = custom_action_executor
|
|
158
|
+
|
|
159
|
+
async def run(
|
|
160
|
+
self,
|
|
161
|
+
tracker: "DialogueStateTracker",
|
|
162
|
+
domain: "Domain",
|
|
163
|
+
include_domain: bool = False,
|
|
164
|
+
) -> Dict[Text, Any]:
|
|
165
|
+
"""Runs the wrapped custom action executor.
|
|
166
|
+
|
|
167
|
+
First request to the action server is made with/without the domain
|
|
168
|
+
as specified by the `include_domain` parameter.
|
|
169
|
+
|
|
170
|
+
If the action server responds with a `DomainNotFound` error, by running the
|
|
171
|
+
custom action executor again with the domain information.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
tracker: The current state of the dialogue.
|
|
175
|
+
domain: The domain object containing domain-specific information.
|
|
176
|
+
include_domain: If True, the domain information is included in the request
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
The response from the execution of the custom action.
|
|
180
|
+
"""
|
|
181
|
+
try:
|
|
182
|
+
return await self._custom_action_executor.run(
|
|
183
|
+
tracker, domain, include_domain=include_domain
|
|
184
|
+
)
|
|
185
|
+
except DomainNotFound:
|
|
186
|
+
return await self._custom_action_executor.run(
|
|
187
|
+
tracker, domain, include_domain=True
|
|
188
|
+
)
|
rasa/core/actions/forms.py
CHANGED
|
@@ -11,7 +11,8 @@ from rasa.core.channels import OutputChannel
|
|
|
11
11
|
from rasa.shared.core.domain import Domain, KEY_SLOTS
|
|
12
12
|
from rasa.shared.core.constants import SlotMappingType, SLOT_MAPPINGS, MAPPING_TYPE
|
|
13
13
|
|
|
14
|
-
from rasa.core.actions.action import
|
|
14
|
+
from rasa.core.actions.action import RemoteAction
|
|
15
|
+
from rasa.core.actions.action_exceptions import ActionExecutionRejection
|
|
15
16
|
from rasa.shared.core.constants import (
|
|
16
17
|
ACTION_EXTRACT_SLOTS,
|
|
17
18
|
ACTION_LISTEN_NAME,
|
|
@@ -293,7 +294,8 @@ class FormAction(LoopAction):
|
|
|
293
294
|
+ [SlotSet(REQUESTED_SLOT, self.get_slot_to_fill(current_tracker))]
|
|
294
295
|
# Insert form execution event so that it's clearly distinguishable which
|
|
295
296
|
# events were newly added.
|
|
296
|
-
+ [ActionExecuted(self.name())]
|
|
297
|
+
+ [ActionExecuted(self.name())]
|
|
298
|
+
+ additional_events,
|
|
297
299
|
slots=domain.slots,
|
|
298
300
|
)
|
|
299
301
|
|
|
@@ -565,6 +567,7 @@ class FormAction(LoopAction):
|
|
|
565
567
|
nlg: "NaturalLanguageGenerator",
|
|
566
568
|
tracker: "DialogueStateTracker",
|
|
567
569
|
domain: "Domain",
|
|
570
|
+
metadata: Optional[Dict[Text, Any]] = None,
|
|
568
571
|
) -> List[Event]:
|
|
569
572
|
"""Activate form if the form is called for the first time.
|
|
570
573
|
|
|
@@ -596,7 +599,7 @@ class FormAction(LoopAction):
|
|
|
596
599
|
)
|
|
597
600
|
|
|
598
601
|
extraction_events = await action_extract_slots.run(
|
|
599
|
-
output_channel, nlg, tracker, domain
|
|
602
|
+
output_channel, nlg, tracker, domain, metadata
|
|
600
603
|
)
|
|
601
604
|
|
|
602
605
|
events_as_str = "\n".join(str(e) for e in extraction_events)
|
|
@@ -712,9 +715,7 @@ class FormAction(LoopAction):
|
|
|
712
715
|
if isinstance(event, ActiveLoop)
|
|
713
716
|
),
|
|
714
717
|
None,
|
|
715
|
-
) == ActiveLoop(
|
|
716
|
-
None
|
|
717
|
-
)
|
|
718
|
+
) == ActiveLoop(None)
|
|
718
719
|
|
|
719
720
|
async def deactivate(self, *args: Any, **kwargs: Any) -> List[Event]:
|
|
720
721
|
"""Deactivates form."""
|
|
@@ -727,11 +728,14 @@ class FormAction(LoopAction):
|
|
|
727
728
|
nlg: "NaturalLanguageGenerator",
|
|
728
729
|
tracker: "DialogueStateTracker",
|
|
729
730
|
domain: "Domain",
|
|
731
|
+
metadata: Optional[Dict[Text, Any]] = None,
|
|
730
732
|
) -> List[Event]:
|
|
731
733
|
events = self._default_activation_events()
|
|
732
734
|
|
|
733
735
|
temp_tracker = tracker.copy()
|
|
734
736
|
temp_tracker.update_with_events(events)
|
|
735
|
-
events += await self.activate(
|
|
737
|
+
events += await self.activate(
|
|
738
|
+
output_channel, nlg, temp_tracker, domain, metadata
|
|
739
|
+
)
|
|
736
740
|
|
|
737
741
|
return events
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Tuple
|
|
3
|
+
from urllib.parse import urlparse
|
|
4
|
+
|
|
5
|
+
import grpc
|
|
6
|
+
import structlog
|
|
7
|
+
from google.protobuf.json_format import MessageToDict, Parse, ParseDict
|
|
8
|
+
from rasa_sdk.grpc_errors import ResourceNotFound, ResourceNotFoundType
|
|
9
|
+
from rasa_sdk.grpc_py import action_webhook_pb2, action_webhook_pb2_grpc
|
|
10
|
+
|
|
11
|
+
from rasa.core.actions.action_exceptions import DomainNotFound
|
|
12
|
+
from rasa.core.actions.constants import SSL_CLIENT_CERT_FIELD, SSL_CLIENT_KEY_FIELD
|
|
13
|
+
from rasa.core.actions.custom_action_executor import (
|
|
14
|
+
CustomActionExecutor,
|
|
15
|
+
CustomActionRequestWriter,
|
|
16
|
+
)
|
|
17
|
+
from rasa.shared.exceptions import RasaException
|
|
18
|
+
from rasa.shared.utils.io import file_as_bytes
|
|
19
|
+
from rasa.utils.endpoints import EndpointConfig
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from rasa.shared.core.domain import Domain
|
|
23
|
+
from rasa.shared.core.trackers import DialogueStateTracker
|
|
24
|
+
|
|
25
|
+
structlogger = structlog.get_logger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class GRPCCustomActionExecutor(CustomActionExecutor):
|
|
29
|
+
"""gRPC-based implementation of the CustomActionExecutor.
|
|
30
|
+
|
|
31
|
+
Executes custom actions by making gRPC requests to the action endpoint.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
action_name: str,
|
|
37
|
+
action_endpoint: EndpointConfig,
|
|
38
|
+
) -> None:
|
|
39
|
+
"""Initializes the gRPC custom action executor.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
action_name: Name of the custom action.
|
|
43
|
+
action_endpoint: Endpoint configuration of the custom action.
|
|
44
|
+
"""
|
|
45
|
+
self.action_name = action_name
|
|
46
|
+
self.request_writer = CustomActionRequestWriter(action_name, action_endpoint)
|
|
47
|
+
self.action_endpoint = action_endpoint
|
|
48
|
+
|
|
49
|
+
parsed_url = urlparse(self.action_endpoint.url)
|
|
50
|
+
self.request_url = parsed_url.netloc
|
|
51
|
+
|
|
52
|
+
self.cert_ca = (
|
|
53
|
+
file_as_bytes(self.action_endpoint.cafile)
|
|
54
|
+
if self.action_endpoint.cafile
|
|
55
|
+
else None
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
self.client_cert = None
|
|
59
|
+
self.client_key = None
|
|
60
|
+
|
|
61
|
+
client_cert_file = self.action_endpoint.kwargs.get(SSL_CLIENT_CERT_FIELD)
|
|
62
|
+
client_key_file = self.action_endpoint.kwargs.get(SSL_CLIENT_KEY_FIELD)
|
|
63
|
+
if client_cert_file and client_key_file:
|
|
64
|
+
self.client_cert = file_as_bytes(client_cert_file)
|
|
65
|
+
self.client_key = file_as_bytes(client_key_file)
|
|
66
|
+
elif client_key_file and not client_cert_file:
|
|
67
|
+
structlogger.error(
|
|
68
|
+
f"rasa.core.actions.grpc_custom_action_executor.{SSL_CLIENT_CERT_FIELD}_missing",
|
|
69
|
+
event_info=(
|
|
70
|
+
f"Client key file '{SSL_CLIENT_KEY_FIELD}' is provided but "
|
|
71
|
+
f"client certificate file '{SSL_CLIENT_CERT_FIELD}' "
|
|
72
|
+
f"is not provided in the endpoint configuration. "
|
|
73
|
+
f"Both fields are required for client TLS authentication."
|
|
74
|
+
f"Continuing without client TLS authentication."
|
|
75
|
+
),
|
|
76
|
+
)
|
|
77
|
+
elif client_cert_file and not client_key_file:
|
|
78
|
+
structlogger.error(
|
|
79
|
+
f"rasa.core.actions.grpc_custom_action_executor.{SSL_CLIENT_KEY_FIELD}_missing",
|
|
80
|
+
event_info=(
|
|
81
|
+
f"Client certificate file '{SSL_CLIENT_CERT_FIELD}' "
|
|
82
|
+
f" is provided but client key file '{SSL_CLIENT_KEY_FIELD}'"
|
|
83
|
+
f" is not provided in the endpoint configuration. "
|
|
84
|
+
f"Both fields are required for client TLS authentication."
|
|
85
|
+
f"Continuing without client TLS authentication."
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
async def run(
|
|
90
|
+
self,
|
|
91
|
+
tracker: "DialogueStateTracker",
|
|
92
|
+
domain: "Domain",
|
|
93
|
+
include_domain: bool = False,
|
|
94
|
+
) -> Dict[str, Any]:
|
|
95
|
+
"""Execute the custom action using a gRPC request.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
tracker: Tracker for the current conversation.
|
|
99
|
+
domain: Domain of the assistant.
|
|
100
|
+
include_domain: If True, the domain information is included in the request.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Response from the action server.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
request = self._create_payload(
|
|
107
|
+
tracker=tracker, domain=domain, include_domain=include_domain
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return self._request(request)
|
|
111
|
+
|
|
112
|
+
def _request(
|
|
113
|
+
self,
|
|
114
|
+
request: action_webhook_pb2.WebhookRequest,
|
|
115
|
+
) -> Dict[str, Any]:
|
|
116
|
+
"""Perform a single gRPC request to the action server.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
request: gRPC Request to be sent to the action server.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Response from the action server.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
client = self._create_grpc_client()
|
|
126
|
+
metadata = self._build_metadata()
|
|
127
|
+
try:
|
|
128
|
+
response = client.Webhook(request, metadata=metadata)
|
|
129
|
+
return MessageToDict(response)
|
|
130
|
+
except grpc.RpcError as rpc_error:
|
|
131
|
+
if not isinstance(rpc_error, grpc.Call):
|
|
132
|
+
raise RasaException(
|
|
133
|
+
f"Failed to execute custom action '{self.action_name}'. "
|
|
134
|
+
f"Unknown error occurred while calling the "
|
|
135
|
+
f"action server over gRPC protocol."
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
status_code = rpc_error.code()
|
|
139
|
+
details = rpc_error.details()
|
|
140
|
+
if status_code is not grpc.StatusCode.NOT_FOUND:
|
|
141
|
+
raise RasaException(
|
|
142
|
+
f"Failed to execute custom action '{self.action_name}'. "
|
|
143
|
+
f"Error: {details}"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
resource_not_found_error = ResourceNotFound.model_validate_json(details)
|
|
147
|
+
if (
|
|
148
|
+
resource_not_found_error
|
|
149
|
+
and resource_not_found_error.resource_type
|
|
150
|
+
== ResourceNotFoundType.DOMAIN
|
|
151
|
+
):
|
|
152
|
+
structlogger.error(
|
|
153
|
+
"rasa.core.actions.grpc_custom_action_executor.domain_not_found",
|
|
154
|
+
event_info=(
|
|
155
|
+
f"Failed to execute custom action '{self.action_endpoint}'. "
|
|
156
|
+
f"Could not find domain. {resource_not_found_error.message}"
|
|
157
|
+
),
|
|
158
|
+
)
|
|
159
|
+
raise DomainNotFound()
|
|
160
|
+
raise RasaException(
|
|
161
|
+
f"Failed to execute custom action '{self.action_name}'. "
|
|
162
|
+
f"Error: {details}"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
def _build_metadata(self) -> List[Tuple[str, Any]]:
|
|
166
|
+
"""Build metadata for the gRPC request.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Metadata for the gRPC request.
|
|
170
|
+
"""
|
|
171
|
+
metadata = []
|
|
172
|
+
for key, value in self.action_endpoint.headers.items():
|
|
173
|
+
metadata.append((key, value))
|
|
174
|
+
return metadata
|
|
175
|
+
|
|
176
|
+
def _create_payload(
|
|
177
|
+
self,
|
|
178
|
+
tracker: "DialogueStateTracker",
|
|
179
|
+
domain: "Domain",
|
|
180
|
+
include_domain: bool = False,
|
|
181
|
+
) -> action_webhook_pb2.WebhookRequest:
|
|
182
|
+
"""Create the gRPC payload for the action server.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
tracker: Tracker for the current conversation.
|
|
186
|
+
domain: Domain of the assistant.
|
|
187
|
+
include_domain: If True, the domain information is included in the request.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
gRPC payload for the action server.
|
|
191
|
+
"""
|
|
192
|
+
json_body = self.request_writer.create(
|
|
193
|
+
tracker=tracker, domain=domain, include_domain=include_domain
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
request_proto = action_webhook_pb2.WebhookRequest()
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
return ParseDict(
|
|
200
|
+
js_dict=json_body, message=request_proto, ignore_unknown_fields=True
|
|
201
|
+
)
|
|
202
|
+
except Exception:
|
|
203
|
+
structlogger.warning(
|
|
204
|
+
(
|
|
205
|
+
"rasa.core.actions.grpc_custom_action_executor."
|
|
206
|
+
"create_grpc_payload_from_dict_failed"
|
|
207
|
+
),
|
|
208
|
+
event_info=(
|
|
209
|
+
"Failed to create gRPC payload from Python dictionary. "
|
|
210
|
+
"Falling back to create payload from JSON intermediary."
|
|
211
|
+
),
|
|
212
|
+
)
|
|
213
|
+
json_text = json.dumps(json_body)
|
|
214
|
+
return Parse(
|
|
215
|
+
text=json_text, message=request_proto, ignore_unknown_fields=True
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
def _create_grpc_client(
|
|
219
|
+
self,
|
|
220
|
+
) -> action_webhook_pb2_grpc.ActionServiceStub:
|
|
221
|
+
"""Create a gRPC client for the action server.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
gRPC client for the action server.
|
|
225
|
+
"""
|
|
226
|
+
channel = self._create_channel()
|
|
227
|
+
return action_webhook_pb2_grpc.ActionServiceStub(channel)
|
|
228
|
+
|
|
229
|
+
def _create_channel(
|
|
230
|
+
self,
|
|
231
|
+
) -> grpc.Channel:
|
|
232
|
+
"""Create a gRPC channel for the action server.
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
gRPC channel for the action server.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
compression = grpc.Compression.Gzip
|
|
239
|
+
|
|
240
|
+
if self.cert_ca:
|
|
241
|
+
credentials = grpc.ssl_channel_credentials(
|
|
242
|
+
root_certificates=self.cert_ca,
|
|
243
|
+
private_key=self.client_key,
|
|
244
|
+
certificate_chain=self.client_cert,
|
|
245
|
+
)
|
|
246
|
+
return grpc.secure_channel(
|
|
247
|
+
target=self.request_url,
|
|
248
|
+
credentials=credentials,
|
|
249
|
+
compression=compression,
|
|
250
|
+
)
|
|
251
|
+
return grpc.insecure_channel(target=self.request_url, compression=compression)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict
|
|
4
|
+
|
|
5
|
+
import aiohttp
|
|
6
|
+
|
|
7
|
+
from rasa.core.actions.action_exceptions import ActionExecutionRejection, DomainNotFound
|
|
8
|
+
from rasa.core.actions.custom_action_executor import (
|
|
9
|
+
CustomActionExecutor,
|
|
10
|
+
CustomActionRequestWriter,
|
|
11
|
+
)
|
|
12
|
+
from rasa.core.constants import (
|
|
13
|
+
COMPRESS_ACTION_SERVER_REQUEST_ENV_NAME,
|
|
14
|
+
DEFAULT_COMPRESS_ACTION_SERVER_REQUEST,
|
|
15
|
+
DEFAULT_REQUEST_TIMEOUT,
|
|
16
|
+
)
|
|
17
|
+
from rasa.shared.exceptions import RasaException
|
|
18
|
+
from rasa.utils.common import get_bool_env_variable
|
|
19
|
+
from rasa.utils.endpoints import ClientResponseError, EndpointConfig
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from rasa.shared.core.domain import Domain
|
|
23
|
+
from rasa.shared.core.trackers import DialogueStateTracker
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class HTTPCustomActionExecutor(CustomActionExecutor):
|
|
30
|
+
"""HTTP-based implementation of the CustomActionExecutor.
|
|
31
|
+
|
|
32
|
+
Executes custom actions by making HTTP POST requests to the action endpoint.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
action_name: str,
|
|
38
|
+
action_endpoint: EndpointConfig,
|
|
39
|
+
) -> None:
|
|
40
|
+
self.action_name = action_name
|
|
41
|
+
self.action_endpoint = action_endpoint
|
|
42
|
+
self.request_writer = CustomActionRequestWriter(action_name, action_endpoint)
|
|
43
|
+
self.should_compress = get_bool_env_variable(
|
|
44
|
+
COMPRESS_ACTION_SERVER_REQUEST_ENV_NAME,
|
|
45
|
+
DEFAULT_COMPRESS_ACTION_SERVER_REQUEST,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
async def run(
|
|
49
|
+
self,
|
|
50
|
+
tracker: "DialogueStateTracker",
|
|
51
|
+
domain: "Domain",
|
|
52
|
+
include_domain: bool = False,
|
|
53
|
+
) -> Dict[str, Any]:
|
|
54
|
+
"""Execute the custom action using an HTTP POST request.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
tracker: The current state of the dialogue.
|
|
58
|
+
domain: The domain object containing domain-specific information.
|
|
59
|
+
include_domain: If `True`, the domain information
|
|
60
|
+
is included in the request.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
A dictionary containing the response from the custom action endpoint.
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
RasaException: If an error occurs while making the HTTP request.
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
logger.debug(
|
|
70
|
+
"Calling action endpoint to run action '{}'.".format(self.action_name)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
json_body = self.request_writer.create(
|
|
74
|
+
tracker=tracker, domain=domain, include_domain=include_domain
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
response = await self._perform_request_with_retries(json_body)
|
|
78
|
+
|
|
79
|
+
if response is None:
|
|
80
|
+
response = {}
|
|
81
|
+
|
|
82
|
+
return response
|
|
83
|
+
|
|
84
|
+
except ClientResponseError as e:
|
|
85
|
+
if e.status == 400:
|
|
86
|
+
response_data = json.loads(e.text)
|
|
87
|
+
exception = ActionExecutionRejection(
|
|
88
|
+
response_data["action_name"], response_data.get("error")
|
|
89
|
+
)
|
|
90
|
+
logger.error(exception.message)
|
|
91
|
+
raise exception
|
|
92
|
+
else:
|
|
93
|
+
raise RasaException(
|
|
94
|
+
f"Failed to execute custom action '{self.action_name}'"
|
|
95
|
+
) from e
|
|
96
|
+
|
|
97
|
+
except aiohttp.ClientConnectionError as e:
|
|
98
|
+
logger.error(
|
|
99
|
+
f"Failed to run custom action '{self.action_name}'. Couldn't connect "
|
|
100
|
+
f"to the server at '{self.action_endpoint.url}'. "
|
|
101
|
+
f"Is the server running? "
|
|
102
|
+
f"Error: {e}"
|
|
103
|
+
)
|
|
104
|
+
raise RasaException(
|
|
105
|
+
f"Failed to execute custom action '{self.action_name}'. "
|
|
106
|
+
f"Couldn't connect to the server at '{self.action_endpoint.url}."
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
except aiohttp.ClientError as e:
|
|
110
|
+
# not all errors have a status attribute, but
|
|
111
|
+
# helpful to log if they got it
|
|
112
|
+
|
|
113
|
+
# noinspection PyUnresolvedReferences
|
|
114
|
+
status = getattr(e, "status", None)
|
|
115
|
+
raise RasaException(
|
|
116
|
+
"Failed to run custom action '{}'. Action server "
|
|
117
|
+
"responded with a non 200 status code of {}. "
|
|
118
|
+
"Make sure your action server properly runs actions "
|
|
119
|
+
"and returns a 200 once the action is executed. "
|
|
120
|
+
"Error: {}".format(self.action_name, status, e)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
async def _perform_request_with_retries(
|
|
124
|
+
self,
|
|
125
|
+
json_body: Dict[str, Any],
|
|
126
|
+
) -> Any:
|
|
127
|
+
"""Attempts to perform the request with retries if necessary."""
|
|
128
|
+
assert self.action_endpoint is not None
|
|
129
|
+
try:
|
|
130
|
+
return await self.action_endpoint.request(
|
|
131
|
+
json=json_body,
|
|
132
|
+
method="post",
|
|
133
|
+
timeout=DEFAULT_REQUEST_TIMEOUT,
|
|
134
|
+
compress=self.should_compress,
|
|
135
|
+
)
|
|
136
|
+
except ClientResponseError as e:
|
|
137
|
+
# Repeat the request because Domain was not in the payload
|
|
138
|
+
if e.status == 449:
|
|
139
|
+
raise DomainNotFound()
|
|
140
|
+
raise e
|
rasa/core/actions/loops.py
CHANGED
|
@@ -28,6 +28,7 @@ class LoopAction(Action, ABC):
|
|
|
28
28
|
nlg,
|
|
29
29
|
tracker,
|
|
30
30
|
domain,
|
|
31
|
+
metadata,
|
|
31
32
|
)
|
|
32
33
|
|
|
33
34
|
if not await self.is_done(output_channel, nlg, tracker, domain, events):
|
|
@@ -60,6 +61,7 @@ class LoopAction(Action, ABC):
|
|
|
60
61
|
nlg: "NaturalLanguageGenerator",
|
|
61
62
|
tracker: "DialogueStateTracker",
|
|
62
63
|
domain: "Domain",
|
|
64
|
+
metadata: Optional[Dict[Text, Any]] = None,
|
|
63
65
|
) -> List[Event]:
|
|
64
66
|
# can be overwritten
|
|
65
67
|
return []
|
|
@@ -104,6 +106,7 @@ class LoopAction(Action, ABC):
|
|
|
104
106
|
nlg: "NaturalLanguageGenerator",
|
|
105
107
|
tracker: "DialogueStateTracker",
|
|
106
108
|
domain: "Domain",
|
|
109
|
+
metadata: Optional[Dict[Text, Any]] = None,
|
|
107
110
|
) -> List[Event]:
|
|
108
111
|
events = self._default_activation_events()
|
|
109
112
|
events += await self.activate(output_channel, nlg, tracker, domain)
|
|
@@ -181,6 +181,6 @@ def _message_clarification(tracker: DialogueStateTracker) -> List[Event]:
|
|
|
181
181
|
)
|
|
182
182
|
|
|
183
183
|
clarification = copy.deepcopy(latest_message)
|
|
184
|
-
clarification.parse_data[INTENT][PREDICTED_CONFIDENCE_KEY] = 1.0 # type: ignore[literal-required]
|
|
184
|
+
clarification.parse_data[INTENT][PREDICTED_CONFIDENCE_KEY] = 1.0 # type: ignore[literal-required]
|
|
185
185
|
clarification.timestamp = time.time()
|
|
186
186
|
return [ActionExecuted(ACTION_LISTEN_NAME), clarification]
|