rasa-pro 3.13.0.dev7__py3-none-any.whl → 3.13.0.dev9__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.

Files changed (215) hide show
  1. rasa/__main__.py +0 -3
  2. rasa/api.py +1 -1
  3. rasa/cli/dialogue_understanding_test.py +1 -1
  4. rasa/cli/e2e_test.py +1 -1
  5. rasa/cli/evaluate.py +1 -1
  6. rasa/cli/export.py +1 -1
  7. rasa/cli/llm_fine_tuning.py +12 -11
  8. rasa/cli/project_templates/defaults.py +133 -0
  9. rasa/cli/run.py +1 -1
  10. rasa/cli/studio/link.py +53 -0
  11. rasa/cli/studio/pull.py +78 -0
  12. rasa/cli/studio/push.py +78 -0
  13. rasa/cli/studio/studio.py +12 -0
  14. rasa/cli/studio/upload.py +8 -0
  15. rasa/cli/train.py +1 -1
  16. rasa/cli/utils.py +1 -1
  17. rasa/cli/x.py +1 -1
  18. rasa/constants.py +2 -0
  19. rasa/core/__init__.py +0 -16
  20. rasa/core/actions/action.py +5 -1
  21. rasa/core/actions/action_repeat_bot_messages.py +18 -22
  22. rasa/core/actions/action_run_slot_rejections.py +0 -1
  23. rasa/core/agent.py +16 -1
  24. rasa/core/available_endpoints.py +146 -0
  25. rasa/core/brokers/pika.py +1 -2
  26. rasa/core/channels/botframework.py +2 -2
  27. rasa/core/channels/channel.py +2 -2
  28. rasa/core/channels/development_inspector.py +1 -1
  29. rasa/core/channels/facebook.py +1 -4
  30. rasa/core/channels/hangouts.py +8 -5
  31. rasa/core/channels/inspector/README.md +3 -3
  32. rasa/core/channels/inspector/dist/assets/{arc-c4b064fc.js → arc-02053cc1.js} +1 -1
  33. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-215b5026.js → blockDiagram-38ab4fdb-008b6289.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-2b54a0a3.js → c4Diagram-3d4e48cf-fb2597be.js} +1 -1
  35. rasa/core/channels/inspector/dist/assets/channel-078dada8.js +1 -0
  36. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-daacea5f.js → classDiagram-70f12bd4-7f847e00.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-930d4dc2.js → classDiagram-v2-f2320105-ba1d689b.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/clone-5b4516de.js +1 -0
  39. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-83c206ba.js → createText-2e5e7dd3-dd8e67c4.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-b0eb01d0.js → edges-e0da2a9e-10784939.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-17586500.js → erDiagram-9861fffd-24947ae6.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-be2a1776.js → flowDb-956e92f1-a9ced505.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-c2120ebd.js → flowDiagram-66a62f08-afda9c7c.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-f9613071.js +1 -0
  45. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-a6ab5c48.js → flowchart-elk-definition-4a651766-6ef530b8.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-ef613457.js → ganttDiagram-c361ad54-0c7dd39a.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-d59185b3.js → gitGraphDiagram-72cf32ee-b57239d6.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{graph-0f155405.js → graph-9ed57cec.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/{index-3862675e-d5f1d1b7.js → index-3862675e-233090de.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{index-47737d3a.js → index-72184470.js} +3 -3
  51. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-b07d141f.js → infoDiagram-f8f76790-aa116649.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-1936d429.js → journeyDiagram-49397b02-e51877cc.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{layout-dde8d0f3.js → layout-3ca3798c.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{line-0c2c7ee0.js → line-26ee10d3.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{linear-35dd89a4.js → linear-aedded32.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-56192851.js → mindmap-definition-fc14e90a-d8957261.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-fc21ed78.js → pieDiagram-8a3498a8-d771f885.js} +1 -1
  58. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-25e98518.js → quadrantDiagram-120e2f19-09fdf50c.js} +1 -1
  59. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-546ff1f5.js → requirementDiagram-deff3bca-9f0af02e.js} +1 -1
  60. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-02d8b82d.js → sankeyDiagram-04a897e0-84415b37.js} +1 -1
  61. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-3ca5a92e.js → sequenceDiagram-704730f1-8dec4055.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-128ea07c.js → stateDiagram-587899a1-c5431d07.js} +1 -1
  63. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-95f290af.js → stateDiagram-v2-d93cdb3a-274e77d9.js} +1 -1
  64. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-4984898a.js → styles-6aaf32cf-e364a1d7.js} +1 -1
  65. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-1bf266ba.js → styles-9a916d00-0dae36f6.js} +1 -1
  66. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-60521c63.js → styles-c10674c1-c4641675.js} +1 -1
  67. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-a25b6e12.js → svgDrawCommon-08f97a94-831fe9a1.js} +1 -1
  68. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-0fc086bf.js → timeline-definition-85554ec2-c3304b3a.js} +1 -1
  69. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-44ee592e.js → xychartDiagram-e933f94c-da799369.js} +1 -1
  70. rasa/core/channels/inspector/dist/index.html +1 -1
  71. rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +1 -1
  72. rasa/core/channels/mattermost.py +1 -1
  73. rasa/core/channels/rasa_chat.py +2 -4
  74. rasa/core/channels/rest.py +5 -4
  75. rasa/core/channels/socketio.py +56 -41
  76. rasa/core/channels/studio_chat.py +314 -10
  77. rasa/core/channels/vier_cvg.py +1 -2
  78. rasa/core/channels/voice_ready/audiocodes.py +2 -9
  79. rasa/core/channels/voice_stream/audiocodes.py +8 -5
  80. rasa/core/channels/voice_stream/browser_audio.py +1 -1
  81. rasa/core/channels/voice_stream/genesys.py +2 -2
  82. rasa/core/channels/voice_stream/tts/__init__.py +8 -0
  83. rasa/core/channels/voice_stream/twilio_media_streams.py +10 -5
  84. rasa/core/channels/voice_stream/voice_channel.py +39 -23
  85. rasa/core/http_interpreter.py +3 -7
  86. rasa/core/information_retrieval/faiss.py +18 -11
  87. rasa/core/information_retrieval/ingestion/__init__.py +0 -0
  88. rasa/core/information_retrieval/ingestion/faq_parser.py +158 -0
  89. rasa/core/jobs.py +2 -1
  90. rasa/core/nlg/contextual_response_rephraser.py +44 -10
  91. rasa/core/nlg/generator.py +0 -1
  92. rasa/core/nlg/interpolator.py +2 -3
  93. rasa/core/nlg/summarize.py +39 -5
  94. rasa/core/policies/enterprise_search_policy.py +262 -62
  95. rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +63 -0
  96. rasa/core/policies/flow_policy.py +1 -1
  97. rasa/core/policies/flows/flow_executor.py +96 -17
  98. rasa/core/policies/intentless_policy.py +56 -17
  99. rasa/core/processor.py +104 -51
  100. rasa/core/run.py +33 -11
  101. rasa/core/tracker_stores/tracker_store.py +1 -1
  102. rasa/core/training/interactive.py +1 -1
  103. rasa/core/utils.py +24 -97
  104. rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
  105. rasa/dialogue_understanding/coexistence/llm_based_router.py +9 -6
  106. rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
  107. rasa/dialogue_understanding/commands/cancel_flow_command.py +5 -1
  108. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
  109. rasa/dialogue_understanding/commands/clarify_command.py +5 -1
  110. rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
  111. rasa/dialogue_understanding/commands/correct_slots_command.py +1 -3
  112. rasa/dialogue_understanding/commands/human_handoff_command.py +2 -0
  113. rasa/dialogue_understanding/commands/knowledge_answer_command.py +4 -2
  114. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
  115. rasa/dialogue_understanding/commands/set_slot_command.py +11 -1
  116. rasa/dialogue_understanding/commands/skip_question_command.py +2 -0
  117. rasa/dialogue_understanding/commands/start_flow_command.py +4 -0
  118. rasa/dialogue_understanding/commands/utils.py +26 -2
  119. rasa/dialogue_understanding/generator/__init__.py +7 -1
  120. rasa/dialogue_understanding/generator/command_generator.py +4 -2
  121. rasa/dialogue_understanding/generator/command_parser.py +2 -2
  122. rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
  123. rasa/dialogue_understanding/generator/nlu_command_adapter.py +2 -2
  124. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +12 -33
  125. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +78 -0
  126. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +26 -461
  127. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
  128. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +477 -0
  129. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +11 -64
  130. rasa/dialogue_understanding/patterns/cancel.py +1 -2
  131. rasa/dialogue_understanding/patterns/clarify.py +1 -1
  132. rasa/dialogue_understanding/patterns/correction.py +2 -2
  133. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +37 -25
  134. rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
  135. rasa/dialogue_understanding/processor/command_processor.py +6 -7
  136. rasa/dialogue_understanding/processor/command_processor_component.py +3 -3
  137. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +17 -4
  138. rasa/dialogue_understanding/stack/utils.py +3 -1
  139. rasa/dialogue_understanding/utils.py +68 -12
  140. rasa/dialogue_understanding_test/du_test_case.py +1 -1
  141. rasa/dialogue_understanding_test/du_test_runner.py +4 -22
  142. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -6
  143. rasa/e2e_test/e2e_test_runner.py +1 -1
  144. rasa/engine/constants.py +1 -1
  145. rasa/engine/graph.py +2 -2
  146. rasa/engine/recipes/default_recipe.py +26 -2
  147. rasa/engine/validation.py +3 -2
  148. rasa/hooks.py +0 -28
  149. rasa/llm_fine_tuning/annotation_module.py +39 -9
  150. rasa/llm_fine_tuning/conversations.py +3 -0
  151. rasa/llm_fine_tuning/llm_data_preparation_module.py +66 -49
  152. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +1 -5
  153. rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +52 -44
  154. rasa/llm_fine_tuning/paraphrasing_module.py +10 -12
  155. rasa/llm_fine_tuning/storage.py +4 -4
  156. rasa/llm_fine_tuning/utils.py +63 -1
  157. rasa/model_manager/model_api.py +88 -0
  158. rasa/model_manager/trainer_service.py +4 -4
  159. rasa/plugin.py +1 -11
  160. rasa/privacy/__init__.py +0 -0
  161. rasa/privacy/constants.py +83 -0
  162. rasa/privacy/event_broker_utils.py +77 -0
  163. rasa/privacy/privacy_config.py +281 -0
  164. rasa/privacy/privacy_config_schema.json +86 -0
  165. rasa/privacy/privacy_filter.py +340 -0
  166. rasa/privacy/privacy_manager.py +576 -0
  167. rasa/server.py +23 -2
  168. rasa/shared/constants.py +14 -0
  169. rasa/shared/core/command_payload_reader.py +1 -5
  170. rasa/shared/core/constants.py +4 -3
  171. rasa/shared/core/domain.py +7 -0
  172. rasa/shared/core/events.py +38 -10
  173. rasa/shared/core/flows/flow.py +1 -2
  174. rasa/shared/core/flows/flows_yaml_schema.json +3 -0
  175. rasa/shared/core/flows/steps/collect.py +46 -2
  176. rasa/shared/core/flows/validation.py +16 -3
  177. rasa/shared/core/slots.py +28 -0
  178. rasa/shared/core/training_data/story_reader/yaml_story_reader.py +1 -4
  179. rasa/shared/exceptions.py +4 -0
  180. rasa/shared/utils/common.py +1 -1
  181. rasa/shared/utils/llm.py +191 -6
  182. rasa/shared/utils/yaml.py +32 -0
  183. rasa/studio/data_handler.py +3 -3
  184. rasa/studio/download/download.py +37 -60
  185. rasa/studio/download/flows.py +23 -31
  186. rasa/studio/link.py +200 -0
  187. rasa/studio/pull.py +94 -0
  188. rasa/studio/push.py +131 -0
  189. rasa/studio/upload.py +117 -67
  190. rasa/telemetry.py +82 -25
  191. rasa/tracing/config.py +3 -4
  192. rasa/tracing/constants.py +19 -1
  193. rasa/tracing/instrumentation/attribute_extractors.py +10 -2
  194. rasa/tracing/instrumentation/instrumentation.py +53 -2
  195. rasa/tracing/instrumentation/metrics.py +98 -15
  196. rasa/tracing/metric_instrument_provider.py +75 -3
  197. rasa/utils/common.py +1 -27
  198. rasa/utils/log_utils.py +1 -45
  199. rasa/validator.py +2 -8
  200. rasa/version.py +1 -1
  201. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/METADATA +7 -8
  202. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/RECORD +205 -189
  203. rasa/anonymization/__init__.py +0 -2
  204. rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
  205. rasa/anonymization/anonymization_pipeline.py +0 -286
  206. rasa/anonymization/anonymization_rule_executor.py +0 -266
  207. rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
  208. rasa/anonymization/schemas/config.yml +0 -47
  209. rasa/anonymization/utils.py +0 -118
  210. rasa/core/channels/inspector/dist/assets/channel-3730f5fd.js +0 -1
  211. rasa/core/channels/inspector/dist/assets/clone-e847561e.js +0 -1
  212. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-efbbfe00.js +0 -1
  213. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/NOTICE +0 -0
  214. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/WHEEL +0 -0
  215. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/entry_points.txt +0 -0
