rasa-pro 3.10.7.dev4__py3-none-any.whl → 3.10.8__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 (76) hide show
  1. README.md +37 -1
  2. rasa/api.py +2 -8
  3. rasa/cli/arguments/default_arguments.py +2 -23
  4. rasa/cli/arguments/run.py +0 -2
  5. rasa/cli/e2e_test.py +8 -10
  6. rasa/cli/inspect.py +2 -5
  7. rasa/cli/run.py +0 -7
  8. rasa/cli/studio/studio.py +21 -1
  9. rasa/cli/train.py +4 -9
  10. rasa/cli/utils.py +3 -3
  11. rasa/core/agent.py +2 -2
  12. rasa/core/brokers/kafka.py +1 -3
  13. rasa/core/brokers/pika.py +1 -3
  14. rasa/core/channels/socketio.py +1 -5
  15. rasa/core/channels/voice_aware/utils.py +5 -6
  16. rasa/core/nlg/contextual_response_rephraser.py +2 -11
  17. rasa/core/policies/enterprise_search_policy.py +2 -11
  18. rasa/core/policies/intentless_policy.py +2 -9
  19. rasa/core/run.py +1 -2
  20. rasa/core/secrets_manager/constants.py +0 -4
  21. rasa/core/secrets_manager/factory.py +0 -8
  22. rasa/core/secrets_manager/vault.py +1 -11
  23. rasa/core/utils.py +19 -30
  24. rasa/dialogue_understanding/coexistence/llm_based_router.py +2 -9
  25. rasa/dialogue_understanding/commands/__init__.py +2 -0
  26. rasa/dialogue_understanding/commands/restart_command.py +58 -0
  27. rasa/dialogue_understanding/commands/set_slot_command.py +5 -1
  28. rasa/dialogue_understanding/commands/utils.py +3 -1
  29. rasa/dialogue_understanding/generator/llm_based_command_generator.py +2 -11
  30. rasa/dialogue_understanding/generator/llm_command_generator.py +1 -1
  31. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +15 -15
  32. rasa/dialogue_understanding/patterns/restart.py +37 -0
  33. rasa/e2e_test/e2e_test_runner.py +1 -1
  34. rasa/engine/graph.py +1 -0
  35. rasa/engine/recipes/config_files/default_config.yml +3 -0
  36. rasa/engine/recipes/default_recipe.py +1 -0
  37. rasa/engine/recipes/graph_recipe.py +1 -0
  38. rasa/engine/storage/local_model_storage.py +1 -0
  39. rasa/engine/storage/storage.py +5 -1
  40. rasa/model_training.py +6 -11
  41. rasa/{core → nlu}/persistor.py +1 -1
  42. rasa/server.py +1 -1
  43. rasa/shared/constants.py +3 -2
  44. rasa/shared/core/domain.py +47 -101
  45. rasa/shared/core/flows/flows_list.py +6 -19
  46. rasa/shared/core/flows/validation.py +0 -25
  47. rasa/shared/core/flows/yaml_flows_io.py +24 -3
  48. rasa/shared/importers/importer.py +32 -32
  49. rasa/shared/importers/multi_project.py +11 -23
  50. rasa/shared/importers/rasa.py +2 -7
  51. rasa/shared/importers/remote_importer.py +2 -2
  52. rasa/shared/importers/utils.py +1 -3
  53. rasa/shared/nlu/training_data/training_data.py +19 -18
  54. rasa/shared/providers/_configs/azure_openai_client_config.py +5 -3
  55. rasa/shared/providers/llm/_base_litellm_client.py +26 -10
  56. rasa/shared/providers/llm/self_hosted_llm_client.py +15 -3
  57. rasa/shared/utils/common.py +22 -3
  58. rasa/shared/utils/llm.py +5 -29
  59. rasa/shared/utils/schemas/model_config.yml +10 -0
  60. rasa/studio/auth.py +4 -0
  61. rasa/tracing/instrumentation/attribute_extractors.py +1 -1
  62. rasa/validator.py +5 -2
  63. rasa/version.py +1 -1
  64. {rasa_pro-3.10.7.dev4.dist-info → rasa_pro-3.10.8.dist-info}/METADATA +43 -7
  65. {rasa_pro-3.10.7.dev4.dist-info → rasa_pro-3.10.8.dist-info}/RECORD +68 -74
  66. rasa/model_manager/__init__.py +0 -0
  67. rasa/model_manager/config.py +0 -12
  68. rasa/model_manager/model_api.py +0 -464
  69. rasa/model_manager/runner_service.py +0 -185
  70. rasa/model_manager/socket_bridge.py +0 -44
  71. rasa/model_manager/trainer_service.py +0 -240
  72. rasa/model_manager/utils.py +0 -27
  73. rasa/model_service.py +0 -66
  74. {rasa_pro-3.10.7.dev4.dist-info → rasa_pro-3.10.8.dist-info}/NOTICE +0 -0
  75. {rasa_pro-3.10.7.dev4.dist-info → rasa_pro-3.10.8.dist-info}/WHEEL +0 -0
  76. {rasa_pro-3.10.7.dev4.dist-info → rasa_pro-3.10.8.dist-info}/entry_points.txt +0 -0
