rasa-pro 3.13.0.dev2__py3-none-any.whl → 3.13.0.dev5__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/__main__.py +3 -1
- rasa/cli/inspect.py +8 -4
- rasa/cli/project_templates/default/config.yml +5 -32
- rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_cancels_during_a_correction.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_changes_mind_on_a_whim.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_handle.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_name.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_adds_contact_to_their_list.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_lists_contacts.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact_from_list.yml +1 -1
- rasa/cli/project_templates/default/endpoints.yml +18 -2
- rasa/cli/run.py +10 -6
- rasa/cli/scaffold.py +3 -4
- rasa/cli/studio/download.py +1 -1
- rasa/cli/studio/upload.py +0 -6
- rasa/cli/utils.py +7 -0
- rasa/core/channels/channel.py +93 -0
- rasa/core/channels/inspector/dist/assets/{arc-c7691751.js → arc-9f75cc3b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-ab99dff7.js → blockDiagram-38ab4fdb-7f34db23.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-08c35a6b.js → c4Diagram-3d4e48cf-948bab2c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-dfa68278.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-9e9c71c9.js → classDiagram-70f12bd4-53b0dd0e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-15e7e2bf.js → classDiagram-v2-f2320105-fdf789e7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-edb7f119.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-9c105cb1.js → createText-2e5e7dd3-87c4ece5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-77e89e48.js → edges-e0da2a9e-5a8b0749.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-7a011646.js → erDiagram-9861fffd-66da90e2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-b6f105ac.js → flowDb-956e92f1-10044f05.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-ce4f18c2.js → flowDiagram-66a62f08-f338f66a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-65e7c670.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-cb5f6da4.js → flowchart-elk-definition-4a651766-b13140aa.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-e4d19e28.js → ganttDiagram-c361ad54-f2b4a55a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-727b1c33.js → gitGraphDiagram-72cf32ee-dedc298d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-6e2ab9a7.js → graph-4ede11ff.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3862675e-84ec700f.js → index-3862675e-65549d37.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-098a1a24.js → index-3a23e736.js} +142 -129
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-78dda442.js → infoDiagram-f8f76790-65439671.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-f1cc6dd1.js → journeyDiagram-49397b02-56d03d98.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-d98dcd0c.js → layout-dd48f7f4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-838e3d82.js → line-1569ad2c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-eae72406.js → linear-48bf4935.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-c96fd84b.js → mindmap-definition-fc14e90a-688504c1.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-c936d4e2.js → pieDiagram-8a3498a8-78b6d7e6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-b338eb8f.js → quadrantDiagram-120e2f19-048b84b3.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-c6b6c0d5.js → requirementDiagram-deff3bca-dd67f107.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-b9372e19.js → sankeyDiagram-04a897e0-8128436e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-479e0a3f.js → sequenceDiagram-704730f1-1a0d1461.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-fd26eebc.js → stateDiagram-587899a1-46d388ed.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-3233e0ae.js → stateDiagram-v2-d93cdb3a-ea42951a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-1fdd392b.js → styles-6aaf32cf-7427ed0c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-6d7bfa1b.js → styles-9a916d00-ff5e5a16.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-f86aab11.js → styles-c10674c1-7b3680cf.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-e3e49d7a.js → svgDrawCommon-08f97a94-f860f2ad.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-6fe08b4d.js → timeline-definition-85554ec2-2eebf0c8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-c2e06fd6.js → xychartDiagram-e933f94c-5d7f4e96.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/src/App.tsx +3 -2
- rasa/core/channels/inspector/src/components/Chat.tsx +23 -2
- rasa/core/channels/inspector/src/components/DiagramFlow.tsx +2 -5
- rasa/core/channels/inspector/src/helpers/conversation.ts +16 -0
- rasa/core/channels/inspector/src/types.ts +1 -1
- rasa/core/channels/voice_ready/audiocodes.py +41 -15
- rasa/core/channels/voice_ready/jambonz.py +25 -5
- rasa/core/channels/voice_ready/jambonz_protocol.py +4 -0
- rasa/core/channels/voice_ready/twilio_voice.py +48 -1
- rasa/core/channels/voice_stream/tts/azure.py +11 -2
- rasa/core/channels/voice_stream/twilio_media_streams.py +101 -26
- rasa/core/channels/voice_stream/voice_channel.py +28 -2
- rasa/core/concurrent_lock_store.py +24 -10
- rasa/core/information_retrieval/faiss.py +7 -68
- rasa/core/information_retrieval/information_retrieval.py +2 -40
- rasa/core/information_retrieval/milvus.py +2 -7
- rasa/core/information_retrieval/qdrant.py +2 -7
- rasa/core/lock_store.py +151 -60
- rasa/core/nlg/contextual_response_rephraser.py +3 -0
- rasa/core/policies/enterprise_search_policy.py +310 -61
- rasa/core/policies/intentless_policy.py +3 -0
- rasa/dialogue_understanding/coexistence/llm_based_router.py +8 -0
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -2
- rasa/dialogue_understanding/generator/command_parser.py +1 -1
- rasa/dialogue_understanding/generator/flow_retrieval.py +1 -4
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +1 -2
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +13 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +1 -1
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -1
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +2 -24
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +22 -17
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +27 -12
- rasa/dialogue_understanding_test/du_test_case.py +16 -8
- rasa/dialogue_understanding_test/io.py +8 -13
- rasa/e2e_test/utils/validation.py +3 -3
- rasa/engine/recipes/default_components.py +0 -2
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +3 -0
- rasa/plugin.py +0 -3
- rasa/shared/constants.py +1 -0
- rasa/shared/core/domain.py +165 -11
- rasa/shared/core/flows/flow.py +155 -131
- rasa/shared/core/flows/flow_step.py +19 -3
- rasa/shared/core/flows/flow_step_links.py +15 -0
- rasa/shared/core/flows/flow_step_sequence.py +6 -0
- rasa/shared/core/flows/nlu_trigger.py +13 -0
- rasa/shared/core/flows/steps/action.py +7 -4
- rasa/shared/core/flows/steps/call.py +11 -4
- rasa/shared/core/flows/steps/collect.py +27 -6
- rasa/shared/core/flows/steps/internal.py +6 -1
- rasa/shared/core/flows/steps/link.py +7 -4
- rasa/shared/core/flows/steps/no_operation.py +7 -4
- rasa/shared/core/flows/steps/set_slots.py +8 -4
- rasa/shared/core/flows/yaml_flows_io.py +106 -5
- rasa/shared/importers/importer.py +8 -0
- rasa/shared/providers/_utils.py +83 -0
- rasa/shared/providers/llm/_base_litellm_client.py +6 -3
- rasa/shared/providers/llm/azure_openai_llm_client.py +6 -68
- rasa/shared/providers/router/_base_litellm_router_client.py +53 -1
- rasa/shared/utils/common.py +42 -0
- rasa/shared/utils/constants.py +3 -0
- rasa/shared/utils/llm.py +70 -24
- rasa/studio/download/domains.py +49 -0
- rasa/studio/download/download.py +439 -0
- rasa/studio/download/flows.py +359 -0
- rasa/studio/results_logger.py +6 -1
- rasa/studio/upload.py +69 -5
- rasa/tracing/instrumentation/attribute_extractors.py +7 -10
- rasa/tracing/instrumentation/instrumentation.py +12 -12
- rasa/utils/common.py +36 -0
- rasa/utils/endpoints.py +22 -1
- rasa/utils/licensing.py +1 -1
- rasa/validator.py +1 -2
- rasa/version.py +1 -1
- {rasa_pro-3.13.0.dev2.dist-info → rasa_pro-3.13.0.dev5.dist-info}/METADATA +7 -7
- {rasa_pro-3.13.0.dev2.dist-info → rasa_pro-3.13.0.dev5.dist-info}/RECORD +149 -166
- rasa/cli/project_templates/calm/config.yml +0 -10
- rasa/cli/project_templates/calm/credentials.yml +0 -33
- rasa/cli/project_templates/calm/endpoints.yml +0 -58
- rasa/cli/project_templates/default/actions/actions.py +0 -27
- rasa/cli/project_templates/default/data/nlu.yml +0 -91
- rasa/cli/project_templates/default/data/rules.yml +0 -13
- rasa/cli/project_templates/default/data/stories.yml +0 -30
- rasa/cli/project_templates/default/domain.yml +0 -34
- rasa/cli/project_templates/default/tests/test_stories.yml +0 -91
- rasa/core/channels/inspector/dist/assets/channel-11268142.js +0 -1
- rasa/core/channels/inspector/dist/assets/clone-ff7f2ce7.js +0 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-cba7ae20.js +0 -1
- rasa/document_retrieval/__init__.py +0 -0
- rasa/document_retrieval/constants.py +0 -32
- rasa/document_retrieval/document_post_processor.py +0 -351
- rasa/document_retrieval/document_post_processor_prompt_template.jinja2 +0 -0
- rasa/document_retrieval/document_retriever.py +0 -333
- rasa/document_retrieval/knowledge_base_connectors/__init__.py +0 -0
- rasa/document_retrieval/knowledge_base_connectors/api_connector.py +0 -39
- rasa/document_retrieval/knowledge_base_connectors/knowledge_base_connector.py +0 -34
- rasa/document_retrieval/knowledge_base_connectors/vector_store_connector.py +0 -226
- rasa/document_retrieval/query_rewriter.py +0 -234
- rasa/document_retrieval/query_rewriter_prompt_template.jinja2 +0 -8
- rasa/studio/download.py +0 -489
- /rasa/cli/project_templates/{calm → default}/actions/action_template.py +0 -0
- /rasa/cli/project_templates/{calm → default}/actions/add_contact.py +0 -0
- /rasa/cli/project_templates/{calm → default}/actions/db.py +0 -0
- /rasa/cli/project_templates/{calm → default}/actions/list_contacts.py +0 -0
- /rasa/cli/project_templates/{calm → default}/actions/remove_contact.py +0 -0
- /rasa/cli/project_templates/{calm → default}/data/flows/add_contact.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/data/flows/list_contacts.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/data/flows/remove_contact.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/db/contacts.json +0 -0
- /rasa/cli/project_templates/{calm → default}/domain/add_contact.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/domain/list_contacts.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/domain/remove_contact.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/domain/shared.yml +0 -0
- /rasa/{cli/project_templates/calm/actions → studio/download}/__init__.py +0 -0
- {rasa_pro-3.13.0.dev2.dist-info → rasa_pro-3.13.0.dev5.dist-info}/NOTICE +0 -0
- {rasa_pro-3.13.0.dev2.dist-info → rasa_pro-3.13.0.dev5.dist-info}/WHEEL +0 -0
- {rasa_pro-3.13.0.dev2.dist-info → rasa_pro-3.13.0.dev5.dist-info}/entry_points.txt +0 -0
rasa/shared/utils/llm.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import importlib.resources
|
|
2
2
|
import json
|
|
3
|
+
import logging
|
|
3
4
|
from copy import deepcopy
|
|
4
5
|
from functools import wraps
|
|
5
6
|
from typing import (
|
|
@@ -7,6 +8,7 @@ from typing import (
|
|
|
7
8
|
Any,
|
|
8
9
|
Callable,
|
|
9
10
|
Dict,
|
|
11
|
+
Literal,
|
|
10
12
|
Optional,
|
|
11
13
|
Text,
|
|
12
14
|
Type,
|
|
@@ -59,6 +61,7 @@ from rasa.shared.providers.mappings import (
|
|
|
59
61
|
get_embedding_client_from_provider,
|
|
60
62
|
get_llm_client_from_provider,
|
|
61
63
|
)
|
|
64
|
+
from rasa.shared.utils.constants import LOG_COMPONENT_SOURCE_METHOD_INIT
|
|
62
65
|
|
|
63
66
|
if TYPE_CHECKING:
|
|
64
67
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
@@ -654,35 +657,59 @@ def embedder_client_factory(
|
|
|
654
657
|
|
|
655
658
|
|
|
656
659
|
def get_prompt_template(
|
|
657
|
-
jinja_file_path: Optional[Text],
|
|
660
|
+
jinja_file_path: Optional[Text],
|
|
661
|
+
default_prompt_template: Text,
|
|
662
|
+
*,
|
|
663
|
+
log_source_component: Optional[Text] = None,
|
|
664
|
+
log_source_method: Optional[Literal["init", "fingerprint_addon"]] = None,
|
|
658
665
|
) -> Text:
|
|
659
666
|
"""Returns the jinja template.
|
|
660
667
|
|
|
661
668
|
Args:
|
|
662
|
-
jinja_file_path:
|
|
663
|
-
|
|
669
|
+
jinja_file_path: The path to the jinja template file. If not provided, the
|
|
670
|
+
default template will be used.
|
|
671
|
+
default_prompt_template: The fallback prompt template to use if no file is
|
|
672
|
+
found or specified.
|
|
673
|
+
log_source_component: The name of the component emitting the log, used to
|
|
674
|
+
identify the source in structured logging.
|
|
675
|
+
log_source_method: The name of the method or function emitting the log for
|
|
676
|
+
better traceability.
|
|
664
677
|
|
|
665
678
|
Returns:
|
|
666
679
|
The prompt template.
|
|
667
680
|
"""
|
|
681
|
+
|
|
668
682
|
try:
|
|
669
683
|
if jinja_file_path is not None:
|
|
670
684
|
prompt_template = rasa.shared.utils.io.read_file(jinja_file_path)
|
|
671
|
-
|
|
672
|
-
|
|
685
|
+
|
|
686
|
+
log_level = (
|
|
687
|
+
logging.INFO
|
|
688
|
+
if log_source_method == LOG_COMPONENT_SOURCE_METHOD_INIT
|
|
689
|
+
else logging.DEBUG
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
structlogger.log(
|
|
693
|
+
log_level,
|
|
694
|
+
"utils.llm.get_prompt_template"
|
|
695
|
+
".custom_prompt_template_read_successfully",
|
|
673
696
|
event_info=(
|
|
674
697
|
f"Custom prompt template read successfully from "
|
|
675
698
|
f"`{jinja_file_path}`."
|
|
676
699
|
),
|
|
677
700
|
prompt_file_path=jinja_file_path,
|
|
701
|
+
log_source_component=log_source_component,
|
|
702
|
+
log_source_method=log_source_method,
|
|
678
703
|
)
|
|
679
704
|
return prompt_template
|
|
680
705
|
except (FileIOException, FileNotFoundException):
|
|
681
706
|
structlogger.warning(
|
|
682
|
-
"utils.llm.get_prompt_template.failed_to_read_custom_prompt_template",
|
|
707
|
+
"utils.llm.get_prompt_template" ".failed_to_read_custom_prompt_template",
|
|
683
708
|
event_info=(
|
|
684
709
|
"Failed to read custom prompt template. Using default template instead."
|
|
685
710
|
),
|
|
711
|
+
log_source_component=log_source_component,
|
|
712
|
+
log_source_method=log_source_method,
|
|
686
713
|
)
|
|
687
714
|
return default_prompt_template
|
|
688
715
|
|
|
@@ -692,50 +719,66 @@ def get_default_prompt_template_based_on_model(
|
|
|
692
719
|
model_prompt_mapping: Dict[str, Any],
|
|
693
720
|
default_prompt_path: str,
|
|
694
721
|
fallback_prompt_path: str,
|
|
722
|
+
*,
|
|
723
|
+
log_source_component: Optional[Text] = None,
|
|
724
|
+
log_source_method: Optional[Literal["init", "fingerprint_addon"]] = None,
|
|
695
725
|
) -> Text:
|
|
696
726
|
"""Returns the default prompt template based on the model name.
|
|
697
727
|
|
|
698
728
|
Args:
|
|
699
729
|
llm_config: The model config.
|
|
700
|
-
model_prompt_mapping: The
|
|
701
|
-
default_prompt_path: The default prompt
|
|
702
|
-
fallback_prompt_path: The fallback prompt path for all other models
|
|
703
|
-
|
|
730
|
+
model_prompt_mapping: The model name -> prompt template mapping.
|
|
731
|
+
default_prompt_path: The path to the default prompt template for the component.
|
|
732
|
+
fallback_prompt_path: The fallback prompt path for all other models that do not
|
|
733
|
+
have a mapping in the model_prompt_mapping.
|
|
734
|
+
log_source_component: The name of the component emitting the log, used to
|
|
735
|
+
identify the source in structured logging.
|
|
736
|
+
log_source_method: The name of the method or function emitting the log for
|
|
737
|
+
better traceability.
|
|
704
738
|
|
|
705
739
|
Returns:
|
|
706
740
|
The default prompt template.
|
|
707
741
|
"""
|
|
742
|
+
# Extract the provider and model name information from the configuration
|
|
708
743
|
_llm_config = deepcopy(llm_config)
|
|
709
744
|
if MODELS_CONFIG_KEY in _llm_config:
|
|
710
745
|
_llm_config = _llm_config[MODELS_CONFIG_KEY][0]
|
|
711
746
|
provider = _llm_config.get(PROVIDER_CONFIG_KEY)
|
|
712
747
|
model = _llm_config.get(MODEL_CONFIG_KEY)
|
|
748
|
+
|
|
749
|
+
# If the model is not defined, we default to the default prompt template.
|
|
713
750
|
if not model:
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
"
|
|
751
|
+
structlogger.debug(
|
|
752
|
+
"utils.llm.get_default_prompt_template_based_on_model"
|
|
753
|
+
".using_default_prompt_template",
|
|
717
754
|
event_info=(
|
|
718
755
|
f"Model not defined in the config. Default prompt template read from"
|
|
719
756
|
f" - `{default_prompt_path}`."
|
|
720
757
|
),
|
|
721
758
|
default_prompt_path=default_prompt_path,
|
|
759
|
+
log_source_component=log_source_component,
|
|
760
|
+
log_source_method=log_source_method,
|
|
722
761
|
)
|
|
723
762
|
return importlib.resources.read_text(
|
|
724
763
|
DEFAULT_PROMPT_PACKAGE_NAME, default_prompt_path
|
|
725
764
|
)
|
|
726
765
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
766
|
+
full_model_name = model if provider and provider in model else f"{provider}/{model}"
|
|
767
|
+
|
|
768
|
+
# If the model is found in the mapping, we use the model-specific prompt
|
|
769
|
+
# template.
|
|
770
|
+
if prompt_file_path := model_prompt_mapping.get(full_model_name):
|
|
771
|
+
structlogger.debug(
|
|
772
|
+
"utils.llm.get_default_prompt_template_based_on_model"
|
|
773
|
+
".using_model_specific_prompt_template",
|
|
733
774
|
event_info=(
|
|
734
775
|
f"Using model-specific default prompt template. Default prompt "
|
|
735
776
|
f"template read from - `{prompt_file_path}`."
|
|
736
777
|
),
|
|
737
778
|
default_prompt_path=prompt_file_path,
|
|
738
|
-
model_name=
|
|
779
|
+
model_name=full_model_name,
|
|
780
|
+
log_source_component=log_source_component,
|
|
781
|
+
log_source_method=log_source_method,
|
|
739
782
|
)
|
|
740
783
|
return importlib.resources.read_text(
|
|
741
784
|
DEFAULT_PROMPT_PACKAGE_NAME, prompt_file_path
|
|
@@ -743,14 +786,17 @@ def get_default_prompt_template_based_on_model(
|
|
|
743
786
|
|
|
744
787
|
# If the model is not found in the mapping, we default to the fallback prompt
|
|
745
788
|
# template.
|
|
746
|
-
structlogger.
|
|
747
|
-
"utils.llm.get_default_prompt_template_based_on_model
|
|
789
|
+
structlogger.debug(
|
|
790
|
+
"utils.llm.get_default_prompt_template_based_on_model"
|
|
791
|
+
".using_fallback_prompt_template",
|
|
748
792
|
event_info=(
|
|
749
793
|
f"Model not found in the model prompt mapping. Fallback prompt template "
|
|
750
794
|
f"read from - `{fallback_prompt_path}`."
|
|
751
795
|
),
|
|
752
796
|
fallback_prompt_path=fallback_prompt_path,
|
|
753
|
-
model_name=
|
|
797
|
+
model_name=full_model_name,
|
|
798
|
+
log_source_component=log_source_component,
|
|
799
|
+
log_source_method=log_source_method,
|
|
754
800
|
)
|
|
755
801
|
return importlib.resources.read_text(
|
|
756
802
|
DEFAULT_PROMPT_PACKAGE_NAME, fallback_prompt_path
|
|
@@ -762,7 +808,7 @@ def allowed_values_for_slot(slot: Slot) -> Union[str, None]:
|
|
|
762
808
|
if isinstance(slot, BooleanSlot):
|
|
763
809
|
return str([True, False])
|
|
764
810
|
if isinstance(slot, CategoricalSlot):
|
|
765
|
-
return str([v for v in slot.values if v != "__other__"]
|
|
811
|
+
return str([v for v in slot.values if v != "__other__"])
|
|
766
812
|
else:
|
|
767
813
|
return None
|
|
768
814
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from rasa.shared.core.domain import (
|
|
5
|
+
Domain,
|
|
6
|
+
)
|
|
7
|
+
from rasa.shared.importers.importer import TrainingDataImporter
|
|
8
|
+
from rasa.studio.constants import STUDIO_DOMAIN_FILENAME
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def merge_domain_with_overwrite(
|
|
14
|
+
data_from_studio: TrainingDataImporter,
|
|
15
|
+
data_local: TrainingDataImporter,
|
|
16
|
+
domain_path: Path,
|
|
17
|
+
) -> None:
|
|
18
|
+
"""
|
|
19
|
+
Merges the domain from Rasa Studio with the local domain.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
data_from_studio: The training data importer for the Rasa Studio domain.
|
|
23
|
+
data_local: The training data importer for the local domain.
|
|
24
|
+
domain_path: The path to the domain file or directory.
|
|
25
|
+
"""
|
|
26
|
+
if domain_path.is_file():
|
|
27
|
+
all_local_domain_files = [str(domain_path)]
|
|
28
|
+
domain_path = domain_path.parent
|
|
29
|
+
else:
|
|
30
|
+
all_local_domain_files = data_local.get_domain_files([str(domain_path)])
|
|
31
|
+
|
|
32
|
+
# leftover_domain represents the items in the studio
|
|
33
|
+
# domain that are not in the local domain
|
|
34
|
+
leftover_domain = data_from_studio.get_user_domain()
|
|
35
|
+
for file_path in all_local_domain_files:
|
|
36
|
+
# For each local domain file, we do a partial merge
|
|
37
|
+
local_domain = Domain.from_file(str(file_path))
|
|
38
|
+
updated_local_domain = local_domain.partial_merge(leftover_domain)
|
|
39
|
+
|
|
40
|
+
# If this partial merge made changes, persist them
|
|
41
|
+
if local_domain != updated_local_domain:
|
|
42
|
+
updated_local_domain.persist(file_path)
|
|
43
|
+
|
|
44
|
+
# Remove every item now present in updated_local_domain from leftover_domain
|
|
45
|
+
leftover_domain = leftover_domain.difference(updated_local_domain)
|
|
46
|
+
|
|
47
|
+
# If there are still items in leftover_domain, persist them
|
|
48
|
+
if not leftover_domain.is_empty():
|
|
49
|
+
leftover_domain.persist(domain_path / STUDIO_DOMAIN_FILENAME)
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Dict, List, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
import questionary
|
|
6
|
+
import structlog
|
|
7
|
+
from ruamel import yaml
|
|
8
|
+
from ruamel.yaml.scalarstring import LiteralScalarString
|
|
9
|
+
|
|
10
|
+
import rasa.cli.utils
|
|
11
|
+
import rasa.shared.utils.cli
|
|
12
|
+
from rasa.shared.constants import (
|
|
13
|
+
DEFAULT_CONFIG_PATH,
|
|
14
|
+
DEFAULT_DATA_PATH,
|
|
15
|
+
DEFAULT_DOMAIN_PATH,
|
|
16
|
+
DEFAULT_DOMAIN_PATHS,
|
|
17
|
+
DEFAULT_ENDPOINTS_PATH,
|
|
18
|
+
)
|
|
19
|
+
from rasa.shared.core.flows.yaml_flows_io import YamlFlowsWriter
|
|
20
|
+
from rasa.shared.importers.importer import TrainingDataImporter
|
|
21
|
+
from rasa.shared.utils.yaml import read_yaml
|
|
22
|
+
from rasa.studio import data_handler
|
|
23
|
+
from rasa.studio.config import StudioConfig
|
|
24
|
+
from rasa.studio.constants import (
|
|
25
|
+
STUDIO_DOMAIN_FILENAME,
|
|
26
|
+
STUDIO_FLOWS_FILENAME,
|
|
27
|
+
STUDIO_NLU_FILENAME,
|
|
28
|
+
)
|
|
29
|
+
from rasa.studio.data_handler import StudioDataHandler, import_data_from_studio
|
|
30
|
+
from rasa.studio.download.domains import merge_domain_with_overwrite
|
|
31
|
+
from rasa.studio.download.flows import merge_flows_with_overwrite
|
|
32
|
+
from rasa.utils.mapper import RasaPrimitiveStorageMapper
|
|
33
|
+
|
|
34
|
+
structlogger = structlog.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def handle_download(args: argparse.Namespace) -> None:
|
|
38
|
+
"""Main function to handle downloading and merging data.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
args: The command line arguments.
|
|
42
|
+
"""
|
|
43
|
+
handler = StudioDataHandler(
|
|
44
|
+
studio_config=StudioConfig.read_config(),
|
|
45
|
+
assistant_name=args.assistant_name[0],
|
|
46
|
+
)
|
|
47
|
+
handler.request_all_data()
|
|
48
|
+
|
|
49
|
+
domain_path, data_paths = _prepare_data_and_domain_paths(args)
|
|
50
|
+
|
|
51
|
+
# Handle config and endpoints.
|
|
52
|
+
config_path, write_config = _handle_file_overwrite(
|
|
53
|
+
args.config, DEFAULT_CONFIG_PATH, "config"
|
|
54
|
+
)
|
|
55
|
+
endpoints_path, write_endpoints = _handle_file_overwrite(
|
|
56
|
+
args.endpoints, DEFAULT_ENDPOINTS_PATH, "endpoints"
|
|
57
|
+
)
|
|
58
|
+
message_parts = []
|
|
59
|
+
config_path = config_path if write_config else None
|
|
60
|
+
endpoints_path = endpoints_path if write_endpoints else None
|
|
61
|
+
|
|
62
|
+
if config_path:
|
|
63
|
+
config_data = handler.get_config()
|
|
64
|
+
if not config_data:
|
|
65
|
+
rasa.shared.utils.cli.print_error_and_exit("No config data found.")
|
|
66
|
+
with open(config_path, "w") as f:
|
|
67
|
+
f.write(config_data)
|
|
68
|
+
message_parts.append(f"config to '{config_path}'")
|
|
69
|
+
if endpoints_path:
|
|
70
|
+
endpoints_data = handler.get_endpoints()
|
|
71
|
+
if not endpoints_data:
|
|
72
|
+
raise ValueError("No endpoints data found.")
|
|
73
|
+
with open(endpoints_path, "w") as f:
|
|
74
|
+
f.write(endpoints_data)
|
|
75
|
+
message_parts.append(f"endpoints to '{endpoints_path}'")
|
|
76
|
+
if message_parts:
|
|
77
|
+
message = "Downloaded " + " and ".join(message_parts)
|
|
78
|
+
structlogger.info("studio.download.config_endpoints", event_info=message)
|
|
79
|
+
|
|
80
|
+
if not args.overwrite:
|
|
81
|
+
_handle_download_no_overwrite(handler, domain_path, data_paths)
|
|
82
|
+
else:
|
|
83
|
+
_handle_download_with_overwrite(handler, domain_path, data_paths)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _prepare_data_and_domain_paths(args: argparse.Namespace) -> Tuple[Path, List[Path]]:
|
|
87
|
+
"""Prepars the domain and data paths based on the provided arguments.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
args: The command line arguments.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
A tuple containing the domain path and a list of data paths.
|
|
94
|
+
"""
|
|
95
|
+
# Prepare domain path.
|
|
96
|
+
domain_path = rasa.cli.utils.get_validated_path(
|
|
97
|
+
args.domain, "domain", DEFAULT_DOMAIN_PATHS, none_is_valid=True
|
|
98
|
+
)
|
|
99
|
+
domain_or_default_path = args.domain or DEFAULT_DOMAIN_PATH
|
|
100
|
+
|
|
101
|
+
if domain_path is None:
|
|
102
|
+
domain_path = Path(domain_or_default_path)
|
|
103
|
+
domain_path.touch()
|
|
104
|
+
|
|
105
|
+
if isinstance(domain_path, str):
|
|
106
|
+
domain_path = Path(domain_path)
|
|
107
|
+
|
|
108
|
+
if domain_path.is_file():
|
|
109
|
+
if not args.overwrite:
|
|
110
|
+
domain_path.unlink()
|
|
111
|
+
domain_path.touch()
|
|
112
|
+
|
|
113
|
+
if domain_path.is_dir():
|
|
114
|
+
if not args.overwrite:
|
|
115
|
+
domain_path = domain_path / STUDIO_DOMAIN_FILENAME
|
|
116
|
+
domain_path.touch()
|
|
117
|
+
|
|
118
|
+
# Prepare data paths.
|
|
119
|
+
data_paths: List[Path] = []
|
|
120
|
+
for f in args.data:
|
|
121
|
+
data_path = rasa.cli.utils.get_validated_path(
|
|
122
|
+
f, "data", DEFAULT_DATA_PATH, none_is_valid=True
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
if data_path is None:
|
|
126
|
+
data_path = Path(f)
|
|
127
|
+
data_path.mkdir(parents=True, exist_ok=True)
|
|
128
|
+
else:
|
|
129
|
+
data_path = Path(data_path)
|
|
130
|
+
|
|
131
|
+
if data_path.is_file() or data_path.is_dir():
|
|
132
|
+
data_paths.append(data_path)
|
|
133
|
+
else:
|
|
134
|
+
data_path.mkdir(parents=True, exist_ok=True)
|
|
135
|
+
data_paths.append(data_path)
|
|
136
|
+
|
|
137
|
+
# Remove duplicates while preserving order.
|
|
138
|
+
data_paths = list(dict.fromkeys(data_paths))
|
|
139
|
+
return domain_path, data_paths
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _handle_file_overwrite(
|
|
143
|
+
file_path: Optional[str], default_path: str, file_type: str
|
|
144
|
+
) -> Tuple[Optional[Path], bool]:
|
|
145
|
+
"""Determines whether to overwrite an existing file or create a new one.
|
|
146
|
+
|
|
147
|
+
Works for config and endpoints at this moment.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
file_path: The path to the file.
|
|
151
|
+
default_path: The default path to use if no file path is provided.
|
|
152
|
+
file_type: The type of the file (e.g., config, endpoints).
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
A tuple of the file path and a boolean indicating overwrite status.
|
|
156
|
+
"""
|
|
157
|
+
file_already_exists = rasa.cli.utils.get_validated_path(
|
|
158
|
+
file_path, file_type, default_path, none_is_valid=True
|
|
159
|
+
)
|
|
160
|
+
write_file = False
|
|
161
|
+
path = None
|
|
162
|
+
file_or_default_path = file_path or default_path
|
|
163
|
+
|
|
164
|
+
if file_already_exists is None:
|
|
165
|
+
path = Path(file_or_default_path)
|
|
166
|
+
if path.is_dir():
|
|
167
|
+
path = path / default_path
|
|
168
|
+
return path, True
|
|
169
|
+
|
|
170
|
+
if questionary.confirm(
|
|
171
|
+
f"{file_type.capitalize()} file '{file_or_default_path}' already exists. "
|
|
172
|
+
f"Do you want to overwrite it?"
|
|
173
|
+
).ask():
|
|
174
|
+
write_file = True
|
|
175
|
+
path = Path(file_or_default_path)
|
|
176
|
+
return path, write_file
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _handle_download_no_overwrite(
|
|
180
|
+
handler: StudioDataHandler, domain_path: Path, data_paths: List[Path]
|
|
181
|
+
) -> None:
|
|
182
|
+
"""Handles downloading without overwriting existing files.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
handler: The StudioDataHandler instance.
|
|
186
|
+
domain_path: The path to the domain file or directory.
|
|
187
|
+
data_paths: The paths to the data files or directories.
|
|
188
|
+
"""
|
|
189
|
+
data_from_studio, data_local = import_data_from_studio(
|
|
190
|
+
handler, domain_path, data_paths
|
|
191
|
+
)
|
|
192
|
+
_merge_domain_no_overwrite(domain_path, data_from_studio, data_local)
|
|
193
|
+
_merge_data_no_overwrite(data_paths, handler, data_from_studio, data_local)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _merge_domain_no_overwrite(
|
|
197
|
+
domain_path: Path,
|
|
198
|
+
data_from_studio: TrainingDataImporter,
|
|
199
|
+
data_local: TrainingDataImporter,
|
|
200
|
+
) -> None:
|
|
201
|
+
"""
|
|
202
|
+
Merges the domain data without overwriting.
|
|
203
|
+
|
|
204
|
+
If the domain path is a directory, a new domain file is created under that folder.
|
|
205
|
+
If the domain path is a file, it merges both domains into that file.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
domain_path: The path to the domain file or directory.
|
|
209
|
+
data_from_studio: The Studio data importer.
|
|
210
|
+
data_local: The local data importer.
|
|
211
|
+
"""
|
|
212
|
+
if domain_path.is_dir():
|
|
213
|
+
_merge_directory_domain(domain_path, data_from_studio, data_local)
|
|
214
|
+
elif domain_path.is_file():
|
|
215
|
+
_merge_file_domain(domain_path, data_from_studio, data_local)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _merge_directory_domain(
|
|
219
|
+
domain_path: Path,
|
|
220
|
+
data_from_studio: TrainingDataImporter,
|
|
221
|
+
data_local: TrainingDataImporter,
|
|
222
|
+
) -> None:
|
|
223
|
+
"""Merges domain data without overwriting when the domain is a directory.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
domain_path: The path to the domain directory.
|
|
227
|
+
data_from_studio: The Studio data importer.
|
|
228
|
+
data_local: The local data importer.
|
|
229
|
+
"""
|
|
230
|
+
from rasa.shared.core.domain import Domain
|
|
231
|
+
|
|
232
|
+
studio_domain_path = domain_path / STUDIO_NLU_FILENAME
|
|
233
|
+
new_domain_data = data_handler.combine_domains(
|
|
234
|
+
data_from_studio.get_user_domain().as_dict(),
|
|
235
|
+
data_local.get_user_domain().as_dict(),
|
|
236
|
+
)
|
|
237
|
+
studio_domain = Domain.from_dict(new_domain_data)
|
|
238
|
+
|
|
239
|
+
if not studio_domain.is_empty():
|
|
240
|
+
studio_domain.persist(studio_domain_path)
|
|
241
|
+
else:
|
|
242
|
+
structlogger.warning(
|
|
243
|
+
"studio.download.merge_directory_domain",
|
|
244
|
+
event_info="No additional domain data found in Studio assistant.",
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _merge_file_domain(
|
|
249
|
+
domain_path: Path,
|
|
250
|
+
data_from_studio: TrainingDataImporter,
|
|
251
|
+
data_local: TrainingDataImporter,
|
|
252
|
+
) -> None:
|
|
253
|
+
"""Merges domain data without overwriting when the domain is a file.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
domain_path: The path to the domain file.
|
|
257
|
+
data_from_studio: The Studio data importer.
|
|
258
|
+
data_local: The local data importer.
|
|
259
|
+
"""
|
|
260
|
+
domain_merged = data_local.get_user_domain().merge(
|
|
261
|
+
data_from_studio.get_user_domain()
|
|
262
|
+
)
|
|
263
|
+
domain_merged.persist(domain_path)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _merge_data_no_overwrite(
|
|
267
|
+
data_paths: List[Path],
|
|
268
|
+
handler: StudioDataHandler,
|
|
269
|
+
data_from_studio: TrainingDataImporter,
|
|
270
|
+
data_local: TrainingDataImporter,
|
|
271
|
+
) -> None:
|
|
272
|
+
"""Merges NLU and flow data without overwriting existing data.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
data_paths: The paths to the data files or directories.
|
|
276
|
+
handler: The StudioDataHandler instance.
|
|
277
|
+
data_from_studio: The Studio data importer.
|
|
278
|
+
data_local: The local data importer.
|
|
279
|
+
"""
|
|
280
|
+
if not data_paths:
|
|
281
|
+
structlogger.warning(
|
|
282
|
+
"studio.download.merge_data_no_overwrite.no_path",
|
|
283
|
+
event_info="No data paths provided. Skipping data merge.",
|
|
284
|
+
)
|
|
285
|
+
return
|
|
286
|
+
|
|
287
|
+
if len(data_paths) == 1:
|
|
288
|
+
data_path = data_paths[0]
|
|
289
|
+
if data_path.is_file():
|
|
290
|
+
_merge_file_data_no_overwrite(
|
|
291
|
+
data_path, handler, data_from_studio, data_local
|
|
292
|
+
)
|
|
293
|
+
elif data_path.is_dir():
|
|
294
|
+
_merge_dir_data_no_overwrite(
|
|
295
|
+
data_path, handler, data_from_studio, data_local
|
|
296
|
+
)
|
|
297
|
+
else:
|
|
298
|
+
structlogger.warning(
|
|
299
|
+
"studio.download.merge_data_no_overwrite.invalid_path",
|
|
300
|
+
event_info=(
|
|
301
|
+
f"Provided path '{data_path}' is neither a file nor a directory."
|
|
302
|
+
),
|
|
303
|
+
)
|
|
304
|
+
else:
|
|
305
|
+
# TODO: Handle multiple data paths.
|
|
306
|
+
raise NotImplementedError("Multiple data paths are not supported yet.")
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _merge_file_data_no_overwrite(
|
|
310
|
+
data_path: Path,
|
|
311
|
+
handler: StudioDataHandler,
|
|
312
|
+
data_from_studio: TrainingDataImporter,
|
|
313
|
+
data_local: TrainingDataImporter,
|
|
314
|
+
) -> None:
|
|
315
|
+
"""Merges NLU and flows data into a single file without overwriting.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
data_path: Path to the single data file.
|
|
319
|
+
handler: The StudioDataHandler instance.
|
|
320
|
+
data_from_studio: The Studio data importer.
|
|
321
|
+
data_local: The local data importer.
|
|
322
|
+
"""
|
|
323
|
+
if handler.has_nlu():
|
|
324
|
+
merged_nlu = data_local.get_nlu_data().merge(data_from_studio.get_nlu_data())
|
|
325
|
+
merged_nlu.persist_nlu(str(data_path))
|
|
326
|
+
if handler.has_flows():
|
|
327
|
+
merged_flows = data_local.get_user_flows().merge(
|
|
328
|
+
data_from_studio.get_user_flows()
|
|
329
|
+
)
|
|
330
|
+
YamlFlowsWriter.dump(merged_flows.underlying_flows, data_path)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _merge_dir_data_no_overwrite(
|
|
334
|
+
dir_path: Path,
|
|
335
|
+
handler: StudioDataHandler,
|
|
336
|
+
data_from_studio: TrainingDataImporter,
|
|
337
|
+
data_local: TrainingDataImporter,
|
|
338
|
+
) -> None:
|
|
339
|
+
"""Merges NLU and flows data into a single directory without overwriting.
|
|
340
|
+
|
|
341
|
+
Args:
|
|
342
|
+
dir_path: Path to the data directory.
|
|
343
|
+
handler: The StudioDataHandler instance.
|
|
344
|
+
data_from_studio: The Studio data importer.
|
|
345
|
+
data_local: The local data importer.
|
|
346
|
+
"""
|
|
347
|
+
if handler.has_nlu():
|
|
348
|
+
nlu_path = dir_path / Path(STUDIO_NLU_FILENAME)
|
|
349
|
+
_persist_nlu_diff(data_local, data_from_studio, nlu_path)
|
|
350
|
+
if handler.has_flows():
|
|
351
|
+
flows_path = dir_path / Path(STUDIO_FLOWS_FILENAME)
|
|
352
|
+
_persist_flows_diff(data_local, data_from_studio, flows_path)
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def _handle_download_with_overwrite(
|
|
356
|
+
handler: StudioDataHandler, domain_path: Path, data_paths: List[Path]
|
|
357
|
+
) -> None:
|
|
358
|
+
"""Handles downloading and merging data when the user opts for overwrite.
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
handler: The StudioDataHandler instance.
|
|
362
|
+
domain_path: The path to the domain file or directory.
|
|
363
|
+
data_paths: The paths to the data files or directories.
|
|
364
|
+
"""
|
|
365
|
+
data_from_studio, data_local = import_data_from_studio(
|
|
366
|
+
handler, domain_path, data_paths
|
|
367
|
+
)
|
|
368
|
+
mapper = RasaPrimitiveStorageMapper(
|
|
369
|
+
domain_path=domain_path, training_data_paths=data_paths
|
|
370
|
+
)
|
|
371
|
+
merge_domain_with_overwrite(data_from_studio, data_local, domain_path)
|
|
372
|
+
merge_flows_with_overwrite(
|
|
373
|
+
data_paths, handler, data_from_studio, data_local, mapper
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def _persist_nlu_diff(
|
|
378
|
+
data_local: TrainingDataImporter,
|
|
379
|
+
data_from_studio: TrainingDataImporter,
|
|
380
|
+
data_path: Path,
|
|
381
|
+
) -> None:
|
|
382
|
+
"""Creates a new NLU file from the diff of local and studio data.
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
data_local: The local training data.
|
|
386
|
+
data_from_studio: The training data from Rasa Studio.
|
|
387
|
+
data_path: The path to the NLU file.
|
|
388
|
+
"""
|
|
389
|
+
new_nlu_data = data_handler.create_new_nlu_from_diff(
|
|
390
|
+
read_yaml(data_from_studio.get_nlu_data().nlu_as_yaml()),
|
|
391
|
+
read_yaml(data_local.get_nlu_data().nlu_as_yaml()),
|
|
392
|
+
)
|
|
393
|
+
if new_nlu_data["nlu"]:
|
|
394
|
+
pretty_write_nlu_yaml(new_nlu_data, data_path)
|
|
395
|
+
else:
|
|
396
|
+
structlogger.warning(
|
|
397
|
+
"studio.download.persist_nlu_diff",
|
|
398
|
+
event_info="No additional nlu data found.",
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _persist_flows_diff(
|
|
403
|
+
data_local: TrainingDataImporter,
|
|
404
|
+
data_from_studio: TrainingDataImporter,
|
|
405
|
+
data_path: Path,
|
|
406
|
+
) -> None:
|
|
407
|
+
"""Creates a new flows file from the diff of local and studio data.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
data_local: The local training data.
|
|
411
|
+
data_from_studio: The training data from Rasa Studio.
|
|
412
|
+
data_path: The path to the flows file.
|
|
413
|
+
"""
|
|
414
|
+
new_flows_data = data_handler.create_new_flows_from_diff(
|
|
415
|
+
data_from_studio.get_user_flows().underlying_flows,
|
|
416
|
+
data_local.get_user_flows().underlying_flows,
|
|
417
|
+
)
|
|
418
|
+
if new_flows_data:
|
|
419
|
+
YamlFlowsWriter.dump(new_flows_data, data_path)
|
|
420
|
+
else:
|
|
421
|
+
structlogger.warning(
|
|
422
|
+
"studio.download.persist_flows_diff",
|
|
423
|
+
event_info="No additional flows data found.",
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def pretty_write_nlu_yaml(data: Dict, file: Path) -> None:
|
|
428
|
+
"""Writes the NLU YAML in a pretty way.
|
|
429
|
+
|
|
430
|
+
Args:
|
|
431
|
+
data: The data to write.
|
|
432
|
+
file: The file to write to.
|
|
433
|
+
"""
|
|
434
|
+
dumper = yaml.YAML()
|
|
435
|
+
for item in data["nlu"]:
|
|
436
|
+
if item.get("examples"):
|
|
437
|
+
item["examples"] = LiteralScalarString(item["examples"])
|
|
438
|
+
with file.open("w", encoding="utf-8") as outfile:
|
|
439
|
+
dumper.dump(data, outfile)
|