rasa-pro 3.13.0.dev20250613__py3-none-any.whl → 3.13.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rasa-pro might be problematic. Click here for more details.
- rasa/cli/e2e_test.py +0 -7
- rasa/cli/export.py +2 -0
- rasa/cli/project_templates/tutorial/config.yml +1 -1
- rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
- rasa/cli/studio/download.py +1 -23
- rasa/cli/studio/link.py +1 -2
- rasa/cli/studio/pull.py +3 -2
- rasa/cli/studio/push.py +1 -1
- rasa/cli/studio/train.py +0 -1
- rasa/core/channels/__init__.py +2 -0
- rasa/core/channels/development_inspector.py +1 -1
- rasa/core/channels/facebook.py +1 -4
- rasa/core/channels/inspector/README.md +3 -3
- rasa/core/channels/inspector/dist/assets/{arc-c4b064fc.js → arc-371401b1.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-215b5026.js → blockDiagram-38ab4fdb-3f126156.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-2b54a0a3.js → c4Diagram-3d4e48cf-12f22eb7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-f1efda17.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-daacea5f.js → classDiagram-70f12bd4-03b1d386.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-930d4dc2.js → classDiagram-v2-f2320105-84f69d63.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-fdf164e2.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-83c206ba.js → createText-2e5e7dd3-ca47fd38.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-b0eb01d0.js → edges-e0da2a9e-f837ca8a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-17586500.js → erDiagram-9861fffd-8717ac54.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-be2a1776.js → flowDb-956e92f1-94f38b83.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-c2120ebd.js → flowDiagram-66a62f08-b616f9fb.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-7d7a1629.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-a6ab5c48.js → flowchart-elk-definition-4a651766-f5d24bb8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-ef613457.js → ganttDiagram-c361ad54-b43ba8d9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-d59185b3.js → gitGraphDiagram-72cf32ee-c3aafaa5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-0f155405.js → graph-0d0a2c10.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3862675e-d5f1d1b7.js → index-3862675e-58ea0305.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-47737d3a.js → index-cce6f8a1.js} +3 -3
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-b07d141f.js → infoDiagram-f8f76790-b8f60461.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-1936d429.js → journeyDiagram-49397b02-95be5545.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-dde8d0f3.js → layout-da885b9b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-0c2c7ee0.js → line-f1c817d3.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-35dd89a4.js → linear-d42801e6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-56192851.js → mindmap-definition-fc14e90a-a38923a6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-fc21ed78.js → pieDiagram-8a3498a8-ca6e71e9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-25e98518.js → quadrantDiagram-120e2f19-b290dae9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-546ff1f5.js → requirementDiagram-deff3bca-03f02ceb.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-02d8b82d.js → sankeyDiagram-04a897e0-c49eee40.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-3ca5a92e.js → sequenceDiagram-704730f1-b2cd6a3d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-128ea07c.js → stateDiagram-587899a1-e53a2028.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-95f290af.js → stateDiagram-v2-d93cdb3a-e1982a03.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-4984898a.js → styles-6aaf32cf-d0226ca5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-1bf266ba.js → styles-9a916d00-0e21dc00.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-60521c63.js → styles-c10674c1-9588494e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-a25b6e12.js → svgDrawCommon-08f97a94-be478d4f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-0fc086bf.js → timeline-definition-85554ec2-74631749.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-44ee592e.js → xychartDiagram-e933f94c-a043552f.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +1 -1
- rasa/core/channels/socketio.py +56 -41
- rasa/core/channels/studio_chat.py +311 -8
- rasa/core/channels/voice_ready/audiocodes.py +1 -1
- rasa/core/channels/voice_stream/asr/azure.py +9 -0
- rasa/core/channels/voice_stream/audiocodes.py +1 -1
- rasa/core/channels/voice_stream/browser_audio.py +1 -1
- rasa/core/channels/voice_stream/jambonz.py +166 -0
- rasa/core/channels/voice_stream/tts/__init__.py +8 -0
- rasa/core/channels/voice_stream/twilio_media_streams.py +7 -0
- rasa/core/channels/voice_stream/voice_channel.py +14 -5
- rasa/core/exporter.py +36 -0
- rasa/core/information_retrieval/faiss.py +18 -11
- rasa/core/information_retrieval/ingestion/faq_parser.py +158 -0
- rasa/core/nlg/contextual_response_rephraser.py +10 -1
- rasa/core/policies/enterprise_search_policy.py +152 -262
- rasa/core/policies/enterprise_search_policy_config.py +241 -0
- rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +6 -5
- rasa/core/policies/intentless_policy.py +47 -10
- rasa/core/utils.py +11 -2
- rasa/dialogue_understanding/coexistence/llm_based_router.py +9 -18
- rasa/dialogue_understanding/commands/__init__.py +4 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +4 -2
- rasa/dialogue_understanding/commands/clarify_command.py +2 -2
- rasa/dialogue_understanding/commands/correct_slots_command.py +5 -6
- rasa/dialogue_understanding/commands/error_command.py +1 -1
- rasa/dialogue_understanding/commands/human_handoff_command.py +1 -3
- rasa/dialogue_understanding/commands/set_slot_command.py +4 -4
- rasa/dialogue_understanding/commands/skip_question_command.py +1 -3
- rasa/dialogue_understanding/commands/start_flow_command.py +3 -3
- rasa/dialogue_understanding/generator/command_generator.py +11 -1
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +2 -2
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +0 -2
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +1 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +79 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +1 -0
- rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +2 -2
- rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +2 -18
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +8 -11
- rasa/dialogue_understanding/patterns/cancel.py +1 -2
- rasa/dialogue_understanding/patterns/clarify.py +1 -1
- rasa/dialogue_understanding/patterns/correction.py +2 -2
- rasa/dialogue_understanding/processor/command_processor.py +8 -9
- rasa/dialogue_understanding/stack/utils.py +3 -1
- rasa/e2e_test/e2e_test_coverage_report.py +1 -1
- rasa/engine/graph.py +2 -2
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +1 -5
- rasa/shared/constants.py +12 -0
- rasa/shared/core/command_payload_reader.py +1 -5
- rasa/shared/core/events.py +1 -3
- rasa/shared/core/flows/constants.py +2 -0
- rasa/shared/core/flows/flow.py +126 -12
- rasa/shared/core/flows/flows_list.py +18 -1
- rasa/shared/core/flows/steps/link.py +7 -2
- rasa/shared/core/flows/validation.py +25 -5
- rasa/shared/core/training_data/story_reader/yaml_story_reader.py +1 -4
- rasa/shared/providers/_configs/azure_openai_client_config.py +2 -2
- rasa/shared/providers/_configs/default_litellm_client_config.py +1 -1
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +1 -1
- rasa/shared/providers/_configs/openai_client_config.py +1 -1
- rasa/shared/providers/_configs/rasa_llm_client_config.py +1 -1
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -1
- rasa/shared/providers/_configs/utils.py +0 -99
- rasa/shared/utils/common.py +1 -1
- rasa/shared/utils/configs.py +110 -0
- rasa/shared/utils/constants.py +0 -3
- rasa/shared/utils/llm.py +37 -6
- rasa/shared/utils/pykwalify_extensions.py +0 -9
- rasa/studio/constants.py +1 -0
- rasa/studio/data_handler.py +8 -1
- rasa/studio/download.py +167 -0
- rasa/studio/link.py +1 -1
- rasa/studio/prompts.py +223 -0
- rasa/studio/pull/__init__.py +0 -0
- rasa/studio/{download/flows.py → pull/data.py} +2 -131
- rasa/studio/{download → pull}/domains.py +1 -1
- rasa/studio/pull/pull.py +235 -0
- rasa/studio/push.py +5 -0
- rasa/studio/train.py +1 -1
- rasa/tracing/instrumentation/attribute_extractors.py +20 -6
- rasa/utils/common.py +11 -0
- rasa/version.py +1 -1
- {rasa_pro-3.13.0.dev20250613.dist-info → rasa_pro-3.13.0rc1.dist-info}/METADATA +4 -4
- {rasa_pro-3.13.0.dev20250613.dist-info → rasa_pro-3.13.0rc1.dist-info}/RECORD +141 -134
- rasa/core/channels/inspector/dist/assets/channel-3730f5fd.js +0 -1
- rasa/core/channels/inspector/dist/assets/clone-e847561e.js +0 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-efbbfe00.js +0 -1
- rasa/studio/download/download.py +0 -416
- rasa/studio/pull.py +0 -94
- /rasa/{studio/download → core/information_retrieval/ingestion}/__init__.py +0 -0
- {rasa_pro-3.13.0.dev20250613.dist-info → rasa_pro-3.13.0rc1.dist-info}/NOTICE +0 -0
- {rasa_pro-3.13.0.dev20250613.dist-info → rasa_pro-3.13.0rc1.dist-info}/WHEEL +0 -0
- {rasa_pro-3.13.0.dev20250613.dist-info → rasa_pro-3.13.0rc1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import structlog
|
|
4
|
+
|
|
5
|
+
from rasa.shared.utils.io import raise_deprecation_warning
|
|
6
|
+
|
|
7
|
+
structlogger = structlog.get_logger()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def resolve_aliases(config: dict, deprecated_alias_mapping: dict) -> dict:
|
|
11
|
+
"""
|
|
12
|
+
Resolve aliases in the configuration to standard keys.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
config: Dictionary containing the configuration.
|
|
16
|
+
deprecated_alias_mapping: Dictionary mapping aliases to
|
|
17
|
+
their standard keys.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
New dictionary containing the processed configuration.
|
|
21
|
+
"""
|
|
22
|
+
config = config.copy()
|
|
23
|
+
|
|
24
|
+
for alias, standard_key in deprecated_alias_mapping.items():
|
|
25
|
+
# We check for the alias instead of the standard key because our goal is to
|
|
26
|
+
# update the standard key when the alias is found. Since the standard key is
|
|
27
|
+
# always included in the default component configurations, we overwrite it
|
|
28
|
+
# with the alias value if the alias exists.
|
|
29
|
+
if alias in config:
|
|
30
|
+
config[standard_key] = config.pop(alias)
|
|
31
|
+
|
|
32
|
+
return config
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def raise_deprecation_warnings(
|
|
36
|
+
config: dict,
|
|
37
|
+
deprecated_alias_mapping: dict,
|
|
38
|
+
source: Optional[str] = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Raises warnings for deprecated keys in the configuration.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
config: Dictionary containing the configuration.
|
|
45
|
+
deprecated_alias_mapping: Dictionary mapping deprecated keys to
|
|
46
|
+
their standard keys.
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
DeprecationWarning: If any deprecated key is found in the config.
|
|
50
|
+
"""
|
|
51
|
+
for alias, standard_key in deprecated_alias_mapping.items():
|
|
52
|
+
if alias in config:
|
|
53
|
+
source = f"{source}: " or ""
|
|
54
|
+
raise_deprecation_warning(
|
|
55
|
+
message=(
|
|
56
|
+
f"{source}"
|
|
57
|
+
f"'{alias}' is deprecated and will be removed in "
|
|
58
|
+
f"4.0.0. Use '{standard_key}' instead."
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def validate_required_keys(config: dict, required_keys: list) -> None:
|
|
64
|
+
"""
|
|
65
|
+
Validates that the passed config contains all the required keys.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
config: Dictionary containing the configuration.
|
|
69
|
+
required_keys: List of keys that must be present in the config.
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
ValueError: If any required key is missing.
|
|
73
|
+
"""
|
|
74
|
+
missing_keys = [key for key in required_keys if key not in config]
|
|
75
|
+
if missing_keys:
|
|
76
|
+
message = f"Missing required keys '{missing_keys}' for configuration."
|
|
77
|
+
structlogger.error(
|
|
78
|
+
"validate_required_keys",
|
|
79
|
+
message=message,
|
|
80
|
+
missing_keys=missing_keys,
|
|
81
|
+
config=config,
|
|
82
|
+
)
|
|
83
|
+
raise ValueError(message)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def validate_forbidden_keys(config: dict, forbidden_keys: list) -> None:
|
|
87
|
+
"""
|
|
88
|
+
Validates that the passed config doesn't contain any forbidden keys.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
config: Dictionary containing the configuration.
|
|
92
|
+
forbidden_keys: List of keys that are forbidden in the config.
|
|
93
|
+
|
|
94
|
+
Raises:
|
|
95
|
+
ValueError: If any forbidden key is present.
|
|
96
|
+
"""
|
|
97
|
+
forbidden_keys_in_config = set(config.keys()).intersection(set(forbidden_keys))
|
|
98
|
+
|
|
99
|
+
if forbidden_keys_in_config:
|
|
100
|
+
message = (
|
|
101
|
+
f"Forbidden keys '{forbidden_keys_in_config}' present "
|
|
102
|
+
f"in the configuration."
|
|
103
|
+
)
|
|
104
|
+
structlogger.error(
|
|
105
|
+
"validate_forbidden_keys",
|
|
106
|
+
message=message,
|
|
107
|
+
forbidden_keys=forbidden_keys_in_config,
|
|
108
|
+
config=config,
|
|
109
|
+
)
|
|
110
|
+
raise ValueError(message)
|
rasa/shared/utils/constants.py
CHANGED
|
@@ -2,9 +2,6 @@ DEFAULT_ENCODING = "utf-8"
|
|
|
2
2
|
|
|
3
3
|
READ_YAML_FILE_CACHE_MAXSIZE_ENV_VAR = "READ_YAML_FILE_CACHE_MAXSIZE"
|
|
4
4
|
DEFAULT_READ_YAML_FILE_CACHE_MAXSIZE = 256
|
|
5
|
-
RASA_PRO_BETA_PREDICATES_IN_RESPONSE_CONDITIONS_ENV_VAR_NAME = (
|
|
6
|
-
"RASA_PRO_BETA_PREDICATES_IN_RESPONSE_CONDITIONS"
|
|
7
|
-
)
|
|
8
5
|
|
|
9
6
|
LOG_COMPONENT_SOURCE_METHOD_INIT = "init"
|
|
10
7
|
LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON = "fingerprint_addon"
|
rasa/shared/utils/llm.py
CHANGED
|
@@ -37,6 +37,8 @@ from rasa.shared.constants import (
|
|
|
37
37
|
MODEL_GROUP_ID_CONFIG_KEY,
|
|
38
38
|
MODEL_GROUPS_CONFIG_KEY,
|
|
39
39
|
MODELS_CONFIG_KEY,
|
|
40
|
+
PROMPT_CONFIG_KEY,
|
|
41
|
+
PROMPT_TEMPLATE_CONFIG_KEY,
|
|
40
42
|
PROVIDER_CONFIG_KEY,
|
|
41
43
|
RASA_PATTERN_INTERNAL_ERROR_USER_INPUT_EMPTY,
|
|
42
44
|
RASA_PATTERN_INTERNAL_ERROR_USER_INPUT_TOO_LONG,
|
|
@@ -85,13 +87,15 @@ USER = "USER"
|
|
|
85
87
|
|
|
86
88
|
AI = "AI"
|
|
87
89
|
|
|
88
|
-
DEFAULT_OPENAI_GENERATE_MODEL_NAME = "gpt-
|
|
90
|
+
DEFAULT_OPENAI_GENERATE_MODEL_NAME = "gpt-4o-2024-11-20"
|
|
89
91
|
|
|
90
|
-
DEFAULT_OPENAI_CHAT_MODEL_NAME = "gpt-
|
|
92
|
+
DEFAULT_OPENAI_CHAT_MODEL_NAME = "gpt-4o-2024-11-20"
|
|
93
|
+
|
|
94
|
+
DEFAULT_ENTERPRISE_SEARCH_POLICY_MODEL_NAME = "gpt-4.1-mini-2025-04-14"
|
|
91
95
|
|
|
92
96
|
DEFAULT_OPENAI_CHAT_MODEL_NAME_ADVANCED = "gpt-4-0613"
|
|
93
97
|
|
|
94
|
-
DEFAULT_OPENAI_EMBEDDING_MODEL_NAME = "text-embedding-
|
|
98
|
+
DEFAULT_OPENAI_EMBEDDING_MODEL_NAME = "text-embedding-3-large"
|
|
95
99
|
|
|
96
100
|
DEFAULT_OPENAI_TEMPERATURE = 0.7
|
|
97
101
|
|
|
@@ -950,6 +954,34 @@ async def create_tracker_for_user_step(
|
|
|
950
954
|
await agent.tracker_store.save(tracker)
|
|
951
955
|
|
|
952
956
|
|
|
957
|
+
def check_prompt_config_keys_and_warn_if_deprecated(
|
|
958
|
+
config: dict, component_source: str
|
|
959
|
+
) -> None:
|
|
960
|
+
"""Checks and warns about deprecated config parameters."""
|
|
961
|
+
if PROMPT_CONFIG_KEY in config and PROMPT_TEMPLATE_CONFIG_KEY in config:
|
|
962
|
+
structlogger.warning(
|
|
963
|
+
f"{component_source}.init"
|
|
964
|
+
".both_deprecated_and_non_deprecated_config_keys_used_at_the_same_time",
|
|
965
|
+
event_info=(
|
|
966
|
+
f"Both '{PROMPT_CONFIG_KEY}' and '{PROMPT_TEMPLATE_CONFIG_KEY}' "
|
|
967
|
+
f"are present in the config. '{PROMPT_CONFIG_KEY}' will be ignored "
|
|
968
|
+
f"in favor of {PROMPT_TEMPLATE_CONFIG_KEY}."
|
|
969
|
+
),
|
|
970
|
+
)
|
|
971
|
+
|
|
972
|
+
# 'prompt' config key is deprecated in favor of 'prompt_template'
|
|
973
|
+
if PROMPT_CONFIG_KEY in config:
|
|
974
|
+
structlogger.warning(
|
|
975
|
+
f"{component_source}.init.deprecated_config_key",
|
|
976
|
+
event_info=(
|
|
977
|
+
f"The config parameter '{PROMPT_CONFIG_KEY}' is deprecated "
|
|
978
|
+
"and will be removed in Rasa 4.0.0. "
|
|
979
|
+
f"Please use the config parameter '{PROMPT_TEMPLATE_CONFIG_KEY}'"
|
|
980
|
+
f" instead. "
|
|
981
|
+
),
|
|
982
|
+
)
|
|
983
|
+
|
|
984
|
+
|
|
953
985
|
def _get_llm_command_generator_config(
|
|
954
986
|
config: Dict[Text, Any],
|
|
955
987
|
) -> Optional[Dict[Text, Any]]:
|
|
@@ -994,7 +1026,7 @@ def _get_command_generator_prompt(
|
|
|
994
1026
|
model_groups=endpoints.get(MODEL_GROUPS_CONFIG_KEY),
|
|
995
1027
|
)
|
|
996
1028
|
return get_default_prompt_template_based_on_model(
|
|
997
|
-
llm_config=llm_config,
|
|
1029
|
+
llm_config=llm_config or {},
|
|
998
1030
|
model_prompt_mapping=MODEL_PROMPT_MAPPER,
|
|
999
1031
|
default_prompt_path=DEFAULT_COMMAND_PROMPT_TEMPLATE_FILE_NAME,
|
|
1000
1032
|
fallback_prompt_path=FALLBACK_COMMAND_PROMPT_TEMPLATE_FILE_NAME,
|
|
@@ -1022,8 +1054,7 @@ def _get_enterprise_search_prompt(config: Dict[Text, Any]) -> Text:
|
|
|
1022
1054
|
def get_system_default_prompts(
|
|
1023
1055
|
config: Dict[Text, Any], endpoints: Dict[Text, Any]
|
|
1024
1056
|
) -> SystemPrompts:
|
|
1025
|
-
"""
|
|
1026
|
-
Returns the system default prompts for the component.
|
|
1057
|
+
"""Returns the system default prompts for the component.
|
|
1027
1058
|
|
|
1028
1059
|
Args:
|
|
1029
1060
|
config: The config.yml file data.
|
|
@@ -8,11 +8,6 @@ from typing import Any, Dict, List, Text, Union
|
|
|
8
8
|
|
|
9
9
|
from pykwalify.errors import SchemaError
|
|
10
10
|
|
|
11
|
-
from rasa.shared.utils.constants import (
|
|
12
|
-
RASA_PRO_BETA_PREDICATES_IN_RESPONSE_CONDITIONS_ENV_VAR_NAME,
|
|
13
|
-
)
|
|
14
|
-
from rasa.utils.beta import ensure_beta_feature_is_enabled
|
|
15
|
-
|
|
16
11
|
|
|
17
12
|
def require_response_keys(
|
|
18
13
|
responses: List[Dict[Text, Any]], _: Dict, __: Text
|
|
@@ -31,10 +26,6 @@ def require_response_keys(
|
|
|
31
26
|
|
|
32
27
|
conditions = response.get("condition", [])
|
|
33
28
|
if isinstance(conditions, str):
|
|
34
|
-
ensure_beta_feature_is_enabled(
|
|
35
|
-
"predicates in response conditions",
|
|
36
|
-
RASA_PRO_BETA_PREDICATES_IN_RESPONSE_CONDITIONS_ENV_VAR_NAME,
|
|
37
|
-
)
|
|
38
29
|
continue
|
|
39
30
|
|
|
40
31
|
for condition in conditions:
|
rasa/studio/constants.py
CHANGED
|
@@ -14,6 +14,7 @@ RASA_STUDIO_CLI_DISABLE_VERIFY_KEY_ENV = "RASA_STUDIO_CLI_DISABLE_VERIFY_KEY"
|
|
|
14
14
|
|
|
15
15
|
STUDIO_NLU_FILENAME = "studio_nlu.yml"
|
|
16
16
|
STUDIO_DOMAIN_FILENAME = "studio_domain.yml"
|
|
17
|
+
DOMAIN_FILENAME = "domain.yml"
|
|
17
18
|
STUDIO_FLOWS_FILENAME = "studio_flows.yml"
|
|
18
19
|
STUDIO_CONFIG_FILENAME = "studio_config.yml"
|
|
19
20
|
STUDIO_ENDPOINTS_FILENAME = "studio_endpoints.yml"
|
rasa/studio/data_handler.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import base64
|
|
2
|
+
import json
|
|
2
3
|
import logging
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from typing import Any, Dict, List, Optional, Tuple
|
|
@@ -50,7 +51,7 @@ class StudioDataHandler:
|
|
|
50
51
|
"query ExportAsEncodedYaml($input: ExportAsEncodedYamlInput!) "
|
|
51
52
|
"{ exportAsEncodedYaml(input: $input) "
|
|
52
53
|
"{ ... on ExportModernAsEncodedYamlOutput "
|
|
53
|
-
"{ nlu flows domain endpoints config } "
|
|
54
|
+
"{ nlu flows domain endpoints config prompts } "
|
|
54
55
|
"... on ExportClassicAsEncodedYamlOutput "
|
|
55
56
|
"{ nlu domain }}}"
|
|
56
57
|
),
|
|
@@ -161,6 +162,9 @@ class StudioDataHandler:
|
|
|
161
162
|
def get_endpoints(self) -> Optional[str]:
|
|
162
163
|
return self.endpoints
|
|
163
164
|
|
|
165
|
+
def get_prompts(self) -> Optional[dict]:
|
|
166
|
+
return self.prompts
|
|
167
|
+
|
|
164
168
|
def _validate_response(self, response: dict) -> bool:
|
|
165
169
|
"""Validates the response from Rasa Studio.
|
|
166
170
|
|
|
@@ -200,6 +204,9 @@ class StudioDataHandler:
|
|
|
200
204
|
self.config = self._decode_response(return_data.get("config"))
|
|
201
205
|
self.endpoints = self._decode_response(return_data.get("endpoints"))
|
|
202
206
|
|
|
207
|
+
prompts_string = self._decode_response(return_data.get("prompts"))
|
|
208
|
+
self.prompts = json.loads(prompts_string) if prompts_string else None
|
|
209
|
+
|
|
203
210
|
if not self.has_nlu() and not self.has_flows():
|
|
204
211
|
raise RasaException("No nlu or flows data in Studio response.")
|
|
205
212
|
|
rasa/studio/download.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import shutil
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Dict
|
|
5
|
+
|
|
6
|
+
import questionary
|
|
7
|
+
import structlog
|
|
8
|
+
from ruamel import yaml
|
|
9
|
+
from ruamel.yaml.scalarstring import LiteralScalarString
|
|
10
|
+
|
|
11
|
+
import rasa.cli.utils
|
|
12
|
+
import rasa.shared.utils.cli
|
|
13
|
+
from rasa.shared.constants import (
|
|
14
|
+
DEFAULT_CONFIG_PATH,
|
|
15
|
+
DEFAULT_DATA_PATH,
|
|
16
|
+
DEFAULT_ENDPOINTS_PATH,
|
|
17
|
+
)
|
|
18
|
+
from rasa.shared.core.flows.yaml_flows_io import FlowsList
|
|
19
|
+
from rasa.shared.nlu.training_data.training_data import (
|
|
20
|
+
DEFAULT_TRAINING_DATA_OUTPUT_PATH,
|
|
21
|
+
)
|
|
22
|
+
from rasa.shared.utils.yaml import read_yaml, write_yaml
|
|
23
|
+
from rasa.studio.config import StudioConfig
|
|
24
|
+
from rasa.studio.constants import DOMAIN_FILENAME
|
|
25
|
+
from rasa.studio.data_handler import StudioDataHandler
|
|
26
|
+
from rasa.studio.prompts import handle_prompts
|
|
27
|
+
from rasa.studio.pull.data import _dump_flows_as_separate_files
|
|
28
|
+
|
|
29
|
+
structlogger = structlog.get_logger()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def handle_download(args: argparse.Namespace) -> None:
|
|
33
|
+
"""Download an assistant from Studio and store it in `<assistant_name>/`.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
args: The command line arguments.
|
|
37
|
+
"""
|
|
38
|
+
assistant_name = args.assistant_name
|
|
39
|
+
target_root = _prepare_target_directory(assistant_name)
|
|
40
|
+
|
|
41
|
+
handler = StudioDataHandler(
|
|
42
|
+
studio_config=StudioConfig.read_config(), assistant_name=assistant_name
|
|
43
|
+
)
|
|
44
|
+
handler.request_all_data()
|
|
45
|
+
|
|
46
|
+
_handle_config(handler, target_root)
|
|
47
|
+
_handle_endpoints(handler, target_root)
|
|
48
|
+
_handle_domain(handler, target_root)
|
|
49
|
+
_handle_data(handler, target_root)
|
|
50
|
+
handle_prompts(handler, target_root)
|
|
51
|
+
|
|
52
|
+
structlogger.info(
|
|
53
|
+
"studio.download.success",
|
|
54
|
+
event_info=f"Downloaded assistant '{assistant_name}' from Studio.",
|
|
55
|
+
assistant_name=assistant_name,
|
|
56
|
+
)
|
|
57
|
+
rasa.shared.utils.cli.print_success(
|
|
58
|
+
f"Downloaded assistant '{assistant_name}' from Studio."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _prepare_target_directory(assistant_name: str) -> Path:
|
|
63
|
+
"""Create (or overwrite) the directory where everything is stored.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
assistant_name: The name of the assistant to download.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
The path to the target directory where the assistant will be stored.
|
|
70
|
+
"""
|
|
71
|
+
target_root = Path(assistant_name)
|
|
72
|
+
|
|
73
|
+
if target_root.exists():
|
|
74
|
+
overwrite = questionary.confirm(
|
|
75
|
+
f"Directory '{assistant_name}' already exists. Overwrite it?"
|
|
76
|
+
).ask()
|
|
77
|
+
if not overwrite:
|
|
78
|
+
rasa.shared.utils.cli.print_error_and_exit("Download cancelled.")
|
|
79
|
+
|
|
80
|
+
shutil.rmtree(target_root)
|
|
81
|
+
|
|
82
|
+
target_root.mkdir(parents=True, exist_ok=True)
|
|
83
|
+
return target_root
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _handle_config(handler: StudioDataHandler, root: Path) -> None:
|
|
87
|
+
"""Download and persist the assistant’s config file.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
handler: The data handler to retrieve the config from.
|
|
91
|
+
root: The root directory where the config file will be stored.
|
|
92
|
+
"""
|
|
93
|
+
config_data = handler.get_config()
|
|
94
|
+
if not config_data:
|
|
95
|
+
rasa.shared.utils.cli.print_error_and_exit("No config data found.")
|
|
96
|
+
|
|
97
|
+
config_path = root / DEFAULT_CONFIG_PATH
|
|
98
|
+
config_path.write_text(config_data, encoding="utf-8")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _handle_endpoints(handler: StudioDataHandler, root: Path) -> None:
|
|
102
|
+
"""Download and persist the assistant’s endpoints file.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
handler: The data handler to retrieve the endpoints from.
|
|
106
|
+
root: The root directory where the endpoints file will be stored.
|
|
107
|
+
"""
|
|
108
|
+
endpoints_data = handler.get_endpoints()
|
|
109
|
+
if not endpoints_data:
|
|
110
|
+
rasa.shared.utils.cli.print_error_and_exit("No endpoints data found.")
|
|
111
|
+
|
|
112
|
+
endpoints_path = root / DEFAULT_ENDPOINTS_PATH
|
|
113
|
+
endpoints_path.write_text(endpoints_data, encoding="utf-8")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _handle_domain(handler: StudioDataHandler, root: Path) -> None:
|
|
117
|
+
"""Persist the assistant’s domain file.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
handler: The data handler to retrieve the domain from.
|
|
121
|
+
root: The root directory where the domain file will be stored.
|
|
122
|
+
"""
|
|
123
|
+
domain_yaml = handler.domain
|
|
124
|
+
data = read_yaml(domain_yaml)
|
|
125
|
+
target = root / DOMAIN_FILENAME
|
|
126
|
+
write_yaml(
|
|
127
|
+
data=data,
|
|
128
|
+
target=target,
|
|
129
|
+
should_preserve_key_order=True,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _handle_data(handler: StudioDataHandler, root: Path) -> None:
|
|
134
|
+
"""Persist NLU data and flows.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
handler: The data handler to retrieve the NLU data and flows from.
|
|
138
|
+
root: The root directory where the NLU data and flows will be stored.
|
|
139
|
+
"""
|
|
140
|
+
data_path = root / DEFAULT_DATA_PATH
|
|
141
|
+
data_path.mkdir(parents=True, exist_ok=True)
|
|
142
|
+
|
|
143
|
+
if handler.has_nlu():
|
|
144
|
+
nlu_yaml = handler.nlu
|
|
145
|
+
nlu_data = read_yaml(nlu_yaml)
|
|
146
|
+
if nlu_data.get("nlu"):
|
|
147
|
+
pretty_write_nlu_yaml(
|
|
148
|
+
nlu_data, data_path / DEFAULT_TRAINING_DATA_OUTPUT_PATH
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
if handler.has_flows():
|
|
152
|
+
flows_yaml = handler.flows
|
|
153
|
+
data = read_yaml(flows_yaml)
|
|
154
|
+
flows_data = data.get("flows", {})
|
|
155
|
+
flows_list = FlowsList.from_json(flows_data)
|
|
156
|
+
_dump_flows_as_separate_files(flows_list.underlying_flows, data_path)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def pretty_write_nlu_yaml(data: Dict, file: Path) -> None:
|
|
160
|
+
"""Writes the NLU YAML in a pretty way."""
|
|
161
|
+
dumper = yaml.YAML()
|
|
162
|
+
if nlu_data := data.get("nlu"):
|
|
163
|
+
for item in nlu_data:
|
|
164
|
+
if item.get("examples"):
|
|
165
|
+
item["examples"] = LiteralScalarString(item["examples"])
|
|
166
|
+
with file.open("w", encoding="utf-8") as outfile:
|
|
167
|
+
dumper.dump(data, outfile)
|
rasa/studio/link.py
CHANGED
|
@@ -167,7 +167,7 @@ def handle_link(args: argparse.Namespace) -> None:
|
|
|
167
167
|
Args:
|
|
168
168
|
args: The command line arguments.
|
|
169
169
|
"""
|
|
170
|
-
assistant_name: Text = args.assistant_name
|
|
170
|
+
assistant_name: Text = args.assistant_name
|
|
171
171
|
studio_cfg = get_studio_config()
|
|
172
172
|
assistant_exists = _ensure_assistant_exists(assistant_name, studio_cfg, args)
|
|
173
173
|
if not assistant_exists:
|
rasa/studio/prompts.py
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Dict, List, Optional, Text
|
|
3
|
+
|
|
4
|
+
import structlog
|
|
5
|
+
|
|
6
|
+
from rasa.core.policies.enterprise_search_policy import EnterpriseSearchPolicy
|
|
7
|
+
from rasa.dialogue_understanding.generator.llm_based_command_generator import (
|
|
8
|
+
LLMBasedCommandGenerator,
|
|
9
|
+
)
|
|
10
|
+
from rasa.shared.constants import (
|
|
11
|
+
CONFIG_NAME_KEY,
|
|
12
|
+
CONFIG_PIPELINE_KEY,
|
|
13
|
+
CONFIG_POLICIES_KEY,
|
|
14
|
+
DEFAULT_CONFIG_PATH,
|
|
15
|
+
DEFAULT_ENDPOINTS_PATH,
|
|
16
|
+
DEFAULT_PROMPTS_PATH,
|
|
17
|
+
PROMPT_CONFIG_KEY,
|
|
18
|
+
PROMPT_TEMPLATE_CONFIG_KEY,
|
|
19
|
+
)
|
|
20
|
+
from rasa.shared.utils.common import all_subclasses
|
|
21
|
+
from rasa.shared.utils.llm import get_system_default_prompts
|
|
22
|
+
from rasa.shared.utils.yaml import read_yaml, write_yaml
|
|
23
|
+
from rasa.studio.data_handler import StudioDataHandler
|
|
24
|
+
|
|
25
|
+
structlogger = structlog.get_logger()
|
|
26
|
+
|
|
27
|
+
CONTEXTUAL_RESPONSE_REPHRASER_NAME = "contextual_response_rephraser"
|
|
28
|
+
COMMAND_GENERATOR_NAME = "command_generator"
|
|
29
|
+
ENTERPRISE_SEARCH_NAME = "enterprise_search"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def handle_prompts(handler: StudioDataHandler, root: Path) -> None:
|
|
33
|
+
"""Handle prompts for the assistant.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
handler: The data handler to retrieve prompts from.
|
|
37
|
+
root: The root directory where the prompts should be saved.
|
|
38
|
+
"""
|
|
39
|
+
prompts = handler.get_prompts()
|
|
40
|
+
if not prompts:
|
|
41
|
+
return
|
|
42
|
+
|
|
43
|
+
config_path = root / DEFAULT_CONFIG_PATH
|
|
44
|
+
endpoints_path = root / DEFAULT_ENDPOINTS_PATH
|
|
45
|
+
config: Dict = read_yaml(config_path)
|
|
46
|
+
endpoints: Dict = read_yaml(endpoints_path)
|
|
47
|
+
|
|
48
|
+
system_prompts = get_system_default_prompts(config, endpoints)
|
|
49
|
+
|
|
50
|
+
_handle_contextual_response_rephraser(
|
|
51
|
+
root,
|
|
52
|
+
prompts.get(CONTEXTUAL_RESPONSE_REPHRASER_NAME),
|
|
53
|
+
system_prompts.contextual_response_rephraser,
|
|
54
|
+
endpoints,
|
|
55
|
+
)
|
|
56
|
+
_handle_command_generator(
|
|
57
|
+
root,
|
|
58
|
+
prompts.get(COMMAND_GENERATOR_NAME),
|
|
59
|
+
system_prompts.command_generator,
|
|
60
|
+
config,
|
|
61
|
+
)
|
|
62
|
+
_handle_enterprise_search(
|
|
63
|
+
root,
|
|
64
|
+
prompts.get(ENTERPRISE_SEARCH_NAME),
|
|
65
|
+
system_prompts.enterprise_search,
|
|
66
|
+
config,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _handle_contextual_response_rephraser(
|
|
71
|
+
root: Path,
|
|
72
|
+
prompt_content: Optional[Text],
|
|
73
|
+
system_prompt: Optional[Text],
|
|
74
|
+
endpoints: Dict,
|
|
75
|
+
) -> None:
|
|
76
|
+
"""Handles the contextual response rephraser prompt.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
root: The root directory where the prompt file will be saved.
|
|
80
|
+
prompt_content: The content of the contextual response rephraser prompt.
|
|
81
|
+
system_prompt: The system prompt for comparison.
|
|
82
|
+
endpoints: The endpoints configuration to update with the prompt path.
|
|
83
|
+
"""
|
|
84
|
+
if not _is_custom_prompt(prompt_content, system_prompt):
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
prompt_path = _save_prompt_file(
|
|
88
|
+
root, f"{CONTEXTUAL_RESPONSE_REPHRASER_NAME}.jinja", prompt_content
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
endpoints["nlg"] = endpoints.get("nlg") or {}
|
|
92
|
+
endpoints["nlg"]["prompt"] = str(prompt_path)
|
|
93
|
+
|
|
94
|
+
endpoints_path = root / DEFAULT_ENDPOINTS_PATH
|
|
95
|
+
write_yaml(data=endpoints, target=endpoints_path, should_preserve_key_order=True)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _handle_command_generator(
|
|
99
|
+
root: Path,
|
|
100
|
+
prompt_content: Optional[Text],
|
|
101
|
+
system_prompt: Optional[Text],
|
|
102
|
+
config: Dict,
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Handles the command generator prompt.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
root: The root directory where the prompt file will be saved.
|
|
108
|
+
prompt_content: The content of the command generator prompt.
|
|
109
|
+
system_prompt: The system prompt for comparison.
|
|
110
|
+
config: The configuration dictionary to update with the prompt path.
|
|
111
|
+
"""
|
|
112
|
+
if not _is_custom_prompt(prompt_content, system_prompt):
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
prompt_path = _save_prompt_file(
|
|
116
|
+
root, f"{COMMAND_GENERATOR_NAME}.jinja", prompt_content
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
command_generator_names: List[str] = [
|
|
120
|
+
cls.__name__ for cls in all_subclasses(LLMBasedCommandGenerator)
|
|
121
|
+
]
|
|
122
|
+
_add_prompt_to_config(
|
|
123
|
+
config=config,
|
|
124
|
+
section_key=CONFIG_PIPELINE_KEY,
|
|
125
|
+
component_names=command_generator_names,
|
|
126
|
+
prompt_key=PROMPT_TEMPLATE_CONFIG_KEY,
|
|
127
|
+
prompt_path=str(prompt_path),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
config_path = root / DEFAULT_CONFIG_PATH
|
|
131
|
+
write_yaml(data=config, target=config_path, should_preserve_key_order=True)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _handle_enterprise_search(
|
|
135
|
+
root: Path,
|
|
136
|
+
prompt_content: Optional[Text],
|
|
137
|
+
system_prompt: Optional[Text],
|
|
138
|
+
config: Dict,
|
|
139
|
+
) -> None:
|
|
140
|
+
"""Handles the enterprise search prompt.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
root: The root directory where the prompt file will be saved.
|
|
144
|
+
prompt_content: The content of the enterprise search prompt.
|
|
145
|
+
system_prompt: The system prompt for comparison.
|
|
146
|
+
config: The configuration dictionary to update with the prompt path.
|
|
147
|
+
"""
|
|
148
|
+
if not _is_custom_prompt(prompt_content, system_prompt):
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
prompt_path = _save_prompt_file(
|
|
152
|
+
root, f"{ENTERPRISE_SEARCH_NAME}.jinja", prompt_content
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
_add_prompt_to_config(
|
|
156
|
+
config=config,
|
|
157
|
+
section_key=CONFIG_POLICIES_KEY,
|
|
158
|
+
component_names=[EnterpriseSearchPolicy.__name__],
|
|
159
|
+
prompt_key=PROMPT_CONFIG_KEY,
|
|
160
|
+
prompt_path=str(prompt_path),
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
config_path = root / DEFAULT_CONFIG_PATH
|
|
164
|
+
write_yaml(data=config, target=config_path, should_preserve_key_order=True)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _is_custom_prompt(
|
|
168
|
+
studio_prompt: Optional[Text], system_prompt: Optional[Text]
|
|
169
|
+
) -> bool:
|
|
170
|
+
"""Check if the prompt has been customized in Studio.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
studio_prompt: The prompt content from the Studio.
|
|
174
|
+
system_prompt: The default system prompt content.
|
|
175
|
+
"""
|
|
176
|
+
return bool(studio_prompt and studio_prompt != system_prompt)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _save_prompt_file(root: Path, filename: str, content: str) -> Path:
|
|
180
|
+
"""Save a prompt file to the specified root directory.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
root: The root directory where the prompt file will be saved.
|
|
184
|
+
filename: The name of the prompt file.
|
|
185
|
+
content: The content of the prompt.
|
|
186
|
+
"""
|
|
187
|
+
prompts_dir = root / DEFAULT_PROMPTS_PATH
|
|
188
|
+
prompts_dir.mkdir(parents=True, exist_ok=True)
|
|
189
|
+
|
|
190
|
+
file_path = prompts_dir / filename
|
|
191
|
+
file_path.write_text(content, encoding="utf-8")
|
|
192
|
+
|
|
193
|
+
return file_path.relative_to(root)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _add_prompt_to_config(
|
|
197
|
+
*,
|
|
198
|
+
config: Dict,
|
|
199
|
+
section_key: str,
|
|
200
|
+
component_names: List[str],
|
|
201
|
+
prompt_key: str,
|
|
202
|
+
prompt_path: str,
|
|
203
|
+
) -> None:
|
|
204
|
+
"""Add a prompt path to the specified section of the configuration."""
|
|
205
|
+
matches = [
|
|
206
|
+
component
|
|
207
|
+
for component in config.get(section_key, [])
|
|
208
|
+
if component.get(CONFIG_NAME_KEY) in component_names
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
if not matches:
|
|
212
|
+
return
|
|
213
|
+
|
|
214
|
+
# Update the first occurrence of the component.
|
|
215
|
+
matches[0][prompt_key] = prompt_path
|
|
216
|
+
|
|
217
|
+
if len(matches) > 1:
|
|
218
|
+
structlogger.warning(
|
|
219
|
+
"rasa.studio.prompts.add_prompt_to_config.multiple_components",
|
|
220
|
+
event_info=(
|
|
221
|
+
"Multiple components found in the configuration for the same prompt."
|
|
222
|
+
),
|
|
223
|
+
)
|
|
File without changes
|