rasa/shared/constants.py CHANGED
@@ -183,8 +183,6 @@ STREAM_CONFIG_KEY = "stream"
183
183
  N_REPHRASES_CONFIG_KEY = "n"
184
184
  USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY = "use_chat_completions_endpoint"
185
185
 
186
- LLM_API_HEALTH_CHECK_ENV_VAR = "LLM_API_HEALTH_CHECK"
187
-
188
186
  AZURE_API_KEY_ENV_VAR = "AZURE_API_KEY"
189
187
  AZURE_AD_TOKEN_ENV_VAR = "AZURE_AD_TOKEN"
190
188
  AZURE_API_BASE_ENV_VAR = "AZURE_API_BASE"
@@ -212,6 +210,9 @@ AZURE_OPENAI_PROVIDER = "azure"
212
210
  SELF_HOSTED_PROVIDER = "self-hosted"
213
211
  HUGGINGFACE_LOCAL_EMBEDDING_PROVIDER = "huggingface_local"
214
212
 
213
+ SELF_HOSTED_VLLM_PREFIX = "hosted_vllm"
214
+ SELF_HOSTED_VLLM_API_KEY_ENV_VAR = "HOSTED_VLLM_API_KEY"
215
+
215
216
  AZURE_API_TYPE = "azure"
216
217
  OPENAI_API_TYPE = "openai"
217
218
 
@@ -1,21 +1,16 @@
1
- from __future__ import annotations
2
-
3
1
  import collections
4
2
  import copy
5
3
  import json
6
4
  import os
7
5
  from dataclasses import dataclass
8
- from functools import cached_property
9
6
  from pathlib import Path
10
7
  from typing import (
11
8
  TYPE_CHECKING,
12
9
  Any,
13
10
  Callable,
14
- ClassVar,
15
11
  Dict,
16
12
  Iterable,
17
13
  List,
18
- MutableMapping,
19
14
  NamedTuple,
20
15
  NoReturn,
21
16
  Optional,
@@ -24,6 +19,7 @@ from typing import (
24
19
  Tuple,
25
20
  Union,
26
21
  cast,
22
+ MutableMapping,
27
23
  )
28
24
 
29
25
  import structlog
@@ -55,11 +51,11 @@ from rasa.shared.core.constants import (
55
51
  )
56
52
  from rasa.shared.core.events import SlotSet, UserUttered
57
53
  from rasa.shared.core.slots import (
58
- AnySlot,
59
- CategoricalSlot,
60
- ListSlot,
61
54
  Slot,
55
+ CategoricalSlot,
62
56
  TextSlot,
57
+ AnySlot,
58
+ ListSlot,
63
59
  )
64
60
  from rasa.shared.exceptions import (
65
61
  RasaException,
@@ -67,21 +63,21 @@ from rasa.shared.exceptions import (
67
63
  YamlSyntaxException,
68
64
  )
69
65
  from rasa.shared.nlu.constants import (
70
- ENTITIES,
71
- ENTITY_ATTRIBUTE_GROUP,
72
- ENTITY_ATTRIBUTE_ROLE,
73
66
  ENTITY_ATTRIBUTE_TYPE,
74
- INTENT_NAME_KEY,
67
+ ENTITY_ATTRIBUTE_ROLE,
68
+ ENTITY_ATTRIBUTE_GROUP,
75
69
  RESPONSE_IDENTIFIER_DELIMITER,
70
+ INTENT_NAME_KEY,
71
+ ENTITIES,
76
72
  )
77
73
  from rasa.shared.utils.cli import print_error_and_exit
78
74
  from rasa.shared.utils.yaml import (
79
75
  KEY_TRAINING_DATA_FORMAT_VERSION,
80
- dump_obj_as_yaml_to_string,
81
76
  read_yaml,
77
+ validate_training_data_format_version,
82
78
  read_yaml_file,
79
+ dump_obj_as_yaml_to_string,
83
80
  validate_raw_yaml_using_schema_file_with_responses,
84
- validate_training_data_format_version,
85
81
  )
86
82
 
87
83
  if TYPE_CHECKING:
@@ -159,7 +155,7 @@ class SessionConfig(NamedTuple):
159
155
  carry_over_slots: bool
160
156
 
161
157
  @staticmethod
162
- def default() -> SessionConfig:
158
+ def default() -> "SessionConfig":
163
159
  """Returns the SessionConfig with the default values."""
164
160
  return SessionConfig(
165
161
  DEFAULT_SESSION_EXPIRATION_TIME_IN_MINUTES,
@@ -195,15 +191,13 @@ class Domain:
195
191
  and entities it can recognise.
196
192
  """
197
193
 
198
- validate_yaml: ClassVar[bool] = True
199
-
200
194
  @classmethod
201
- def empty(cls) -> Domain:
195
+ def empty(cls) -> "Domain":
202
196
  """Returns empty Domain."""
203
197
  return Domain.from_dict({})
204
198
 
205
199
  @classmethod
206
- def load(cls, paths: Union[List[Union[Path, Text]], Text, Path]) -> Domain:
200
+ def load(cls, paths: Union[List[Union[Path, Text]], Text, Path]) -> "Domain":
207
201
  """Returns loaded Domain after merging all domain files."""
208
202
  if not paths:
209
203
  raise InvalidDomain(
@@ -221,15 +215,8 @@ class Domain:
221
215
  return domain
222
216
 
223
217
  @classmethod
224
- def from_path(cls, path: Union[Text, Path]) -> Domain:
225
- """Loads the `Domain` from a path.
226
-
227
- Args:
228
- path: Path to the domain file.
229
-
230
- Returns:
231
- The instantiated `Domain` object.
232
- """
218
+ def from_path(cls, path: Union[Text, Path]) -> "Domain":
219
+ """Loads the `Domain` from a path."""
233
220
  path = os.path.abspath(path)
234
221
 
235
222
  if os.path.isfile(path):
@@ -245,30 +232,17 @@ class Domain:
245
232
  return domain
246
233
 
247
234
  @classmethod
248
- def from_file(cls, path: Text) -> Domain:
249
- """Loads the `Domain` from a YAML file.
250
-
251
- Args:
252
- path: Path to the domain file.
253
-
254
- Returns:
255
- The instantiated `Domain` object.
256
- """
235
+ def from_file(cls, path: Text) -> "Domain":
236
+ """Loads the `Domain` from a YAML file."""
257
237
  return cls.from_yaml(rasa.shared.utils.io.read_file(path), path)
258
238
 
259
239
  @classmethod
260
- def from_yaml(cls, yaml: Text, original_filename: Text = "") -> Domain:
261
- """Loads the `Domain` from YAML text after validating it.
262
-
263
- Args:
264
- yaml: The YAML string to load the domain from.
265
- original_filename: The filename of the original YAML file.
266
-
267
- Returns:
268
- The instantiated `Domain` object.
269
- """
240
+ def from_yaml(cls, yaml: Text, original_filename: Text = "") -> "Domain":
241
+ """Loads the `Domain` from YAML text after validating it."""
270
242
  try:
271
- data = cls._dict_from_raw_yaml_content(yaml)
243
+ validate_raw_yaml_using_schema_file_with_responses(yaml, DOMAIN_SCHEMA_FILE)
244
+
245
+ data = read_yaml(yaml)
272
246
  if not validate_training_data_format_version(data, original_filename):
273
247
  return Domain.empty()
274
248
  return cls.from_dict(data)
@@ -277,7 +251,7 @@ class Domain:
277
251
  raise e
278
252
 
279
253
  @classmethod
280
- def from_dict(cls, data: Dict) -> Domain:
254
+ def from_dict(cls, data: Dict) -> "Domain":
281
255
  """Deserializes and creates domain.
282
256
 
283
257
  Args:
@@ -308,8 +282,8 @@ class Domain:
308
282
  _validate_forms(forms)
309
283
 
310
284
  return cls(
311
- intents=intents or [],
312
- entities=data.get(KEY_ENTITIES, []),
285
+ intents=intents,
286
+ entities=data.get(KEY_ENTITIES, {}),
313
287
  slots=slots,
314
288
  responses=responses,
315
289
  action_names=actions,
@@ -334,15 +308,8 @@ class Domain:
334
308
  return SessionConfig(session_expiration_time_min, carry_over_slots)
335
309
 
336
310
  @classmethod
337
- def from_directory(cls, path: Text) -> Domain:
338
- """Loads and merges multiple domain files recursively from a directory tree.
339
-
340
- Args:
341
- path: Path to the root directory.
342
-
343
- Returns:
344
- The instantiated `Domain` object.
345
- """
311
+ def from_directory(cls, path: Text) -> "Domain":
312
+ """Loads and merges multiple domain files recursively from a directory tree."""
346
313
  combined: Dict[Text, Any] = {}
347
314
  duplicates: List[Dict[Text, List[Text]]] = []
348
315
 
@@ -352,10 +319,10 @@ class Domain:
352
319
  if not Domain.is_domain_file(full_path):
353
320
  continue
354
321
 
355
- other_dict = cls._dict_from_raw_yaml_content(
356
- rasa.shared.utils.io.read_file(full_path)
357
- )
322
+ # does the validation here only
323
+ _ = Domain.from_file(full_path)
358
324
 
325
+ other_dict = read_yaml(rasa.shared.utils.io.read_file(full_path))
359
326
  combined = Domain.merge_domain_dicts(other_dict, combined)
360
327
  duplicates.append(combined.pop("duplicates", {}))
361
328
 
@@ -401,10 +368,10 @@ class Domain:
401
368
 
402
369
  def merge(
403
370
  self,
404
- domain: Optional[Domain],
371
+ domain: Optional["Domain"],
405
372
  override: bool = False,
406
373
  ignore_warnings_about_duplicates: bool = False,
407
- ) -> Domain:
374
+ ) -> "Domain":
408
375
  """Merges this domain dict with another one, combining their attributes.
409
376
 
410
377
  This method merges domain dicts, and ensures all attributes (like ``intents``,
@@ -707,7 +674,7 @@ class Domain:
707
674
 
708
675
  return intent
709
676
 
710
- @cached_property
677
+ @rasa.shared.utils.common.lazy_property
711
678
  def retrieval_intents(self) -> List[Text]:
712
679
  """List retrieval intents present in the domain."""
713
680
  return [
@@ -932,7 +899,7 @@ class Domain:
932
899
  self.store_entities_as_slots = store_entities_as_slots
933
900
  self._check_domain_sanity()
934
901
 
935
- def __deepcopy__(self, memo: Optional[Dict[int, Any]]) -> Domain:
902
+ def __deepcopy__(self, memo: Optional[Dict[int, Any]]) -> "Domain":
936
903
  """Enables making a deep copy of the `Domain` using `copy.deepcopy`.
937
904
 
938
905
  See https://docs.python.org/3/library/copy.html#copy.deepcopy
@@ -1036,23 +1003,23 @@ class Domain:
1036
1003
  sorted_intents = sorted(intents, key=sort)
1037
1004
  return sorted_intents
1038
1005
 
1039
- @cached_property
1006
+ @rasa.shared.utils.common.lazy_property
1040
1007
  def user_actions_and_forms(self) -> List[Text]:
1041
1008
  """Returns combination of user actions and forms."""
1042
1009
  return self.user_actions + self.form_names
1043
1010
 
1044
- @cached_property
1011
+ @rasa.shared.utils.common.lazy_property
1045
1012
  def num_actions(self) -> int:
1046
1013
  """Returns the number of available actions."""
1047
1014
  # noinspection PyTypeChecker
1048
1015
  return len(self.action_names_or_texts)
1049
1016
 
1050
- @cached_property
1017
+ @rasa.shared.utils.common.lazy_property
1051
1018
  def num_states(self) -> int:
1052
1019
  """Number of used input states for the action prediction."""
1053
1020
  return len(self.input_states)
1054
1021
 
1055
- @cached_property
1022
+ @rasa.shared.utils.common.lazy_property
1056
1023
  def retrieval_intent_responses(self) -> Dict[Text, List[Dict[Text, Any]]]:
1057
1024
  """Return only the responses which are defined for retrieval intents."""
1058
1025
  return dict(
@@ -1204,7 +1171,8 @@ class Domain:
1204
1171
  f"Available actions are: \n{action_names}"
1205
1172
  )
1206
1173
 
1207
- @cached_property
1174
+ # noinspection PyTypeChecker
1175
+ @rasa.shared.utils.common.lazy_property
1208
1176
  def slot_states(self) -> List[Text]:
1209
1177
  """Returns all available slot state strings."""
1210
1178
  return [
@@ -1213,7 +1181,8 @@ class Domain:
1213
1181
  for feature_index in range(0, slot.feature_dimensionality())
1214
1182
  ]
1215
1183
 
1216
- @cached_property
1184
+ # noinspection PyTypeChecker
1185
+ @rasa.shared.utils.common.lazy_property
1217
1186
  def entity_states(self) -> List[Text]:
1218
1187
  """Returns all available entity state strings."""
1219
1188
  entity_states = copy.deepcopy(self.entities)
@@ -1261,12 +1230,12 @@ class Domain:
1261
1230
  for entity_sub_label in entity_sub_labels
1262
1231
  ]
1263
1232
 
1264
- @cached_property
1233
+ @rasa.shared.utils.common.lazy_property
1265
1234
  def input_state_map(self) -> Dict[Text, int]:
1266
1235
  """Provide a mapping from state names to indices."""
1267
1236
  return {f: i for i, f in enumerate(self.input_states)}
1268
1237
 
1269
- @cached_property
1238
+ @rasa.shared.utils.common.lazy_property
1270
1239
  def input_states(self) -> List[Text]:
1271
1240
  """Returns all available states."""
1272
1241
  return (
@@ -1496,7 +1465,7 @@ class Domain:
1496
1465
  ignore_rule_only_turns: bool = False,
1497
1466
  rule_only_data: Optional[Dict[Text, Any]] = None,
1498
1467
  ) -> List[State]:
1499
- """List of states for each state of the tracker's history.
1468
+ """List of states for each state of the trackers history.
1500
1469
 
1501
1470
  Args:
1502
1471
  tracker: Dialogue state tracker containing the dialogue so far.
@@ -1713,12 +1682,12 @@ class Domain:
1713
1682
  """Return the configuration for an intent."""
1714
1683
  return self.intent_properties.get(intent_name, {})
1715
1684
 
1716
- @cached_property
1685
+ @rasa.shared.utils.common.lazy_property
1717
1686
  def intents(self) -> List[Text]:
1718
1687
  """Returns sorted list of intents."""
1719
1688
  return sorted(self.intent_properties.keys())
1720
1689
 
1721
- @cached_property
1690
+ @rasa.shared.utils.common.lazy_property
1722
1691
  def entities(self) -> List[Text]:
1723
1692
  """Returns sorted list of entities."""
1724
1693
  return sorted(self.entity_properties.entities)
@@ -2086,29 +2055,6 @@ class Domain:
2086
2055
  def is_custom_action(self, action_name: str) -> bool:
2087
2056
  return action_name in self._custom_actions
2088
2057
 
2089
- @classmethod
2090
- def _dict_from_raw_yaml_content(cls, raw_yaml_content: Text) -> Any:
2091
- """Loads the Domain dict from raw YAML content.
2092
-
2093
- Validates the raw YAML content using the schema file if `validate_yaml` is set
2094
- to `True`.
2095
-
2096
- Args:
2097
- raw_yaml_content: The raw YAML content of the domain file.
2098
-
2099
- Returns:
2100
- The Domain dict.
2101
- """
2102
- if cls.validate_yaml:
2103
- structlogger.info(
2104
- "domain.from_yaml.validating",
2105
- )
2106
- validate_raw_yaml_using_schema_file_with_responses(
2107
- raw_yaml_content, DOMAIN_SCHEMA_FILE
2108
- )
2109
-
2110
- return read_yaml(raw_yaml_content)
2111
-
2112
2058
 
2113
2059
  def warn_about_duplicates_found_during_domain_merging(
2114
2060
  duplicates: Dict[Text, List[Text]],
@@ -16,7 +16,6 @@ from rasa.shared.core.flows.validation import (
16
16
  validate_patterns_are_not_called_or_linked,
17
17
  validate_patterns_are_not_calling_or_linking_other_flows,
18
18
  validate_step_ids_are_unique,
19
- DuplicatedFlowIdException,
20
19
  )
21
20
  from rasa.shared.core.slots import Slot
22
21
 
@@ -50,31 +49,21 @@ class FlowsList:
50
49
  return len(self.underlying_flows) == 0
51
50
 
52
51
  @classmethod
53
- def from_multiple_flows_lists(
54
- cls, *other: FlowsList, ignore_duplicates: bool = True
55
- ) -> FlowsList:
52
+ def from_multiple_flows_lists(cls, *other: FlowsList) -> FlowsList:
56
53
  """Merges multiple lists of flows into a single flow ensuring each flow is
57
54
  unique, based on its ID.
58
55
 
59
56
  Args:
60
57
  other: Variable number of flow lists instances to be merged.
61
- ignore_duplicates: Whether to ignore duplicate flow ids, or raise an error.
62
58
 
63
59
  Returns:
64
60
  Merged flow list.
65
61
  """
66
- merged_flows: Dict[Text, Flow] = dict()
62
+ merged_flows = dict()
67
63
  for flow_list in other:
68
64
  for flow in flow_list:
69
- if flow.id in merged_flows:
70
- if ignore_duplicates:
71
- continue
72
- current_flow_path = flow.file_path
73
- other_flow_path = merged_flows[flow.id].file_path
74
- raise DuplicatedFlowIdException(
75
- flow.id, current_flow_path, other_flow_path
76
- )
77
- merged_flows[flow.id] = flow
65
+ if flow.id not in merged_flows:
66
+ merged_flows[flow.id] = flow
78
67
  return FlowsList(list(merged_flows.values()))
79
68
 
80
69
  @classmethod
@@ -129,11 +118,9 @@ class FlowsList:
129
118
  flow_dicts = [flow.as_json() for flow in self.underlying_flows]
130
119
  return rasa.shared.utils.io.get_list_fingerprint(flow_dicts)
131
120
 
132
- def merge(self, other: FlowsList, ignore_duplicates: bool = True) -> FlowsList:
121
+ def merge(self, other: FlowsList) -> FlowsList:
133
122
  """Merges two lists of flows together."""
134
- return FlowsList.from_multiple_flows_lists(
135
- self, other, ignore_duplicates=ignore_duplicates
136
- )
123
+ return FlowsList.from_multiple_flows_lists(self, other)
137
124
 
138
125
  def flow_by_id(self, flow_id: Text) -> Optional[Flow]:
139
126
  """Return the flow with the given id."""
@@ -101,31 +101,6 @@ class DuplicatedStepIdException(RasaException):
101
101
  )
102
102
 
103
103
 
104
- class DuplicatedFlowIdException(RasaException):
105
- """Raised when a flow is using the same id as another flow."""
106
-
107
- def __init__(
108
- self, flow_id: str, first_file_path: str, second_file_path: str
109
- ) -> None:
110
- """Initializes the exception."""
111
- self.flow_id = flow_id
112
- self.first_file_path = first_file_path
113
- self.second_file_path = second_file_path
114
-
115
- def __str__(self) -> str:
116
- """Return a string representation of the exception."""
117
- if self.first_file_path == self.second_file_path:
118
- return (
119
- f"Flow '{self.flow_id}' is used twice in `{self.first_file_path}`. "
120
- f"Please make sure flow IDs are unique across all files."
121
- )
122
- return (
123
- f"Flow '{self.flow_id}' is used in both "
124
- f"`{self.first_file_path}` and `{self.second_file_path}`. "
125
- f"Please make sure flow IDs are unique across all files."
126
- )
127
-
128
-
129
104
  class MissingElseBranchException(RasaException):
130
105
  """Raised when a flow step is missing an else branch."""
131
106
 
@@ -1,5 +1,6 @@
1
+ import textwrap
1
2
  from pathlib import Path
2
- from typing import Any, Dict, List, Optional, Text, Union
3
+ from typing import Any, Dict, List, Text, Union, Optional
3
4
 
4
5
  import jsonschema
5
6
  import ruamel.yaml.nodes as yaml_nodes
@@ -11,11 +12,12 @@ import rasa.shared.utils.io
11
12
  from rasa.shared.core.flows.flow import Flow
12
13
  from rasa.shared.core.flows.flows_list import FlowsList
13
14
  from rasa.shared.exceptions import RasaException, YamlException
15
+ from rasa.shared.importers.importer import FlowSyncImporter
14
16
  from rasa.shared.utils.yaml import (
17
+ validate_yaml_with_jsonschema,
18
+ read_yaml,
15
19
  dump_obj_as_yaml_to_string,
16
20
  is_key_in_yaml,
17
- read_yaml,
18
- validate_yaml_with_jsonschema,
19
21
  )
20
22
 
21
23
  FLOWS_SCHEMA_FILE = "shared/core/flows/flows_yaml_schema.json"
@@ -260,6 +262,25 @@ class YamlFlowsWriter:
260
262
  rasa.shared.utils.io.write_text_file(YamlFlowsWriter.dumps(flows), filename)
261
263
 
262
264
 
265
+ def flows_from_str(yaml_str: str) -> FlowsList:
266
+ """Reads flows from a YAML string."""
267
+ flows = YAMLFlowsReader.read_from_string(
268
+ textwrap.dedent(yaml_str), add_line_numbers=False
269
+ )
270
+ flows.validate()
271
+ return flows
272
+
273
+
274
+ def flows_from_str_including_defaults(yaml_str: str) -> FlowsList:
275
+ """Reads flows from a YAML string and combine them with default flows."""
276
+ flows = YAMLFlowsReader.read_from_string(
277
+ textwrap.dedent(yaml_str), add_line_numbers=False
278
+ )
279
+ all_flows = FlowSyncImporter.merge_with_default_flows(flows)
280
+ all_flows.validate()
281
+ return all_flows
282
+
283
+
263
284
  def is_flows_file(file_path: Union[Text, Path]) -> bool:
264
285
  """Check if file contains Flow training data.
265
286