rasa-pro 3.13.0.dev7__py3-none-any.whl → 3.13.0.dev9__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 (215) hide show
  1. rasa/__main__.py +0 -3
  2. rasa/api.py +1 -1
  3. rasa/cli/dialogue_understanding_test.py +1 -1
  4. rasa/cli/e2e_test.py +1 -1
  5. rasa/cli/evaluate.py +1 -1
  6. rasa/cli/export.py +1 -1
  7. rasa/cli/llm_fine_tuning.py +12 -11
  8. rasa/cli/project_templates/defaults.py +133 -0
  9. rasa/cli/run.py +1 -1
  10. rasa/cli/studio/link.py +53 -0
  11. rasa/cli/studio/pull.py +78 -0
  12. rasa/cli/studio/push.py +78 -0
  13. rasa/cli/studio/studio.py +12 -0
  14. rasa/cli/studio/upload.py +8 -0
  15. rasa/cli/train.py +1 -1
  16. rasa/cli/utils.py +1 -1
  17. rasa/cli/x.py +1 -1
  18. rasa/constants.py +2 -0
  19. rasa/core/__init__.py +0 -16
  20. rasa/core/actions/action.py +5 -1
  21. rasa/core/actions/action_repeat_bot_messages.py +18 -22
  22. rasa/core/actions/action_run_slot_rejections.py +0 -1
  23. rasa/core/agent.py +16 -1
  24. rasa/core/available_endpoints.py +146 -0
  25. rasa/core/brokers/pika.py +1 -2
  26. rasa/core/channels/botframework.py +2 -2
  27. rasa/core/channels/channel.py +2 -2
  28. rasa/core/channels/development_inspector.py +1 -1
  29. rasa/core/channels/facebook.py +1 -4
  30. rasa/core/channels/hangouts.py +8 -5
  31. rasa/core/channels/inspector/README.md +3 -3
  32. rasa/core/channels/inspector/dist/assets/{arc-c4b064fc.js → arc-02053cc1.js} +1 -1
  33. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-215b5026.js → blockDiagram-38ab4fdb-008b6289.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-2b54a0a3.js → c4Diagram-3d4e48cf-fb2597be.js} +1 -1
  35. rasa/core/channels/inspector/dist/assets/channel-078dada8.js +1 -0
  36. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-daacea5f.js → classDiagram-70f12bd4-7f847e00.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-930d4dc2.js → classDiagram-v2-f2320105-ba1d689b.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/clone-5b4516de.js +1 -0
  39. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-83c206ba.js → createText-2e5e7dd3-dd8e67c4.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-b0eb01d0.js → edges-e0da2a9e-10784939.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-17586500.js → erDiagram-9861fffd-24947ae6.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-be2a1776.js → flowDb-956e92f1-a9ced505.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-c2120ebd.js → flowDiagram-66a62f08-afda9c7c.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-f9613071.js +1 -0
  45. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-a6ab5c48.js → flowchart-elk-definition-4a651766-6ef530b8.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-ef613457.js → ganttDiagram-c361ad54-0c7dd39a.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-d59185b3.js → gitGraphDiagram-72cf32ee-b57239d6.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{graph-0f155405.js → graph-9ed57cec.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/{index-3862675e-d5f1d1b7.js → index-3862675e-233090de.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{index-47737d3a.js → index-72184470.js} +3 -3
  51. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-b07d141f.js → infoDiagram-f8f76790-aa116649.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-1936d429.js → journeyDiagram-49397b02-e51877cc.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{layout-dde8d0f3.js → layout-3ca3798c.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{line-0c2c7ee0.js → line-26ee10d3.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{linear-35dd89a4.js → linear-aedded32.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-56192851.js → mindmap-definition-fc14e90a-d8957261.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-fc21ed78.js → pieDiagram-8a3498a8-d771f885.js} +1 -1
  58. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-25e98518.js → quadrantDiagram-120e2f19-09fdf50c.js} +1 -1
  59. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-546ff1f5.js → requirementDiagram-deff3bca-9f0af02e.js} +1 -1
  60. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-02d8b82d.js → sankeyDiagram-04a897e0-84415b37.js} +1 -1
  61. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-3ca5a92e.js → sequenceDiagram-704730f1-8dec4055.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-128ea07c.js → stateDiagram-587899a1-c5431d07.js} +1 -1
  63. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-95f290af.js → stateDiagram-v2-d93cdb3a-274e77d9.js} +1 -1
  64. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-4984898a.js → styles-6aaf32cf-e364a1d7.js} +1 -1
  65. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-1bf266ba.js → styles-9a916d00-0dae36f6.js} +1 -1
  66. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-60521c63.js → styles-c10674c1-c4641675.js} +1 -1
  67. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-a25b6e12.js → svgDrawCommon-08f97a94-831fe9a1.js} +1 -1
  68. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-0fc086bf.js → timeline-definition-85554ec2-c3304b3a.js} +1 -1
  69. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-44ee592e.js → xychartDiagram-e933f94c-da799369.js} +1 -1
  70. rasa/core/channels/inspector/dist/index.html +1 -1
  71. rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +1 -1
  72. rasa/core/channels/mattermost.py +1 -1
  73. rasa/core/channels/rasa_chat.py +2 -4
  74. rasa/core/channels/rest.py +5 -4
  75. rasa/core/channels/socketio.py +56 -41
  76. rasa/core/channels/studio_chat.py +314 -10
  77. rasa/core/channels/vier_cvg.py +1 -2
  78. rasa/core/channels/voice_ready/audiocodes.py +2 -9
  79. rasa/core/channels/voice_stream/audiocodes.py +8 -5
  80. rasa/core/channels/voice_stream/browser_audio.py +1 -1
  81. rasa/core/channels/voice_stream/genesys.py +2 -2
  82. rasa/core/channels/voice_stream/tts/__init__.py +8 -0
  83. rasa/core/channels/voice_stream/twilio_media_streams.py +10 -5
  84. rasa/core/channels/voice_stream/voice_channel.py +39 -23
  85. rasa/core/http_interpreter.py +3 -7
  86. rasa/core/information_retrieval/faiss.py +18 -11
  87. rasa/core/information_retrieval/ingestion/__init__.py +0 -0
  88. rasa/core/information_retrieval/ingestion/faq_parser.py +158 -0
  89. rasa/core/jobs.py +2 -1
  90. rasa/core/nlg/contextual_response_rephraser.py +44 -10
  91. rasa/core/nlg/generator.py +0 -1
  92. rasa/core/nlg/interpolator.py +2 -3
  93. rasa/core/nlg/summarize.py +39 -5
  94. rasa/core/policies/enterprise_search_policy.py +262 -62
  95. rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +63 -0
  96. rasa/core/policies/flow_policy.py +1 -1
  97. rasa/core/policies/flows/flow_executor.py +96 -17
  98. rasa/core/policies/intentless_policy.py +56 -17
  99. rasa/core/processor.py +104 -51
  100. rasa/core/run.py +33 -11
  101. rasa/core/tracker_stores/tracker_store.py +1 -1
  102. rasa/core/training/interactive.py +1 -1
  103. rasa/core/utils.py +24 -97
  104. rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
  105. rasa/dialogue_understanding/coexistence/llm_based_router.py +9 -6
  106. rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
  107. rasa/dialogue_understanding/commands/cancel_flow_command.py +5 -1
  108. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
  109. rasa/dialogue_understanding/commands/clarify_command.py +5 -1
  110. rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
  111. rasa/dialogue_understanding/commands/correct_slots_command.py +1 -3
  112. rasa/dialogue_understanding/commands/human_handoff_command.py +2 -0
  113. rasa/dialogue_understanding/commands/knowledge_answer_command.py +4 -2
  114. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
  115. rasa/dialogue_understanding/commands/set_slot_command.py +11 -1
  116. rasa/dialogue_understanding/commands/skip_question_command.py +2 -0
  117. rasa/dialogue_understanding/commands/start_flow_command.py +4 -0
  118. rasa/dialogue_understanding/commands/utils.py +26 -2
  119. rasa/dialogue_understanding/generator/__init__.py +7 -1
  120. rasa/dialogue_understanding/generator/command_generator.py +4 -2
  121. rasa/dialogue_understanding/generator/command_parser.py +2 -2
  122. rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
  123. rasa/dialogue_understanding/generator/nlu_command_adapter.py +2 -2
  124. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +12 -33
  125. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +78 -0
  126. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +26 -461
  127. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
  128. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +477 -0
  129. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +11 -64
  130. rasa/dialogue_understanding/patterns/cancel.py +1 -2
  131. rasa/dialogue_understanding/patterns/clarify.py +1 -1
  132. rasa/dialogue_understanding/patterns/correction.py +2 -2
  133. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +37 -25
  134. rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
  135. rasa/dialogue_understanding/processor/command_processor.py +6 -7
  136. rasa/dialogue_understanding/processor/command_processor_component.py +3 -3
  137. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +17 -4
  138. rasa/dialogue_understanding/stack/utils.py +3 -1
  139. rasa/dialogue_understanding/utils.py +68 -12
  140. rasa/dialogue_understanding_test/du_test_case.py +1 -1
  141. rasa/dialogue_understanding_test/du_test_runner.py +4 -22
  142. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -6
  143. rasa/e2e_test/e2e_test_runner.py +1 -1
  144. rasa/engine/constants.py +1 -1
  145. rasa/engine/graph.py +2 -2
  146. rasa/engine/recipes/default_recipe.py +26 -2
  147. rasa/engine/validation.py +3 -2
  148. rasa/hooks.py +0 -28
  149. rasa/llm_fine_tuning/annotation_module.py +39 -9
  150. rasa/llm_fine_tuning/conversations.py +3 -0
  151. rasa/llm_fine_tuning/llm_data_preparation_module.py +66 -49
  152. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +1 -5
  153. rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +52 -44
  154. rasa/llm_fine_tuning/paraphrasing_module.py +10 -12
  155. rasa/llm_fine_tuning/storage.py +4 -4
  156. rasa/llm_fine_tuning/utils.py +63 -1
  157. rasa/model_manager/model_api.py +88 -0
  158. rasa/model_manager/trainer_service.py +4 -4
  159. rasa/plugin.py +1 -11
  160. rasa/privacy/__init__.py +0 -0
  161. rasa/privacy/constants.py +83 -0
  162. rasa/privacy/event_broker_utils.py +77 -0
  163. rasa/privacy/privacy_config.py +281 -0
  164. rasa/privacy/privacy_config_schema.json +86 -0
  165. rasa/privacy/privacy_filter.py +340 -0
  166. rasa/privacy/privacy_manager.py +576 -0
  167. rasa/server.py +23 -2
  168. rasa/shared/constants.py +14 -0
  169. rasa/shared/core/command_payload_reader.py +1 -5
  170. rasa/shared/core/constants.py +4 -3
  171. rasa/shared/core/domain.py +7 -0
  172. rasa/shared/core/events.py +38 -10
  173. rasa/shared/core/flows/flow.py +1 -2
  174. rasa/shared/core/flows/flows_yaml_schema.json +3 -0
  175. rasa/shared/core/flows/steps/collect.py +46 -2
  176. rasa/shared/core/flows/validation.py +16 -3
  177. rasa/shared/core/slots.py +28 -0
  178. rasa/shared/core/training_data/story_reader/yaml_story_reader.py +1 -4
  179. rasa/shared/exceptions.py +4 -0
  180. rasa/shared/utils/common.py +1 -1
  181. rasa/shared/utils/llm.py +191 -6
  182. rasa/shared/utils/yaml.py +32 -0
  183. rasa/studio/data_handler.py +3 -3
  184. rasa/studio/download/download.py +37 -60
  185. rasa/studio/download/flows.py +23 -31
  186. rasa/studio/link.py +200 -0
  187. rasa/studio/pull.py +94 -0
  188. rasa/studio/push.py +131 -0
  189. rasa/studio/upload.py +117 -67
  190. rasa/telemetry.py +82 -25
  191. rasa/tracing/config.py +3 -4
  192. rasa/tracing/constants.py +19 -1
  193. rasa/tracing/instrumentation/attribute_extractors.py +10 -2
  194. rasa/tracing/instrumentation/instrumentation.py +53 -2
  195. rasa/tracing/instrumentation/metrics.py +98 -15
  196. rasa/tracing/metric_instrument_provider.py +75 -3
  197. rasa/utils/common.py +1 -27
  198. rasa/utils/log_utils.py +1 -45
  199. rasa/validator.py +2 -8
  200. rasa/version.py +1 -1
  201. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/METADATA +7 -8
  202. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/RECORD +205 -189
  203. rasa/anonymization/__init__.py +0 -2
  204. rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
  205. rasa/anonymization/anonymization_pipeline.py +0 -286
  206. rasa/anonymization/anonymization_rule_executor.py +0 -266
  207. rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
  208. rasa/anonymization/schemas/config.yml +0 -47
  209. rasa/anonymization/utils.py +0 -118
  210. rasa/core/channels/inspector/dist/assets/channel-3730f5fd.js +0 -1
  211. rasa/core/channels/inspector/dist/assets/clone-e847561e.js +0 -1
  212. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-efbbfe00.js +0 -1
  213. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/NOTICE +0 -0
  214. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/WHEEL +0 -0
  215. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/entry_points.txt +0 -0
