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/shared/utils/yaml.py CHANGED
@@ -1,40 +1,25 @@
1
- from io import StringIO
2
- from pathlib import Path
3
- import re
4
- from collections import OrderedDict
5
1
  import logging
6
2
  import os
3
+ import re
4
+ from collections import OrderedDict
5
+ from dataclasses import dataclass
6
+ from dataclasses import field
7
+ from functools import lru_cache
8
+ from io import StringIO
9
+ from pathlib import Path
7
10
  from typing import Dict, List, Optional, Any, Callable, Tuple, Union
8
11
 
9
-
12
+ import jsonschema
10
13
  from importlib_resources import files
11
14
  from packaging import version
12
15
  from packaging.version import LegacyVersion
13
16
  from pykwalify.core import Core
14
17
  from pykwalify.errors import SchemaError
15
- from dataclasses import dataclass
16
- import jsonschema
17
- from dataclasses import field
18
+ from ruamel import yaml as yaml
18
19
  from ruamel.yaml import RoundTripRepresenter, YAMLError
19
20
  from ruamel.yaml.constructor import DuplicateKeyError, BaseConstructor, ScalarNode
20
- from ruamel import yaml as yaml
21
21
  from ruamel.yaml.comments import CommentedSeq, CommentedMap
22
22
 
23
- from rasa.shared.utils.constants import DEFAULT_ENCODING
24
- from rasa.shared.utils.io import (
25
- read_file,
26
- convert_to_ordered_dict,
27
- raise_warning,
28
- read_json_file,
29
- )
30
-
31
- from rasa.shared.exceptions import (
32
- YamlException,
33
- YamlSyntaxException,
34
- SchemaValidationError,
35
- RasaException,
36
- FileNotFoundException,
37
- )
38
23
  from rasa.shared.constants import (
39
24
  MODEL_CONFIG_SCHEMA_FILE,
40
25
  CONFIG_SCHEMA_FILE,
@@ -44,11 +29,32 @@ from rasa.shared.constants import (
44
29
  SCHEMA_EXTENSIONS_FILE,
45
30
  RESPONSES_SCHEMA_FILE,
46
31
  )
32
+ from rasa.shared.exceptions import (
33
+ YamlException,
34
+ YamlSyntaxException,
35
+ SchemaValidationError,
36
+ RasaException,
37
+ FileNotFoundException,
38
+ )
39
+ from rasa.shared.utils.constants import (
40
+ DEFAULT_ENCODING,
41
+ READ_YAML_FILE_CACHE_MAXSIZE_ENV_VAR,
42
+ DEFAULT_READ_YAML_FILE_CACHE_MAXSIZE,
43
+ )
44
+ from rasa.shared.utils.io import (
45
+ read_file,
46
+ convert_to_ordered_dict,
47
+ raise_warning,
48
+ read_json_file,
49
+ )
47
50
 
48
51
  logger = logging.getLogger(__name__)
49
52
 
50
53
  KEY_TRAINING_DATA_FORMAT_VERSION = "version"
51
54
  YAML_VERSION = (1, 2)
55
+ READ_YAML_FILE_CACHE_MAXSIZE = os.environ.get(
56
+ READ_YAML_FILE_CACHE_MAXSIZE_ENV_VAR, DEFAULT_READ_YAML_FILE_CACHE_MAXSIZE
57
+ )
52
58
 
53
59
 
54
60
  @dataclass
@@ -437,8 +443,9 @@ def _is_ascii(text: str) -> bool:
437
443
  return all(ord(character) < 128 for character in text)
438
444
 
439
445
 
446
+ @lru_cache(maxsize=READ_YAML_FILE_CACHE_MAXSIZE)
440
447
  def read_yaml_file(
441
- filename: Union[str, Path], reader_type: Union[str, List[str]] = "safe"
448
+ filename: Union[str, Path], reader_type: Union[str, Tuple[str]] = "safe"
442
449
  ) -> Union[List[Any], Dict[str, Any]]:
443
450
  """Parses a yaml file.
444
451
 
@@ -452,7 +459,10 @@ def read_yaml_file(
452
459
  Parsed content of the file.
453
460
  """
454
461
  try:
455
- return read_yaml(read_file(filename, DEFAULT_ENCODING), reader_type)
462
+ fixed_reader_type = (
463
+ list(reader_type) if isinstance(reader_type, tuple) else reader_type
464
+ )
465
+ return read_yaml(read_file(filename, DEFAULT_ENCODING), fixed_reader_type)
456
466
  except (YAMLError, DuplicateKeyError) as e:
457
467
  raise YamlSyntaxException(filename, e)
458
468
 
@@ -635,14 +645,14 @@ def validate_training_data_format_version(
635
645
  """Validates version on the training data content using `version` field.
636
646
 
637
647
  Warns users if the file is not compatible with the current version of
638
- Rasa Open Source.
648
+ Rasa Pro.
639
649
 
640
650
  Args:
641
651
  yaml_file_content: Raw content of training data file as a dictionary.
642
652
  filename: Name of the validated file.
643
653
 
644
654
  Returns:
645
- `True` if the file can be processed by current version of Rasa Open Source,
655
+ `True` if the file can be processed by current version of Rasa Pro,
646
656
  `False` otherwise.
647
657
  """
648
658
  if filename:
@@ -662,7 +672,7 @@ def validate_training_data_format_version(
662
672
  logger.info(
663
673
  f"The '{KEY_TRAINING_DATA_FORMAT_VERSION}' key is missing in "
664
674
  f"the training data file {filename}. "
665
- f"Rasa Open Source will read the file as a "
675
+ f"Rasa Pro will read the file as a "
666
676
  f"version '{LATEST_TRAINING_DATA_FORMAT_VERSION}' file. "
667
677
  f"See {DOCS_URL_TRAINING_DATA}."
668
678
  )
@@ -680,9 +690,9 @@ def validate_training_data_format_version(
680
690
  if parsed_version < latest_version:
681
691
  raise_warning(
682
692
  f"Training data file {filename} has a lower "
683
- f"format version than your Rasa Open Source installation: "
693
+ f"format version than your Rasa Pro installation: "
684
694
  f"{version_value} < {LATEST_TRAINING_DATA_FORMAT_VERSION}. "
685
- f"Rasa Open Source will read the file as a version "
695
+ f"Rasa Pro will read the file as a version "
686
696
  f"{LATEST_TRAINING_DATA_FORMAT_VERSION} file. "
687
697
  f"Please update your version key to "
688
698
  f"{LATEST_TRAINING_DATA_FORMAT_VERSION}. "
@@ -690,7 +700,6 @@ def validate_training_data_format_version(
690
700
  )
691
701
 
692
702
  if latest_version >= parsed_version:
693
-
694
703
  return True
695
704
 
696
705
  except TypeError:
@@ -699,7 +708,7 @@ def validate_training_data_format_version(
699
708
  f"'{KEY_TRAINING_DATA_FORMAT_VERSION}' as string, for example:\n"
700
709
  f"{KEY_TRAINING_DATA_FORMAT_VERSION}: "
701
710
  f"'{LATEST_TRAINING_DATA_FORMAT_VERSION}'\n"
702
- f"Rasa Open Source will read the file as a "
711
+ f"Rasa Pro will read the file as a "
703
712
  f"version '{LATEST_TRAINING_DATA_FORMAT_VERSION}' file.",
704
713
  docs=DOCS_URL_TRAINING_DATA,
705
714
  )
@@ -707,9 +716,9 @@ def validate_training_data_format_version(
707
716
 
708
717
  raise_warning(
709
718
  f"Training data file {filename} has a greater "
710
- f"format version than your Rasa Open Source installation: "
719
+ f"format version than your Rasa Pro installation: "
711
720
  f"{version_value} > {LATEST_TRAINING_DATA_FORMAT_VERSION}. "
712
- f"Please consider updating to the latest version of Rasa Open Source."
721
+ f"Please consider updating to the latest version of Rasa Pro."
713
722
  f"This file will be skipped.",
714
723
  docs=DOCS_URL_TRAINING_DATA,
715
724
  )
rasa/studio/auth.py CHANGED
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
  from typing import Any, Dict, List, Optional, Text, Union
7
7
 
8
8
  import jwt
9
- from keycloak import KeycloakOpenID
9
+ from keycloak import KeycloakOpenID, KeycloakError
10
10
  from rasa.shared.exceptions import RasaException
11
11
  from rasa.shared.utils.yaml import read_yaml_file, write_yaml
12
12
 
@@ -17,6 +17,7 @@ from rasa.studio.constants import (
17
17
  KEYCLOAK_REFRESH_EXPIRES_IN_KEY,
18
18
  KEYCLOAK_REFRESH_TOKEN,
19
19
  )
20
+ from rasa.studio.results_logger import with_studio_error_handler, StudioResult
20
21
 
21
22
 
22
23
  class StudioAuth:
@@ -31,26 +32,39 @@ class StudioAuth:
31
32
  realm_name=studio_config.realm_name,
32
33
  )
33
34
 
34
- def login(self, username: Text, password: Text, totp: Optional[int] = None) -> None:
35
+ def health_check(self) -> bool:
36
+ """Check if the Keycloak server is reachable.
35
37
 
38
+ Returns:
39
+ True if the server is reachable, False otherwise.
40
+ """
36
41
  try:
37
- token_dict = self.keycloak_openid.token(
38
- username=username, password=password, totp=totp
39
- )
40
- except Exception as e:
41
- raise RasaException(f"Could not login. Error: {e}")
42
-
42
+ self.keycloak_openid.well_known()
43
+ return True
44
+ except Exception:
45
+ return False
46
+
47
+ @with_studio_error_handler
48
+ def login(
49
+ self, username: Text, password: Text, totp: Optional[int] = None
50
+ ) -> StudioResult:
51
+ token_dict = self.keycloak_openid.token(
52
+ username=username, password=password, totp=totp
53
+ )
43
54
  keycloak_token = self._resolve_token(token_dict)
44
55
 
45
56
  KeycloakTokenWriter.write_token_to_file(
46
57
  keycloak_token, token_file_location=DEFAULT_TOKEN_FILE_PATH
47
58
  )
48
59
 
49
- def refresh_token(self, refresh_token: Text) -> None:
60
+ return StudioResult.success("Login successful.")
61
+
62
+ @with_studio_error_handler
63
+ def refresh_token(self, refresh_token: Text) -> StudioResult:
50
64
  try:
51
65
  token_dict = self.keycloak_openid.refresh_token(refresh_token)
52
66
  except Exception as e:
53
- raise RasaException(f"Could not refresh token. Error: {e}")
67
+ raise KeycloakError(f"Could not refresh token. Error: {e}")
54
68
 
55
69
  keycloak_token = self._resolve_token(token_dict)
56
70
 
@@ -58,6 +72,8 @@ class StudioAuth:
58
72
  keycloak_token, token_file_location=DEFAULT_TOKEN_FILE_PATH
59
73
  )
60
74
 
75
+ return StudioResult.success("Token refreshed successfully.")
76
+
61
77
  @staticmethod
62
78
  def _resolve_token(token_dict: Dict[Text, Any]) -> KeycloakToken:
63
79
  return KeycloakToken(
rasa/studio/constants.py CHANGED
@@ -14,3 +14,5 @@ RASA_STUDIO_CLI_CLIENT_ID_KEY_ENV = "RASA_STUDIO_CLI_CLIENT_ID_KEY"
14
14
  STUDIO_NLU_FILENAME = "studio_nlu.yml"
15
15
  STUDIO_DOMAIN_FILENAME = "studio_domain.yml"
16
16
  STUDIO_FLOWS_FILENAME = "studio_flows.yml"
17
+ STUDIO_CONFIG_FILENAME = "studio_config.yml"
18
+ STUDIO_ENDPOINTS_FILENAME = "studio_endpoints.yml"
@@ -50,7 +50,7 @@ class StudioDataHandler:
50
50
  "query ExportAsEncodedYaml($input: ExportAsEncodedYamlInput!) "
51
51
  "{ exportAsEncodedYaml(input: $input) "
52
52
  "{ ... on ExportModernAsEncodedYamlOutput "
53
- "{ nlu flows domain } "
53
+ "{ nlu flows domain endpoints config } "
54
54
  "... on ExportClassicAsEncodedYamlOutput "
55
55
  "{ nlu domain }}}"
56
56
  ),
@@ -148,6 +148,12 @@ class StudioDataHandler:
148
148
  response = self._make_request(GQL_req)
149
149
  self._extract_data(response)
150
150
 
151
+ def get_config(self) -> Optional[str]:
152
+ return self.config
153
+
154
+ def get_endpoints(self) -> Optional[str]:
155
+ return self.endpoints
156
+
151
157
  def _validate_response(self, response: dict) -> bool:
152
158
  """Validates the response from Rasa Studio.
153
159
 
@@ -184,6 +190,8 @@ class StudioDataHandler:
184
190
  self.nlu = self._decode_response(return_data.get("nlu"))
185
191
  self.domain = self._decode_response(return_data.get("domain"))
186
192
  self.flows = self._decode_response(return_data.get("flows"))
193
+ self.config = self._decode_response(return_data.get("config"))
194
+ self.endpoints = self._decode_response(return_data.get("endpoints"))
187
195
 
188
196
  if not self.has_nlu() and not self.has_flows():
189
197
  raise RasaException("No nlu or flows data in Studio response.")
@@ -195,114 +203,113 @@ class StudioDataHandler:
195
203
  return base64.b64decode(data).decode("utf-8")
196
204
 
197
205
 
198
- class DataDiffGenerator:
199
- """Generate diff between original and studio data."""
200
-
201
- def __init__(
202
- self,
203
- original_domain: Optional[Dict] = None,
204
- studio_domain: Optional[Dict] = None,
205
- original_nlu: Optional[Dict[str, List]] = None,
206
- studio_nlu: Optional[Dict[str, List]] = None,
207
- original_flows: Optional[List[Flow]] = None,
208
- studio_flows: Optional[List[Flow]] = None,
209
- ):
210
- self.original_domain = original_domain
211
- self.studio_domain = studio_domain
212
- self.original_nlu = original_nlu
213
- self.studio_nlu = studio_nlu
214
- self.original_flows = original_flows
215
- self.studio_flows = studio_flows
216
-
217
- def create_new_domain_from_diff(self) -> Dict:
218
- """Create a new domain file from the diff."""
219
- if self.studio_domain is None or self.original_domain is None:
220
- return {}
221
- return self._domain_from_diff_rec(self.studio_domain, self.original_domain)
222
-
223
- @staticmethod
224
- def _domain_from_diff_rec(studio: Dict, original: Dict) -> Dict:
225
- ret_dict = {}
226
- for key in studio:
227
- if key not in original:
228
- ret_dict[key] = studio[key]
229
- elif isinstance(studio[key], dict):
230
- ret_dict[key] = DataDiffGenerator._domain_from_diff_rec(
231
- studio[key], original[key]
232
- )
233
- # remove empty diffs
234
- if not ret_dict[key]:
235
- del ret_dict[key]
236
- elif key not in [KEY_SLOTS, KEY_RESPONSES]:
237
- # copy over the whole key not just the diff in case of items
238
- # to get complete (valid) data not just the diff
239
- ret_dict[key] = studio[key]
240
- elif isinstance(studio[key], list):
241
- ret_dict[key] = []
242
- for item in studio[key]:
243
- if item not in original[key]:
244
- ret_dict[key].append(item)
245
-
246
- # if list is empty, remove it
247
- if not ret_dict[key]:
248
- del ret_dict[key]
249
-
250
- return ret_dict
251
-
252
- def create_new_nlu_from_diff(self) -> Dict:
253
- """Create a new nlu file from the diff."""
254
- if self.studio_nlu is None or self.original_nlu is None:
255
- return {"nlu": []}
256
-
257
- nlu_diff = []
258
- nlu_new_examples = [
259
- new for new in self.studio_nlu["nlu"] if new not in self.original_nlu["nlu"]
260
- ]
261
- if nlu_new_examples:
262
- for new_example in nlu_new_examples:
263
- nlu_diff.append(new_example)
264
- intent = new_example.get("intent")
265
- if intent:
266
- self._diff_nlu_examples(new_example, nlu_diff, "intent", intent)
267
- synonym = new_example.get("synonym")
268
- if synonym:
269
- self._diff_nlu_examples(new_example, nlu_diff, "synonym", synonym)
270
-
271
- return {"nlu": nlu_diff}
272
-
273
- def create_new_flows_from_diff(self) -> List[Flow]:
274
- """Create a new flows file from the diff."""
275
- if self.studio_flows is None or self.original_flows is None:
276
- return []
277
-
278
- flows_new = [new for new in self.studio_flows if new not in self.original_flows]
279
- return flows_new
280
-
281
- def _diff_nlu_examples(
282
- self, new_example: Dict, nlu_diff: List, match_key: str, match_value: str
283
- ) -> None:
284
- """Creates diff of nlu data examples.
285
-
286
- Args:
287
- new_example (Dict): intent or synonym with examples
288
- nlu_diff (List): list of diff examples
289
- match_key (str): intent or synonym name
290
- match_value (str): intent or synonym value
291
- """
292
- orig = list(
293
- filter(
294
- lambda x: x.get(match_key) == match_value,
295
- self.original_nlu["nlu"], # type: ignore[index]
206
+ def combine_domains(
207
+ studio_domain: Dict[str, Any], original_domain: Dict[str, Any]
208
+ ) -> Dict:
209
+ """Create a new domain file from the diff."""
210
+ if studio_domain is None or original_domain is None:
211
+ return {}
212
+ return _combine_domain_keys(studio_domain, original_domain)
213
+
214
+
215
+ def _combine_domain_keys(
216
+ first_domain: Dict[str, Any], second_domain: Dict[str, Any]
217
+ ) -> Dict[str, Any]:
218
+ combined_keys = {}
219
+ for key in first_domain:
220
+ if key not in second_domain:
221
+ combined_keys[key] = first_domain[key]
222
+ elif isinstance(first_domain[key], dict):
223
+ combined_keys[key] = _combine_domain_keys(
224
+ first_domain[key], second_domain[key]
296
225
  )
226
+ # remove empty diffs
227
+ if not combined_keys[key]:
228
+ del combined_keys[key]
229
+ elif key not in [KEY_SLOTS, KEY_RESPONSES]:
230
+ # for all keys except slots and responses, we want to keep the
231
+ # keys from the first domain
232
+ combined_keys[key] = first_domain[key]
233
+ elif isinstance(first_domain[key], list):
234
+ combined_keys[key] = []
235
+ for item in first_domain[key]:
236
+ if item not in second_domain[key]:
237
+ combined_keys[key].append(item)
238
+
239
+ # if list is empty, remove it
240
+ if not combined_keys[key]:
241
+ del combined_keys[key]
242
+
243
+ return combined_keys
244
+
245
+
246
+ def _diff_nlu_examples(
247
+ new_example: Dict,
248
+ nlu_diff: List,
249
+ match_key: str,
250
+ match_value: str,
251
+ original_nlu_examples: List,
252
+ ) -> None:
253
+ """Creates diff of nlu data examples.
254
+
255
+ Args:
256
+ new_example (Dict): intent or synonym with examples
257
+ nlu_diff (List): list of diff examples
258
+ match_key (str): intent or synonym name
259
+ match_value (str): intent or synonym value
260
+ original_nlu_examples (List): original nlu examples
261
+ """
262
+ orig = list(
263
+ filter(
264
+ lambda x: x.get(match_key) == match_value,
265
+ original_nlu_examples,
297
266
  )
298
- if len(orig) == 1:
299
- orig_ex = orig[0]["examples"].split("\n")
300
- new_ex = new_example["examples"].split("\n")
301
- new_example["examples"] = "\n".join(
302
- list(set(new_ex).difference(set(orig_ex)))
303
- )
304
- if not new_example["examples"]:
305
- nlu_diff.remove(new_example)
267
+ )
268
+ if len(orig) == 1:
269
+ orig_ex = orig[0]["examples"].split("\n")
270
+ new_ex = new_example["examples"].split("\n")
271
+ new_example["examples"] = "\n".join(list(set(new_ex).difference(set(orig_ex))))
272
+ if not new_example["examples"]:
273
+ nlu_diff.remove(new_example)
274
+
275
+
276
+ def create_new_nlu_from_diff(
277
+ studio_nlu: Dict[str, Any], original_nlu: Dict[str, Any]
278
+ ) -> Dict:
279
+ """Create a new nlu file from the diff."""
280
+ # `or []` handles the case where the data contains the property as an empty
281
+ # key, example yaml:
282
+ # ```
283
+ # nlu:
284
+ # ```
285
+ # in this case, the yaml parser will return an empty dict (because it
286
+ # can't know that it is supposed to be a list, so we need to convert it
287
+ # to a list
288
+ studio_nlu_data = studio_nlu.get("nlu", []) or []
289
+ original_nlu_data = original_nlu.get("nlu", []) or []
290
+
291
+ nlu_diff = []
292
+ for new in studio_nlu_data:
293
+ if new in original_nlu_data:
294
+ continue
295
+
296
+ nlu_diff.append(new)
297
+ intent = new.get("intent")
298
+ if intent:
299
+ _diff_nlu_examples(new, nlu_diff, "intent", intent, original_nlu_data)
300
+ synonym = new.get("synonym")
301
+ if synonym:
302
+ _diff_nlu_examples(new, nlu_diff, "synonym", synonym, original_nlu_data)
303
+
304
+ return {"nlu": nlu_diff}
305
+
306
+
307
+ def create_new_flows_from_diff(
308
+ studio_flows: List[Flow], original_flows: List[Flow]
309
+ ) -> List[Flow]:
310
+ """Create a new flows file from the diff."""
311
+ flows_new = [new for new in studio_flows if new not in original_flows]
312
+ return flows_new
306
313
 
307
314
 
308
315
  def import_data_from_studio(