rasa-pro 3.10.16__py3-none-any.whl → 3.11.0a1__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 (185) hide show
  1. README.md +396 -17
  2. rasa/api.py +9 -3
  3. rasa/cli/arguments/default_arguments.py +23 -2
  4. rasa/cli/arguments/run.py +15 -0
  5. rasa/cli/arguments/train.py +3 -9
  6. rasa/cli/e2e_test.py +1 -1
  7. rasa/cli/evaluate.py +1 -1
  8. rasa/cli/inspect.py +8 -4
  9. rasa/cli/llm_fine_tuning.py +12 -15
  10. rasa/cli/run.py +8 -1
  11. rasa/cli/studio/studio.py +8 -18
  12. rasa/cli/train.py +11 -53
  13. rasa/cli/utils.py +8 -10
  14. rasa/cli/x.py +1 -1
  15. rasa/constants.py +1 -1
  16. rasa/core/actions/action.py +2 -0
  17. rasa/core/actions/action_hangup.py +29 -0
  18. rasa/core/agent.py +2 -2
  19. rasa/core/brokers/kafka.py +3 -1
  20. rasa/core/brokers/pika.py +3 -1
  21. rasa/core/channels/__init__.py +8 -6
  22. rasa/core/channels/channel.py +21 -4
  23. rasa/core/channels/development_inspector.py +143 -46
  24. rasa/core/channels/inspector/README.md +1 -1
  25. rasa/core/channels/inspector/dist/assets/{arc-b6e548fe.js → arc-86942a71.js} +1 -1
  26. rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-fa03ac9e.js → c4Diagram-d0fbc5ce-b0290676.js} +1 -1
  27. rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-ee67392a.js → classDiagram-936ed81e-f6405f6e.js} +1 -1
  28. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-9b283fae.js → classDiagram-v2-c3cb15f1-ef61ac77.js} +1 -1
  29. rasa/core/channels/inspector/dist/assets/{createText-62fc7601-8b6fcc2a.js → createText-62fc7601-f0411e58.js} +1 -1
  30. rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-22e77f4f.js → edges-f2ad444c-7dcc4f3b.js} +1 -1
  31. rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-60ffc87f.js → erDiagram-9d236eb7-e0c092d7.js} +1 -1
  32. rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-9dd802e4.js → flowDb-1972c806-fba2e3ce.js} +1 -1
  33. rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-5fa1912f.js → flowDiagram-7ea5b25a-7a70b71a.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-24a5f41a.js +1 -0
  35. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-622a1fd2.js → flowchart-elk-definition-abe16c3d-00a59b68.js} +1 -1
  36. rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-e285a63a.js → ganttDiagram-9b5ea136-293c91fa.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-f237bdca.js → gitGraphDiagram-99d0ae7c-07b2d68c.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-4b03d70e.js → index-2c4b9a3b-bc959fbd.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/index-3a8a5a28.js +1317 -0
  40. rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-72a0fa5f.js → infoDiagram-736b4530-4a350f72.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-82218c41.js → journeyDiagram-df861f2b-af464fb7.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{layout-78cff630.js → layout-0071f036.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/{line-5038b469.js → line-2f73cc83.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/{linear-c4fc4098.js → linear-f014b4cc.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-c33c8ea6.js → mindmap-definition-beec6740-d2426fb6.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-a8d03059.js → pieDiagram-dbbf0591-776f01a2.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-6a0e56b2.js → quadrantDiagram-4d7f4fd6-82e00b57.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-2dc7c7bd.js → requirementDiagram-6fc4c22a-ea13c6bb.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-2360fe39.js → sankeyDiagram-8f13d901-1feca7e9.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-41b9f9ad.js → sequenceDiagram-b655622a-070c61d2.js} +1 -1
  51. rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-0aad326f.js → stateDiagram-59f0c015-24f46263.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-9847d984.js → stateDiagram-v2-2b26beab-c9056051.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{styles-080da4f6-564d890e.js → styles-080da4f6-08abc34a.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-38957613.js → styles-3dcbcfbf-bc74c25a.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{styles-9c745c82-f0fc6921.js → styles-9c745c82-4e5d66de.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-ef3c5a77.js → svgDrawCommon-4835440b-849c4517.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-bf3e91c1.js → timeline-definition-5b62e21b-d0fb1598.js} +1 -1
  58. rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-4d4026c0.js → xychartDiagram-2b33534f-04d115e2.js} +1 -1
  59. rasa/core/channels/inspector/dist/index.html +18 -17
  60. rasa/core/channels/inspector/index.html +17 -16
  61. rasa/core/channels/inspector/package.json +5 -1
  62. rasa/core/channels/inspector/src/App.tsx +117 -67
  63. rasa/core/channels/inspector/src/components/Chat.tsx +95 -0
  64. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +11 -10
  65. rasa/core/channels/inspector/src/components/DialogueStack.tsx +10 -25
  66. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +1 -1
  67. rasa/core/channels/inspector/src/helpers/formatters.test.ts +10 -0
  68. rasa/core/channels/inspector/src/helpers/formatters.ts +107 -41
  69. rasa/core/channels/inspector/src/helpers/utils.ts +92 -7
  70. rasa/core/channels/inspector/src/types.ts +21 -1
  71. rasa/core/channels/inspector/yarn.lock +94 -1
  72. rasa/core/channels/rest.py +51 -46
  73. rasa/core/channels/socketio.py +22 -0
  74. rasa/core/channels/{audiocodes.py → voice_ready/audiocodes.py} +110 -68
  75. rasa/core/channels/{voice_aware → voice_ready}/jambonz.py +11 -4
  76. rasa/core/channels/{voice_aware → voice_ready}/jambonz_protocol.py +57 -5
  77. rasa/core/channels/{twilio_voice.py → voice_ready/twilio_voice.py} +58 -7
  78. rasa/core/channels/{voice_aware → voice_ready}/utils.py +16 -0
  79. rasa/core/channels/voice_stream/asr/__init__.py +0 -0
  80. rasa/core/channels/voice_stream/asr/asr_engine.py +71 -0
  81. rasa/core/channels/voice_stream/asr/asr_event.py +13 -0
  82. rasa/core/channels/voice_stream/asr/deepgram.py +77 -0
  83. rasa/core/channels/voice_stream/audio_bytes.py +7 -0
  84. rasa/core/channels/voice_stream/tts/__init__.py +0 -0
  85. rasa/core/channels/voice_stream/tts/azure.py +100 -0
  86. rasa/core/channels/voice_stream/tts/cartesia.py +114 -0
  87. rasa/core/channels/voice_stream/tts/tts_cache.py +27 -0
  88. rasa/core/channels/voice_stream/tts/tts_engine.py +48 -0
  89. rasa/core/channels/voice_stream/twilio_media_streams.py +164 -0
  90. rasa/core/channels/voice_stream/util.py +57 -0
  91. rasa/core/channels/voice_stream/voice_channel.py +247 -0
  92. rasa/core/featurizers/single_state_featurizer.py +1 -22
  93. rasa/core/featurizers/tracker_featurizers.py +18 -115
  94. rasa/core/nlg/contextual_response_rephraser.py +11 -2
  95. rasa/{nlu → core}/persistor.py +16 -38
  96. rasa/core/policies/enterprise_search_policy.py +12 -15
  97. rasa/core/policies/flows/flow_executor.py +8 -18
  98. rasa/core/policies/intentless_policy.py +10 -15
  99. rasa/core/policies/ted_policy.py +33 -58
  100. rasa/core/policies/unexpected_intent_policy.py +7 -15
  101. rasa/core/processor.py +13 -64
  102. rasa/core/run.py +11 -1
  103. rasa/core/secrets_manager/constants.py +4 -0
  104. rasa/core/secrets_manager/factory.py +8 -0
  105. rasa/core/secrets_manager/vault.py +11 -1
  106. rasa/core/training/interactive.py +1 -1
  107. rasa/core/utils.py +1 -11
  108. rasa/dialogue_understanding/coexistence/llm_based_router.py +10 -10
  109. rasa/dialogue_understanding/commands/__init__.py +2 -0
  110. rasa/dialogue_understanding/commands/change_flow_command.py +0 -6
  111. rasa/dialogue_understanding/commands/session_end_command.py +61 -0
  112. rasa/dialogue_understanding/generator/flow_retrieval.py +0 -7
  113. rasa/dialogue_understanding/generator/llm_based_command_generator.py +12 -3
  114. rasa/dialogue_understanding/generator/llm_command_generator.py +1 -1
  115. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +3 -28
  116. rasa/dialogue_understanding/generator/nlu_command_adapter.py +1 -19
  117. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +4 -37
  118. rasa/e2e_test/aggregate_test_stats_calculator.py +1 -11
  119. rasa/e2e_test/assertions.py +6 -48
  120. rasa/e2e_test/e2e_test_runner.py +6 -9
  121. rasa/e2e_test/utils/e2e_yaml_utils.py +1 -1
  122. rasa/e2e_test/utils/io.py +1 -3
  123. rasa/engine/graph.py +3 -10
  124. rasa/engine/recipes/config_files/default_config.yml +0 -3
  125. rasa/engine/recipes/default_recipe.py +0 -1
  126. rasa/engine/recipes/graph_recipe.py +0 -1
  127. rasa/engine/runner/dask.py +2 -2
  128. rasa/engine/storage/local_model_storage.py +12 -42
  129. rasa/engine/storage/storage.py +1 -5
  130. rasa/engine/validation.py +1 -78
  131. rasa/keys +1 -0
  132. rasa/model_training.py +13 -16
  133. rasa/nlu/classifiers/diet_classifier.py +25 -38
  134. rasa/nlu/classifiers/logistic_regression_classifier.py +9 -22
  135. rasa/nlu/classifiers/sklearn_intent_classifier.py +16 -37
  136. rasa/nlu/extractors/crf_entity_extractor.py +50 -93
  137. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +16 -45
  138. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +17 -52
  139. rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +3 -5
  140. rasa/server.py +1 -1
  141. rasa/shared/constants.py +3 -12
  142. rasa/shared/core/constants.py +4 -0
  143. rasa/shared/core/domain.py +101 -47
  144. rasa/shared/core/events.py +29 -0
  145. rasa/shared/core/flows/flows_list.py +20 -11
  146. rasa/shared/core/flows/validation.py +25 -0
  147. rasa/shared/core/flows/yaml_flows_io.py +3 -24
  148. rasa/shared/importers/importer.py +40 -39
  149. rasa/shared/importers/multi_project.py +23 -11
  150. rasa/shared/importers/rasa.py +7 -2
  151. rasa/shared/importers/remote_importer.py +196 -0
  152. rasa/shared/importers/utils.py +3 -1
  153. rasa/shared/nlu/training_data/features.py +2 -120
  154. rasa/shared/nlu/training_data/training_data.py +18 -19
  155. rasa/shared/providers/_configs/azure_openai_client_config.py +3 -5
  156. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +1 -6
  157. rasa/shared/providers/llm/_base_litellm_client.py +11 -31
  158. rasa/shared/providers/llm/self_hosted_llm_client.py +3 -15
  159. rasa/shared/utils/common.py +3 -22
  160. rasa/shared/utils/io.py +0 -1
  161. rasa/shared/utils/llm.py +30 -27
  162. rasa/shared/utils/schemas/events.py +2 -0
  163. rasa/shared/utils/schemas/model_config.yml +0 -10
  164. rasa/shared/utils/yaml.py +44 -0
  165. rasa/studio/auth.py +5 -3
  166. rasa/studio/config.py +4 -13
  167. rasa/studio/constants.py +0 -1
  168. rasa/studio/data_handler.py +3 -10
  169. rasa/studio/upload.py +8 -17
  170. rasa/tracing/instrumentation/attribute_extractors.py +1 -1
  171. rasa/utils/io.py +66 -0
  172. rasa/utils/tensorflow/model_data.py +193 -2
  173. rasa/validator.py +0 -12
  174. rasa/version.py +1 -1
  175. rasa_pro-3.11.0a1.dist-info/METADATA +576 -0
  176. {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0a1.dist-info}/RECORD +181 -164
  177. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-1844e5a5.js +0 -1
  178. rasa/core/channels/inspector/dist/assets/index-a5d3e69d.js +0 -1040
  179. rasa/utils/tensorflow/feature_array.py +0 -366
  180. rasa_pro-3.10.16.dist-info/METADATA +0 -196
  181. /rasa/core/channels/{voice_aware → voice_ready}/__init__.py +0 -0
  182. /rasa/core/channels/{voice_native → voice_stream}/__init__.py +0 -0
  183. {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0a1.dist-info}/NOTICE +0 -0
  184. {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0a1.dist-info}/WHEEL +0 -0
  185. {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0a1.dist-info}/entry_points.txt +0 -0
rasa/shared/utils/yaml.py CHANGED
@@ -416,6 +416,47 @@ def validate_raw_yaml_using_schema_file_with_responses(
416
416
  )
417
417
 
418
418
 
419
+ def process_content(content: str) -> str:
420
+ """
421
+ Process the content to handle both Windows paths and emojis.
422
+ Windows paths are processed by escaping backslashes but emojis are left untouched.
423
+
424
+ Args:
425
+ content: yaml content to be processed
426
+ """
427
+ # Detect common Windows path patterns: e.g., C:\ or \\
428
+ UNESCAPED_WINDOWS_PATH_PATTERN = re.compile(
429
+ r"(?<!\w)[a-zA-Z]:(\\[a-zA-Z0-9_ -]+)*(\\)?(?!\\n)"
430
+ )
431
+ ESCAPED_WINDOWS_PATH_PATTERN = re.compile(
432
+ r"(?<!\w)[a-zA-Z]:(\\\\[a-zA-Z0-9_ -]+)+\\\\?(?!\\n)"
433
+ )
434
+
435
+ # Function to escape backslashes in Windows paths but leave other content as is
436
+ def escape_windows_paths(match: re.Match) -> str:
437
+ path = str(match.group(0))
438
+ return path.replace("\\", "\\\\") # Escape backslashes only in Windows paths
439
+
440
+ def unescape_windows_paths(match: re.Match) -> str:
441
+ path = str(match.group(0))
442
+ return path.replace("\\\\", "\\")
443
+
444
+ # First, process Windows paths by escaping backslashes
445
+ content = re.sub(UNESCAPED_WINDOWS_PATH_PATTERN, escape_windows_paths, content)
446
+
447
+ # Ensure proper handling of emojis by decoding Unicode sequences
448
+ content = (
449
+ content.encode("utf-8")
450
+ .decode("raw_unicode_escape")
451
+ .encode("utf-16", "surrogatepass")
452
+ .decode("utf-16")
453
+ )
454
+
455
+ content = re.sub(ESCAPED_WINDOWS_PATH_PATTERN, unescape_windows_paths, content)
456
+
457
+ return content
458
+
459
+
419
460
  def read_yaml(
420
461
  content: str,
421
462
  reader_type: Union[str, List[str]] = "safe",
@@ -431,6 +472,9 @@ def read_yaml(
431
472
  Raises:
432
473
  ruamel.yaml.parser.ParserError: If there was an error when parsing the YAML.
433
474
  """
475
+ if _is_ascii(content):
476
+ content = process_content(content)
477
+
434
478
  custom_constructor = kwargs.get("custom_constructor", None)
435
479
 
436
480
  # Create YAML parser with custom constructor
rasa/studio/auth.py CHANGED
@@ -23,10 +23,12 @@ from rasa.studio.results_logger import with_studio_error_handler, StudioResult
23
23
  class StudioAuth:
24
24
  """Handles the authentication with the Rasa Studio authentication server."""
25
25
 
26
- def __init__(self, studio_config: StudioConfig) -> None:
26
+ def __init__(
27
+ self,
28
+ studio_config: StudioConfig,
29
+ verify: bool = True,
30
+ ) -> None:
27
31
  self.config = studio_config
28
- verify = not studio_config.disable_verify
29
-
30
32
  self.keycloak_openid = KeycloakOpenID(
31
33
  server_url=studio_config.authentication_server_url,
32
34
  client_id=studio_config.client_id,
rasa/studio/config.py CHANGED
@@ -2,14 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  from dataclasses import dataclass
5
- from typing import Any, Dict, Optional, Text
5
+ from typing import 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,
13
12
  RASA_STUDIO_CLI_REALM_NAME_KEY_ENV,
14
13
  RASA_STUDIO_CLI_STUDIO_URL_ENV,
15
14
  STUDIO_CONFIG_KEY,
@@ -20,7 +19,6 @@ STUDIO_URL_KEY = "studio_url"
20
19
  CLIENT_ID_KEY = "client_id"
21
20
  REALM_NAME_KEY = "realm_name"
22
21
  CLIENT_SECRET_KEY = "client_secret"
23
- DISABLE_VERIFY = "disable_verify"
24
22
 
25
23
 
26
24
  @dataclass
@@ -29,15 +27,13 @@ class StudioConfig:
29
27
  studio_url: Optional[Text]
30
28
  client_id: Optional[Text]
31
29
  realm_name: Optional[Text]
32
- disable_verify: bool = False
33
30
 
34
- def to_dict(self) -> Dict[Text, Optional[Any]]:
31
+ def to_dict(self) -> Dict[Text, Optional[Text]]:
35
32
  return {
36
33
  AUTH_SERVER_URL_KEY: self.authentication_server_url,
37
34
  STUDIO_URL_KEY: self.studio_url,
38
35
  CLIENT_ID_KEY: self.client_id,
39
36
  REALM_NAME_KEY: self.realm_name,
40
- DISABLE_VERIFY: self.disable_verify,
41
37
  }
42
38
 
43
39
  @classmethod
@@ -47,7 +43,6 @@ class StudioConfig:
47
43
  studio_url=data[STUDIO_URL_KEY],
48
44
  client_id=data[CLIENT_ID_KEY],
49
45
  realm_name=data[REALM_NAME_KEY],
50
- disable_verify=data.get(DISABLE_VERIFY, False),
51
46
  )
52
47
 
53
48
  def write_config(self) -> None:
@@ -78,7 +73,7 @@ class StudioConfig:
78
73
  config = read_global_config_value(STUDIO_CONFIG_KEY, unavailable_ok=True)
79
74
 
80
75
  if config is None:
81
- return StudioConfig(None, None, None, None, False)
76
+ return StudioConfig(None, None, None, None)
82
77
 
83
78
  if not isinstance(config, dict):
84
79
  raise ValueError(
@@ -88,7 +83,7 @@ class StudioConfig:
88
83
  )
89
84
 
90
85
  for key in config:
91
- if not isinstance(config[key], str) and key != DISABLE_VERIFY:
86
+ if not isinstance(config[key], str):
92
87
  raise ValueError(
93
88
  "Invalid config file format. "
94
89
  f"Key '{key}' is not a text value."
@@ -107,9 +102,6 @@ class StudioConfig:
107
102
  studio_url=StudioConfig._read_env_value(RASA_STUDIO_CLI_STUDIO_URL_ENV),
108
103
  client_id=StudioConfig._read_env_value(RASA_STUDIO_CLI_CLIENT_ID_KEY_ENV),
109
104
  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
- ),
113
105
  )
114
106
 
115
107
  @staticmethod
@@ -132,5 +124,4 @@ class StudioConfig:
132
124
  studio_url=self.studio_url or other.studio_url,
133
125
  client_id=self.client_id or other.client_id,
134
126
  realm_name=self.realm_name or other.realm_name,
135
- disable_verify=self.disable_verify or other.disable_verify,
136
127
  )
rasa/studio/constants.py CHANGED
@@ -10,7 +10,6 @@ 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"
14
13
 
15
14
  STUDIO_NLU_FILENAME = "studio_nlu.yml"
16
15
  STUDIO_DOMAIN_FILENAME = "studio_domain.yml"
@@ -76,9 +76,7 @@ class StudioDataHandler:
76
76
 
77
77
  return request
78
78
 
79
- def _make_request(
80
- self, GQL_req: Dict[Any, Any], verify: bool = True
81
- ) -> Dict[Any, Any]:
79
+ def _make_request(self, GQL_req: Dict[Any, Any]) -> Dict[Any, Any]:
82
80
  token = KeycloakTokenReader().get_token()
83
81
  if token.is_expired():
84
82
  token = self.refresh_token(token)
@@ -95,7 +93,6 @@ class StudioDataHandler:
95
93
  "Authorization": f"{token.token_type} {token.access_token}",
96
94
  "Content-Type": "application/json",
97
95
  },
98
- verify=verify,
99
96
  )
100
97
 
101
98
  if res.status_code != 200:
@@ -131,9 +128,7 @@ class StudioDataHandler:
131
128
  The data from Rasa Studio.
132
129
  """
133
130
  GQL_req = self._build_request()
134
- verify = not self.studio_config.disable_verify
135
-
136
- response = self._make_request(GQL_req, verify=verify)
131
+ response = self._make_request(GQL_req)
137
132
  self._extract_data(response)
138
133
 
139
134
  def request_data(
@@ -150,9 +145,7 @@ class StudioDataHandler:
150
145
  The data from Rasa Studio.
151
146
  """
152
147
  GQL_req = self._build_request(intent_names, entity_names)
153
- verify = not self.studio_config.disable_verify
154
-
155
- response = self._make_request(GQL_req, verify=verify)
148
+ response = self._make_request(GQL_req)
156
149
  self._extract_data(response)
157
150
 
158
151
  def get_config(self) -> Optional[str]:
rasa/studio/upload.py CHANGED
@@ -56,10 +56,7 @@ 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
- studio_config = StudioConfig.read_config()
60
- endpoint = studio_config.studio_url
61
- verify = not studio_config.disable_verify
62
-
59
+ endpoint = StudioConfig.read_config().studio_url
63
60
  if not endpoint:
64
61
  rasa.shared.utils.cli.print_error_and_exit(
65
62
  "No GraphQL endpoint found in config. Please run `rasa studio config`."
@@ -79,9 +76,9 @@ def handle_upload(args: argparse.Namespace) -> None:
79
76
 
80
77
  # check safely if args.calm is set and not fail if not
81
78
  if hasattr(args, "calm") and args.calm:
82
- upload_calm_assistant(args, endpoint, verify=verify)
79
+ upload_calm_assistant(args, endpoint)
83
80
  else:
84
- upload_nlu_assistant(args, endpoint, verify=verify)
81
+ upload_nlu_assistant(args, endpoint)
85
82
 
86
83
 
87
84
  config_keys = [
@@ -129,9 +126,7 @@ def _get_assistant_name(config: Dict[Text, Any]) -> str:
129
126
 
130
127
 
131
128
  @with_studio_error_handler
132
- def upload_calm_assistant(
133
- args: argparse.Namespace, endpoint: str, verify: bool = True
134
- ) -> StudioResult:
129
+ def upload_calm_assistant(args: argparse.Namespace, endpoint: str) -> StudioResult:
135
130
  """Uploads the CALM assistant data to Rasa Studio.
136
131
 
137
132
  Args:
@@ -221,13 +216,11 @@ def upload_calm_assistant(
221
216
  )
222
217
 
223
218
  structlogger.info("Uploading to Rasa Studio...")
224
- return make_request(endpoint, graphql_req, verify)
219
+ return make_request(endpoint, graphql_req)
225
220
 
226
221
 
227
222
  @with_studio_error_handler
228
- def upload_nlu_assistant(
229
- args: argparse.Namespace, endpoint: str, verify: bool = True
230
- ) -> StudioResult:
223
+ def upload_nlu_assistant(args: argparse.Namespace, endpoint: str) -> StudioResult:
231
224
  """Uploads the classic (dm1) assistant data to Rasa Studio.
232
225
 
233
226
  Args:
@@ -275,16 +268,15 @@ def upload_nlu_assistant(
275
268
  graphql_req = build_request(assistant_name, nlu_examples_yaml, domain_yaml)
276
269
 
277
270
  structlogger.info("Uploading to Rasa Studio...")
278
- return make_request(endpoint, graphql_req, verify)
271
+ return make_request(endpoint, graphql_req)
279
272
 
280
273
 
281
- def make_request(endpoint: str, graphql_req: Dict, verify: bool = True) -> StudioResult:
274
+ def make_request(endpoint: str, graphql_req: Dict) -> StudioResult:
282
275
  """Makes a request to the studio endpoint to upload data.
283
276
 
284
277
  Args:
285
278
  endpoint: The studio endpoint
286
279
  graphql_req: The graphql request
287
- verify: Whether to verify SSL
288
280
  """
289
281
  token = KeycloakTokenReader().get_token()
290
282
  res = requests.post(
@@ -294,7 +286,6 @@ def make_request(endpoint: str, graphql_req: Dict, verify: bool = True) -> Studi
294
286
  "Authorization": f"{token.token_type} {token.access_token}",
295
287
  "Content-Type": "application/json",
296
288
  },
297
- verify=verify,
298
289
  )
299
290
 
300
291
  if results_logger.response_has_errors(res.json()):
@@ -664,7 +664,7 @@ def extract_attrs_for_custom_action_executor_run(
664
664
 
665
665
  attrs: Dict[str, Any] = {
666
666
  "class_name": self.__class__.__name__,
667
- "action_name": self.action_name,
667
+ "action_name": self.action_name if hasattr(self, "action_name") else "None",
668
668
  "sender_id": tracker.sender_id,
669
669
  "url": str(url),
670
670
  "actions_module": str(actions_module),
rasa/utils/io.py CHANGED
@@ -2,6 +2,7 @@ import asyncio
2
2
  import filecmp
3
3
  import logging
4
4
  import os
5
+ import pickle
5
6
  import tempfile
6
7
  import warnings
7
8
  import re
@@ -97,6 +98,29 @@ def enable_async_loop_debugging(
97
98
  return event_loop
98
99
 
99
100
 
101
+ def pickle_dump(filename: Union[Text, Path], obj: Any) -> None:
102
+ """Saves object to file.
103
+
104
+ Args:
105
+ filename: the filename to save the object to
106
+ obj: the object to store
107
+ """
108
+ with open(filename, "wb") as f:
109
+ pickle.dump(obj, f)
110
+
111
+
112
+ def pickle_load(filename: Union[Text, Path]) -> Any:
113
+ """Loads an object from a file.
114
+
115
+ Args:
116
+ filename: the filename to load the object from
117
+
118
+ Returns: the loaded object
119
+ """
120
+ with open(filename, "rb") as f:
121
+ return pickle.load(f)
122
+
123
+
100
124
  def create_temporary_file(data: Any, suffix: Text = "", mode: Text = "w+") -> Text:
101
125
  """Creates a tempfile.NamedTemporaryFile object for data."""
102
126
  encoding = None if "b" in mode else rasa.shared.utils.io.DEFAULT_ENCODING
@@ -167,6 +191,48 @@ def create_validator(
167
191
  return FunctionValidator
168
192
 
169
193
 
194
+ def json_unpickle(
195
+ file_name: Union[Text, Path], encode_non_string_keys: bool = False
196
+ ) -> Any:
197
+ """Unpickle an object from file using json.
198
+
199
+ Args:
200
+ file_name: the file to load the object from
201
+ encode_non_string_keys: If set to `True` then jsonpickle will encode non-string
202
+ dictionary keys instead of coercing them into strings via `repr()`.
203
+
204
+ Returns: the object
205
+ """
206
+ import jsonpickle.ext.numpy as jsonpickle_numpy
207
+ import jsonpickle
208
+
209
+ jsonpickle_numpy.register_handlers()
210
+
211
+ file_content = rasa.shared.utils.io.read_file(file_name)
212
+ return jsonpickle.loads(file_content, keys=encode_non_string_keys)
213
+
214
+
215
+ def json_pickle(
216
+ file_name: Union[Text, Path], obj: Any, encode_non_string_keys: bool = False
217
+ ) -> None:
218
+ """Pickle an object to a file using json.
219
+
220
+ Args:
221
+ file_name: the file to store the object to
222
+ obj: the object to store
223
+ encode_non_string_keys: If set to `True` then jsonpickle will encode non-string
224
+ dictionary keys instead of coercing them into strings via `repr()`.
225
+ """
226
+ import jsonpickle.ext.numpy as jsonpickle_numpy
227
+ import jsonpickle
228
+
229
+ jsonpickle_numpy.register_handlers()
230
+
231
+ rasa.shared.utils.io.write_text_file(
232
+ jsonpickle.dumps(obj, keys=encode_non_string_keys), file_name
233
+ )
234
+
235
+
170
236
  def get_emoji_regex() -> Pattern:
171
237
  """Returns regex to identify emojis."""
172
238
  return re.compile(
@@ -20,8 +20,6 @@ import numpy as np
20
20
  import scipy.sparse
21
21
  from sklearn.model_selection import train_test_split
22
22
 
23
- from rasa.utils.tensorflow.feature_array import FeatureArray
24
-
25
23
  logger = logging.getLogger(__name__)
26
24
 
27
25
 
@@ -39,6 +37,199 @@ def ragged_array_to_ndarray(ragged_array: Iterable[np.ndarray]) -> np.ndarray:
39
37
  return np.array(ragged_array, dtype=object)
40
38
 
41
39
 
40
+ class FeatureArray(np.ndarray):
41
+ """Stores any kind of features ready to be used by a RasaModel.
42
+
43
+ Next to the input numpy array of features, it also received the number of
44
+ dimensions of the features.
45
+ As our features can have 1 to 4 dimensions we might have different number of numpy
46
+ arrays stacked. The number of dimensions helps us to figure out how to handle this
47
+ particular feature array. Also, it is automatically determined whether the feature
48
+ array is sparse or not and the number of units is determined as well.
49
+
50
+ Subclassing np.array: https://numpy.org/doc/stable/user/basics.subclassing.html
51
+ """
52
+
53
+ def __new__(
54
+ cls, input_array: np.ndarray, number_of_dimensions: int
55
+ ) -> "FeatureArray":
56
+ """Create and return a new object. See help(type) for accurate signature."""
57
+ FeatureArray._validate_number_of_dimensions(number_of_dimensions, input_array)
58
+
59
+ feature_array = np.asarray(input_array).view(cls)
60
+
61
+ if number_of_dimensions <= 2:
62
+ feature_array.units = input_array.shape[-1]
63
+ feature_array.is_sparse = isinstance(input_array[0], scipy.sparse.spmatrix)
64
+ elif number_of_dimensions == 3:
65
+ feature_array.units = input_array[0].shape[-1]
66
+ feature_array.is_sparse = isinstance(input_array[0], scipy.sparse.spmatrix)
67
+ elif number_of_dimensions == 4:
68
+ feature_array.units = input_array[0][0].shape[-1]
69
+ feature_array.is_sparse = isinstance(
70
+ input_array[0][0], scipy.sparse.spmatrix
71
+ )
72
+ else:
73
+ raise ValueError(
74
+ f"Number of dimensions '{number_of_dimensions}' currently not "
75
+ f"supported."
76
+ )
77
+
78
+ feature_array.number_of_dimensions = number_of_dimensions
79
+
80
+ return feature_array
81
+
82
+ def __init__(
83
+ self, input_array: Any, number_of_dimensions: int, **kwargs: Any
84
+ ) -> None:
85
+ """Initialize. FeatureArray.
86
+
87
+ Needed in order to avoid 'Invalid keyword argument number_of_dimensions
88
+ to function FeatureArray.__init__ '
89
+ Args:
90
+ input_array: the array that contains features
91
+ number_of_dimensions: number of dimensions in input_array
92
+ """
93
+ super().__init__(**kwargs)
94
+ self.number_of_dimensions = number_of_dimensions
95
+
96
+ def __array_finalize__(self, obj: Optional[np.ndarray]) -> None:
97
+ """This method is called when the system allocates a new array from obj.
98
+
99
+ Args:
100
+ obj: A subclass (subtype) of ndarray.
101
+ """
102
+ if obj is None:
103
+ return
104
+
105
+ self.units = getattr(obj, "units", None)
106
+ self.number_of_dimensions = getattr(obj, "number_of_dimensions", None) # type: ignore[assignment]
107
+ self.is_sparse = getattr(obj, "is_sparse", None)
108
+
109
+ default_attributes = {
110
+ "units": self.units,
111
+ "number_of_dimensions": self.number_of_dimensions,
112
+ "is_spare": self.is_sparse,
113
+ }
114
+ self.__dict__.update(default_attributes)
115
+
116
+ # pytype: disable=attribute-error
117
+ def __array_ufunc__(
118
+ self, ufunc: Any, method: Text, *inputs: Any, **kwargs: Any
119
+ ) -> Any:
120
+ """Overwrite this method as we are subclassing numpy array.
121
+
122
+ Args:
123
+ ufunc: The ufunc object that was called.
124
+ method: A string indicating which Ufunc method was called
125
+ (one of "__call__", "reduce", "reduceat", "accumulate", "outer",
126
+ "inner").
127
+ *inputs: A tuple of the input arguments to the ufunc.
128
+ **kwargs: Any additional arguments
129
+
130
+ Returns:
131
+ The result of the operation.
132
+ """
133
+ f = {
134
+ "reduce": ufunc.reduce,
135
+ "accumulate": ufunc.accumulate,
136
+ "reduceat": ufunc.reduceat,
137
+ "outer": ufunc.outer,
138
+ "at": ufunc.at,
139
+ "__call__": ufunc,
140
+ }
141
+ # convert the inputs to np.ndarray to prevent recursion, call the function,
142
+ # then cast it back as FeatureArray
143
+ output = FeatureArray(
144
+ f[method](*(i.view(np.ndarray) for i in inputs), **kwargs),
145
+ number_of_dimensions=kwargs["number_of_dimensions"],
146
+ )
147
+ output.__dict__ = self.__dict__ # carry forward attributes
148
+ return output
149
+
150
+ def __reduce__(self) -> Tuple[Any, Any, Any]:
151
+ """Needed in order to pickle this object.
152
+
153
+ Returns:
154
+ A tuple.
155
+ """
156
+ pickled_state = super(FeatureArray, self).__reduce__()
157
+ if isinstance(pickled_state, str):
158
+ raise TypeError("np array __reduce__ returned string instead of tuple.")
159
+ new_state = pickled_state[2] + (
160
+ self.number_of_dimensions,
161
+ self.is_sparse,
162
+ self.units,
163
+ )
164
+ return pickled_state[0], pickled_state[1], new_state
165
+
166
+ def __setstate__(self, state: Any, **kwargs: Any) -> None:
167
+ """Sets the state.
168
+
169
+ Args:
170
+ state: The state argument must be a sequence that contains the following
171
+ elements version, shape, dtype, isFortan, rawdata.
172
+ **kwargs: Any additional parameter
173
+ """
174
+ # Needed in order to load the object
175
+ self.number_of_dimensions = state[-3]
176
+ self.is_sparse = state[-2]
177
+ self.units = state[-1]
178
+ super(FeatureArray, self).__setstate__(state[0:-3], **kwargs)
179
+
180
+ # pytype: enable=attribute-error
181
+
182
+ @staticmethod
183
+ def _validate_number_of_dimensions(
184
+ number_of_dimensions: int, input_array: np.ndarray
185
+ ) -> None:
186
+ """Validates if the the input array has given number of dimensions.
187
+
188
+ Args:
189
+ number_of_dimensions: number of dimensions
190
+ input_array: input array
191
+
192
+ Raises: ValueError in case the dimensions do not match
193
+ """
194
+ _sub_array = input_array
195
+ dim = 0
196
+ # Go number_of_dimensions into the given input_array
197
+ for i in range(1, number_of_dimensions + 1):
198
+ _sub_array = _sub_array[0]
199
+ if isinstance(_sub_array, scipy.sparse.spmatrix):
200
+ dim = i
201
+ break
202
+ if isinstance(_sub_array, np.ndarray) and _sub_array.shape[0] == 0:
203
+ # sequence dimension is 0, we are dealing with "fake" features
204
+ dim = i
205
+ break
206
+
207
+ # If the resulting sub_array is sparse, the remaining number of dimensions
208
+ # should be at least 2
209
+ if isinstance(_sub_array, scipy.sparse.spmatrix):
210
+ if dim > 2:
211
+ raise ValueError(
212
+ f"Given number of dimensions '{number_of_dimensions}' does not "
213
+ f"match dimensions of given input array: {input_array}."
214
+ )
215
+ elif isinstance(_sub_array, np.ndarray) and _sub_array.shape[0] == 0:
216
+ # sequence dimension is 0, we are dealing with "fake" features,
217
+ # but they should be of dim 2
218
+ if dim > 2:
219
+ raise ValueError(
220
+ f"Given number of dimensions '{number_of_dimensions}' does not "
221
+ f"match dimensions of given input array: {input_array}."
222
+ )
223
+ # If the resulting sub_array is dense, the sub_array should be a single number
224
+ elif not np.issubdtype(type(_sub_array), np.integer) and not isinstance(
225
+ _sub_array, (np.float32, np.float64)
226
+ ):
227
+ raise ValueError(
228
+ f"Given number of dimensions '{number_of_dimensions}' does not match "
229
+ f"dimensions of given input array: {input_array}."
230
+ )
231
+
232
+
42
233
  class FeatureSignature(NamedTuple):
43
234
  """Signature of feature arrays.
44
235
 
rasa/validator.py CHANGED
@@ -303,18 +303,6 @@ class Validator:
303
303
  everything_is_alright = True
304
304
 
305
305
  for response_text, response_variations in self.domain.responses.items():
306
- if not response_variations:
307
- structlogger.error(
308
- "validator.empty_response",
309
- response=response_text,
310
- event_info=(
311
- f"The response '{response_text}' in the domain file "
312
- f"does not have any variations. Please add at least one "
313
- f"variation to the response."
314
- ),
315
- )
316
- everything_is_alright = False
317
-
318
306
  for response in response_variations:
319
307
  if any(
320
308
  self.check_for_placeholder(response.get(key))
rasa/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  # this file will automatically be changed,
2
2
  # do not add anything but the version number here!
3
- __version__ = "3.10.16"
3
+ __version__ = "3.11.0a1"