rasa-pro 3.13.0.dev3__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.

Files changed (132) hide show
  1. rasa/__main__.py +3 -1
  2. rasa/cli/inspect.py +8 -4
  3. rasa/cli/project_templates/default/config.yml +5 -32
  4. rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_cancels_during_a_correction.yml +1 -1
  5. rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_changes_mind_on_a_whim.yml +1 -1
  6. rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_handle.yml +1 -1
  7. rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_name.yml +1 -1
  8. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_adds_contact_to_their_list.yml +1 -1
  9. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_lists_contacts.yml +1 -1
  10. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact.yml +1 -1
  11. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact_from_list.yml +1 -1
  12. rasa/cli/project_templates/default/endpoints.yml +18 -2
  13. rasa/cli/scaffold.py +3 -4
  14. rasa/cli/studio/download.py +1 -1
  15. rasa/cli/studio/upload.py +0 -6
  16. rasa/core/channels/channel.py +68 -5
  17. rasa/core/channels/inspector/dist/assets/{arc-c7691751.js → arc-9f75cc3b.js} +1 -1
  18. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-ab99dff7.js → blockDiagram-38ab4fdb-7f34db23.js} +1 -1
  19. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-08c35a6b.js → c4Diagram-3d4e48cf-948bab2c.js} +1 -1
  20. rasa/core/channels/inspector/dist/assets/channel-dfa68278.js +1 -0
  21. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-9e9c71c9.js → classDiagram-70f12bd4-53b0dd0e.js} +1 -1
  22. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-15e7e2bf.js → classDiagram-v2-f2320105-fdf789e7.js} +1 -1
  23. rasa/core/channels/inspector/dist/assets/clone-edb7f119.js +1 -0
  24. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-9c105cb1.js → createText-2e5e7dd3-87c4ece5.js} +1 -1
  25. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-77e89e48.js → edges-e0da2a9e-5a8b0749.js} +1 -1
  26. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-7a011646.js → erDiagram-9861fffd-66da90e2.js} +1 -1
  27. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-b6f105ac.js → flowDb-956e92f1-10044f05.js} +1 -1
  28. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-ce4f18c2.js → flowDiagram-66a62f08-f338f66a.js} +1 -1
  29. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-65e7c670.js +1 -0
  30. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-cb5f6da4.js → flowchart-elk-definition-4a651766-b13140aa.js} +1 -1
  31. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-e4d19e28.js → ganttDiagram-c361ad54-f2b4a55a.js} +1 -1
  32. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-727b1c33.js → gitGraphDiagram-72cf32ee-dedc298d.js} +1 -1
  33. rasa/core/channels/inspector/dist/assets/{graph-6e2ab9a7.js → graph-4ede11ff.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/{index-3862675e-84ec700f.js → index-3862675e-65549d37.js} +1 -1
  35. rasa/core/channels/inspector/dist/assets/{index-098a1a24.js → index-3a23e736.js} +142 -129
  36. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-78dda442.js → infoDiagram-f8f76790-65439671.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-f1cc6dd1.js → journeyDiagram-49397b02-56d03d98.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{layout-d98dcd0c.js → layout-dd48f7f4.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/{line-838e3d82.js → line-1569ad2c.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{linear-eae72406.js → linear-48bf4935.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-c96fd84b.js → mindmap-definition-fc14e90a-688504c1.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-c936d4e2.js → pieDiagram-8a3498a8-78b6d7e6.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-b338eb8f.js → quadrantDiagram-120e2f19-048b84b3.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-c6b6c0d5.js → requirementDiagram-deff3bca-dd67f107.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-b9372e19.js → sankeyDiagram-04a897e0-8128436e.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-479e0a3f.js → sequenceDiagram-704730f1-1a0d1461.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-fd26eebc.js → stateDiagram-587899a1-46d388ed.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-3233e0ae.js → stateDiagram-v2-d93cdb3a-ea42951a.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-1fdd392b.js → styles-6aaf32cf-7427ed0c.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-6d7bfa1b.js → styles-9a916d00-ff5e5a16.js} +1 -1
  51. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-f86aab11.js → styles-c10674c1-7b3680cf.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-e3e49d7a.js → svgDrawCommon-08f97a94-f860f2ad.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-6fe08b4d.js → timeline-definition-85554ec2-2eebf0c8.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-c2e06fd6.js → xychartDiagram-e933f94c-5d7f4e96.js} +1 -1
  55. rasa/core/channels/inspector/dist/index.html +1 -1
  56. rasa/core/channels/inspector/src/App.tsx +3 -2
  57. rasa/core/channels/inspector/src/components/Chat.tsx +23 -2
  58. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +2 -5
  59. rasa/core/channels/inspector/src/helpers/conversation.ts +16 -0
  60. rasa/core/channels/inspector/src/types.ts +1 -1
  61. rasa/core/channels/voice_ready/audiocodes.py +41 -15
  62. rasa/core/channels/voice_ready/twilio_voice.py +48 -1
  63. rasa/core/channels/voice_stream/tts/azure.py +11 -2
  64. rasa/core/channels/voice_stream/twilio_media_streams.py +101 -26
  65. rasa/core/channels/voice_stream/voice_channel.py +28 -2
  66. rasa/core/concurrent_lock_store.py +24 -10
  67. rasa/core/lock_store.py +151 -60
  68. rasa/dialogue_understanding_test/du_test_case.py +16 -8
  69. rasa/plugin.py +0 -3
  70. rasa/shared/constants.py +1 -0
  71. rasa/shared/core/domain.py +165 -11
  72. rasa/shared/core/flows/flow.py +155 -131
  73. rasa/shared/core/flows/flow_step.py +19 -3
  74. rasa/shared/core/flows/flow_step_links.py +15 -0
  75. rasa/shared/core/flows/flow_step_sequence.py +6 -0
  76. rasa/shared/core/flows/nlu_trigger.py +13 -0
  77. rasa/shared/core/flows/steps/action.py +7 -4
  78. rasa/shared/core/flows/steps/call.py +11 -4
  79. rasa/shared/core/flows/steps/collect.py +27 -6
  80. rasa/shared/core/flows/steps/internal.py +6 -1
  81. rasa/shared/core/flows/steps/link.py +7 -4
  82. rasa/shared/core/flows/steps/no_operation.py +7 -4
  83. rasa/shared/core/flows/steps/set_slots.py +8 -4
  84. rasa/shared/core/flows/yaml_flows_io.py +106 -5
  85. rasa/shared/importers/importer.py +8 -0
  86. rasa/shared/providers/_utils.py +83 -0
  87. rasa/shared/providers/llm/_base_litellm_client.py +6 -3
  88. rasa/shared/providers/llm/azure_openai_llm_client.py +6 -68
  89. rasa/shared/providers/router/_base_litellm_router_client.py +53 -1
  90. rasa/shared/utils/common.py +42 -0
  91. rasa/studio/download/domains.py +49 -0
  92. rasa/studio/download/download.py +439 -0
  93. rasa/studio/download/flows.py +359 -0
  94. rasa/studio/results_logger.py +6 -1
  95. rasa/studio/upload.py +69 -5
  96. rasa/utils/common.py +36 -0
  97. rasa/utils/endpoints.py +22 -1
  98. rasa/utils/licensing.py +1 -1
  99. rasa/validator.py +1 -2
  100. rasa/version.py +1 -1
  101. {rasa_pro-3.13.0.dev3.dist-info → rasa_pro-3.13.0.dev5.dist-info}/METADATA +8 -8
  102. {rasa_pro-3.13.0.dev3.dist-info → rasa_pro-3.13.0.dev5.dist-info}/RECORD +119 -125
  103. rasa/cli/project_templates/calm/config.yml +0 -10
  104. rasa/cli/project_templates/calm/credentials.yml +0 -33
  105. rasa/cli/project_templates/calm/endpoints.yml +0 -58
  106. rasa/cli/project_templates/default/actions/actions.py +0 -27
  107. rasa/cli/project_templates/default/data/nlu.yml +0 -91
  108. rasa/cli/project_templates/default/data/rules.yml +0 -13
  109. rasa/cli/project_templates/default/data/stories.yml +0 -30
  110. rasa/cli/project_templates/default/domain.yml +0 -34
  111. rasa/cli/project_templates/default/tests/test_stories.yml +0 -91
  112. rasa/core/channels/inspector/dist/assets/channel-11268142.js +0 -1
  113. rasa/core/channels/inspector/dist/assets/clone-ff7f2ce7.js +0 -1
  114. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-cba7ae20.js +0 -1
  115. rasa/studio/download.py +0 -489
  116. /rasa/cli/project_templates/{calm → default}/actions/action_template.py +0 -0
  117. /rasa/cli/project_templates/{calm → default}/actions/add_contact.py +0 -0
  118. /rasa/cli/project_templates/{calm → default}/actions/db.py +0 -0
  119. /rasa/cli/project_templates/{calm → default}/actions/list_contacts.py +0 -0
  120. /rasa/cli/project_templates/{calm → default}/actions/remove_contact.py +0 -0
  121. /rasa/cli/project_templates/{calm → default}/data/flows/add_contact.yml +0 -0
  122. /rasa/cli/project_templates/{calm → default}/data/flows/list_contacts.yml +0 -0
  123. /rasa/cli/project_templates/{calm → default}/data/flows/remove_contact.yml +0 -0
  124. /rasa/cli/project_templates/{calm → default}/db/contacts.json +0 -0
  125. /rasa/cli/project_templates/{calm → default}/domain/add_contact.yml +0 -0
  126. /rasa/cli/project_templates/{calm → default}/domain/list_contacts.yml +0 -0
  127. /rasa/cli/project_templates/{calm → default}/domain/remove_contact.yml +0 -0
  128. /rasa/cli/project_templates/{calm → default}/domain/shared.yml +0 -0
  129. /rasa/{cli/project_templates/calm/actions → studio/download}/__init__.py +0 -0
  130. {rasa_pro-3.13.0.dev3.dist-info → rasa_pro-3.13.0.dev5.dist-info}/NOTICE +0 -0
  131. {rasa_pro-3.13.0.dev3.dist-info → rasa_pro-3.13.0.dev5.dist-info}/WHEEL +0 -0
  132. {rasa_pro-3.13.0.dev3.dist-info → rasa_pro-3.13.0.dev5.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,359 @@
1
+ import logging
2
+ from pathlib import Path
3
+ from typing import Any, Dict, List, Set, Text
4
+
5
+ from rasa.shared.core.flows import Flow
6
+ from rasa.shared.core.flows.flow_step_links import StaticFlowStepLink
7
+ from rasa.shared.core.flows.flows_list import FlowsList
8
+ from rasa.shared.core.flows.yaml_flows_io import YAMLFlowsReader, YamlFlowsWriter
9
+ from rasa.shared.importers.importer import TrainingDataImporter
10
+ from rasa.shared.utils.yaml import read_yaml
11
+ from rasa.studio.constants import STUDIO_NLU_FILENAME
12
+ from rasa.studio.data_handler import StudioDataHandler
13
+ from rasa.utils.mapper import RasaPrimitiveStorageMapper
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ STUDIO_FLOWS_DIR_NAME = "studio_flows"
18
+
19
+
20
+ def merge_flows_with_overwrite(
21
+ data_paths: List[Path],
22
+ handler: Any,
23
+ data_from_studio: TrainingDataImporter,
24
+ data_local: TrainingDataImporter,
25
+ mapper: RasaPrimitiveStorageMapper,
26
+ ) -> None:
27
+ """
28
+ Merges flows data from a file or directory when overwrite is enabled.
29
+
30
+ Args:
31
+ data_paths: List of paths to the training data.
32
+ handler: The StudioDataHandler instance.
33
+ data_from_studio: The TrainingDataImporter instance for Studio data.
34
+ data_local: The TrainingDataImporter instance for local data.
35
+ mapper: The RasaPrimitiveStorageMapper instance for mapping.
36
+ """
37
+ if len(data_paths) != 1:
38
+ # TODO: Handle multiple data paths.
39
+ raise NotImplementedError("Multiple data paths are not supported yet.")
40
+
41
+ data_path = data_paths[0]
42
+ if data_path.is_file():
43
+ merge_training_data_file(handler, data_from_studio, data_local, data_path)
44
+ elif data_path.is_dir():
45
+ merge_training_data_dir(
46
+ handler, data_from_studio, data_local, data_path, mapper
47
+ )
48
+ else:
49
+ raise ValueError("Provided data path is neither a file nor a directory.")
50
+
51
+
52
+ def merge_training_data_file(
53
+ handler: StudioDataHandler,
54
+ data_from_studio: TrainingDataImporter,
55
+ data_local: TrainingDataImporter,
56
+ file_path: Path,
57
+ ) -> None:
58
+ """
59
+ Merges NLU and flows data when training data is stored in a single file.
60
+
61
+ Args:
62
+ handler: The StudioDataHandler instance.
63
+ data_from_studio: The TrainingDataImporter instance for Studio data.
64
+ data_local: The TrainingDataImporter instance for local data.
65
+ file_path: The path to the training data file.
66
+ """
67
+ if handler.has_nlu():
68
+ nlu_data_merged = data_from_studio.get_nlu_data().merge(
69
+ data_local.get_nlu_data()
70
+ )
71
+ nlu_data_merged.persist_nlu(file_path)
72
+
73
+ if handler.has_flows():
74
+ flows_data_merged = data_from_studio.get_user_flows().merge(
75
+ data_local.get_user_flows()
76
+ )
77
+ YamlFlowsWriter.dump(
78
+ flows=flows_data_merged.underlying_flows,
79
+ filename=file_path,
80
+ should_clean_json=True,
81
+ )
82
+
83
+
84
+ def merge_training_data_dir(
85
+ handler: StudioDataHandler,
86
+ data_from_studio: TrainingDataImporter,
87
+ data_local: TrainingDataImporter,
88
+ data_path: Path,
89
+ mapper: RasaPrimitiveStorageMapper,
90
+ ) -> None:
91
+ """
92
+ Merges NLU and flows data when training data is stored in a directory.
93
+
94
+ Args:
95
+ handler: The StudioDataHandler instance.
96
+ data_from_studio: The TrainingDataImporter instance for Studio data.
97
+ data_local: The TrainingDataImporter instance for local data.
98
+ data_path: The path to the training data directory.
99
+ mapper: The RasaPrimitiveStorageMapper instance for mapping.
100
+ """
101
+ if handler.has_nlu():
102
+ merge_nlu_in_directory(data_from_studio, data_local, data_path, mapper)
103
+
104
+ if handler.has_flows():
105
+ merge_flows_in_directory(data_from_studio, data_path, mapper)
106
+
107
+
108
+ def merge_nlu_in_directory(
109
+ data_from_studio: TrainingDataImporter,
110
+ data_local: TrainingDataImporter,
111
+ data_path: Path,
112
+ mapper: RasaPrimitiveStorageMapper,
113
+ ) -> None:
114
+ """
115
+ Merges NLU data by checking for an existing NLU file in the directory.
116
+ If it exists, the new Studio data is merged with the local data.
117
+
118
+ Args:
119
+ data_from_studio: The TrainingDataImporter instance for Studio data.
120
+ data_local: The TrainingDataImporter instance for local data.
121
+ data_path: The path to the training data directory.
122
+ mapper: The RasaPrimitiveStorageMapper instance for mapping.
123
+ """
124
+ from rasa.studio.download.download import pretty_write_nlu_yaml
125
+
126
+ nlu_data = data_from_studio.get_nlu_data()
127
+ nlu_file_path = get_nlu_path(data_path, data_local, mapper)
128
+
129
+ if nlu_file_path.exists():
130
+ local_nlu = TrainingDataImporter.load_from_dict(
131
+ training_data_paths=[str(nlu_file_path)]
132
+ )
133
+ nlu_data = nlu_data.merge(local_nlu.get_nlu_data())
134
+
135
+ pretty_write_nlu_yaml(read_yaml(nlu_data.nlu_as_yaml()), nlu_file_path)
136
+
137
+
138
+ def get_nlu_path(
139
+ base_path: Path,
140
+ data_local: TrainingDataImporter,
141
+ mapper: RasaPrimitiveStorageMapper,
142
+ ) -> Path:
143
+ """Determines where NLU data should be stored.
144
+
145
+ Args:
146
+ base_path: The base path for the training data.
147
+ data_local: The TrainingDataImporter instance for local data.
148
+ mapper: The RasaPrimitiveStorageMapper instance for mapping.
149
+
150
+ Returns:
151
+ The path where NLU data should be stored.
152
+ """
153
+ nlu_paths = set()
154
+ for intent in data_local.get_nlu_data().intents:
155
+ for p in mapper.get_file(intent, "intents").get("training", []):
156
+ nlu_paths.add(p)
157
+
158
+ return _select_path(nlu_paths, "nlu", base_path, STUDIO_NLU_FILENAME)
159
+
160
+
161
+ def get_flows_path(
162
+ base_path: Path,
163
+ data_local: TrainingDataImporter,
164
+ mapper: RasaPrimitiveStorageMapper,
165
+ ) -> Path:
166
+ """Determines where flows data should be stored.
167
+
168
+ Args:
169
+ base_path: The base path for the training data.
170
+ data_local: The TrainingDataImporter instance for local data.
171
+ mapper: The RasaPrimitiveStorageMapper instance for mapping.
172
+
173
+ Returns:
174
+ The path where flows data should be stored.
175
+ """
176
+ flow_paths = set()
177
+ for flow in data_local.get_user_flows().underlying_flows:
178
+ for p in mapper.get_file(flow.id, "flows").get("training", []):
179
+ flow_paths.add(p)
180
+
181
+ return _select_path(flow_paths, "flows", base_path, "flows.yml")
182
+
183
+
184
+ def merge_flows_in_directory(
185
+ data_from_studio: TrainingDataImporter,
186
+ data_path: Path,
187
+ mapper: RasaPrimitiveStorageMapper,
188
+ ) -> None:
189
+ """
190
+ Merges flows data by updating local flow files in a directory with any changes
191
+ from Studio, and then dumping any new flows that do not exist in any local file.
192
+
193
+ Args:
194
+ data_from_studio: Training data importer containing the flows from Studio.
195
+ data_path: The path to the directory where local flows reside.
196
+ mapper: Utility for mapping flow IDs to their respective file paths.
197
+ """
198
+ # Extract flows from Studio data and map flow IDs to their instances.
199
+ studio_flows = data_from_studio.get_user_flows()
200
+ studio_flow_map: Dict[Text, Flow] = {
201
+ flow.id: flow for flow in studio_flows.underlying_flows
202
+ }
203
+
204
+ # Load existing local flows from the specified directory.
205
+ local_flows_file = TrainingDataImporter.load_from_dict(
206
+ training_data_paths=[str(data_path)]
207
+ )
208
+ local_flows = local_flows_file.get_user_flows().underlying_flows
209
+
210
+ # Gather the unique file paths where each local flow is stored.
211
+ local_flow_paths: Set[Path] = _get_local_flow_paths(local_flows, mapper)
212
+
213
+ # Track updated flows and update local files with Studio flow data.
214
+ all_updated_flows: List[Flow] = []
215
+ for flow_file_path in local_flow_paths:
216
+ updated_file_flows = _update_flow_file(flow_file_path, studio_flow_map)
217
+ all_updated_flows.extend(updated_file_flows)
218
+
219
+ # Identify new Studio flows and save them as separate files in the directory.
220
+ new_flows = [
221
+ flow for flow in studio_flow_map.values() if flow not in all_updated_flows
222
+ ]
223
+ _dump_flows_as_separate_files(new_flows, data_path)
224
+
225
+
226
+ def _get_local_flow_paths(
227
+ local_flows: List[Any],
228
+ mapper: RasaPrimitiveStorageMapper,
229
+ ) -> Set[Path]:
230
+ """
231
+ Args:
232
+ local_flows: List of local flows.
233
+ mapper: The RasaPrimitiveStorageMapper instance for mapping.
234
+
235
+ Returns:
236
+ A set of paths for the local flow files.
237
+ """
238
+ paths: Set[Path] = set()
239
+ for flow in local_flows:
240
+ paths.update(mapper.get_file(flow.id, "flows").get("training", []))
241
+ return paths
242
+
243
+
244
+ def _update_flow_file(
245
+ flow_file_path: Path, studio_flows_map: Dict[Text, Any]
246
+ ) -> List[Flow]:
247
+ """
248
+ Reads a flow file, updates outdated flows, and replaces them with studio versions.
249
+
250
+ Args:
251
+ flow_file_path: The path to the flow file.
252
+ studio_flows_map: A dictionary mapping flow IDs to their updated versions.
253
+
254
+ Returns:
255
+ A list of flows from the updated flow file.
256
+ """
257
+ file_flows = YAMLFlowsReader.read_from_file(flow_file_path, False)
258
+ updated_list: List[Any] = []
259
+ has_changes = False
260
+
261
+ for flow in file_flows.underlying_flows:
262
+ studio_flow = studio_flows_map.get(flow.id)
263
+ if studio_flow is not None and studio_flow != flow:
264
+ updated_list.append(studio_flow)
265
+ has_changes = True
266
+ else:
267
+ updated_list.append(flow)
268
+
269
+ if has_changes:
270
+ new_flows_list = FlowsList(underlying_flows=updated_list)
271
+ new_flows_list = strip_default_next_references(new_flows_list)
272
+ YamlFlowsWriter.dump(
273
+ flows=new_flows_list.underlying_flows,
274
+ filename=flow_file_path,
275
+ should_clean_json=True,
276
+ )
277
+ return new_flows_list.underlying_flows
278
+
279
+ return file_flows.underlying_flows
280
+
281
+
282
+ def _dump_flows_as_separate_files(flows: List[Any], data_path: Path) -> None:
283
+ """Dump flow into separate files within a directory.
284
+
285
+ Creates a new directory under the given data_path and writes each flow
286
+ into a separate YAML file. Each file is named after the flow's id.
287
+
288
+ Args:
289
+ flows: List of new flows to be dumped.
290
+ data_path: The path to the directory where the files will be created.
291
+ """
292
+ # If there are no flows, don't create a directory.
293
+ if not flows:
294
+ return
295
+
296
+ new_flows_dir = data_path / STUDIO_FLOWS_DIR_NAME
297
+ new_flows_dir.mkdir(parents=True, exist_ok=True)
298
+ for flow in flows:
299
+ file_name = f"{flow.id}.yml"
300
+ file_path = new_flows_dir / file_name
301
+ single_flow_list = FlowsList(underlying_flows=[flow])
302
+ YamlFlowsWriter.dump(
303
+ flows=single_flow_list.underlying_flows,
304
+ filename=file_path,
305
+ should_clean_json=True,
306
+ )
307
+
308
+
309
+ def strip_default_next_references(flows: FlowsList) -> FlowsList:
310
+ """Strips default next references from flows.
311
+
312
+ Args:
313
+ flows: The FlowsList instance containing the flows.
314
+
315
+ Returns:
316
+ An updated FlowsList instance with default next references removed.
317
+ """
318
+ default_step_ids = [step.default_id for flow in flows for step in flow.steps]
319
+ for flow in flows:
320
+ for step in flow.steps:
321
+ if (
322
+ step.next.links
323
+ and isinstance(step.next.links[0], StaticFlowStepLink)
324
+ and step.next.links[0].target in default_step_ids
325
+ ):
326
+ step.next.links = []
327
+ return flows
328
+
329
+
330
+ def _select_path(
331
+ paths: Set[Path], primitive_type: str, default_path: Path, default: str
332
+ ) -> Path:
333
+ """Selects a path from a set of paths.
334
+
335
+ If exactly one path exists, returns it.
336
+ If multiple exist, returns one with a warning.
337
+ If none exist, returns a default path.
338
+
339
+ Args:
340
+ paths: A set of paths.
341
+ primitive_type: The type of the primitive (e.g., "domain", "nlu").
342
+ default_path: The default path to use if no paths exist.
343
+ default: The default file name.
344
+
345
+ Returns:
346
+ The selected path.
347
+ """
348
+ if len(paths) == 1:
349
+ path = paths.pop()
350
+ elif len(paths) > 1:
351
+ path = paths.pop()
352
+ logger.warning(
353
+ f"Saving {primitive_type} to {path}. "
354
+ f"Please keep Studio-related {primitive_type} in a single file."
355
+ )
356
+ else:
357
+ path = default_path / Path(default)
358
+ logger.info(f"Saving {primitive_type} to {path}.")
359
+ return path
@@ -1,6 +1,6 @@
1
1
  from dataclasses import dataclass
2
2
  from functools import wraps
3
- from typing import Any, Callable, Dict
3
+ from typing import Any, Callable, Dict, Text
4
4
 
5
5
  import structlog
6
6
  from keycloak.exceptions import KeycloakError
@@ -78,6 +78,11 @@ def response_has_errors(response: Dict) -> bool:
78
78
  )
79
79
 
80
80
 
81
+ def response_has_id(response: Dict[Text, Any]) -> bool:
82
+ response_id = response.get("id")
83
+ return isinstance(response_id, str) and bool(response_id)
84
+
85
+
81
86
  def _handle_rasa_exception(e: RasaException) -> StudioResult:
82
87
  error_msg = "Rasa internal exception was raised while interacting with Studio."
83
88
  structlogger.error("studio.rasa_error", event_info=error_msg, exception=str(e))
rasa/studio/upload.py CHANGED
@@ -158,11 +158,7 @@ def handle_upload(args: argparse.Namespace) -> None:
158
158
  RasaYAMLReader.expand_env_vars = False
159
159
  YAMLFlowsReader.expand_env_vars = False
160
160
 
161
- # check safely if args.calm is set and not fail if not
162
- if hasattr(args, "calm") and args.calm:
163
- upload_calm_assistant(args, endpoint, verify=verify)
164
- else:
165
- upload_nlu_assistant(args, endpoint, verify=verify)
161
+ upload_calm_assistant(args, endpoint, verify=verify)
166
162
 
167
163
 
168
164
  config_keys = [
@@ -226,6 +222,7 @@ def upload_calm_assistant(
226
222
  - endpoints: The path to the endpoints
227
223
  - config: The path to the config
228
224
  endpoint: The studio endpoint
225
+ verify: Whether to verify SSL
229
226
  Returns:
230
227
  None
231
228
  """
@@ -244,6 +241,7 @@ def upload_calm_assistant(
244
241
  # Prepare config and domain
245
242
  config = importer.get_config()
246
243
  assistant_name = _get_assistant_name(config)
244
+
247
245
  config_from_files = read_yaml_file(args.config, expand_env_vars=False)
248
246
  domain_from_files = importer.get_user_domain().as_dict()
249
247
 
@@ -563,3 +561,69 @@ def remove_quotes(node: Any) -> Any:
563
561
  return {k: remove_quotes(v) for k, v in node.items()}
564
562
  else:
565
563
  return node
564
+
565
+
566
+ def check_if_assistant_already_exists(
567
+ assistant_name: str, endpoint: str, verify: bool = True
568
+ ) -> bool:
569
+ """Checks if the assistant already exists in Studio.
570
+
571
+ Args:
572
+ assistant_name: The name of the assistant
573
+ endpoint: The studio endpoint
574
+ verify: Whether to verify SSL
575
+
576
+ Returns:
577
+ bool: The upload confirmation
578
+ """
579
+ graphql_req = build_get_assistant_by_name_request(assistant_name)
580
+
581
+ structlogger.info(
582
+ "rasa.studio.upload.assistant_already_exists",
583
+ event_info="Checking if assistant already exists...",
584
+ assistant_name=assistant_name,
585
+ )
586
+
587
+ token = KeycloakTokenReader().get_token()
588
+ res = requests.post(
589
+ endpoint,
590
+ json=graphql_req,
591
+ headers={
592
+ "Authorization": f"{token.token_type} {token.access_token}",
593
+ "Content-Type": "application/json",
594
+ },
595
+ verify=verify,
596
+ )
597
+ response = res.json()["data"]["assistantByName"] or {}
598
+ if results_logger.response_has_id(response):
599
+ structlogger.info(
600
+ "rasa.studio.upload.assistant_already_exists",
601
+ event_info="Assistant already exists.",
602
+ )
603
+ return True
604
+
605
+ structlogger.info(
606
+ "rasa.studio.upload.assistant_already_exists", event_info="Assistant not found."
607
+ )
608
+ return False
609
+
610
+
611
+ def build_get_assistant_by_name_request(
612
+ assistant_name: str,
613
+ ) -> Dict:
614
+ graphql_req = {
615
+ "query": (
616
+ "query AssistantByName($input: AssistantByNameInput!) {"
617
+ " assistantByName(input: $input) {"
618
+ " ... on Assistant { id name mode }"
619
+ " ... on AssistantByName_AssistantNotFound { _ }"
620
+ " }"
621
+ "}"
622
+ ),
623
+ "variables": {
624
+ "input": {
625
+ "assistantName": assistant_name,
626
+ }
627
+ },
628
+ }
629
+ return graphql_req
rasa/utils/common.py CHANGED
@@ -48,6 +48,7 @@ T = TypeVar("T")
48
48
 
49
49
  EXPECTED_WARNINGS: List[Tuple[Type[Warning], str]] = [
50
50
  # TODO (issue #9932)
51
+ # DM1 warnings
51
52
  (
52
53
  np.VisibleDeprecationWarning,
53
54
  "Creating an ndarray from ragged nested sequences.*",
@@ -94,6 +95,41 @@ EXPECTED_WARNINGS: List[Tuple[Type[Warning], str]] = [
94
95
  DeprecationWarning,
95
96
  "https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy",
96
97
  ),
98
+ # CALM warnings
99
+ # Ignore deprecation warning for ml_dtypes.float8_e4m3b11
100
+ (
101
+ DeprecationWarning,
102
+ "ml_dtypes.float8_e4m3b11 is deprecated. Use ml_dtypes.float8_e4m3b11fnuz",
103
+ ),
104
+ # Ignore deprecation warning for the load_module() method
105
+ (
106
+ DeprecationWarning,
107
+ "the load_module\\(\\) method is deprecated and slated for removal*",
108
+ ),
109
+ # Ignore deprecation warning for the rule-based policy
110
+ (
111
+ UserWarning,
112
+ "Found a rule-based policy in your configuration but no rule-based training*",
113
+ ),
114
+ # Ignore deprecation warning for the TEDPolicy
115
+ (
116
+ UserWarning,
117
+ "Skipping training of `TEDPolicy` as no data was provided.*",
118
+ ),
119
+ # Ignore deprecation warning for the LexicalSyntacticFeaturizer
120
+ (
121
+ UserWarning,
122
+ "No lexical syntactic features could be extracted from the training data.*",
123
+ ),
124
+ # Ignore deprecation warning for the LexicalSyntacticFeaturizer2
125
+ (
126
+ UserWarning,
127
+ "The LexicalSyntacticFeaturizer run_LexicalSyntacticFeaturizer2 has not been*",
128
+ ),
129
+ (
130
+ FutureWarning,
131
+ "'request_timeout' is deprecated and will be removed in 4.0.0. Use 'timeout'*",
132
+ ),
97
133
  ]
98
134
 
99
135
  PYTHON_LOGGING_SCHEMA_DOCS = (
rasa/utils/endpoints.py CHANGED
@@ -190,7 +190,13 @@ class EndpointConfig:
190
190
  sslcontext = None
191
191
  if self.cafile:
192
192
  try:
193
- sslcontext = ssl.create_default_context(cafile=self.cafile)
193
+ # create a SSL context with the provided CA file
194
+ # and set the minimum TLS version to 1.2
195
+ # Purpose is set to SERVER_AUTH to verify the server's certificate
196
+ sslcontext = ssl.create_default_context(
197
+ purpose=ssl.Purpose.SERVER_AUTH, cafile=self.cafile
198
+ )
199
+ sslcontext.minimum_version = ssl.TLSVersion.TLSv1_2
194
200
  except FileNotFoundError as e:
195
201
  raise FileNotFoundException(
196
202
  f"Failed to find certificate file, "
@@ -233,6 +239,21 @@ class EndpointConfig:
233
239
  **self.kwargs,
234
240
  )
235
241
 
242
+ def to_dict(self) -> Dict[Text, Any]:
243
+ """Convert the endpoint config to a dictionary."""
244
+ data = {
245
+ "url": self.url,
246
+ "params": self.params,
247
+ "headers": self.headers,
248
+ "basic_auth": self.basic_auth,
249
+ "token": self.token,
250
+ "token_name": self.token_name,
251
+ "cafile": self.cafile,
252
+ "actions_module": self.actions_module,
253
+ }
254
+ data.update(self.kwargs)
255
+ return data
256
+
236
257
  def __eq__(self, other: Any) -> bool:
237
258
  if isinstance(self, type(other)):
238
259
  return (
rasa/utils/licensing.py CHANGED
@@ -15,7 +15,6 @@ from dotenv import dotenv_values
15
15
  from sanic import Sanic
16
16
 
17
17
  from rasa import telemetry
18
- from rasa.core import jobs
19
18
  from rasa.shared.utils.cli import print_error_and_exit
20
19
 
21
20
  if typing.TYPE_CHECKING:
@@ -506,6 +505,7 @@ async def _schedule_conversation_counting(
506
505
  app: Sanic, tracker_store: Optional["TrackerStore"], max_number_conversations: int
507
506
  ) -> None:
508
507
  """Schedule a job counting the number of conversations in the current month."""
508
+ from rasa.core import jobs
509
509
 
510
510
  async def conversation_counting_job(
511
511
  app: Sanic,
rasa/validator.py CHANGED
@@ -531,7 +531,7 @@ class Validator:
531
531
  condition_active_loop
532
532
  and condition_active_loop not in self.domain.form_names
533
533
  ):
534
- structlogger.warn(
534
+ structlogger.error(
535
535
  "validator.verify_slot_mappings.not_in_domain",
536
536
  slot=slot.name,
537
537
  form=condition_active_loop,
@@ -566,7 +566,6 @@ class Validator:
566
566
  f"The slot needs to be added to this key."
567
567
  ),
568
568
  )
569
- everything_is_alright = False
570
569
 
571
570
  return everything_is_alright
572
571
 
rasa/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  # this file will automatically be changed,
2
2
  # do not add anything but the version number here!
3
- __version__ = "3.13.0.dev3"
3
+ __version__ = "3.13.0.dev5"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rasa-pro
3
- Version: 3.13.0.dev3
3
+ Version: 3.13.0.dev5
4
4
  Summary: State-of-the-art open-core Conversational AI framework for Enterprises that natively leverages generative AI for effortless assistant development.
5
5
  Keywords: nlp,machine-learning,machine-learning-library,bot,bots,botkit,rasa conversational-agents,conversational-ai,chatbot,chatbot-framework,bot-framework
6
6
  Author: Rasa Technologies GmbH
@@ -22,11 +22,11 @@ Provides-Extra: spacy
22
22
  Provides-Extra: transformers
23
23
  Requires-Dist: CacheControl (>=0.14.2,<0.15.0)
24
24
  Requires-Dist: PyJWT[crypto] (>=2.8.0,<3.0.0)
25
- Requires-Dist: SQLAlchemy (>=2.0.22,<2.1.0)
25
+ Requires-Dist: SQLAlchemy (>=2.0.40,<2.1.0)
26
26
  Requires-Dist: absl-py (>=2.0,<2.1)
27
27
  Requires-Dist: aio-pika (>=8.2.3,<9.4.4)
28
28
  Requires-Dist: aiogram (>=3.15,<3.16)
29
- Requires-Dist: aiohttp (>=3.9.4,<3.10)
29
+ Requires-Dist: aiohttp (>=3.10,<3.11)
30
30
  Requires-Dist: apscheduler (>=3.10,<3.11)
31
31
  Requires-Dist: attrs (>=23.1,<23.2)
32
32
  Requires-Dist: azure-identity (>=1.19.0,<1.20.0)
@@ -37,7 +37,7 @@ Requires-Dist: colorama (>=0.4.6,<0.5.0) ; sys_platform == "win32"
37
37
  Requires-Dist: colorclass (>=2.2,<2.3)
38
38
  Requires-Dist: coloredlogs (>=15,<16)
39
39
  Requires-Dist: colorhash (>=2.0,<2.1.0)
40
- Requires-Dist: confluent-kafka (>=2.3.0,<3.0.0)
40
+ Requires-Dist: confluent-kafka (>=2.10.0,<3.0.0)
41
41
  Requires-Dist: cryptography (>=44.0.1)
42
42
  Requires-Dist: cvg-python-sdk (>=0.5.1,<0.6.0)
43
43
  Requires-Dist: dask (>=2024.7.0,<2024.8.0)
@@ -63,7 +63,7 @@ Requires-Dist: keras (==2.14.0)
63
63
  Requires-Dist: langchain (>=0.2.17,<0.3.0)
64
64
  Requires-Dist: langchain-community (>=0.2.19,<0.3.0)
65
65
  Requires-Dist: langcodes (>=3.5.0,<4.0.0)
66
- Requires-Dist: litellm (>=1.52.6,<1.53.0)
66
+ Requires-Dist: litellm (==1.52.6)
67
67
  Requires-Dist: matplotlib (>=3.7,<3.8)
68
68
  Requires-Dist: mattermostwrapper (>=2.2,<2.3)
69
69
  Requires-Dist: networkx (>=3.1,<3.2)
@@ -83,7 +83,7 @@ Requires-Dist: presidio-anonymizer (>=2.2.354,<3.0.0)
83
83
  Requires-Dist: prompt-toolkit (>=3.0.28,<3.0.29)
84
84
  Requires-Dist: protobuf (>=4.23.3,<4.25.4)
85
85
  Requires-Dist: psutil (>=5.9.5,<6.0.0)
86
- Requires-Dist: psycopg2-binary (>=2.9.9,<2.10.0)
86
+ Requires-Dist: psycopg2-binary (>=2.9.10,<2.10.0)
87
87
  Requires-Dist: pycountry (>=22.3.5,<23.0.0)
88
88
  Requires-Dist: pydot (>=1.4,<1.5)
89
89
  Requires-Dist: pykwalify (>=1.8,<1.9)
@@ -92,7 +92,7 @@ Requires-Dist: pymongo (>=4.10.1,<4.11.0)
92
92
  Requires-Dist: pypred (>=0.4.0,<0.5.0)
93
93
  Requires-Dist: python-dateutil (>=2.8.2,<2.9.0)
94
94
  Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
95
- Requires-Dist: python-engineio (>=4.5.1,<6,!=5.0.0)
95
+ Requires-Dist: python-engineio (>=4.12.0,<6,!=5.0.0)
96
96
  Requires-Dist: python-keycloak (>=3.12.0,<4.0.0)
97
97
  Requires-Dist: python-socketio (>=5.8,<6)
98
98
  Requires-Dist: pytz (>=2022.7.1,<2023.0)
@@ -100,7 +100,7 @@ Requires-Dist: pyyaml (>=6.0)
100
100
  Requires-Dist: qdrant-client (>=1.9.1,<1.10.0)
101
101
  Requires-Dist: questionary (>=1.10.0,<2.1.0)
102
102
  Requires-Dist: randomname (>=0.2.1,<0.3.0)
103
- Requires-Dist: rasa-sdk (==3.12.0)
103
+ Requires-Dist: rasa-sdk (==3.13.0.dev1)
104
104
  Requires-Dist: redis (>=4.6.0,<6.0)
105
105
  Requires-Dist: regex (>=2024.7.24,<2024.8.0)
106
106
  Requires-Dist: requests (>=2.32.3,<2.33.0)