rasa/studio/link.py ADDED
@@ -0,0 +1,200 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import datetime
5
+ import sys
6
+ from pathlib import Path
7
+ from typing import Any, Dict, List, Optional, Text, Union
8
+
9
+ import questionary
10
+ import structlog
11
+ from pydantic import BaseModel, Field
12
+
13
+ import rasa.shared.utils.cli
14
+ from rasa.constants import RASA_DIR_NAME
15
+ from rasa.shared.utils.yaml import read_yaml_file, write_yaml
16
+ from rasa.studio.config import StudioConfig
17
+ from rasa.studio.upload import (
18
+ check_if_assistant_already_exists,
19
+ handle_upload,
20
+ is_auth_working,
21
+ )
22
+
23
+ structlogger = structlog.get_logger(__name__)
24
+
25
+ _LINK_FILE_NAME: Text = "studio.yml"
26
+
27
+
28
+ class AssistantLinkPayload(BaseModel):
29
+ assistant_name: Text
30
+ studio_url: Text
31
+ linked_at: Text = Field(
32
+ default_factory=lambda: datetime.datetime.utcnow().isoformat() + "Z"
33
+ )
34
+
35
+
36
+ def _link_file(project_root: Path) -> Path:
37
+ """Return `<project-root>/.rasa/studio.yml`.
38
+
39
+ Args:
40
+ project_root: The path to the project root.
41
+
42
+ Returns:
43
+ The path to the link file.
44
+ """
45
+ return project_root / RASA_DIR_NAME / _LINK_FILE_NAME
46
+
47
+
48
+ def _write_link_file(
49
+ project_root: Path, assistant_name: Text, studio_url: Text
50
+ ) -> None:
51
+ """Persist assistant information inside the project.
52
+
53
+ Args:
54
+ project_root: The path to the project root.
55
+ assistant_name: The name of the assistant.
56
+ studio_url: The URL of the Rasa Studio instance.
57
+ """
58
+ file_path = _link_file(project_root)
59
+ file_path.parent.mkdir(exist_ok=True, parents=True)
60
+
61
+ payload = AssistantLinkPayload(
62
+ assistant_name=assistant_name,
63
+ studio_url=studio_url,
64
+ )
65
+ write_yaml(payload.model_dump(), file_path)
66
+
67
+
68
+ def _read_link_file(
69
+ project_root: Path = Path.cwd(),
70
+ ) -> Optional[Union[List[Any], Dict[Text, Any]]]:
71
+ """Reads the link configuration file.
72
+
73
+ Args:
74
+ project_root: The path to the project root.
75
+
76
+ Returns:
77
+ The assistant information if the file exists, otherwise None.
78
+ """
79
+ file_path = _link_file(project_root)
80
+ if not file_path.is_file():
81
+ return None
82
+
83
+ return read_yaml_file(file_path)
84
+
85
+
86
+ def read_assistant_name(project_root: Path = Path.cwd()) -> Optional[Text]:
87
+ """Reads the assistant_name from the linked configuration file.
88
+
89
+ Args:
90
+ project_root: The path to the project root.
91
+
92
+ Returns:
93
+ The assistant name if the file exists, otherwise None.
94
+ """
95
+ linked = _read_link_file(project_root)
96
+ assistant_name = (
97
+ linked.get("assistant_name") if linked and isinstance(linked, dict) else None
98
+ )
99
+
100
+ if not assistant_name:
101
+ rasa.shared.utils.cli.print_error_and_exit(
102
+ "This project is not linked to any Rasa Studio assistant.\n"
103
+ "Run `rasa studio link <assistant-name>` first."
104
+ )
105
+
106
+ return assistant_name
107
+
108
+
109
+ def get_studio_config() -> StudioConfig:
110
+ """Get the StudioConfig object or exit with an error message.
111
+
112
+ Returns:
113
+ A valid StudioConfig object.
114
+ """
115
+ config = StudioConfig.read_config()
116
+ if not config.is_valid():
117
+ rasa.shared.utils.cli.print_error_and_exit(
118
+ "Rasa Studio is not configured correctly. Run `rasa studio config` first."
119
+ )
120
+ if not is_auth_working(config.studio_url, not config.disable_verify):
121
+ rasa.shared.utils.cli.print_error_and_exit(
122
+ "Authentication invalid or expired. Please run `rasa studio login`."
123
+ )
124
+ return config
125
+
126
+
127
+ def _ensure_assistant_exists(
128
+ assistant_name: Text,
129
+ studio_cfg: StudioConfig,
130
+ args: argparse.Namespace,
131
+ ) -> bool:
132
+ """Create the assistant on Studio if it does not yet exist.
133
+
134
+ Args:
135
+ assistant_name: The name the user provided on the CLI.
136
+ studio_cfg: The validated Studio configuration.
137
+ args: The original CLI args (needed for `handle_upload`).
138
+
139
+ Returns:
140
+ True if the assistant already exists or was created, False otherwise.
141
+ """
142
+ verify_ssl = not studio_cfg.disable_verify
143
+ assistant_already_exists = check_if_assistant_already_exists(
144
+ assistant_name, studio_cfg.studio_url, verify_ssl
145
+ )
146
+ if not assistant_already_exists:
147
+ should_create_assistant = questionary.confirm(
148
+ f"Assistant '{assistant_name}' was not found on Rasa Studio. "
149
+ f"Do you want to create it?"
150
+ ).ask()
151
+ if should_create_assistant:
152
+ # `handle_upload` expects the name to live in `args.assistant_name`
153
+ args.assistant_name = assistant_name
154
+ handle_upload(args)
155
+
156
+ rasa.shared.utils.cli.print_info(
157
+ f"Assistant {assistant_name} successfully created."
158
+ )
159
+ return should_create_assistant
160
+
161
+ return assistant_already_exists
162
+
163
+
164
+ def handle_link(args: argparse.Namespace) -> None:
165
+ """Implementation of `rasa studio link <assistant-name>` CLI command.
166
+
167
+ Args:
168
+ args: The command line arguments.
169
+ """
170
+ assistant_name: Text = args.assistant_name[0]
171
+ studio_cfg = get_studio_config()
172
+ assistant_exists = _ensure_assistant_exists(assistant_name, studio_cfg, args)
173
+ if not assistant_exists:
174
+ rasa.shared.utils.cli.print_error_and_exit(
175
+ "Project has not been linked with Studio assistant."
176
+ )
177
+
178
+ project_root = Path.cwd()
179
+ link_file = _link_file(project_root)
180
+
181
+ if link_file.exists():
182
+ overwrite = questionary.confirm(
183
+ f"This project is already linked " f"(link file '{link_file}').\nOverwrite?"
184
+ ).ask()
185
+ if not overwrite:
186
+ rasa.shared.utils.cli.print_info(
187
+ "Existing link kept – nothing was changed."
188
+ )
189
+ sys.exit(0)
190
+
191
+ _write_link_file(project_root, assistant_name, studio_cfg.studio_url)
192
+
193
+ structlogger.info(
194
+ "studio.link.success",
195
+ event_info=f"Project linked to Studio assistant '{assistant_name}'.",
196
+ assistant_name=assistant_name,
197
+ )
198
+ rasa.shared.utils.cli.print_success(
199
+ f"Project successfully linked to assistant '{assistant_name}'."
200
+ )
rasa/studio/pull.py ADDED
@@ -0,0 +1,94 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ from pathlib import Path
5
+ from typing import Text, Union
6
+
7
+ import structlog
8
+
9
+ import rasa.cli.utils
10
+ import rasa.shared.utils.cli
11
+ from rasa.shared.constants import DEFAULT_CONFIG_PATH, DEFAULT_ENDPOINTS_PATH
12
+ from rasa.shared.utils.io import write_text_file
13
+ from rasa.studio.data_handler import StudioDataHandler
14
+ from rasa.studio.download.download import handle_download
15
+ from rasa.studio.link import get_studio_config, read_assistant_name
16
+
17
+ structlogger = structlog.get_logger(__name__)
18
+
19
+
20
+ def _write_to_file(
21
+ content: Text, file_type: Text, file_path: Text, default_path: Text
22
+ ) -> None:
23
+ """Write the content to a file.
24
+
25
+ Args:
26
+ content: The content to write.
27
+ file_type: The type of file (e.g., "config" or "endpoints".).
28
+ file_path: The path to the file.
29
+ default_path: The default path to use file_path is not valid.
30
+ """
31
+ path: Union[Path, str, None] = rasa.cli.utils.get_validated_path(
32
+ file_path, file_type, default_path, none_is_valid=True
33
+ )
34
+ write_text_file(content, path, encoding="utf-8")
35
+ rasa.shared.utils.cli.print_success(f"Pulled {file_type} data from assistant.")
36
+
37
+
38
+ def handle_pull(args: argparse.Namespace) -> None:
39
+ """Pull all data and overwrite the local assistant.
40
+
41
+ Args:
42
+ args: The parsed arguments.
43
+ """
44
+ assistant_name = read_assistant_name()
45
+
46
+ # Use the CLI command logic to download with overwrite
47
+ download_args = argparse.Namespace(**vars(args))
48
+ download_args.assistant_name = [assistant_name]
49
+ download_args.overwrite = True
50
+
51
+ handle_download(download_args)
52
+ rasa.shared.utils.cli.print_success("Pulled the data from assistant.")
53
+
54
+
55
+ def handle_pull_config(args: argparse.Namespace) -> None:
56
+ """Pull just the assistant's `config.yml`.
57
+
58
+ Args:
59
+ args: The parsed arguments.
60
+ """
61
+ studio_cfg = get_studio_config()
62
+ assistant_name = read_assistant_name()
63
+
64
+ handler = StudioDataHandler(studio_cfg, assistant_name)
65
+ handler.request_all_data()
66
+
67
+ config_yaml = handler.get_config()
68
+ if not config_yaml:
69
+ rasa.shared.utils.cli.print_error_and_exit(
70
+ "No configuration data was found in the Studio assistant."
71
+ )
72
+
73
+ _write_to_file(config_yaml, "config", args.config, DEFAULT_CONFIG_PATH)
74
+
75
+
76
+ def handle_pull_endpoints(args: argparse.Namespace) -> None:
77
+ """Pull just the assistant's `endpoints.yml`.
78
+
79
+ Args:
80
+ args: The parsed arguments.
81
+ """
82
+ studio_cfg = get_studio_config()
83
+ assistant_name = read_assistant_name()
84
+
85
+ handler = StudioDataHandler(studio_cfg, assistant_name)
86
+ handler.request_all_data()
87
+
88
+ endpoints_yaml = handler.get_endpoints()
89
+ if not endpoints_yaml:
90
+ rasa.shared.utils.cli.print_error_and_exit(
91
+ "No endpoints data was found in the Studio assistant."
92
+ )
93
+
94
+ _write_to_file(endpoints_yaml, "endpoints", args.endpoints, DEFAULT_ENDPOINTS_PATH)
rasa/studio/push.py ADDED
@@ -0,0 +1,131 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ from pathlib import Path
5
+ from typing import Dict, Text
6
+
7
+ import structlog
8
+
9
+ import rasa.shared.utils.cli
10
+ from rasa.shared.core.flows.yaml_flows_io import YamlFlowsWriter
11
+ from rasa.shared.importers.importer import TrainingDataImporter
12
+ from rasa.shared.nlu.training_data.formats.rasa_yaml import RasaYAMLWriter
13
+ from rasa.shared.utils.io import read_file
14
+ from rasa.shared.utils.yaml import dump_obj_as_yaml_to_string
15
+ from rasa.studio.config import StudioConfig
16
+ from rasa.studio.link import get_studio_config, read_assistant_name
17
+ from rasa.studio.upload import (
18
+ build_import_request,
19
+ make_request,
20
+ run_validation,
21
+ )
22
+
23
+ structlogger = structlog.get_logger(__name__)
24
+
25
+
26
+ def _send_to_studio(
27
+ assistant_name: Text,
28
+ payload_parts: Dict[Text, Text],
29
+ studio_cfg: StudioConfig,
30
+ ) -> None:
31
+ """Build the GraphQL request and send it.
32
+
33
+ Args:
34
+ assistant_name: The name of the assistant.
35
+ payload_parts: The parts of the payload to send.
36
+ studio_cfg: The StudioConfig object.
37
+ """
38
+ graphql_req = build_import_request(
39
+ assistant_name=assistant_name,
40
+ flows_yaml=payload_parts.get("flows"),
41
+ domain_yaml=payload_parts.get("domain"),
42
+ config_yaml=payload_parts.get("config"),
43
+ endpoints=payload_parts.get("endpoints"),
44
+ nlu_yaml=payload_parts.get("nlu"),
45
+ )
46
+ verify = not studio_cfg.disable_verify
47
+ result = make_request(studio_cfg.studio_url, graphql_req, verify)
48
+ if not result.was_successful:
49
+ rasa.shared.utils.cli.print_error_and_exit(result.message)
50
+
51
+ rasa.shared.utils.cli.print_success(f"Pushed data to assistant '{assistant_name}'.")
52
+
53
+
54
+ def handle_push(args: argparse.Namespace) -> None:
55
+ """Push the entire assistant.
56
+
57
+ Args:
58
+ args: The command line arguments.
59
+ """
60
+ studio_cfg = get_studio_config()
61
+
62
+ run_validation(args)
63
+
64
+ importer = TrainingDataImporter.load_from_dict(
65
+ domain_path=args.domain,
66
+ config_path=args.config,
67
+ expand_env_vars=False,
68
+ )
69
+
70
+ domain_yaml = dump_obj_as_yaml_to_string(importer.get_user_domain().as_dict())
71
+ config_yaml = read_file(Path(args.config))
72
+ endpoints_yaml = read_file(Path(args.endpoints))
73
+
74
+ flow_importer = TrainingDataImporter.load_from_dict(
75
+ training_data_paths=args.data, expand_env_vars=False
76
+ )
77
+ flows_yaml = YamlFlowsWriter().dumps(flow_importer.get_user_flows())
78
+
79
+ nlu_importer = TrainingDataImporter.load_from_dict(
80
+ training_data_paths=args.data, expand_env_vars=False
81
+ )
82
+ nlu_yaml = RasaYAMLWriter().dumps(nlu_importer.get_nlu_data())
83
+
84
+ assistant_name = read_assistant_name()
85
+ _send_to_studio(
86
+ assistant_name,
87
+ {
88
+ "flows": flows_yaml,
89
+ "domain": domain_yaml,
90
+ "config": config_yaml,
91
+ "endpoints": endpoints_yaml,
92
+ "nlu": nlu_yaml,
93
+ },
94
+ studio_cfg,
95
+ )
96
+
97
+
98
+ def handle_push_config(args: argparse.Namespace) -> None:
99
+ """Push only the assistant configuration (config.yml).
100
+
101
+ Args:
102
+ args: The command line arguments.
103
+ """
104
+ studio_cfg = get_studio_config()
105
+ assistant_name = read_assistant_name()
106
+
107
+ config_yaml = read_file(Path(args.config))
108
+ if not config_yaml:
109
+ rasa.shared.utils.cli.print_error_and_exit(
110
+ "No configuration data was found in the assistant."
111
+ )
112
+
113
+ _send_to_studio(assistant_name, {"config": config_yaml}, studio_cfg)
114
+
115
+
116
+ def handle_push_endpoints(args: argparse.Namespace) -> None:
117
+ """Push only the endpoints configuration (endpoints.yml).
118
+
119
+ Args:
120
+ args: The command line arguments.
121
+ """
122
+ studio_cfg = get_studio_config()
123
+ assistant_name = read_assistant_name()
124
+
125
+ endpoints_yaml = read_file(Path(args.endpoints))
126
+ if not endpoints_yaml:
127
+ rasa.shared.utils.cli.print_error_and_exit(
128
+ "No endpoints data was found in the assistant."
129
+ )
130
+
131
+ _send_to_studio(assistant_name, {"endpoints": endpoints_yaml}, studio_cfg)
rasa/studio/upload.py CHANGED
@@ -2,11 +2,12 @@ import argparse
2
2
  import base64
3
3
  import re
4
4
  import sys
5
- from typing import Any, Dict, Iterable, List, Set, Text, Tuple, Union
5
+ from typing import Any, Dict, Iterable, List, Optional, Set, Text, Tuple, Union
6
6
 
7
7
  import questionary
8
8
  import requests
9
9
  import structlog
10
+ from pydantic import BaseModel, Field
10
11
 
11
12
  import rasa.cli.telemetry
12
13
  import rasa.cli.utils
@@ -32,6 +33,7 @@ from rasa.shared.nlu.training_data.formats.rasa_yaml import (
32
33
  )
33
34
  from rasa.shared.utils.yaml import (
34
35
  dump_obj_as_yaml_to_string,
36
+ read_yaml,
35
37
  read_yaml_file,
36
38
  )
37
39
  from rasa.studio import results_logger
@@ -64,6 +66,16 @@ DOMAIN_KEYS = [
64
66
  ]
65
67
 
66
68
 
69
+ class CALMImportParts(BaseModel):
70
+ """All pieces that will be uploaded to Rasa Studio."""
71
+
72
+ flows: Dict[str, Any]
73
+ domain: Dict[str, Any]
74
+ config: Dict[str, Any]
75
+ endpoints: Dict[str, Any]
76
+ nlu: Dict[str, Any] = Field(default_factory=dict)
77
+
78
+
67
79
  def _get_selected_entities_and_intents(
68
80
  args: argparse.Namespace,
69
81
  intents_from_files: Set[Text],
@@ -208,79 +220,93 @@ def _get_assistant_name(config: Dict[Text, Any]) -> str:
208
220
  return assistant_name
209
221
 
210
222
 
211
- @with_studio_error_handler
212
- def upload_calm_assistant(
213
- args: argparse.Namespace, endpoint: str, verify: bool = True
214
- ) -> StudioResult:
215
- """Validates and uploads the CALM assistant data to Rasa Studio.
223
+ def build_calm_import_parts(
224
+ data_path: Union[Text, List[Text]],
225
+ domain_path: Text,
226
+ config_path: Text,
227
+ endpoints_path: Optional[Text] = None,
228
+ assistant_name: Optional[Text] = None,
229
+ ) -> Tuple[str, CALMImportParts]:
230
+ """Builds the parts of the assistant to be uploaded to Studio.
216
231
 
217
232
  Args:
218
- args: The command line arguments
219
- - data: The path to the training data
220
- - domain: The path to the domain
221
- - flows: The path to the flows
222
- - endpoints: The path to the endpoints
223
- - config: The path to the config
224
- endpoint: The studio endpoint
225
- verify: Whether to verify SSL
233
+ data_path: The path to the training data
234
+ domain_path: The path to the domain
235
+ config_path: The path to the config
236
+ endpoints_path: The path to the endpoints
237
+ assistant_name: The name of the assistant
238
+
226
239
  Returns:
227
- None
240
+ The assistant name and the parts to be uploaded
228
241
  """
229
- run_validation(args)
230
-
231
- structlogger.info(
232
- "rasa.studio.upload.loading_data", event_info="Parsing CALM assistant data..."
233
- )
234
-
235
242
  importer = TrainingDataImporter.load_from_dict(
236
- domain_path=args.domain,
237
- config_path=args.config,
243
+ domain_path=domain_path,
244
+ config_path=config_path,
238
245
  expand_env_vars=False,
239
246
  )
240
247
 
241
- # Prepare config and domain
242
- config = importer.get_config()
243
- assistant_name = _get_assistant_name(config)
248
+ config = read_yaml_file(config_path, expand_env_vars=False)
249
+ endpoints = read_yaml_file(endpoints_path, expand_env_vars=False)
250
+ assistant_name = assistant_name or _get_assistant_name(config)
244
251
 
245
- config_from_files = read_yaml_file(args.config, expand_env_vars=False)
246
252
  domain_from_files = importer.get_user_domain().as_dict()
247
-
248
- # Extract domain and config values
249
253
  domain = extract_values(domain_from_files, DOMAIN_KEYS)
250
254
 
251
- # Prepare flows
252
255
  flow_importer = FlowSyncImporter.load_from_dict(
253
- training_data_paths=args.data, expand_env_vars=False
256
+ training_data_paths=data_path, expand_env_vars=False
254
257
  )
258
+
255
259
  flows = list(flow_importer.get_user_flows())
260
+ flows_yaml = YamlFlowsWriter().dumps(flows)
261
+ flows = read_yaml(flows_yaml, expand_env_vars=False)
256
262
 
257
- # We instantiate the TrainingDataImporter again on purpose to avoid
258
- # adding patterns to domain's actions. More info https://t.ly/W8uuc
259
263
  nlu_importer = TrainingDataImporter.load_from_dict(
260
- training_data_paths=args.data, expand_env_vars=False
264
+ training_data_paths=data_path, expand_env_vars=False
261
265
  )
262
266
  nlu_data = nlu_importer.get_nlu_data()
263
267
  nlu_examples = nlu_data.filter_training_examples(
264
268
  lambda ex: ex.get("intent") in nlu_data.intents
265
269
  )
266
270
  nlu_examples_yaml = RasaYAMLWriter().dumps(nlu_examples)
271
+ nlu = read_yaml(nlu_examples_yaml, expand_env_vars=False)
272
+
273
+ parts = CALMImportParts(
274
+ flows=flows,
275
+ domain=domain,
276
+ config=config,
277
+ endpoints=endpoints,
278
+ nlu=nlu,
279
+ )
280
+
281
+ return assistant_name, parts
282
+
283
+
284
+ @with_studio_error_handler
285
+ def upload_calm_assistant(
286
+ args: argparse.Namespace, endpoint: str, verify: bool = True
287
+ ) -> StudioResult:
288
+ def yaml_or_empty(part: Dict[Text, Any]) -> Optional[str]:
289
+ return dump_obj_as_yaml_to_string(part) if part else None
267
290
 
268
- # Prepare endpoints
269
- endpoints_from_files = read_yaml_file(args.endpoints, expand_env_vars=False)
270
- endpoints_str = dump_obj_as_yaml_to_string(
271
- endpoints_from_files, transform=remove_quotes
291
+ run_validation(args)
292
+ structlogger.info(
293
+ "rasa.studio.upload.loading_data", event_info="Parsing CALM assistant data..."
294
+ )
295
+ assistant_name, parts = build_calm_import_parts(
296
+ data_path=args.data,
297
+ domain_path=args.domain,
298
+ config_path=args.config,
299
+ endpoints_path=args.endpoints,
272
300
  )
273
301
 
274
- # Build GraphQL request
275
302
  graphql_req = build_import_request(
276
303
  assistant_name,
277
- flows_yaml=YamlFlowsWriter().dumps(flows),
278
- domain_yaml=dump_obj_as_yaml_to_string(domain),
279
- config_yaml=dump_obj_as_yaml_to_string(config_from_files),
280
- endpoints=endpoints_str,
281
- nlu_yaml=nlu_examples_yaml,
304
+ flows_yaml=yaml_or_empty(parts.flows),
305
+ domain_yaml=yaml_or_empty(parts.domain),
306
+ config_yaml=yaml_or_empty(parts.config),
307
+ endpoints=yaml_or_empty(parts.endpoints),
308
+ nlu_yaml=yaml_or_empty(parts.nlu),
282
309
  )
283
-
284
310
  structlogger.info(
285
311
  "rasa.studio.upload.calm", event_info="Uploading to Rasa Studio..."
286
312
  )
@@ -393,7 +419,6 @@ def make_request(endpoint: str, graphql_req: Dict, verify: bool = True) -> Studi
393
419
  },
394
420
  verify=verify,
395
421
  )
396
-
397
422
  if results_logger.response_has_errors(res.json()):
398
423
  track_upload_to_studio_failed(res.json())
399
424
  return StudioResult.error(res.json())
@@ -421,39 +446,64 @@ def _add_missing_entities(
421
446
 
422
447
  def build_import_request(
423
448
  assistant_name: str,
424
- flows_yaml: str,
425
- domain_yaml: str,
426
- config_yaml: str,
427
- endpoints: str,
428
- nlu_yaml: str = "",
449
+ flows_yaml: Optional[str] = None,
450
+ domain_yaml: Optional[str] = None,
451
+ config_yaml: Optional[str] = None,
452
+ endpoints: Optional[str] = None,
453
+ nlu_yaml: Optional[str] = None,
429
454
  ) -> Dict:
430
- # b64encode expects bytes and returns bytes so we need to decode to string
431
- base64_domain = base64.b64encode(domain_yaml.encode("utf-8")).decode("utf-8")
432
- base64_flows = base64.b64encode(flows_yaml.encode("utf-8")).decode("utf-8")
433
- base64_config = base64.b64encode(config_yaml.encode("utf-8")).decode("utf-8")
434
- base64_nlu = base64.b64encode(nlu_yaml.encode("utf-8")).decode("utf-8")
435
- base64_endpoints = base64.b64encode(endpoints.encode("utf-8")).decode("utf-8")
455
+ """Builds the GraphQL request for uploading a modern assistant.
456
+
457
+ Args:
458
+ assistant_name: The name of the assistant
459
+ flows_yaml: The YAML representation of the flows
460
+ domain_yaml: The YAML representation of the domain
461
+ config_yaml: The YAML representation of the config
462
+ endpoints: The YAML representation of the endpoints
463
+ nlu_yaml: The YAML representation of the NLU data
464
+
465
+ Returns:
466
+ A dictionary representing the GraphQL request for uploading the assistant.
467
+ """
468
+ inputs_map = {
469
+ "domain": domain_yaml,
470
+ "flows": flows_yaml,
471
+ "config": config_yaml,
472
+ "endpoints": endpoints,
473
+ "nlu": nlu_yaml,
474
+ }
475
+
476
+ payload = {
477
+ field: convert_string_to_base64(value)
478
+ for field, value in inputs_map.items()
479
+ if value is not None
480
+ }
481
+
482
+ variables_input = {"assistantName": assistant_name, **payload}
436
483
 
437
484
  graphql_req = {
438
485
  "query": (
439
486
  "mutation UploadModernAssistant($input: UploadModernAssistantInput!)"
440
487
  "{\n uploadModernAssistant(input: $input)\n}"
441
488
  ),
442
- "variables": {
443
- "input": {
444
- "assistantName": assistant_name,
445
- "domain": base64_domain,
446
- "flows": base64_flows,
447
- "nlu": base64_nlu,
448
- "config": base64_config,
449
- "endpoints": base64_endpoints,
450
- }
451
- },
489
+ "variables": {"input": variables_input},
452
490
  }
453
491
 
454
492
  return graphql_req
455
493
 
456
494
 
495
+ def convert_string_to_base64(string: str) -> str:
496
+ """Converts a string to base64.
497
+
498
+ Args:
499
+ string: The string to convert
500
+
501
+ Returns:
502
+ The base64 encoded string
503
+ """
504
+ return base64.b64encode(string.encode("utf-8")).decode("utf-8")
505
+
506
+
457
507
  def build_request(
458
508
  assistant_name: str, nlu_examples_yaml: str, domain_yaml: str
459
509
  ) -> Dict: