rasa-pro 3.9.18__py3-none-any.whl → 3.10.16__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 (183) hide show
  1. README.md +0 -374
  2. rasa/__init__.py +1 -2
  3. rasa/__main__.py +5 -0
  4. rasa/anonymization/anonymization_rule_executor.py +2 -2
  5. rasa/api.py +27 -23
  6. rasa/cli/arguments/data.py +27 -2
  7. rasa/cli/arguments/default_arguments.py +25 -3
  8. rasa/cli/arguments/run.py +9 -9
  9. rasa/cli/arguments/train.py +11 -3
  10. rasa/cli/data.py +70 -8
  11. rasa/cli/e2e_test.py +104 -431
  12. rasa/cli/evaluate.py +1 -1
  13. rasa/cli/interactive.py +1 -0
  14. rasa/cli/llm_fine_tuning.py +398 -0
  15. rasa/cli/project_templates/calm/endpoints.yml +1 -1
  16. rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
  17. rasa/cli/run.py +15 -14
  18. rasa/cli/scaffold.py +10 -8
  19. rasa/cli/studio/studio.py +35 -5
  20. rasa/cli/train.py +56 -8
  21. rasa/cli/utils.py +22 -5
  22. rasa/cli/x.py +1 -1
  23. rasa/constants.py +7 -1
  24. rasa/core/actions/action.py +98 -49
  25. rasa/core/actions/action_run_slot_rejections.py +4 -1
  26. rasa/core/actions/custom_action_executor.py +9 -6
  27. rasa/core/actions/direct_custom_actions_executor.py +80 -0
  28. rasa/core/actions/e2e_stub_custom_action_executor.py +68 -0
  29. rasa/core/actions/grpc_custom_action_executor.py +2 -2
  30. rasa/core/actions/http_custom_action_executor.py +6 -5
  31. rasa/core/agent.py +21 -17
  32. rasa/core/channels/__init__.py +2 -0
  33. rasa/core/channels/audiocodes.py +1 -16
  34. rasa/core/channels/voice_aware/__init__.py +0 -0
  35. rasa/core/channels/voice_aware/jambonz.py +103 -0
  36. rasa/core/channels/voice_aware/jambonz_protocol.py +344 -0
  37. rasa/core/channels/voice_aware/utils.py +20 -0
  38. rasa/core/channels/voice_native/__init__.py +0 -0
  39. rasa/core/constants.py +6 -1
  40. rasa/core/information_retrieval/faiss.py +7 -4
  41. rasa/core/information_retrieval/information_retrieval.py +8 -0
  42. rasa/core/information_retrieval/milvus.py +9 -2
  43. rasa/core/information_retrieval/qdrant.py +1 -1
  44. rasa/core/nlg/contextual_response_rephraser.py +32 -10
  45. rasa/core/nlg/summarize.py +4 -3
  46. rasa/core/policies/enterprise_search_policy.py +113 -45
  47. rasa/core/policies/flows/flow_executor.py +122 -76
  48. rasa/core/policies/intentless_policy.py +83 -29
  49. rasa/core/processor.py +72 -54
  50. rasa/core/run.py +5 -4
  51. rasa/core/tracker_store.py +8 -4
  52. rasa/core/training/interactive.py +1 -1
  53. rasa/core/utils.py +56 -57
  54. rasa/dialogue_understanding/coexistence/llm_based_router.py +53 -13
  55. rasa/dialogue_understanding/commands/__init__.py +6 -0
  56. rasa/dialogue_understanding/commands/restart_command.py +58 -0
  57. rasa/dialogue_understanding/commands/session_start_command.py +59 -0
  58. rasa/dialogue_understanding/commands/utils.py +40 -0
  59. rasa/dialogue_understanding/generator/constants.py +10 -3
  60. rasa/dialogue_understanding/generator/flow_retrieval.py +21 -5
  61. rasa/dialogue_understanding/generator/llm_based_command_generator.py +13 -3
  62. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +134 -90
  63. rasa/dialogue_understanding/generator/nlu_command_adapter.py +47 -7
  64. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +127 -41
  65. rasa/dialogue_understanding/patterns/restart.py +37 -0
  66. rasa/dialogue_understanding/patterns/session_start.py +37 -0
  67. rasa/dialogue_understanding/processor/command_processor.py +16 -3
  68. rasa/dialogue_understanding/processor/command_processor_component.py +6 -2
  69. rasa/e2e_test/aggregate_test_stats_calculator.py +134 -0
  70. rasa/e2e_test/assertions.py +1223 -0
  71. rasa/e2e_test/assertions_schema.yml +106 -0
  72. rasa/e2e_test/constants.py +20 -0
  73. rasa/e2e_test/e2e_config.py +220 -0
  74. rasa/e2e_test/e2e_config_schema.yml +26 -0
  75. rasa/e2e_test/e2e_test_case.py +131 -8
  76. rasa/e2e_test/e2e_test_converter.py +363 -0
  77. rasa/e2e_test/e2e_test_converter_prompt.jinja2 +70 -0
  78. rasa/e2e_test/e2e_test_coverage_report.py +364 -0
  79. rasa/e2e_test/e2e_test_result.py +26 -6
  80. rasa/e2e_test/e2e_test_runner.py +493 -71
  81. rasa/e2e_test/e2e_test_schema.yml +96 -0
  82. rasa/e2e_test/pykwalify_extensions.py +39 -0
  83. rasa/e2e_test/stub_custom_action.py +70 -0
  84. rasa/e2e_test/utils/__init__.py +0 -0
  85. rasa/e2e_test/utils/e2e_yaml_utils.py +55 -0
  86. rasa/e2e_test/utils/io.py +598 -0
  87. rasa/e2e_test/utils/validation.py +80 -0
  88. rasa/engine/graph.py +9 -3
  89. rasa/engine/recipes/default_components.py +0 -2
  90. rasa/engine/recipes/default_recipe.py +10 -2
  91. rasa/engine/storage/local_model_storage.py +40 -12
  92. rasa/engine/validation.py +78 -1
  93. rasa/env.py +9 -0
  94. rasa/graph_components/providers/story_graph_provider.py +59 -6
  95. rasa/llm_fine_tuning/__init__.py +0 -0
  96. rasa/llm_fine_tuning/annotation_module.py +241 -0
  97. rasa/llm_fine_tuning/conversations.py +144 -0
  98. rasa/llm_fine_tuning/llm_data_preparation_module.py +178 -0
  99. rasa/llm_fine_tuning/notebooks/unsloth_finetuning.ipynb +407 -0
  100. rasa/llm_fine_tuning/paraphrasing/__init__.py +0 -0
  101. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +281 -0
  102. rasa/llm_fine_tuning/paraphrasing/default_rephrase_prompt_template.jina2 +44 -0
  103. rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +121 -0
  104. rasa/llm_fine_tuning/paraphrasing/rephrased_user_message.py +10 -0
  105. rasa/llm_fine_tuning/paraphrasing_module.py +128 -0
  106. rasa/llm_fine_tuning/storage.py +174 -0
  107. rasa/llm_fine_tuning/train_test_split_module.py +441 -0
  108. rasa/model_training.py +56 -16
  109. rasa/nlu/persistor.py +157 -36
  110. rasa/server.py +45 -10
  111. rasa/shared/constants.py +76 -16
  112. rasa/shared/core/domain.py +27 -19
  113. rasa/shared/core/events.py +28 -2
  114. rasa/shared/core/flows/flow.py +208 -13
  115. rasa/shared/core/flows/flow_path.py +84 -0
  116. rasa/shared/core/flows/flows_list.py +33 -11
  117. rasa/shared/core/flows/flows_yaml_schema.json +269 -193
  118. rasa/shared/core/flows/validation.py +112 -25
  119. rasa/shared/core/flows/yaml_flows_io.py +149 -10
  120. rasa/shared/core/trackers.py +6 -0
  121. rasa/shared/core/training_data/structures.py +20 -0
  122. rasa/shared/core/training_data/visualization.html +2 -2
  123. rasa/shared/exceptions.py +4 -0
  124. rasa/shared/importers/importer.py +64 -16
  125. rasa/shared/nlu/constants.py +2 -0
  126. rasa/shared/providers/_configs/__init__.py +0 -0
  127. rasa/shared/providers/_configs/azure_openai_client_config.py +183 -0
  128. rasa/shared/providers/_configs/client_config.py +57 -0
  129. rasa/shared/providers/_configs/default_litellm_client_config.py +130 -0
  130. rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +234 -0
  131. rasa/shared/providers/_configs/openai_client_config.py +175 -0
  132. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +176 -0
  133. rasa/shared/providers/_configs/utils.py +101 -0
  134. rasa/shared/providers/_ssl_verification_utils.py +124 -0
  135. rasa/shared/providers/embedding/__init__.py +0 -0
  136. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +259 -0
  137. rasa/shared/providers/embedding/_langchain_embedding_client_adapter.py +74 -0
  138. rasa/shared/providers/embedding/azure_openai_embedding_client.py +277 -0
  139. rasa/shared/providers/embedding/default_litellm_embedding_client.py +102 -0
  140. rasa/shared/providers/embedding/embedding_client.py +90 -0
  141. rasa/shared/providers/embedding/embedding_response.py +41 -0
  142. rasa/shared/providers/embedding/huggingface_local_embedding_client.py +191 -0
  143. rasa/shared/providers/embedding/openai_embedding_client.py +172 -0
  144. rasa/shared/providers/llm/__init__.py +0 -0
  145. rasa/shared/providers/llm/_base_litellm_client.py +251 -0
  146. rasa/shared/providers/llm/azure_openai_llm_client.py +338 -0
  147. rasa/shared/providers/llm/default_litellm_llm_client.py +84 -0
  148. rasa/shared/providers/llm/llm_client.py +76 -0
  149. rasa/shared/providers/llm/llm_response.py +50 -0
  150. rasa/shared/providers/llm/openai_llm_client.py +155 -0
  151. rasa/shared/providers/llm/self_hosted_llm_client.py +293 -0
  152. rasa/shared/providers/mappings.py +75 -0
  153. rasa/shared/utils/cli.py +30 -0
  154. rasa/shared/utils/io.py +65 -2
  155. rasa/shared/utils/llm.py +246 -200
  156. rasa/shared/utils/yaml.py +121 -15
  157. rasa/studio/auth.py +6 -4
  158. rasa/studio/config.py +13 -4
  159. rasa/studio/constants.py +1 -0
  160. rasa/studio/data_handler.py +10 -3
  161. rasa/studio/download.py +19 -13
  162. rasa/studio/train.py +2 -3
  163. rasa/studio/upload.py +19 -11
  164. rasa/telemetry.py +113 -58
  165. rasa/tracing/instrumentation/attribute_extractors.py +32 -17
  166. rasa/utils/common.py +18 -19
  167. rasa/utils/endpoints.py +7 -4
  168. rasa/utils/json_utils.py +60 -0
  169. rasa/utils/licensing.py +9 -1
  170. rasa/utils/ml_utils.py +4 -2
  171. rasa/validator.py +213 -3
  172. rasa/version.py +1 -1
  173. rasa_pro-3.10.16.dist-info/METADATA +196 -0
  174. {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/RECORD +179 -113
  175. rasa/nlu/classifiers/llm_intent_classifier.py +0 -519
  176. rasa/shared/providers/openai/clients.py +0 -43
  177. rasa/shared/providers/openai/session_handler.py +0 -110
  178. rasa_pro-3.9.18.dist-info/METADATA +0 -563
  179. /rasa/{shared/providers/openai → cli/project_templates/tutorial/actions}/__init__.py +0 -0
  180. /rasa/cli/project_templates/tutorial/{actions.py → actions/actions.py} +0 -0
  181. {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/NOTICE +0 -0
  182. {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/WHEEL +0 -0
  183. {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/entry_points.txt +0 -0
rasa/shared/utils/yaml.py CHANGED
@@ -1,3 +1,4 @@
1
+ import datetime
1
2
  import logging
2
3
  import os
3
4
  import re
@@ -12,15 +13,17 @@ from typing import Dict, List, Optional, Any, Callable, Tuple, Union
12
13
  import jsonschema
13
14
  from importlib_resources import files
14
15
  from packaging import version
15
- from packaging.version import LegacyVersion
16
16
  from pykwalify.core import Core
17
17
  from pykwalify.errors import SchemaError
18
18
  from ruamel import yaml as yaml
19
19
  from ruamel.yaml import RoundTripRepresenter, YAMLError
20
20
  from ruamel.yaml.constructor import DuplicateKeyError, BaseConstructor, ScalarNode
21
21
  from ruamel.yaml.comments import CommentedSeq, CommentedMap
22
+ from ruamel.yaml.loader import SafeLoader
22
23
 
23
24
  from rasa.shared.constants import (
25
+ ASSERTIONS_SCHEMA_EXTENSIONS_FILE,
26
+ ASSERTIONS_SCHEMA_FILE,
24
27
  MODEL_CONFIG_SCHEMA_FILE,
25
28
  CONFIG_SCHEMA_FILE,
26
29
  DOCS_URL_TRAINING_DATA,
@@ -413,30 +416,108 @@ def validate_raw_yaml_using_schema_file_with_responses(
413
416
  )
414
417
 
415
418
 
416
- def read_yaml(content: str, reader_type: Union[str, List[str]] = "safe") -> Any:
419
+ def read_yaml(
420
+ content: str,
421
+ reader_type: Union[str, List[str]] = "safe",
422
+ **kwargs: Any,
423
+ ) -> Any:
417
424
  """Parses yaml from a text.
418
425
 
419
426
  Args:
420
427
  content: A text containing yaml content.
421
428
  reader_type: Reader type to use. By default, "safe" will be used.
429
+ **kwargs: Any
422
430
 
423
431
  Raises:
424
432
  ruamel.yaml.parser.ParserError: If there was an error when parsing the YAML.
425
433
  """
426
- if _is_ascii(content):
427
- # Required to make sure emojis are correctly parsed
428
- content = (
429
- content.encode("utf-8")
430
- .decode("raw_unicode_escape")
431
- .encode("utf-16", "surrogatepass")
432
- .decode("utf-16")
433
- )
434
+ custom_constructor = kwargs.get("custom_constructor", None)
435
+
436
+ # Create YAML parser with custom constructor
437
+ yaml_parser, reset_constructors = create_yaml_parser(
438
+ reader_type, custom_constructor
439
+ )
440
+ yaml_content = yaml_parser.load(content) or {}
441
+
442
+ # Reset to default constructors
443
+ reset_constructors()
444
+
445
+ return yaml_content
446
+
447
+
448
+ def create_yaml_parser(
449
+ reader_type: str,
450
+ custom_constructor: Optional[Callable] = None,
451
+ ) -> Tuple[yaml.YAML, Callable[[], None]]:
452
+ """Create a YAML parser with an optional custom constructor.
453
+
454
+ Args:
455
+ reader_type (str): The type of the reader
456
+ (e.g., 'safe', 'rt', 'unsafe').
457
+ custom_constructor (Optional[Callable]):
458
+ A custom constructor function for YAML parsing.
434
459
 
460
+ Returns:
461
+ Tuple[yaml.YAML, Callable[[], None]]: A tuple containing
462
+ the YAML parser and a function to reset constructors to
463
+ their original state.
464
+ """
435
465
  yaml_parser = yaml.YAML(typ=reader_type)
436
466
  yaml_parser.version = YAML_VERSION # type: ignore[assignment]
437
467
  yaml_parser.preserve_quotes = True # type: ignore[assignment]
438
468
 
439
- return yaml_parser.load(content) or {}
469
+ # Save the original constructors
470
+ original_mapping_constructor = yaml_parser.constructor.yaml_constructors.get(
471
+ yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG
472
+ )
473
+ original_sequence_constructor = yaml_parser.constructor.yaml_constructors.get(
474
+ yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG
475
+ )
476
+
477
+ if custom_constructor is not None:
478
+ # Attach the custom constructor to the loader
479
+ yaml_parser.constructor.add_constructor(
480
+ yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, custom_constructor
481
+ )
482
+ yaml_parser.constructor.add_constructor(
483
+ yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, custom_constructor
484
+ )
485
+
486
+ def reset_constructors() -> None:
487
+ """Reset the constructors back to their original state."""
488
+ yaml_parser.constructor.add_constructor(
489
+ yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, original_mapping_constructor
490
+ )
491
+ yaml_parser.constructor.add_constructor(
492
+ yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG,
493
+ original_sequence_constructor,
494
+ )
495
+
496
+ def custom_date_constructor(loader: SafeLoader, node: ScalarNode) -> str:
497
+ """Custom constructor for parsing dates in the format '%Y-%m-%d'.
498
+
499
+ This constructor parses dates in the '%Y-%m-%d' format and returns them as
500
+ strings instead of datetime objects. This change was introduced because the
501
+ default timestamp constructor in ruamel.yaml returns datetime objects, which
502
+ caused issues in our use case where the `api_version` in the LLM config must
503
+ be a string, but was being interpreted as a datetime object.
504
+ """
505
+ value = loader.construct_scalar(node)
506
+ try:
507
+ # Attempt to parse the date
508
+ date_obj = datetime.datetime.strptime(value, "%Y-%m-%d").date()
509
+ # Return the date as a string instead of a datetime object
510
+ return date_obj.strftime("%Y-%m-%d")
511
+ except ValueError:
512
+ # If the date is not in the correct format, return the original value
513
+ return value
514
+
515
+ # Add the custom date constructor
516
+ yaml_parser.constructor.add_constructor(
517
+ "tag:yaml.org,2002:timestamp", custom_date_constructor
518
+ )
519
+
520
+ return yaml_parser, reset_constructors
440
521
 
441
522
 
442
523
  def _is_ascii(text: str) -> bool:
@@ -684,9 +765,6 @@ def validate_training_data_format_version(
684
765
  parsed_version = version.parse(version_value)
685
766
  latest_version = version.parse(LATEST_TRAINING_DATA_FORMAT_VERSION)
686
767
 
687
- if isinstance(parsed_version, LegacyVersion):
688
- raise TypeError
689
-
690
768
  if parsed_version < latest_version:
691
769
  raise_warning(
692
770
  f"Training data file {filename} has a lower "
@@ -702,7 +780,7 @@ def validate_training_data_format_version(
702
780
  if latest_version >= parsed_version:
703
781
  return True
704
782
 
705
- except TypeError:
783
+ except (TypeError, version.InvalidVersion):
706
784
  raise_warning(
707
785
  f"Training data file {filename} must specify "
708
786
  f"'{KEY_TRAINING_DATA_FORMAT_VERSION}' as string, for example:\n"
@@ -784,3 +862,31 @@ def validate_yaml_with_jsonschema(
784
862
  errors,
785
863
  content=source_data,
786
864
  )
865
+
866
+
867
+ def validate_yaml_data_using_schema_with_assertions(
868
+ yaml_data: Any,
869
+ schema_content: Union[List[Any], Dict[str, Any]],
870
+ package_name: str = PACKAGE_NAME,
871
+ ) -> None:
872
+ """Validate raw yaml content using a schema with assertions sub-schema.
873
+
874
+ Args:
875
+ yaml_data: the parsed yaml data to be validated
876
+ schema_content: the content of the YAML schema
877
+ package_name: the name of the package the schema is located in. defaults
878
+ to `rasa`.
879
+ """
880
+ # test case assertions are part of the schema extension
881
+ # it will be included if the schema explicitly references it with
882
+ # include: assertions
883
+ e2e_test_cases_schema_content = read_schema_file(
884
+ ASSERTIONS_SCHEMA_FILE, package_name
885
+ )
886
+
887
+ schema_content = dict(schema_content, **e2e_test_cases_schema_content)
888
+ schema_extensions = [
889
+ str(files(package_name).joinpath(ASSERTIONS_SCHEMA_EXTENSIONS_FILE))
890
+ ]
891
+
892
+ validate_yaml_content_using_schema(yaml_data, schema_content, schema_extensions)
rasa/studio/auth.py CHANGED
@@ -21,15 +21,17 @@ from rasa.studio.results_logger import with_studio_error_handler, StudioResult
21
21
 
22
22
 
23
23
  class StudioAuth:
24
- def __init__(
25
- self,
26
- studio_config: StudioConfig,
27
- ) -> None:
24
+ """Handles the authentication with the Rasa Studio authentication server."""
25
+
26
+ def __init__(self, studio_config: StudioConfig) -> None:
28
27
  self.config = studio_config
28
+ verify = not studio_config.disable_verify
29
+
29
30
  self.keycloak_openid = KeycloakOpenID(
30
31
  server_url=studio_config.authentication_server_url,
31
32
  client_id=studio_config.client_id,
32
33
  realm_name=studio_config.realm_name,
34
+ verify=verify,
33
35
  )
34
36
 
35
37
  def health_check(self) -> bool:
rasa/studio/config.py CHANGED
@@ -2,13 +2,14 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  from dataclasses import dataclass
5
- from typing import Dict, Optional, Text
5
+ from typing import Any, Dict, Optional, Text
6
6
 
7
7
  from rasa.utils.common import read_global_config_value, write_global_config_value
8
8
 
9
9
  from rasa.studio.constants import (
10
10
  RASA_STUDIO_AUTH_SERVER_URL_ENV,
11
11
  RASA_STUDIO_CLI_CLIENT_ID_KEY_ENV,
12
+ RASA_STUDIO_CLI_DISABLE_VERIFY_KEY_ENV,
12
13
  RASA_STUDIO_CLI_REALM_NAME_KEY_ENV,
13
14
  RASA_STUDIO_CLI_STUDIO_URL_ENV,
14
15
  STUDIO_CONFIG_KEY,
@@ -19,6 +20,7 @@ STUDIO_URL_KEY = "studio_url"
19
20
  CLIENT_ID_KEY = "client_id"
20
21
  REALM_NAME_KEY = "realm_name"
21
22
  CLIENT_SECRET_KEY = "client_secret"
23
+ DISABLE_VERIFY = "disable_verify"
22
24
 
23
25
 
24
26
  @dataclass
@@ -27,13 +29,15 @@ class StudioConfig:
27
29
  studio_url: Optional[Text]
28
30
  client_id: Optional[Text]
29
31
  realm_name: Optional[Text]
32
+ disable_verify: bool = False
30
33
 
31
- def to_dict(self) -> Dict[Text, Optional[Text]]:
34
+ def to_dict(self) -> Dict[Text, Optional[Any]]:
32
35
  return {
33
36
  AUTH_SERVER_URL_KEY: self.authentication_server_url,
34
37
  STUDIO_URL_KEY: self.studio_url,
35
38
  CLIENT_ID_KEY: self.client_id,
36
39
  REALM_NAME_KEY: self.realm_name,
40
+ DISABLE_VERIFY: self.disable_verify,
37
41
  }
38
42
 
39
43
  @classmethod
@@ -43,6 +47,7 @@ class StudioConfig:
43
47
  studio_url=data[STUDIO_URL_KEY],
44
48
  client_id=data[CLIENT_ID_KEY],
45
49
  realm_name=data[REALM_NAME_KEY],
50
+ disable_verify=data.get(DISABLE_VERIFY, False),
46
51
  )
47
52
 
48
53
  def write_config(self) -> None:
@@ -73,7 +78,7 @@ class StudioConfig:
73
78
  config = read_global_config_value(STUDIO_CONFIG_KEY, unavailable_ok=True)
74
79
 
75
80
  if config is None:
76
- return StudioConfig(None, None, None, None)
81
+ return StudioConfig(None, None, None, None, False)
77
82
 
78
83
  if not isinstance(config, dict):
79
84
  raise ValueError(
@@ -83,7 +88,7 @@ class StudioConfig:
83
88
  )
84
89
 
85
90
  for key in config:
86
- if not isinstance(config[key], str):
91
+ if not isinstance(config[key], str) and key != DISABLE_VERIFY:
87
92
  raise ValueError(
88
93
  "Invalid config file format. "
89
94
  f"Key '{key}' is not a text value."
@@ -102,6 +107,9 @@ class StudioConfig:
102
107
  studio_url=StudioConfig._read_env_value(RASA_STUDIO_CLI_STUDIO_URL_ENV),
103
108
  client_id=StudioConfig._read_env_value(RASA_STUDIO_CLI_CLIENT_ID_KEY_ENV),
104
109
  realm_name=StudioConfig._read_env_value(RASA_STUDIO_CLI_REALM_NAME_KEY_ENV),
110
+ disable_verify=bool(
111
+ os.getenv(RASA_STUDIO_CLI_DISABLE_VERIFY_KEY_ENV, False)
112
+ ),
105
113
  )
106
114
 
107
115
  @staticmethod
@@ -124,4 +132,5 @@ class StudioConfig:
124
132
  studio_url=self.studio_url or other.studio_url,
125
133
  client_id=self.client_id or other.client_id,
126
134
  realm_name=self.realm_name or other.realm_name,
135
+ disable_verify=self.disable_verify or other.disable_verify,
127
136
  )
rasa/studio/constants.py CHANGED
@@ -10,6 +10,7 @@ RASA_STUDIO_AUTH_SERVER_URL_ENV = "RASA_STUDIO_AUTH_SERVER_URL"
10
10
  RASA_STUDIO_CLI_STUDIO_URL_ENV = "RASA_STUDIO_CLI_STUDIO_URL"
11
11
  RASA_STUDIO_CLI_REALM_NAME_KEY_ENV = "RASA_STUDIO_CLI_REALM_NAME_KEY"
12
12
  RASA_STUDIO_CLI_CLIENT_ID_KEY_ENV = "RASA_STUDIO_CLI_CLIENT_ID_KEY"
13
+ RASA_STUDIO_CLI_DISABLE_VERIFY_KEY_ENV = "RASA_STUDIO_CLI_DISABLE_VERIFY_KEY"
13
14
 
14
15
  STUDIO_NLU_FILENAME = "studio_nlu.yml"
15
16
  STUDIO_DOMAIN_FILENAME = "studio_domain.yml"
@@ -76,7 +76,9 @@ class StudioDataHandler:
76
76
 
77
77
  return request
78
78
 
79
- def _make_request(self, GQL_req: Dict[Any, Any]) -> Dict[Any, Any]:
79
+ def _make_request(
80
+ self, GQL_req: Dict[Any, Any], verify: bool = True
81
+ ) -> Dict[Any, Any]:
80
82
  token = KeycloakTokenReader().get_token()
81
83
  if token.is_expired():
82
84
  token = self.refresh_token(token)
@@ -93,6 +95,7 @@ class StudioDataHandler:
93
95
  "Authorization": f"{token.token_type} {token.access_token}",
94
96
  "Content-Type": "application/json",
95
97
  },
98
+ verify=verify,
96
99
  )
97
100
 
98
101
  if res.status_code != 200:
@@ -128,7 +131,9 @@ class StudioDataHandler:
128
131
  The data from Rasa Studio.
129
132
  """
130
133
  GQL_req = self._build_request()
131
- response = self._make_request(GQL_req)
134
+ verify = not self.studio_config.disable_verify
135
+
136
+ response = self._make_request(GQL_req, verify=verify)
132
137
  self._extract_data(response)
133
138
 
134
139
  def request_data(
@@ -145,7 +150,9 @@ class StudioDataHandler:
145
150
  The data from Rasa Studio.
146
151
  """
147
152
  GQL_req = self._build_request(intent_names, entity_names)
148
- response = self._make_request(GQL_req)
153
+ verify = not self.studio_config.disable_verify
154
+
155
+ response = self._make_request(GQL_req, verify=verify)
149
156
  self._extract_data(response)
150
157
 
151
158
  def get_config(self) -> Optional[str]:
rasa/studio/download.py CHANGED
@@ -210,8 +210,8 @@ def _handle_download_no_overwrite(
210
210
  if domain_path.is_dir():
211
211
  studio_domain_path = domain_path / STUDIO_DOMAIN_FILENAME
212
212
  new_domain_data = data_handler.combine_domains(
213
- data_from_studio.get_domain().as_dict(),
214
- data_original.get_domain().as_dict(),
213
+ data_from_studio.get_user_domain().as_dict(),
214
+ data_original.get_user_domain().as_dict(),
215
215
  )
216
216
  studio_domain = Domain.from_dict(new_domain_data)
217
217
  if not studio_domain.is_empty():
@@ -219,7 +219,9 @@ def _handle_download_no_overwrite(
219
219
  else:
220
220
  logger.warning("No additional domain data found.")
221
221
  elif domain_path.is_file():
222
- domain_merged = data_original.get_domain().merge(data_from_studio.get_domain())
222
+ domain_merged = data_original.get_user_domain().merge(
223
+ data_from_studio.get_user_domain()
224
+ )
223
225
  domain_merged.persist(domain_path)
224
226
 
225
227
  if len(data_paths) == 1 and data_paths[0].is_file():
@@ -230,7 +232,9 @@ def _handle_download_no_overwrite(
230
232
  )
231
233
  data_nlu.persist_nlu(data_path)
232
234
  if handler.has_flows():
233
- data_flows = data_original.get_flows().merge(data_from_studio.get_flows())
235
+ data_flows = data_original.get_user_flows().merge(
236
+ data_from_studio.get_user_flows()
237
+ )
234
238
  YamlFlowsWriter.dump(data_flows.underlying_flows, data_path)
235
239
 
236
240
  elif len(data_paths) == 1 and data_paths[0].is_dir():
@@ -276,8 +280,8 @@ def _persist_flows_diff(
276
280
  ) -> None:
277
281
  """Creates a new flows file from the diff of original and studio data."""
278
282
  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,
283
+ data_from_studio.get_user_flows().underlying_flows,
284
+ data_original.get_user_flows().underlying_flows,
281
285
  )
282
286
  if new_flows_data:
283
287
  YamlFlowsWriter.dump(new_flows_data, data_path)
@@ -317,11 +321,13 @@ def _handle_download_with_overwrite(
317
321
  )
318
322
 
319
323
  if domain_path.is_file():
320
- domain_merged = data_from_studio.get_domain().merge(data_original.get_domain())
324
+ domain_merged = data_from_studio.get_user_domain().merge(
325
+ data_original.get_user_domain()
326
+ )
321
327
  domain_merged.persist(domain_path)
322
328
  elif domain_path.is_dir():
323
329
  default = domain_path / Path(STUDIO_DOMAIN_FILENAME)
324
- studio_domain = data_from_studio.get_domain()
330
+ studio_domain = data_from_studio.get_user_domain()
325
331
 
326
332
  paths = get_domain_path(domain_path, data_from_studio, mapper)
327
333
 
@@ -358,8 +364,8 @@ def _handle_download_with_overwrite(
358
364
  )
359
365
  nlu_data_merged.persist_nlu(data_paths[0])
360
366
  if handler.has_flows():
361
- flows_data_merged = data_from_studio.get_flows().merge(
362
- data_original.get_flows()
367
+ flows_data_merged = data_from_studio.get_user_flows().merge(
368
+ data_original.get_user_flows()
363
369
  )
364
370
  YamlFlowsWriter.dump(flows_data_merged.underlying_flows, data_paths[0])
365
371
  elif len(data_paths) == 1 and data_paths[0].is_dir():
@@ -374,12 +380,12 @@ def _handle_download_with_overwrite(
374
380
  nlu_data = nlu_data.merge(nlu_file.get_nlu_data())
375
381
  pretty_write_nlu_yaml(read_yaml(nlu_data.nlu_as_yaml()), paths["nlu_path"])
376
382
  if handler.has_flows():
377
- flows_data = data_from_studio.get_flows()
383
+ flows_data = data_from_studio.get_user_flows()
378
384
  if paths["flow_path"].exists():
379
385
  flows_file = TrainingDataImporter.load_from_dict(
380
386
  training_data_paths=[str(paths["flow_path"])]
381
387
  )
382
- flows_data = flows_data.merge(flows_file.get_flows())
388
+ flows_data = flows_data.merge(flows_file.get_user_flows())
383
389
  YamlFlowsWriter.dump(flows_data.underlying_flows, paths["flow_path"])
384
390
  else:
385
391
  # TODO: we are not handling the case of multiple data paths?
@@ -407,7 +413,7 @@ def get_training_path(
407
413
  for intent in data_original.get_nlu_data().intents:
408
414
  for path in mapper.get_file(intent, "intents").get("training", []):
409
415
  nlu_paths.add(path)
410
- flows = [flow.id for flow in data_original.get_flows().underlying_flows]
416
+ flows = [flow.id for flow in data_original.get_user_flows().underlying_flows]
411
417
  for flow in flows:
412
418
  for path in mapper.get_file(flow, "flows").get("training", []):
413
419
  flow_paths.add(path)
rasa/studio/train.py CHANGED
@@ -20,19 +20,18 @@ 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
22
  from rasa.studio import data_handler
23
- from rasa.utils.common import get_temp_dir_name
24
-
25
23
  from rasa.studio.config import StudioConfig
26
24
  from rasa.studio.data_handler import (
27
25
  StudioDataHandler,
28
26
  import_data_from_studio,
29
27
  )
28
+ from rasa.utils.common import get_temp_dir_name
30
29
 
31
30
  logger = logging.getLogger(__name__)
32
31
 
33
32
 
34
33
  def handle_train(args: argparse.Namespace) -> Optional[str]:
35
- from rasa import train as train_all
34
+ from rasa.api import train as train_all
36
35
 
37
36
  handler = StudioDataHandler(
38
37
  studio_config=StudioConfig.read_config(), assistant_name=args.assistant_name[0]
rasa/studio/upload.py CHANGED
@@ -56,7 +56,10 @@ def _get_selected_entities_and_intents(
56
56
 
57
57
  def handle_upload(args: argparse.Namespace) -> None:
58
58
  """Uploads primitives to rasa studio."""
59
- endpoint = StudioConfig.read_config().studio_url
59
+ studio_config = StudioConfig.read_config()
60
+ endpoint = studio_config.studio_url
61
+ verify = not studio_config.disable_verify
62
+
60
63
  if not endpoint:
61
64
  rasa.shared.utils.cli.print_error_and_exit(
62
65
  "No GraphQL endpoint found in config. Please run `rasa studio config`."
@@ -76,9 +79,9 @@ def handle_upload(args: argparse.Namespace) -> None:
76
79
 
77
80
  # check safely if args.calm is set and not fail if not
78
81
  if hasattr(args, "calm") and args.calm:
79
- upload_calm_assistant(args, endpoint)
82
+ upload_calm_assistant(args, endpoint, verify=verify)
80
83
  else:
81
- upload_nlu_assistant(args, endpoint)
84
+ upload_nlu_assistant(args, endpoint, verify=verify)
82
85
 
83
86
 
84
87
  config_keys = [
@@ -126,7 +129,9 @@ def _get_assistant_name(config: Dict[Text, Any]) -> str:
126
129
 
127
130
 
128
131
  @with_studio_error_handler
129
- def upload_calm_assistant(args: argparse.Namespace, endpoint: str) -> StudioResult:
132
+ def upload_calm_assistant(
133
+ args: argparse.Namespace, endpoint: str, verify: bool = True
134
+ ) -> StudioResult:
130
135
  """Uploads the CALM assistant data to Rasa Studio.
131
136
 
132
137
  Args:
@@ -151,7 +156,7 @@ def upload_calm_assistant(args: argparse.Namespace, endpoint: str) -> StudioResu
151
156
 
152
157
  # Prepare config and domain
153
158
  config = importer.get_config()
154
- domain_from_files = importer.get_domain().as_dict()
159
+ domain_from_files = importer.get_user_domain().as_dict()
155
160
  endpoints_from_files = read_yaml_file(args.endpoints)
156
161
  config_from_files = read_yaml_file(args.config)
157
162
 
@@ -188,8 +193,7 @@ def upload_calm_assistant(args: argparse.Namespace, endpoint: str) -> StudioResu
188
193
  training_data_paths=training_data_paths
189
194
  )
190
195
 
191
- user_flows = flow_importer.get_flows().user_flows
192
- flows = list(user_flows)
196
+ flows = list(flow_importer.get_user_flows())
193
197
 
194
198
  # We instantiate the TrainingDataImporter again on purpose to avoid
195
199
  # adding patterns to domain's actions. More info https://t.ly/W8uuc
@@ -217,11 +221,13 @@ def upload_calm_assistant(args: argparse.Namespace, endpoint: str) -> StudioResu
217
221
  )
218
222
 
219
223
  structlogger.info("Uploading to Rasa Studio...")
220
- return make_request(endpoint, graphql_req)
224
+ return make_request(endpoint, graphql_req, verify)
221
225
 
222
226
 
223
227
  @with_studio_error_handler
224
- def upload_nlu_assistant(args: argparse.Namespace, endpoint: str) -> StudioResult:
228
+ def upload_nlu_assistant(
229
+ args: argparse.Namespace, endpoint: str, verify: bool = True
230
+ ) -> StudioResult:
225
231
  """Uploads the classic (dm1) assistant data to Rasa Studio.
226
232
 
227
233
  Args:
@@ -269,15 +275,16 @@ def upload_nlu_assistant(args: argparse.Namespace, endpoint: str) -> StudioResul
269
275
  graphql_req = build_request(assistant_name, nlu_examples_yaml, domain_yaml)
270
276
 
271
277
  structlogger.info("Uploading to Rasa Studio...")
272
- return make_request(endpoint, graphql_req)
278
+ return make_request(endpoint, graphql_req, verify)
273
279
 
274
280
 
275
- def make_request(endpoint: str, graphql_req: Dict) -> StudioResult:
281
+ def make_request(endpoint: str, graphql_req: Dict, verify: bool = True) -> StudioResult:
276
282
  """Makes a request to the studio endpoint to upload data.
277
283
 
278
284
  Args:
279
285
  endpoint: The studio endpoint
280
286
  graphql_req: The graphql request
287
+ verify: Whether to verify SSL
281
288
  """
282
289
  token = KeycloakTokenReader().get_token()
283
290
  res = requests.post(
@@ -287,6 +294,7 @@ def make_request(endpoint: str, graphql_req: Dict) -> StudioResult:
287
294
  "Authorization": f"{token.token_type} {token.access_token}",
288
295
  "Content-Type": "application/json",
289
296
  },
297
+ verify=verify,
290
298
  )
291
299
 
292
300
  if results_logger.response_has_errors(res.json()):