rasa/shared/utils/llm.py CHANGED
@@ -1,13 +1,17 @@
1
+ from __future__ import annotations
2
+
1
3
  import importlib.resources
2
4
  import json
3
5
  import logging
4
6
  from copy import deepcopy
7
+ from datetime import datetime
5
8
  from functools import wraps
6
9
  from typing import (
7
10
  TYPE_CHECKING,
8
11
  Any,
9
12
  Callable,
10
13
  Dict,
14
+ List,
11
15
  Literal,
12
16
  Optional,
13
17
  Text,
@@ -18,15 +22,23 @@ from typing import (
18
22
  )
19
23
 
20
24
  import structlog
25
+ from pydantic import BaseModel, Field
21
26
 
22
27
  import rasa.shared.utils.io
23
- from rasa.core.utils import AvailableEndpoints
28
+ from rasa.core.available_endpoints import AvailableEndpoints
24
29
  from rasa.shared.constants import (
30
+ CONFIG_NAME_KEY,
31
+ CONFIG_PIPELINE_KEY,
32
+ CONFIG_POLICIES_KEY,
25
33
  DEFAULT_PROMPT_PACKAGE_NAME,
34
+ LLM_CONFIG_KEY,
26
35
  MODEL_CONFIG_KEY,
27
36
  MODEL_GROUP_CONFIG_KEY,
28
37
  MODEL_GROUP_ID_CONFIG_KEY,
38
+ MODEL_GROUPS_CONFIG_KEY,
29
39
  MODELS_CONFIG_KEY,
40
+ PROMPT_CONFIG_KEY,
41
+ PROMPT_TEMPLATE_CONFIG_KEY,
30
42
  PROVIDER_CONFIG_KEY,
31
43
  RASA_PATTERN_INTERNAL_ERROR_USER_INPUT_EMPTY,
32
44
  RASA_PATTERN_INTERNAL_ERROR_USER_INPUT_TOO_LONG,
@@ -61,9 +73,11 @@ from rasa.shared.providers.mappings import (
61
73
  get_embedding_client_from_provider,
62
74
  get_llm_client_from_provider,
63
75
  )
76
+ from rasa.shared.utils.common import all_subclasses
64
77
  from rasa.shared.utils.constants import LOG_COMPONENT_SOURCE_METHOD_INIT
65
78
 
66
79
  if TYPE_CHECKING:
80
+ from rasa.core.agent import Agent
67
81
  from rasa.shared.core.trackers import DialogueStateTracker
68
82
 
69
83
 
@@ -107,6 +121,18 @@ _CombineConfigs_F = TypeVar(
107
121
  )
108
122
 
109
123
 
124
+ class SystemPrompts(BaseModel):
125
+ command_generator: str = Field(
126
+ ..., description="Prompt used by the LLM command generator."
127
+ )
128
+ enterprise_search: str = Field(
129
+ ..., description="Prompt for standard enterprise search requests."
130
+ )
131
+ contextual_response_rephraser: str = Field(
132
+ ..., description="Prompt used for re-phrasing assistant responses."
133
+ )
134
+
135
+
110
136
  def _compute_hash_for_cache_from_configs(
111
137
  config_x: Dict[str, Any], config_y: Dict[str, Any]
112
138
  ) -> int:
@@ -191,6 +217,7 @@ def tracker_as_readable_transcript(
191
217
  human_prefix: str = USER,
192
218
  ai_prefix: str = AI,
193
219
  max_turns: Optional[int] = 20,
220
+ turns_wrapper: Optional[Callable[[List[str]], List[str]]] = None,
194
221
  ) -> str:
195
222
  """Creates a readable dialogue from a tracker.
196
223
 
@@ -199,6 +226,7 @@ def tracker_as_readable_transcript(
199
226
  human_prefix: the prefix to use for human utterances
200
227
  ai_prefix: the prefix to use for ai utterances
201
228
  max_turns: the maximum number of turns to include in the transcript
229
+ turns_wrapper: optional function to wrap the turns in a custom way
202
230
 
203
231
  Example:
204
232
  >>> tracker = Tracker(
@@ -235,8 +263,11 @@ def tracker_as_readable_transcript(
235
263
  elif isinstance(event, BotUttered):
236
264
  transcript.append(f"{ai_prefix}: {sanitize_message_for_prompt(event.text)}")
237
265
 
238
- if max_turns:
239
- transcript = transcript[-max_turns:]
266
+ # turns_wrapper to count multiple utterances by bot/user as single turn
267
+ if turns_wrapper:
268
+ transcript = turns_wrapper(transcript)
269
+ # otherwise, just take the last `max_turns` lines of the transcript
270
+ transcript = transcript[-max_turns if max_turns is not None else None :]
240
271
 
241
272
  return "\n".join(transcript)
242
273
 
@@ -678,7 +709,6 @@ def get_prompt_template(
678
709
  Returns:
679
710
  The prompt template.
680
711
  """
681
-
682
712
  try:
683
713
  if jinja_file_path is not None:
684
714
  prompt_template = rasa.shared.utils.io.read_file(jinja_file_path)
@@ -814,7 +844,9 @@ def allowed_values_for_slot(slot: Slot) -> Union[str, None]:
814
844
 
815
845
 
816
846
  def resolve_model_client_config(
817
- model_config: Optional[Dict[str, Any]], component_name: Optional[str] = None
847
+ model_config: Optional[Dict[str, Any]],
848
+ component_name: Optional[str] = None,
849
+ model_groups: Optional[List[Dict[str, Any]]] = None,
818
850
  ) -> Optional[Dict[str, Any]]:
819
851
  """Resolve the model group in the model config.
820
852
 
@@ -828,6 +860,7 @@ def resolve_model_client_config(
828
860
  model_config: The model config to be resolved.
829
861
  component_name: The name of the component.
830
862
  component_name: The method of the component.
863
+ model_groups: Model groups from endpoints.yml.
831
864
 
832
865
  Returns:
833
866
  The resolved llm config.
@@ -854,7 +887,12 @@ def resolve_model_client_config(
854
887
 
855
888
  model_group_id = model_config.get(MODEL_GROUP_CONFIG_KEY)
856
889
 
857
- endpoints = AvailableEndpoints.get_instance()
890
+ # If `model_groups` is provided, use it to initialise `AvailableEndpoints`,
891
+ # since `get_instance()` reads from the local endpoints file instead.
892
+ if model_groups:
893
+ endpoints = AvailableEndpoints(model_groups=model_groups)
894
+ else:
895
+ endpoints = AvailableEndpoints.get_instance()
858
896
  if endpoints.model_groups is None:
859
897
  _raise_invalid_config_exception(
860
898
  reason=(
@@ -886,3 +924,150 @@ def resolve_model_client_config(
886
924
  )
887
925
 
888
926
  return model_group[0]
927
+
928
+
929
+ def generate_sender_id(test_case_name: str) -> str:
930
+ # add timestamp suffix to ensure sender_id is unique
931
+ return f"{test_case_name}_{datetime.now()}"
932
+
933
+
934
+ async def create_tracker_for_user_step(
935
+ step_sender_id: str,
936
+ agent: "Agent",
937
+ test_case_tracker: "DialogueStateTracker",
938
+ index_user_uttered_event: int,
939
+ ) -> None:
940
+ """Creates a tracker for the user step."""
941
+ tracker = test_case_tracker.copy()
942
+ # modify the sender id so that the original tracker is not overwritten
943
+ tracker.sender_id = step_sender_id
944
+
945
+ if tracker.events:
946
+ # get the timestamp of the event just before the user uttered event
947
+ timestamp = tracker.events[index_user_uttered_event - 1].timestamp
948
+ # revert the tracker to the event just before the user uttered event
949
+ tracker = tracker.travel_back_in_time(timestamp)
950
+
951
+ # store the tracker with the unique sender id
952
+ await agent.tracker_store.save(tracker)
953
+
954
+
955
+ def check_prompt_config_keys_and_warn_if_deprecated(
956
+ config: dict, component_source: str
957
+ ) -> None:
958
+ """Checks and warns about deprecated config parameters."""
959
+ if PROMPT_CONFIG_KEY in config and PROMPT_TEMPLATE_CONFIG_KEY in config:
960
+ structlogger.warning(
961
+ f"{component_source}.init"
962
+ ".both_deprecated_and_non_deprecated_config_keys_used_at_the_same_time",
963
+ event_info=(
964
+ f"Both '{PROMPT_CONFIG_KEY}' and '{PROMPT_TEMPLATE_CONFIG_KEY}' "
965
+ f"are present in the config. '{PROMPT_CONFIG_KEY}' will be ignored "
966
+ f"in favor of {PROMPT_TEMPLATE_CONFIG_KEY}."
967
+ ),
968
+ )
969
+
970
+ # 'prompt' config key is deprecated in favor of 'prompt_template'
971
+ if PROMPT_CONFIG_KEY in config:
972
+ structlogger.warning(
973
+ f"{component_source}.init.deprecated_config_key",
974
+ event_info=(
975
+ f"The config parameter '{PROMPT_CONFIG_KEY}' is deprecated "
976
+ "and will be removed in Rasa 4.0.0. "
977
+ f"Please use the config parameter '{PROMPT_TEMPLATE_CONFIG_KEY}'"
978
+ f" instead. "
979
+ ),
980
+ )
981
+
982
+
983
+ def _get_llm_command_generator_config(
984
+ config: Dict[Text, Any],
985
+ ) -> Optional[Dict[Text, Any]]:
986
+ """Get the llm command generator config from config.yml.
987
+
988
+ Args:
989
+ config: The config.yml file data.
990
+
991
+ Returns:
992
+ The llm command generator config.
993
+ """
994
+ from rasa.dialogue_understanding.generator import LLMBasedCommandGenerator
995
+
996
+ # Collect all LLM based Command Generator class names.
997
+ command_generator_subclasses = all_subclasses(LLMBasedCommandGenerator)
998
+ command_generator_class_names = [
999
+ command_generator.__name__ for command_generator in command_generator_subclasses
1000
+ ]
1001
+
1002
+ # Read the LLM config of the Command Generator from the config.yml file.
1003
+ pipelines = config.get(CONFIG_PIPELINE_KEY, [])
1004
+ for pipeline in pipelines:
1005
+ if pipeline.get(CONFIG_NAME_KEY) in command_generator_class_names:
1006
+ return pipeline.get(LLM_CONFIG_KEY)
1007
+
1008
+ return None
1009
+
1010
+
1011
+ def _get_command_generator_prompt(
1012
+ config: Dict[Text, Any], endpoints: Dict[Text, Any]
1013
+ ) -> Text:
1014
+ """Get the command generator prompt based on the config."""
1015
+ from rasa.dialogue_understanding.generator.single_step.compact_llm_command_generator import ( # noqa: E501
1016
+ DEFAULT_COMMAND_PROMPT_TEMPLATE_FILE_NAME,
1017
+ FALLBACK_COMMAND_PROMPT_TEMPLATE_FILE_NAME,
1018
+ MODEL_PROMPT_MAPPER,
1019
+ )
1020
+
1021
+ model_config = _get_llm_command_generator_config(config)
1022
+ llm_config = resolve_model_client_config(
1023
+ model_config=model_config,
1024
+ model_groups=endpoints.get(MODEL_GROUPS_CONFIG_KEY),
1025
+ )
1026
+ return get_default_prompt_template_based_on_model(
1027
+ llm_config=llm_config,
1028
+ model_prompt_mapping=MODEL_PROMPT_MAPPER,
1029
+ default_prompt_path=DEFAULT_COMMAND_PROMPT_TEMPLATE_FILE_NAME,
1030
+ fallback_prompt_path=FALLBACK_COMMAND_PROMPT_TEMPLATE_FILE_NAME,
1031
+ )
1032
+
1033
+
1034
+ def _get_enterprise_search_prompt(config: Dict[Text, Any]) -> Text:
1035
+ """Get the enterprise search prompt based on the config."""
1036
+ from rasa.core.policies.enterprise_search_policy import EnterpriseSearchPolicy
1037
+
1038
+ def get_enterprise_search_config() -> Dict[Text, Any]:
1039
+ policies = config.get(CONFIG_POLICIES_KEY, [])
1040
+ for policy in policies:
1041
+ if policy.get(CONFIG_NAME_KEY) == EnterpriseSearchPolicy.__name__:
1042
+ return policy
1043
+
1044
+ return {}
1045
+
1046
+ enterprise_search_config = get_enterprise_search_config()
1047
+ return EnterpriseSearchPolicy.get_system_default_prompt_based_on_config(
1048
+ enterprise_search_config
1049
+ )
1050
+
1051
+
1052
+ def get_system_default_prompts(
1053
+ config: Dict[Text, Any], endpoints: Dict[Text, Any]
1054
+ ) -> SystemPrompts:
1055
+ """
1056
+ Returns the system default prompts for the component.
1057
+
1058
+ Args:
1059
+ config: The config.yml file data.
1060
+ endpoints: The endpoints.yml file data.
1061
+
1062
+ Returns:
1063
+ SystemPrompts: A Pydantic model containing all default prompts.
1064
+ """
1065
+ from rasa.core.nlg.contextual_response_rephraser import (
1066
+ DEFAULT_RESPONSE_VARIATION_PROMPT_TEMPLATE,
1067
+ )
1068
+
1069
+ return SystemPrompts(
1070
+ command_generator=_get_command_generator_prompt(config, endpoints),
1071
+ enterprise_search=_get_enterprise_search_prompt(config),
1072
+ contextual_response_rephraser=DEFAULT_RESPONSE_VARIATION_PROMPT_TEMPLATE,
1073
+ )
rasa/shared/utils/yaml.py CHANGED
@@ -21,6 +21,7 @@ from ruamel.yaml import YAML, RoundTripRepresenter, YAMLError
21
21
  from ruamel.yaml.comments import CommentedMap, CommentedSeq
22
22
  from ruamel.yaml.constructor import BaseConstructor, DuplicateKeyError, ScalarNode
23
23
  from ruamel.yaml.loader import SafeLoader
24
+ from ruamel.yaml.scalarstring import LiteralScalarString
24
25
 
25
26
  from rasa.shared.constants import (
26
27
  ASSERTIONS_SCHEMA_EXTENSIONS_FILE,
@@ -794,6 +795,25 @@ def write_yaml(
794
795
  should_preserve_key_order: Whether to force preserve key order in `data`.
795
796
  transform: A function to transform the data before writing it to the file.
796
797
  """
798
+
799
+ def multiline_str_representer(self: Any, value: str) -> Any:
800
+ """Dump multi-line strings as readable YAML block scalars where possible."""
801
+ if "\n" in value:
802
+ # First line after the newline decides: paragraph vs. snippet
803
+ first_line = value.split("\n", 1)[1]
804
+
805
+ # If the first line after the newline is not indented, treat the value
806
+ # as plain text. Indented text is likely pre-formatted YAML/JSON/etc.
807
+ if not first_line.startswith((" ", "\t")):
808
+ return self.represent_scalar(
809
+ "tag:yaml.org,2002:str",
810
+ LiteralScalarString(value),
811
+ style="|",
812
+ )
813
+
814
+ # Fallback: keep default YAML scalar style (plain/quoted)
815
+ return self.represent_scalar("tag:yaml.org,2002:str", value)
816
+
797
817
  _enable_ordered_dict_yaml_dumping()
798
818
 
799
819
  if should_preserve_key_order:
@@ -808,6 +828,7 @@ def write_yaml(
808
828
  type(None),
809
829
  lambda self, _: self.represent_scalar("tag:yaml.org,2002:null", "null"),
810
830
  )
831
+ dumper.representer.add_representer(str, multiline_str_representer)
811
832
 
812
833
  if isinstance(target, StringIO):
813
834
  dumper.dump(data, target, transform=transform)
@@ -1025,6 +1046,17 @@ def validate_yaml_with_jsonschema(
1025
1046
  except (YAMLError, DuplicateKeyError) as e:
1026
1047
  raise YamlSyntaxException(underlying_yaml_exception=e)
1027
1048
 
1049
+ validate_data_with_jsonschema(source_data, schema_content, humanize_error)
1050
+
1051
+
1052
+ def validate_data_with_jsonschema(
1053
+ source_data: Any,
1054
+ schema_content: Any,
1055
+ humanize_error: Callable[
1056
+ [jsonschema.ValidationError], str
1057
+ ] = default_error_humanizer,
1058
+ ) -> None:
1059
+ """Validate Python object against the provided jsonschema content."""
1028
1060
  try:
1029
1061
  jsonschema.validate(source_data, schema_content)
1030
1062
  except jsonschema.ValidationError as error:
@@ -320,14 +320,14 @@ def create_new_flows_from_diff(
320
320
 
321
321
 
322
322
  def import_data_from_studio(
323
- handler: StudioDataHandler, domain_path: Path, data_paths: List[Path]
323
+ handler: StudioDataHandler, domain_path: Path, data_path: Path
324
324
  ) -> Tuple[TrainingDataImporter, TrainingDataImporter]:
325
325
  """Construct TrainingDataImporter from Studio data and original data.
326
326
 
327
327
  Args:
328
328
  handler (StudioDataHandler): handler with data from studio
329
329
  domain_path (Path): Path to a domain file
330
- data_paths (List[Path]): List of paths to training data files
330
+ data_path (List[Path]): List of paths to training data files
331
331
 
332
332
  Returns:
333
333
  Tuple[TrainingDataImporter, TrainingDataImporter]:
@@ -335,7 +335,7 @@ def import_data_from_studio(
335
335
  """
336
336
  tmp_dir = get_temp_dir_name()
337
337
  data_original = TrainingDataImporter.load_from_dict(
338
- domain_path=domain_path, training_data_paths=data_paths
338
+ domain_path=str(domain_path), training_data_paths=[str(data_path)]
339
339
  )
340
340
 
341
341
  data_paths = []
@@ -1,6 +1,6 @@
1
1
  import argparse
2
2
  from pathlib import Path
3
- from typing import Dict, List, Optional, Tuple
3
+ from typing import Dict, Optional, Tuple
4
4
 
5
5
  import questionary
6
6
  import structlog
@@ -46,7 +46,7 @@ def handle_download(args: argparse.Namespace) -> None:
46
46
  )
47
47
  handler.request_all_data()
48
48
 
49
- domain_path, data_paths = _prepare_data_and_domain_paths(args)
49
+ domain_path, data_path = _prepare_data_and_domain_paths(args)
50
50
 
51
51
  # Handle config and endpoints.
52
52
  config_path, write_config = _handle_file_overwrite(
@@ -78,12 +78,12 @@ def handle_download(args: argparse.Namespace) -> None:
78
78
  structlogger.info("studio.download.config_endpoints", event_info=message)
79
79
 
80
80
  if not args.overwrite:
81
- _handle_download_no_overwrite(handler, domain_path, data_paths)
81
+ _handle_download_no_overwrite(handler, domain_path, data_path)
82
82
  else:
83
- _handle_download_with_overwrite(handler, domain_path, data_paths)
83
+ _handle_download_with_overwrite(handler, domain_path, data_path)
84
84
 
85
85
 
86
- def _prepare_data_and_domain_paths(args: argparse.Namespace) -> Tuple[Path, List[Path]]:
86
+ def _prepare_data_and_domain_paths(args: argparse.Namespace) -> Tuple[Path, Path]:
87
87
  """Prepars the domain and data paths based on the provided arguments.
88
88
 
89
89
  Args:
@@ -115,28 +115,15 @@ def _prepare_data_and_domain_paths(args: argparse.Namespace) -> Tuple[Path, List
115
115
  domain_path = domain_path / STUDIO_DOMAIN_FILENAME
116
116
  domain_path.touch()
117
117
 
118
- # Prepare data paths.
119
- data_paths: List[Path] = []
120
- for f in args.data:
121
- data_path = rasa.cli.utils.get_validated_path(
122
- f, "data", DEFAULT_DATA_PATH, none_is_valid=True
123
- )
124
-
125
- if data_path is None:
126
- data_path = Path(f)
127
- data_path.mkdir(parents=True, exist_ok=True)
128
- else:
129
- data_path = Path(data_path)
118
+ data_path = rasa.cli.utils.get_validated_path(
119
+ args.data[0], "data", DEFAULT_DATA_PATH, none_is_valid=True
120
+ )
130
121
 
131
- if data_path.is_file() or data_path.is_dir():
132
- data_paths.append(data_path)
133
- else:
134
- data_path.mkdir(parents=True, exist_ok=True)
135
- data_paths.append(data_path)
122
+ data_path = Path(data_path or args.data[0])
123
+ if not (data_path.is_file() or data_path.is_dir()):
124
+ data_path.mkdir(parents=True, exist_ok=True)
136
125
 
137
- # Remove duplicates while preserving order.
138
- data_paths = list(dict.fromkeys(data_paths))
139
- return domain_path, data_paths
126
+ return domain_path, data_path
140
127
 
141
128
 
142
129
  def _handle_file_overwrite(
@@ -177,7 +164,7 @@ def _handle_file_overwrite(
177
164
 
178
165
 
179
166
  def _handle_download_no_overwrite(
180
- handler: StudioDataHandler, domain_path: Path, data_paths: List[Path]
167
+ handler: StudioDataHandler, domain_path: Path, data_path: Path
181
168
  ) -> None:
182
169
  """Handles downloading without overwriting existing files.
183
170
 
@@ -187,10 +174,10 @@ def _handle_download_no_overwrite(
187
174
  data_paths: The paths to the data files or directories.
188
175
  """
189
176
  data_from_studio, data_local = import_data_from_studio(
190
- handler, domain_path, data_paths
177
+ handler, domain_path, data_path
191
178
  )
192
179
  _merge_domain_no_overwrite(domain_path, data_from_studio, data_local)
193
- _merge_data_no_overwrite(data_paths, handler, data_from_studio, data_local)
180
+ _merge_data_no_overwrite(data_path, handler, data_from_studio, data_local)
194
181
 
195
182
 
196
183
  def _merge_domain_no_overwrite(
@@ -264,7 +251,7 @@ def _merge_file_domain(
264
251
 
265
252
 
266
253
  def _merge_data_no_overwrite(
267
- data_paths: List[Path],
254
+ data_path: Path,
268
255
  handler: StudioDataHandler,
269
256
  data_from_studio: TrainingDataImporter,
270
257
  data_local: TrainingDataImporter,
@@ -272,38 +259,29 @@ def _merge_data_no_overwrite(
272
259
  """Merges NLU and flow data without overwriting existing data.
273
260
 
274
261
  Args:
275
- data_paths: The paths to the data files or directories.
262
+ data_path: The paths to the data files or directories.
276
263
  handler: The StudioDataHandler instance.
277
264
  data_from_studio: The Studio data importer.
278
265
  data_local: The local data importer.
279
266
  """
280
- if not data_paths:
267
+ if not data_path:
281
268
  structlogger.warning(
282
269
  "studio.download.merge_data_no_overwrite.no_path",
283
270
  event_info="No data paths provided. Skipping data merge.",
284
271
  )
285
272
  return
286
273
 
287
- if len(data_paths) == 1:
288
- data_path = data_paths[0]
289
- if data_path.is_file():
290
- _merge_file_data_no_overwrite(
291
- data_path, handler, data_from_studio, data_local
292
- )
293
- elif data_path.is_dir():
294
- _merge_dir_data_no_overwrite(
295
- data_path, handler, data_from_studio, data_local
296
- )
297
- else:
298
- structlogger.warning(
299
- "studio.download.merge_data_no_overwrite.invalid_path",
300
- event_info=(
301
- f"Provided path '{data_path}' is neither a file nor a directory."
302
- ),
303
- )
274
+ if data_path.is_file():
275
+ _merge_file_data_no_overwrite(data_path, handler, data_from_studio, data_local)
276
+ elif data_path.is_dir():
277
+ _merge_dir_data_no_overwrite(data_path, handler, data_from_studio, data_local)
304
278
  else:
305
- # TODO: Handle multiple data paths.
306
- raise NotImplementedError("Multiple data paths are not supported yet.")
279
+ structlogger.warning(
280
+ "studio.download.merge_data_no_overwrite.invalid_path",
281
+ event_info=(
282
+ f"Provided path '{data_path}' is neither a file nor a directory."
283
+ ),
284
+ )
307
285
 
308
286
 
309
287
  def _merge_file_data_no_overwrite(
@@ -353,25 +331,23 @@ def _merge_dir_data_no_overwrite(
353
331
 
354
332
 
355
333
  def _handle_download_with_overwrite(
356
- handler: StudioDataHandler, domain_path: Path, data_paths: List[Path]
334
+ handler: StudioDataHandler, domain_path: Path, data_path: Path
357
335
  ) -> None:
358
336
  """Handles downloading and merging data when the user opts for overwrite.
359
337
 
360
338
  Args:
361
339
  handler: The StudioDataHandler instance.
362
340
  domain_path: The path to the domain file or directory.
363
- data_paths: The paths to the data files or directories.
341
+ data_path: The paths to the data files or directories.
364
342
  """
365
343
  data_from_studio, data_local = import_data_from_studio(
366
- handler, domain_path, data_paths
344
+ handler, domain_path, data_path
367
345
  )
368
346
  mapper = RasaPrimitiveStorageMapper(
369
- domain_path=domain_path, training_data_paths=data_paths
347
+ domain_path=domain_path, training_data_paths=[data_path]
370
348
  )
371
349
  merge_domain_with_overwrite(data_from_studio, data_local, domain_path)
372
- merge_flows_with_overwrite(
373
- data_paths, handler, data_from_studio, data_local, mapper
374
- )
350
+ merge_flows_with_overwrite(data_path, handler, data_from_studio, data_local, mapper)
375
351
 
376
352
 
377
353
  def _persist_nlu_diff(
@@ -432,8 +408,9 @@ def pretty_write_nlu_yaml(data: Dict, file: Path) -> None:
432
408
  file: The file to write to.
433
409
  """
434
410
  dumper = yaml.YAML()
435
- for item in data["nlu"]:
436
- if item.get("examples"):
437
- item["examples"] = LiteralScalarString(item["examples"])
411
+ if nlu_data := data.get("nlu"):
412
+ for item in nlu_data:
413
+ if item.get("examples"):
414
+ item["examples"] = LiteralScalarString(item["examples"])
438
415
  with file.open("w", encoding="utf-8") as outfile:
439
416
  dumper.dump(data, outfile)
@@ -18,7 +18,7 @@ STUDIO_FLOWS_DIR_NAME = "studio_flows"
18
18
 
19
19
 
20
20
  def merge_flows_with_overwrite(
21
- data_paths: List[Path],
21
+ data_path: Path,
22
22
  handler: Any,
23
23
  data_from_studio: TrainingDataImporter,
24
24
  data_local: TrainingDataImporter,
@@ -28,17 +28,12 @@ def merge_flows_with_overwrite(
28
28
  Merges flows data from a file or directory when overwrite is enabled.
29
29
 
30
30
  Args:
31
- data_paths: List of paths to the training data.
31
+ data_path: List of paths to the training data.
32
32
  handler: The StudioDataHandler instance.
33
33
  data_from_studio: The TrainingDataImporter instance for Studio data.
34
34
  data_local: The TrainingDataImporter instance for local data.
35
35
  mapper: The RasaPrimitiveStorageMapper instance for mapping.
36
36
  """
37
- if len(data_paths) != 1:
38
- # TODO: Handle multiple data paths.
39
- raise NotImplementedError("Multiple data paths are not supported yet.")
40
-
41
- data_path = data_paths[0]
42
37
  if data_path.is_file():
43
38
  merge_training_data_file(handler, data_from_studio, data_local, data_path)
44
39
  elif data_path.is_dir():
@@ -132,7 +127,8 @@ def merge_nlu_in_directory(
132
127
  )
133
128
  nlu_data = nlu_data.merge(local_nlu.get_nlu_data())
134
129
 
135
- pretty_write_nlu_yaml(read_yaml(nlu_data.nlu_as_yaml()), nlu_file_path)
130
+ if nlu_yaml := nlu_data.nlu_as_yaml():
131
+ pretty_write_nlu_yaml(read_yaml(nlu_yaml), nlu_file_path)
136
132
 
137
133
 
138
134
  def get_nlu_path(
@@ -211,14 +207,16 @@ def merge_flows_in_directory(
211
207
  local_flow_paths: Set[Path] = _get_local_flow_paths(local_flows, mapper)
212
208
 
213
209
  # Track updated flows and update local files with Studio flow data.
214
- all_updated_flows: List[Flow] = []
210
+ all_updated_flows_ids: List[Text] = []
215
211
  for flow_file_path in local_flow_paths:
216
- updated_file_flows = _update_flow_file(flow_file_path, studio_flow_map)
217
- all_updated_flows.extend(updated_file_flows)
212
+ updated_flows_ids = _update_flow_file(flow_file_path, studio_flow_map)
213
+ all_updated_flows_ids.extend(updated_flows_ids)
218
214
 
219
215
  # Identify new Studio flows and save them as separate files in the directory.
220
216
  new_flows = [
221
- flow for flow in studio_flow_map.values() if flow not in all_updated_flows
217
+ flow
218
+ for flow_id, flow in studio_flow_map.items()
219
+ if flow_id not in all_updated_flows_ids
222
220
  ]
223
221
  _dump_flows_as_separate_files(new_flows, data_path)
224
222
 
@@ -243,7 +241,7 @@ def _get_local_flow_paths(
243
241
 
244
242
  def _update_flow_file(
245
243
  flow_file_path: Path, studio_flows_map: Dict[Text, Any]
246
- ) -> List[Flow]:
244
+ ) -> List[Text]:
247
245
  """
248
246
  Reads a flow file, updates outdated flows, and replaces them with studio versions.
249
247
 
@@ -252,31 +250,25 @@ def _update_flow_file(
252
250
  studio_flows_map: A dictionary mapping flow IDs to their updated versions.
253
251
 
254
252
  Returns:
255
- A list of flows from the updated flow file.
253
+ A list of Flows IDs from the updated flow file.
256
254
  """
257
255
  file_flows = YAMLFlowsReader.read_from_file(flow_file_path, False)
258
- updated_list: List[Any] = []
259
- has_changes = False
260
-
261
- for flow in file_flows.underlying_flows:
262
- studio_flow = studio_flows_map.get(flow.id)
263
- if studio_flow is not None and studio_flow != flow:
264
- updated_list.append(studio_flow)
265
- has_changes = True
266
- else:
267
- updated_list.append(flow)
268
-
269
- if has_changes:
270
- new_flows_list = FlowsList(underlying_flows=updated_list)
271
- new_flows_list = strip_default_next_references(new_flows_list)
256
+
257
+ # Build a list of flows, replacing any outdated flow with its studio version
258
+ updated_flows = [
259
+ studio_flows_map.get(flow.id, flow) or flow
260
+ for flow in file_flows.underlying_flows
261
+ ]
262
+
263
+ # If the updated flows differ from the original file flows, write them back
264
+ if updated_flows != file_flows.underlying_flows:
272
265
  YamlFlowsWriter.dump(
273
- flows=new_flows_list.underlying_flows,
266
+ flows=updated_flows,
274
267
  filename=flow_file_path,
275
268
  should_clean_json=True,
276
269
  )
277
- return new_flows_list.underlying_flows
278
270
 
279
- return file_flows.underlying_flows
271
+ return [flow.id for flow in updated_flows]
280
272
 
281
273
 
282
274
  def _dump_flows_as_separate_files(flows: List[Any], data_path: Path) -> None: