rasa-pro 3.8.18__py3-none-any.whl → 3.9.15__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rasa-pro might be problematic. Click here for more details.

Files changed (278) hide show
  1. README.md +6 -42
  2. rasa/__main__.py +14 -9
  3. rasa/anonymization/anonymization_pipeline.py +0 -1
  4. rasa/anonymization/anonymization_rule_executor.py +3 -3
  5. rasa/anonymization/utils.py +4 -3
  6. rasa/api.py +2 -2
  7. rasa/cli/arguments/default_arguments.py +1 -1
  8. rasa/cli/arguments/run.py +2 -2
  9. rasa/cli/arguments/test.py +1 -1
  10. rasa/cli/arguments/train.py +10 -10
  11. rasa/cli/e2e_test.py +27 -7
  12. rasa/cli/export.py +0 -1
  13. rasa/cli/license.py +3 -3
  14. rasa/cli/project_templates/calm/actions/action_template.py +1 -1
  15. rasa/cli/project_templates/calm/config.yml +1 -1
  16. rasa/cli/project_templates/calm/credentials.yml +1 -1
  17. rasa/cli/project_templates/calm/data/flows/add_contact.yml +1 -1
  18. rasa/cli/project_templates/calm/data/flows/remove_contact.yml +1 -1
  19. rasa/cli/project_templates/calm/domain/add_contact.yml +8 -2
  20. rasa/cli/project_templates/calm/domain/list_contacts.yml +3 -0
  21. rasa/cli/project_templates/calm/domain/remove_contact.yml +9 -2
  22. rasa/cli/project_templates/calm/domain/shared.yml +5 -0
  23. rasa/cli/project_templates/calm/endpoints.yml +4 -4
  24. rasa/cli/project_templates/default/actions/actions.py +1 -1
  25. rasa/cli/project_templates/default/config.yml +5 -5
  26. rasa/cli/project_templates/default/credentials.yml +1 -1
  27. rasa/cli/project_templates/default/endpoints.yml +4 -4
  28. rasa/cli/project_templates/default/tests/test_stories.yml +1 -1
  29. rasa/cli/project_templates/tutorial/config.yml +1 -1
  30. rasa/cli/project_templates/tutorial/credentials.yml +1 -1
  31. rasa/cli/project_templates/tutorial/data/patterns.yml +6 -0
  32. rasa/cli/project_templates/tutorial/domain.yml +4 -0
  33. rasa/cli/project_templates/tutorial/endpoints.yml +6 -6
  34. rasa/cli/run.py +0 -1
  35. rasa/cli/scaffold.py +3 -2
  36. rasa/cli/studio/download.py +11 -0
  37. rasa/cli/studio/studio.py +180 -24
  38. rasa/cli/studio/upload.py +0 -8
  39. rasa/cli/telemetry.py +18 -6
  40. rasa/cli/utils.py +21 -10
  41. rasa/cli/x.py +3 -2
  42. rasa/constants.py +1 -1
  43. rasa/core/actions/action.py +90 -315
  44. rasa/core/actions/action_exceptions.py +24 -0
  45. rasa/core/actions/constants.py +3 -0
  46. rasa/core/actions/custom_action_executor.py +188 -0
  47. rasa/core/actions/forms.py +11 -7
  48. rasa/core/actions/grpc_custom_action_executor.py +251 -0
  49. rasa/core/actions/http_custom_action_executor.py +140 -0
  50. rasa/core/actions/loops.py +3 -0
  51. rasa/core/actions/two_stage_fallback.py +1 -1
  52. rasa/core/agent.py +2 -4
  53. rasa/core/brokers/pika.py +1 -2
  54. rasa/core/channels/audiocodes.py +1 -1
  55. rasa/core/channels/botframework.py +0 -1
  56. rasa/core/channels/callback.py +0 -1
  57. rasa/core/channels/console.py +6 -8
  58. rasa/core/channels/development_inspector.py +1 -1
  59. rasa/core/channels/facebook.py +0 -3
  60. rasa/core/channels/hangouts.py +0 -6
  61. rasa/core/channels/inspector/dist/assets/{arc-5623b6dc.js → arc-b6e548fe.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-685c106a.js → c4Diagram-d0fbc5ce-fa03ac9e.js} +1 -1
  63. rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-8cbed007.js → classDiagram-936ed81e-ee67392a.js} +1 -1
  64. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-5889cf12.js → classDiagram-v2-c3cb15f1-9b283fae.js} +1 -1
  65. rasa/core/channels/inspector/dist/assets/{createText-62fc7601-24c249d7.js → createText-62fc7601-8b6fcc2a.js} +1 -1
  66. rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-7dd06a75.js → edges-f2ad444c-22e77f4f.js} +1 -1
  67. rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-62c1e54c.js → erDiagram-9d236eb7-60ffc87f.js} +1 -1
  68. rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-ce49b86f.js → flowDb-1972c806-9dd802e4.js} +1 -1
  69. rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-4067e48f.js → flowDiagram-7ea5b25a-5fa1912f.js} +1 -1
  70. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-1844e5a5.js +1 -0
  71. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-59fe4051.js → flowchart-elk-definition-abe16c3d-622a1fd2.js} +1 -1
  72. rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-47e3a43b.js → ganttDiagram-9b5ea136-e285a63a.js} +1 -1
  73. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-5a2ac0d9.js → gitGraphDiagram-99d0ae7c-f237bdca.js} +1 -1
  74. rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-dfb8efc4.js → index-2c4b9a3b-4b03d70e.js} +1 -1
  75. rasa/core/channels/inspector/dist/assets/{index-268a75c0.js → index-a5d3e69d.js} +4 -4
  76. rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-b0c470f2.js → infoDiagram-736b4530-72a0fa5f.js} +1 -1
  77. rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-2edb829a.js → journeyDiagram-df861f2b-82218c41.js} +1 -1
  78. rasa/core/channels/inspector/dist/assets/{layout-b6873d69.js → layout-78cff630.js} +1 -1
  79. rasa/core/channels/inspector/dist/assets/{line-1efc5781.js → line-5038b469.js} +1 -1
  80. rasa/core/channels/inspector/dist/assets/{linear-661e9b94.js → linear-c4fc4098.js} +1 -1
  81. rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-2d2e727f.js → mindmap-definition-beec6740-c33c8ea6.js} +1 -1
  82. rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-9d3ea93d.js → pieDiagram-dbbf0591-a8d03059.js} +1 -1
  83. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-06a178a2.js → quadrantDiagram-4d7f4fd6-6a0e56b2.js} +1 -1
  84. rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-0bfedffc.js → requirementDiagram-6fc4c22a-2dc7c7bd.js} +1 -1
  85. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-d76d0a04.js → sankeyDiagram-8f13d901-2360fe39.js} +1 -1
  86. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-37bb4341.js → sequenceDiagram-b655622a-41b9f9ad.js} +1 -1
  87. rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-f52f7f57.js → stateDiagram-59f0c015-0aad326f.js} +1 -1
  88. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-4a986a20.js → stateDiagram-v2-2b26beab-9847d984.js} +1 -1
  89. rasa/core/channels/inspector/dist/assets/{styles-080da4f6-7dd9ae12.js → styles-080da4f6-564d890e.js} +1 -1
  90. rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-46e1ca14.js → styles-3dcbcfbf-38957613.js} +1 -1
  91. rasa/core/channels/inspector/dist/assets/{styles-9c745c82-4a97439a.js → styles-9c745c82-f0fc6921.js} +1 -1
  92. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-823917a3.js → svgDrawCommon-4835440b-ef3c5a77.js} +1 -1
  93. rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-9ea72896.js → timeline-definition-5b62e21b-bf3e91c1.js} +1 -1
  94. rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-b631a8b6.js → xychartDiagram-2b33534f-4d4026c0.js} +1 -1
  95. rasa/core/channels/inspector/dist/index.html +1 -1
  96. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +10 -0
  97. rasa/core/channels/inspector/src/helpers/formatters.test.ts +4 -7
  98. rasa/core/channels/inspector/src/helpers/formatters.ts +3 -2
  99. rasa/core/channels/rest.py +36 -21
  100. rasa/core/channels/rocketchat.py +0 -1
  101. rasa/core/channels/socketio.py +1 -1
  102. rasa/core/channels/telegram.py +3 -3
  103. rasa/core/channels/webexteams.py +0 -1
  104. rasa/core/concurrent_lock_store.py +1 -1
  105. rasa/core/evaluation/marker_base.py +1 -3
  106. rasa/core/evaluation/marker_stats.py +1 -2
  107. rasa/core/featurizers/single_state_featurizer.py +3 -26
  108. rasa/core/featurizers/tracker_featurizers.py +18 -122
  109. rasa/core/information_retrieval/__init__.py +7 -0
  110. rasa/core/information_retrieval/faiss.py +9 -4
  111. rasa/core/information_retrieval/information_retrieval.py +64 -7
  112. rasa/core/information_retrieval/milvus.py +7 -14
  113. rasa/core/information_retrieval/qdrant.py +8 -15
  114. rasa/core/lock_store.py +0 -1
  115. rasa/core/migrate.py +1 -2
  116. rasa/core/nlg/callback.py +3 -4
  117. rasa/core/policies/enterprise_search_policy.py +86 -22
  118. rasa/core/policies/enterprise_search_prompt_template.jinja2 +4 -41
  119. rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +60 -0
  120. rasa/core/policies/flows/flow_executor.py +104 -2
  121. rasa/core/policies/intentless_policy.py +7 -9
  122. rasa/core/policies/memoization.py +3 -3
  123. rasa/core/policies/policy.py +18 -9
  124. rasa/core/policies/rule_policy.py +8 -11
  125. rasa/core/policies/ted_policy.py +61 -88
  126. rasa/core/policies/unexpected_intent_policy.py +8 -17
  127. rasa/core/processor.py +136 -47
  128. rasa/core/run.py +41 -25
  129. rasa/core/secrets_manager/endpoints.py +2 -2
  130. rasa/core/secrets_manager/vault.py +6 -8
  131. rasa/core/test.py +3 -5
  132. rasa/core/tracker_store.py +49 -14
  133. rasa/core/train.py +1 -3
  134. rasa/core/training/interactive.py +9 -6
  135. rasa/core/utils.py +5 -10
  136. rasa/dialogue_understanding/coexistence/intent_based_router.py +11 -4
  137. rasa/dialogue_understanding/coexistence/llm_based_router.py +2 -3
  138. rasa/dialogue_understanding/commands/__init__.py +4 -0
  139. rasa/dialogue_understanding/commands/can_not_handle_command.py +9 -0
  140. rasa/dialogue_understanding/commands/cancel_flow_command.py +9 -0
  141. rasa/dialogue_understanding/commands/change_flow_command.py +38 -0
  142. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +9 -0
  143. rasa/dialogue_understanding/commands/clarify_command.py +9 -0
  144. rasa/dialogue_understanding/commands/correct_slots_command.py +9 -0
  145. rasa/dialogue_understanding/commands/error_command.py +12 -0
  146. rasa/dialogue_understanding/commands/handle_code_change_command.py +9 -0
  147. rasa/dialogue_understanding/commands/human_handoff_command.py +9 -0
  148. rasa/dialogue_understanding/commands/knowledge_answer_command.py +9 -0
  149. rasa/dialogue_understanding/commands/noop_command.py +9 -0
  150. rasa/dialogue_understanding/commands/set_slot_command.py +38 -3
  151. rasa/dialogue_understanding/commands/skip_question_command.py +9 -0
  152. rasa/dialogue_understanding/commands/start_flow_command.py +9 -0
  153. rasa/dialogue_understanding/generator/__init__.py +16 -1
  154. rasa/dialogue_understanding/generator/command_generator.py +92 -6
  155. rasa/dialogue_understanding/generator/constants.py +18 -0
  156. rasa/dialogue_understanding/generator/flow_retrieval.py +7 -5
  157. rasa/dialogue_understanding/generator/llm_based_command_generator.py +467 -0
  158. rasa/dialogue_understanding/generator/llm_command_generator.py +39 -609
  159. rasa/dialogue_understanding/generator/multi_step/__init__.py +0 -0
  160. rasa/dialogue_understanding/generator/multi_step/fill_slots_prompt.jinja2 +62 -0
  161. rasa/dialogue_understanding/generator/multi_step/handle_flows_prompt.jinja2 +38 -0
  162. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +827 -0
  163. rasa/dialogue_understanding/generator/nlu_command_adapter.py +69 -8
  164. rasa/dialogue_understanding/generator/single_step/__init__.py +0 -0
  165. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +345 -0
  166. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +36 -31
  167. rasa/dialogue_understanding/processor/command_processor.py +112 -3
  168. rasa/e2e_test/constants.py +1 -0
  169. rasa/e2e_test/e2e_test_case.py +44 -0
  170. rasa/e2e_test/e2e_test_runner.py +114 -11
  171. rasa/e2e_test/e2e_test_schema.yml +18 -0
  172. rasa/engine/caching.py +0 -1
  173. rasa/engine/graph.py +18 -6
  174. rasa/engine/recipes/config_files/default_config.yml +3 -3
  175. rasa/engine/recipes/default_components.py +1 -1
  176. rasa/engine/recipes/default_recipe.py +4 -5
  177. rasa/engine/recipes/recipe.py +1 -1
  178. rasa/engine/runner/dask.py +3 -9
  179. rasa/engine/storage/local_model_storage.py +0 -2
  180. rasa/engine/validation.py +179 -145
  181. rasa/exceptions.py +2 -2
  182. rasa/graph_components/validators/default_recipe_validator.py +3 -5
  183. rasa/hooks.py +0 -1
  184. rasa/model.py +1 -1
  185. rasa/model_training.py +1 -0
  186. rasa/nlu/classifiers/diet_classifier.py +33 -52
  187. rasa/nlu/classifiers/logistic_regression_classifier.py +9 -22
  188. rasa/nlu/classifiers/sklearn_intent_classifier.py +16 -37
  189. rasa/nlu/extractors/crf_entity_extractor.py +54 -97
  190. rasa/nlu/extractors/duckling_entity_extractor.py +1 -1
  191. rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +1 -5
  192. rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +0 -4
  193. rasa/nlu/featurizers/featurizer.py +1 -1
  194. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +18 -49
  195. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +26 -64
  196. rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +3 -5
  197. rasa/nlu/persistor.py +68 -26
  198. rasa/nlu/selectors/response_selector.py +7 -10
  199. rasa/nlu/test.py +0 -3
  200. rasa/nlu/utils/hugging_face/registry.py +1 -1
  201. rasa/nlu/utils/spacy_utils.py +1 -3
  202. rasa/server.py +22 -7
  203. rasa/shared/constants.py +12 -1
  204. rasa/shared/core/command_payload_reader.py +109 -0
  205. rasa/shared/core/constants.py +4 -5
  206. rasa/shared/core/domain.py +57 -56
  207. rasa/shared/core/events.py +4 -7
  208. rasa/shared/core/flows/flow.py +9 -0
  209. rasa/shared/core/flows/flows_list.py +12 -0
  210. rasa/shared/core/flows/steps/action.py +7 -2
  211. rasa/shared/core/generator.py +12 -11
  212. rasa/shared/core/slot_mappings.py +315 -24
  213. rasa/shared/core/slots.py +4 -2
  214. rasa/shared/core/trackers.py +32 -14
  215. rasa/shared/core/training_data/loading.py +0 -1
  216. rasa/shared/core/training_data/story_reader/story_reader.py +3 -3
  217. rasa/shared/core/training_data/story_reader/yaml_story_reader.py +11 -11
  218. rasa/shared/core/training_data/story_writer/yaml_story_writer.py +5 -3
  219. rasa/shared/core/training_data/structures.py +1 -1
  220. rasa/shared/core/training_data/visualization.py +1 -1
  221. rasa/shared/data.py +58 -1
  222. rasa/shared/exceptions.py +36 -2
  223. rasa/shared/importers/importer.py +1 -2
  224. rasa/shared/importers/rasa.py +0 -1
  225. rasa/shared/nlu/constants.py +2 -0
  226. rasa/shared/nlu/training_data/entities_parser.py +1 -2
  227. rasa/shared/nlu/training_data/features.py +2 -120
  228. rasa/shared/nlu/training_data/formats/dialogflow.py +3 -2
  229. rasa/shared/nlu/training_data/formats/rasa_yaml.py +3 -5
  230. rasa/shared/nlu/training_data/formats/readerwriter.py +0 -1
  231. rasa/shared/nlu/training_data/message.py +13 -0
  232. rasa/shared/nlu/training_data/training_data.py +0 -2
  233. rasa/shared/providers/openai/session_handler.py +2 -2
  234. rasa/shared/utils/constants.py +3 -0
  235. rasa/shared/utils/io.py +11 -1
  236. rasa/shared/utils/llm.py +1 -2
  237. rasa/shared/utils/pykwalify_extensions.py +1 -0
  238. rasa/shared/utils/schemas/domain.yml +3 -0
  239. rasa/shared/utils/yaml.py +44 -35
  240. rasa/studio/auth.py +26 -10
  241. rasa/studio/constants.py +2 -0
  242. rasa/studio/data_handler.py +114 -107
  243. rasa/studio/download.py +160 -27
  244. rasa/studio/results_logger.py +137 -0
  245. rasa/studio/train.py +6 -7
  246. rasa/studio/upload.py +159 -134
  247. rasa/telemetry.py +188 -34
  248. rasa/tracing/config.py +18 -3
  249. rasa/tracing/constants.py +26 -2
  250. rasa/tracing/instrumentation/attribute_extractors.py +50 -41
  251. rasa/tracing/instrumentation/instrumentation.py +290 -44
  252. rasa/tracing/instrumentation/intentless_policy_instrumentation.py +7 -5
  253. rasa/tracing/instrumentation/metrics.py +109 -21
  254. rasa/tracing/metric_instrument_provider.py +83 -3
  255. rasa/utils/cli.py +2 -1
  256. rasa/utils/common.py +1 -1
  257. rasa/utils/endpoints.py +1 -2
  258. rasa/utils/io.py +72 -6
  259. rasa/utils/licensing.py +246 -31
  260. rasa/utils/ml_utils.py +1 -1
  261. rasa/utils/tensorflow/data_generator.py +1 -1
  262. rasa/utils/tensorflow/environment.py +1 -1
  263. rasa/utils/tensorflow/model_data.py +201 -12
  264. rasa/utils/tensorflow/model_data_utils.py +499 -500
  265. rasa/utils/tensorflow/models.py +5 -6
  266. rasa/utils/tensorflow/rasa_layers.py +15 -15
  267. rasa/utils/train_utils.py +1 -1
  268. rasa/utils/url_tools.py +53 -0
  269. rasa/validator.py +305 -3
  270. rasa/version.py +1 -1
  271. {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/METADATA +25 -61
  272. {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/RECORD +276 -259
  273. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-85583a23.js +0 -1
  274. rasa/utils/tensorflow/feature_array.py +0 -370
  275. /rasa/dialogue_understanding/generator/{command_prompt_template.jinja2 → single_step/command_prompt_template.jinja2} +0 -0
  276. {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/NOTICE +0 -0
  277. {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/WHEEL +0 -0
  278. {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/entry_points.txt +0 -0
rasa/studio/download.py CHANGED
@@ -1,19 +1,24 @@
1
1
  import argparse
2
2
  import logging
3
3
  from pathlib import Path
4
- from typing import Any, Dict, List, Optional, Set
4
+ from typing import Any, Dict, List, Optional, Set, Tuple
5
+
6
+ import questionary
7
+ import structlog
5
8
 
6
9
  import rasa.cli.utils
7
10
  import rasa.shared.utils.cli
8
11
  from rasa.shared.constants import (
9
12
  DEFAULT_DATA_PATH,
10
- DEFAULT_DOMAIN_PATHS,
13
+ DEFAULT_DOMAIN_PATH,
14
+ DEFAULT_CONFIG_PATH,
15
+ DEFAULT_ENDPOINTS_PATH,
11
16
  )
12
17
  from rasa.shared.core.domain import Domain
13
18
  from rasa.shared.core.flows.yaml_flows_io import YamlFlowsWriter
14
19
  from rasa.shared.importers.importer import TrainingDataImporter
15
20
  from rasa.shared.utils.yaml import read_yaml
16
-
21
+ from rasa.studio import data_handler
17
22
  from rasa.studio.config import StudioConfig
18
23
  from rasa.studio.constants import (
19
24
  STUDIO_DOMAIN_FILENAME,
@@ -21,13 +26,119 @@ from rasa.studio.constants import (
21
26
  STUDIO_NLU_FILENAME,
22
27
  )
23
28
  from rasa.studio.data_handler import (
24
- DataDiffGenerator,
25
29
  StudioDataHandler,
26
30
  import_data_from_studio,
27
31
  )
28
32
  from rasa.utils.mapper import RasaPrimitiveStorageMapper
29
33
 
30
34
  logger = logging.getLogger(__name__)
35
+ structlogger = structlog.getLogger(__name__)
36
+
37
+
38
+ def _handle_file_overwrite(
39
+ file_path: Optional[str], default_path: str, file_type: str
40
+ ) -> Tuple[Optional[Path], bool]:
41
+ """Handles the logic for determining whether to
42
+ overwrite an existing file or create a new one.
43
+ Works for config and endpoints at this moment
44
+
45
+ Args:
46
+ file_path (Optional[str]): The path to the file
47
+ provided by the user. Can be None.
48
+ default_path (str): The default path to use if `file_path`
49
+ is None or invalid. Must be a file path.
50
+ file_type (str): The type of the file (e.g., "config",
51
+ "endpoints") for logging and messaging purposes.
52
+
53
+ Returns:
54
+ tuple[Optional[Path], bool]: A tuple containing the
55
+ resolved file path and a boolean
56
+ indicating whether to write the file.
57
+ """
58
+ file_already_exists = rasa.cli.utils.get_validated_path(
59
+ file_path, file_type, default_path, none_is_valid=True
60
+ )
61
+ write_file = False
62
+ path = None
63
+ file_or_default_path = file_path or default_path
64
+
65
+ if file_already_exists is None:
66
+ path = Path(file_or_default_path)
67
+ if path.is_dir():
68
+ path = path / default_path
69
+ return path, True
70
+
71
+ if questionary.confirm(
72
+ f"{file_type.capitalize()} file '{file_or_default_path}' "
73
+ f"already exists. Do you want to overwrite it?"
74
+ ).ask():
75
+ write_file = True
76
+ path = Path(file_or_default_path)
77
+ return path, write_file
78
+
79
+
80
+ def _prepare_data_and_domain_paths(args: argparse.Namespace) -> Tuple[Path, List[Path]]:
81
+ """Handles the logic for preparing the domain and data paths
82
+ based on the provided arguments.
83
+
84
+ Args:
85
+ args (argparse.Namespace): The parsed arguments.
86
+
87
+ Returns:
88
+ tuple[Path, list[Path]]: A tuple containing the domain path
89
+ and a list of data paths.
90
+ """
91
+ # prepare domain
92
+ domain_path = rasa.cli.utils.get_validated_path(
93
+ args.domain, "domain", DEFAULT_DOMAIN_PATH, none_is_valid=True
94
+ )
95
+ domain_or_default_path = args.domain or DEFAULT_DOMAIN_PATH
96
+
97
+ if domain_path is None:
98
+ # If the path is None, use the provided domain path
99
+ domain_path = Path(domain_or_default_path)
100
+ domain_path.touch()
101
+
102
+ if isinstance(domain_path, str):
103
+ domain_path = Path(domain_path)
104
+
105
+ if domain_path.is_file():
106
+ if not args.overwrite:
107
+ domain_path.unlink()
108
+ domain_path.touch()
109
+
110
+ if domain_path.is_dir():
111
+ if not args.overwrite:
112
+ domain_path = domain_path / STUDIO_DOMAIN_FILENAME
113
+ domain_path.touch()
114
+
115
+ # prepare data
116
+ data_paths = []
117
+
118
+ for f in args.data:
119
+ data_path = rasa.cli.utils.get_validated_path(
120
+ f, "data", DEFAULT_DATA_PATH, none_is_valid=True
121
+ )
122
+
123
+ if data_path is None:
124
+ # If the path is None, use the default data path
125
+ data_path = Path(f)
126
+ data_path.mkdir(parents=True, exist_ok=True)
127
+ else:
128
+ data_path = Path(data_path)
129
+
130
+ if data_path.is_file() or data_path.is_dir():
131
+ # If it's a file, add it directly
132
+ data_paths.append(data_path)
133
+ else:
134
+ # If it doesn't exist, create the directory
135
+ data_path.mkdir(parents=True, exist_ok=True)
136
+ data_paths.append(data_path)
137
+
138
+ # Remove duplicates while preserving order
139
+ data_paths = list(dict.fromkeys(data_paths))
140
+
141
+ return domain_path, data_paths
31
142
 
32
143
 
33
144
  def handle_download(args: argparse.Namespace) -> None:
@@ -35,19 +146,43 @@ def handle_download(args: argparse.Namespace) -> None:
35
146
  studio_config=StudioConfig.read_config(), assistant_name=args.assistant_name[0]
36
147
  )
37
148
  handler.request_all_data()
38
- domain_path = rasa.cli.utils.get_validated_path(
39
- args.domain, "domain", DEFAULT_DOMAIN_PATHS, none_is_valid=True
149
+
150
+ domain_path, data_paths = _prepare_data_and_domain_paths(args)
151
+
152
+ # handle config and endpoints
153
+ config_path, write_config = _handle_file_overwrite(
154
+ args.config, DEFAULT_CONFIG_PATH, "config"
155
+ )
156
+ endpoints_path, write_endpoints = _handle_file_overwrite(
157
+ args.endpoints, DEFAULT_ENDPOINTS_PATH, "endpoints"
40
158
  )
41
- domain_path = Path(domain_path)
42
159
 
43
- data_paths = [
44
- Path(
45
- rasa.cli.utils.get_validated_path(
46
- f, "data", DEFAULT_DATA_PATH, none_is_valid=False
47
- )
48
- )
49
- for f in args.data
50
- ]
160
+ # generate log message if we write the config or endpoints
161
+ message_parts = []
162
+
163
+ config_path = config_path if write_config else None
164
+ endpoints_path = endpoints_path if write_endpoints else None
165
+
166
+ if config_path:
167
+ config_data = handler.get_config()
168
+ if not config_data:
169
+ rasa.shared.utils.cli.print_error_and_exit("No config data found.")
170
+ with open(config_path, "w") as f:
171
+ f.write(config_data)
172
+ message_parts.append(f"config to '{config_path}'")
173
+
174
+ if endpoints_path:
175
+ endpoints_data = handler.get_endpoints()
176
+ if not endpoints_data:
177
+ raise ValueError("No endpoints data found.")
178
+
179
+ with open(endpoints_path, "w") as f:
180
+ f.write(endpoints_data)
181
+ message_parts.append(f"endpoints to '{endpoints_path}'")
182
+
183
+ if message_parts:
184
+ message = "Downloaded " + " and ".join(message_parts)
185
+ structlogger.info("studio.download.config_endpoints", event_info=message)
51
186
 
52
187
  if not args.overwrite:
53
188
  _handle_download_no_overwrite(
@@ -74,11 +209,10 @@ def _handle_download_no_overwrite(
74
209
 
75
210
  if domain_path.is_dir():
76
211
  studio_domain_path = domain_path / STUDIO_DOMAIN_FILENAME
77
- diff_eng = DataDiffGenerator(
78
- original_domain=data_original.get_domain().as_dict(),
79
- studio_domain=data_from_studio.get_domain().as_dict(),
212
+ new_domain_data = data_handler.combine_domains(
213
+ data_from_studio.get_domain().as_dict(),
214
+ data_original.get_domain().as_dict(),
80
215
  )
81
- new_domain_data = diff_eng.create_new_domain_from_diff()
82
216
  studio_domain = Domain.from_dict(new_domain_data)
83
217
  if not studio_domain.is_empty():
84
218
  studio_domain.persist(studio_domain_path)
@@ -125,11 +259,10 @@ def _persist_nlu_diff(
125
259
  data_path: Path,
126
260
  ) -> None:
127
261
  """Creates a new nlu file from the diff of original and studio data."""
128
- diff_eng = DataDiffGenerator(
129
- original_nlu=read_yaml(data_original.get_nlu_data().nlu_as_yaml()),
130
- studio_nlu=read_yaml(data_from_studio.get_nlu_data().nlu_as_yaml()),
262
+ new_nlu_data = data_handler.create_new_nlu_from_diff(
263
+ read_yaml(data_from_studio.get_nlu_data().nlu_as_yaml()),
264
+ read_yaml(data_original.get_nlu_data().nlu_as_yaml()),
131
265
  )
132
- new_nlu_data = diff_eng.create_new_nlu_from_diff()
133
266
  if new_nlu_data["nlu"]:
134
267
  pretty_write_nlu_yaml(new_nlu_data, data_path)
135
268
  else:
@@ -142,11 +275,10 @@ def _persist_flows_diff(
142
275
  data_path: Path,
143
276
  ) -> None:
144
277
  """Creates a new flows file from the diff of original and studio data."""
145
- diff_eng = DataDiffGenerator(
146
- original_flows=data_original.get_flows().underlying_flows,
147
- studio_flows=data_from_studio.get_flows().underlying_flows,
278
+ new_flows_data = data_handler.create_new_flows_from_diff(
279
+ data_from_studio.get_flows().underlying_flows,
280
+ data_original.get_flows().underlying_flows,
148
281
  )
149
- new_flows_data = diff_eng.create_new_flows_from_diff()
150
282
  if new_flows_data:
151
283
  YamlFlowsWriter.dump(new_flows_data, data_path)
152
284
  else:
@@ -183,6 +315,7 @@ def _handle_download_with_overwrite(
183
315
  mapper = RasaPrimitiveStorageMapper(
184
316
  domain_path=domain_path, training_data_paths=data_paths
185
317
  )
318
+
186
319
  if domain_path.is_file():
187
320
  domain_merged = data_from_studio.get_domain().merge(data_original.get_domain())
188
321
  domain_merged.persist(domain_path)
@@ -0,0 +1,137 @@
1
+ from dataclasses import dataclass
2
+ from functools import wraps
3
+ from typing import Callable, Any, Dict
4
+
5
+ import structlog
6
+ from keycloak.exceptions import KeycloakError
7
+ from requests.exceptions import RequestException, Timeout, ConnectionError
8
+
9
+ from rasa.shared.exceptions import RasaException
10
+ from rasa.shared.utils.cli import print_success, print_error
11
+ from rasa.studio.config import StudioConfig
12
+
13
+ structlogger = structlog.get_logger()
14
+
15
+
16
+ @dataclass
17
+ class StudioResult:
18
+ message: str
19
+ was_successful: bool
20
+
21
+ @staticmethod
22
+ def success(message: str) -> "StudioResult":
23
+ return StudioResult(message, was_successful=True)
24
+
25
+ @staticmethod
26
+ def error(response: Dict[str, Any]) -> "StudioResult":
27
+ """Create a StudioResult from a GraphQL error response.
28
+
29
+ Factory will evaluate the response and return a StudioResult with the
30
+ appropriate message and success status.
31
+ """
32
+ if isinstance(response.get("errors"), list):
33
+ error_details = "; ".join(
34
+ [error.get("message", "Unknown error") for error in response["errors"]]
35
+ )
36
+ else:
37
+ error_details = "No detailed error information available."
38
+
39
+ structlogger.warn(
40
+ "studio.graphql_error", event_info=error_details, response=response
41
+ )
42
+ return StudioResult(error_details, was_successful=False)
43
+
44
+
45
+ def with_studio_error_handler(
46
+ func: Callable[..., StudioResult],
47
+ ) -> Callable[..., StudioResult]:
48
+ @wraps(func)
49
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
50
+ try:
51
+ result = func(*args, **kwargs)
52
+ if result.was_successful:
53
+ print_success(result.message)
54
+ else:
55
+ print_error(result.message)
56
+ return result
57
+ except RasaException as e:
58
+ return _handle_rasa_exception(e)
59
+ except KeycloakError as e:
60
+ return _handle_keycloak_error(e)
61
+ except ConnectionError as e:
62
+ return _handle_connection_error(e)
63
+ except Timeout as e:
64
+ return _handle_timeout_error(e)
65
+ except RequestException as e:
66
+ return _handle_request_exception(e)
67
+ except Exception as e:
68
+ return _handle_unexpected_error(e)
69
+
70
+ return wrapper
71
+
72
+
73
+ def response_has_errors(response: Dict) -> bool:
74
+ return (
75
+ "errors" in response
76
+ and isinstance(response["errors"], list)
77
+ and len(response["errors"]) > 0
78
+ )
79
+
80
+
81
+ def _handle_rasa_exception(e: RasaException) -> StudioResult:
82
+ error_msg = "Rasa internal exception was raised while interacting with Studio."
83
+ structlogger.error("studio.rasa_error", event_info=error_msg, exception=str(e))
84
+ return StudioResult(message=str(e), was_successful=False)
85
+
86
+
87
+ def _handle_keycloak_error(e: KeycloakError) -> StudioResult:
88
+ error_msg = (
89
+ f"Unable to authenticate with Keycloak at "
90
+ f"{StudioConfig.read_config().authentication_server_url} "
91
+ )
92
+ if e.response_code == 401:
93
+ error_msg += "Please check if the credentials are correct."
94
+ elif e.error_message.startswith("Can't connect to server"):
95
+ error_msg += (
96
+ "Please check if the server is running "
97
+ "and the configured URL is correct. \n"
98
+ "You may need to reconfigure Rasa Studio "
99
+ "using 'rasa studio config'."
100
+ )
101
+ else:
102
+ error_msg += f"Error message: {e.error_message}"
103
+ structlogger.error("studio.keycloak_error", event_info=error_msg, exception=str(e))
104
+ return StudioResult(error_msg, was_successful=False)
105
+
106
+
107
+ def _handle_connection_error(e: ConnectionError) -> StudioResult:
108
+ studio_url = StudioConfig.read_config().studio_url
109
+ error_msg = (
110
+ f"Unable to reach Rasa Studio API at {studio_url} \n"
111
+ "Please check if Studio is running and the configured URL is correct. \n"
112
+ "You may need to reconfigure Rasa Studio using 'rasa studio config'."
113
+ )
114
+ structlogger.error("studio.graphql_error", event_info=error_msg, exception=str(e))
115
+ return StudioResult(error_msg, was_successful=False)
116
+
117
+
118
+ def _handle_timeout_error(e: Timeout) -> StudioResult:
119
+ error_msg = "The request timed out. Please try again later."
120
+ structlogger.error("studio.graphql_timeout", event_info=error_msg, exception=str(e))
121
+ return StudioResult(error_msg, was_successful=False)
122
+
123
+
124
+ def _handle_request_exception(e: RequestException) -> StudioResult:
125
+ error_msg = f"An error occurred while communicating with the server: {e!s}"
126
+ structlogger.error(
127
+ "studio.graphql.request_exception", event_info=error_msg, exception=str(e)
128
+ )
129
+ return StudioResult(error_msg, was_successful=False)
130
+
131
+
132
+ def _handle_unexpected_error(e: Exception) -> StudioResult:
133
+ error_msg = f"An unexpected error occurred: {e!s}"
134
+ structlogger.exception(
135
+ "studio.unexpected_error", event_info=error_msg, exception=str(e)
136
+ )
137
+ return StudioResult(error_msg, was_successful=False)
rasa/studio/train.py CHANGED
@@ -19,11 +19,11 @@ from rasa.shared.constants import (
19
19
  from rasa.shared.core.flows.yaml_flows_io import YamlFlowsWriter
20
20
  from rasa.shared.importers.importer import TrainingDataImporter
21
21
  from rasa.shared.utils.yaml import read_yaml, write_yaml
22
+ from rasa.studio import data_handler
22
23
  from rasa.utils.common import get_temp_dir_name
23
24
 
24
25
  from rasa.studio.config import StudioConfig
25
26
  from rasa.studio.data_handler import (
26
- DataDiffGenerator,
27
27
  StudioDataHandler,
28
28
  import_data_from_studio,
29
29
  )
@@ -50,9 +50,9 @@ def handle_train(args: argparse.Namespace) -> Optional[str]:
50
50
  handler, domain, args.data
51
51
  )
52
52
 
53
- domain = data_original.get_domain().merge(data_form_studio.get_domain()) # type: ignore[assignment] # noqa: E501
53
+ domain = data_original.get_domain().merge(data_form_studio.get_domain()) # type: ignore[assignment]
54
54
 
55
- domain_file = _create_temp_file(read_yaml(domain.as_yaml()), "domain.yml") # type: ignore[union-attr] # noqa: E501
55
+ domain_file = _create_temp_file(read_yaml(domain.as_yaml()), "domain.yml") # type: ignore[union-attr]
56
56
 
57
57
  studio_training_files = make_training_files(
58
58
  handler, data_form_studio, data_original
@@ -116,11 +116,10 @@ def make_training_files(
116
116
  training_files.append(training_file)
117
117
 
118
118
  if handler.has_flows():
119
- diff = DataDiffGenerator(
120
- original_flows=data_original.get_flows().underlying_flows,
121
- studio_flows=data_form_studio.get_flows().underlying_flows,
119
+ diff_flows = data_handler.create_new_flows_from_diff(
120
+ data_form_studio.get_flows().underlying_flows,
121
+ data_original.get_flows().underlying_flows,
122
122
  )
123
- diff_flows = diff.create_new_flows_from_diff()
124
123
  tmp_dir = get_temp_dir_name()
125
124
  training_file = Path(tmp_dir, "flows.yml")
126
125
  YamlFlowsWriter.dump(diff_flows, training_file)