rasa-pro 3.12.0.dev13__py3-none-any.whl → 3.12.0rc2__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 (139) hide show
  1. README.md +10 -13
  2. rasa/anonymization/anonymization_rule_executor.py +16 -10
  3. rasa/cli/data.py +16 -0
  4. rasa/cli/project_templates/calm/config.yml +2 -2
  5. rasa/cli/project_templates/calm/domain/list_contacts.yml +1 -2
  6. rasa/cli/project_templates/calm/domain/remove_contact.yml +1 -2
  7. rasa/cli/project_templates/calm/domain/shared.yml +1 -4
  8. rasa/cli/project_templates/calm/endpoints.yml +2 -2
  9. rasa/cli/utils.py +12 -0
  10. rasa/core/actions/action.py +84 -191
  11. rasa/core/actions/action_handle_digressions.py +35 -13
  12. rasa/core/actions/action_run_slot_rejections.py +16 -4
  13. rasa/core/channels/__init__.py +2 -0
  14. rasa/core/channels/studio_chat.py +19 -0
  15. rasa/core/channels/telegram.py +42 -24
  16. rasa/core/channels/voice_ready/utils.py +1 -1
  17. rasa/core/channels/voice_stream/asr/asr_engine.py +10 -4
  18. rasa/core/channels/voice_stream/asr/azure.py +14 -1
  19. rasa/core/channels/voice_stream/asr/deepgram.py +20 -4
  20. rasa/core/channels/voice_stream/audiocodes.py +264 -0
  21. rasa/core/channels/voice_stream/browser_audio.py +4 -1
  22. rasa/core/channels/voice_stream/call_state.py +3 -0
  23. rasa/core/channels/voice_stream/genesys.py +6 -2
  24. rasa/core/channels/voice_stream/tts/azure.py +9 -1
  25. rasa/core/channels/voice_stream/tts/cartesia.py +14 -8
  26. rasa/core/channels/voice_stream/voice_channel.py +23 -2
  27. rasa/core/constants.py +2 -0
  28. rasa/core/nlg/contextual_response_rephraser.py +18 -1
  29. rasa/core/nlg/generator.py +83 -15
  30. rasa/core/nlg/response.py +6 -3
  31. rasa/core/nlg/translate.py +55 -0
  32. rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +1 -1
  33. rasa/core/policies/flows/flow_executor.py +19 -7
  34. rasa/core/processor.py +71 -9
  35. rasa/dialogue_understanding/commands/can_not_handle_command.py +20 -2
  36. rasa/dialogue_understanding/commands/cancel_flow_command.py +24 -6
  37. rasa/dialogue_understanding/commands/change_flow_command.py +20 -2
  38. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +20 -2
  39. rasa/dialogue_understanding/commands/clarify_command.py +29 -3
  40. rasa/dialogue_understanding/commands/command.py +1 -16
  41. rasa/dialogue_understanding/commands/command_syntax_manager.py +55 -0
  42. rasa/dialogue_understanding/commands/handle_digressions_command.py +1 -7
  43. rasa/dialogue_understanding/commands/human_handoff_command.py +20 -2
  44. rasa/dialogue_understanding/commands/knowledge_answer_command.py +20 -2
  45. rasa/dialogue_understanding/commands/prompt_command.py +94 -0
  46. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +20 -2
  47. rasa/dialogue_understanding/commands/set_slot_command.py +24 -2
  48. rasa/dialogue_understanding/commands/skip_question_command.py +20 -2
  49. rasa/dialogue_understanding/commands/start_flow_command.py +22 -2
  50. rasa/dialogue_understanding/commands/utils.py +71 -4
  51. rasa/dialogue_understanding/generator/__init__.py +2 -0
  52. rasa/dialogue_understanding/generator/command_parser.py +15 -12
  53. rasa/dialogue_understanding/generator/constants.py +3 -0
  54. rasa/dialogue_understanding/generator/llm_based_command_generator.py +12 -5
  55. rasa/dialogue_understanding/generator/llm_command_generator.py +5 -3
  56. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +17 -3
  57. rasa/dialogue_understanding/generator/prompt_templates/__init__.py +0 -0
  58. rasa/dialogue_understanding/generator/{single_step → prompt_templates}/command_prompt_template.jinja2 +2 -0
  59. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +77 -0
  60. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_default.jinja2 +68 -0
  61. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +84 -0
  62. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +522 -0
  63. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +12 -310
  64. rasa/dialogue_understanding/patterns/collect_information.py +1 -1
  65. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +16 -0
  66. rasa/dialogue_understanding/patterns/validate_slot.py +65 -0
  67. rasa/dialogue_understanding/processor/command_processor.py +39 -0
  68. rasa/dialogue_understanding/stack/utils.py +38 -0
  69. rasa/dialogue_understanding_test/du_test_case.py +58 -18
  70. rasa/dialogue_understanding_test/du_test_result.py +14 -10
  71. rasa/dialogue_understanding_test/io.py +14 -0
  72. rasa/e2e_test/assertions.py +6 -8
  73. rasa/e2e_test/llm_judge_prompts/answer_relevance_prompt_template.jinja2 +5 -1
  74. rasa/e2e_test/llm_judge_prompts/groundedness_prompt_template.jinja2 +4 -0
  75. rasa/e2e_test/utils/io.py +0 -37
  76. rasa/engine/graph.py +1 -0
  77. rasa/engine/language.py +140 -0
  78. rasa/engine/recipes/config_files/default_config.yml +4 -0
  79. rasa/engine/recipes/default_recipe.py +2 -0
  80. rasa/engine/recipes/graph_recipe.py +2 -0
  81. rasa/engine/storage/local_model_storage.py +1 -0
  82. rasa/engine/storage/storage.py +4 -1
  83. rasa/llm_fine_tuning/conversations.py +1 -1
  84. rasa/model_manager/runner_service.py +7 -4
  85. rasa/model_manager/socket_bridge.py +7 -6
  86. rasa/shared/constants.py +15 -13
  87. rasa/shared/core/constants.py +2 -0
  88. rasa/shared/core/flows/constants.py +11 -0
  89. rasa/shared/core/flows/flow.py +83 -19
  90. rasa/shared/core/flows/flows_yaml_schema.json +31 -3
  91. rasa/shared/core/flows/steps/collect.py +1 -36
  92. rasa/shared/core/flows/utils.py +28 -4
  93. rasa/shared/core/flows/validation.py +1 -1
  94. rasa/shared/core/slot_mappings.py +208 -5
  95. rasa/shared/core/slots.py +137 -1
  96. rasa/shared/core/trackers.py +74 -1
  97. rasa/shared/importers/importer.py +50 -2
  98. rasa/shared/nlu/training_data/schemas/responses.yml +19 -12
  99. rasa/shared/providers/_configs/azure_entra_id_config.py +541 -0
  100. rasa/shared/providers/_configs/azure_openai_client_config.py +138 -3
  101. rasa/shared/providers/_configs/client_config.py +3 -1
  102. rasa/shared/providers/_configs/default_litellm_client_config.py +3 -1
  103. rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +3 -1
  104. rasa/shared/providers/_configs/litellm_router_client_config.py +3 -1
  105. rasa/shared/providers/_configs/model_group_config.py +4 -2
  106. rasa/shared/providers/_configs/oauth_config.py +33 -0
  107. rasa/shared/providers/_configs/openai_client_config.py +3 -1
  108. rasa/shared/providers/_configs/rasa_llm_client_config.py +3 -1
  109. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +3 -1
  110. rasa/shared/providers/constants.py +6 -0
  111. rasa/shared/providers/embedding/azure_openai_embedding_client.py +28 -3
  112. rasa/shared/providers/embedding/litellm_router_embedding_client.py +3 -1
  113. rasa/shared/providers/llm/_base_litellm_client.py +42 -17
  114. rasa/shared/providers/llm/azure_openai_llm_client.py +81 -25
  115. rasa/shared/providers/llm/default_litellm_llm_client.py +3 -1
  116. rasa/shared/providers/llm/litellm_router_llm_client.py +29 -8
  117. rasa/shared/providers/llm/llm_client.py +23 -7
  118. rasa/shared/providers/llm/openai_llm_client.py +9 -3
  119. rasa/shared/providers/llm/rasa_llm_client.py +11 -2
  120. rasa/shared/providers/llm/self_hosted_llm_client.py +30 -11
  121. rasa/shared/providers/router/_base_litellm_router_client.py +3 -1
  122. rasa/shared/providers/router/router_client.py +3 -1
  123. rasa/shared/utils/constants.py +3 -0
  124. rasa/shared/utils/llm.py +33 -7
  125. rasa/shared/utils/pykwalify_extensions.py +24 -0
  126. rasa/shared/utils/schemas/domain.yml +26 -0
  127. rasa/telemetry.py +2 -1
  128. rasa/tracing/config.py +2 -0
  129. rasa/tracing/constants.py +12 -0
  130. rasa/tracing/instrumentation/instrumentation.py +36 -0
  131. rasa/tracing/instrumentation/metrics.py +41 -0
  132. rasa/tracing/metric_instrument_provider.py +40 -0
  133. rasa/validator.py +372 -7
  134. rasa/version.py +1 -1
  135. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/METADATA +13 -14
  136. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/RECORD +139 -124
  137. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/NOTICE +0 -0
  138. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/WHEEL +0 -0
  139. {rasa_pro-3.12.0.dev13.dist-info → rasa_pro-3.12.0rc2.dist-info}/entry_points.txt +0 -0
@@ -4,6 +4,7 @@ from typing import Any, Dict, Optional
4
4
  import structlog
5
5
  from socketio import AsyncServer
6
6
  from socketio.asyncio_client import AsyncClient
7
+ from socketio.exceptions import ConnectionRefusedError
7
8
 
8
9
  from rasa.model_manager.runner_service import BotSession
9
10
  from rasa.model_manager.studio_jwt_auth import (
@@ -29,7 +30,7 @@ async def socketio_websocket_traffic_wrapper(
29
30
 
30
31
  if auth_token is None:
31
32
  structlogger.error("model_runner.user_no_token", sid=sid)
32
- return False
33
+ raise ConnectionRefusedError("model_runner.user_no_token")
33
34
 
34
35
  try:
35
36
  authenticate_user_to_service(auth_token)
@@ -38,22 +39,22 @@ async def socketio_websocket_traffic_wrapper(
38
39
  structlogger.error(
39
40
  "model_runner.user_authentication_failed", sid=sid, error=str(error)
40
41
  )
41
- return False
42
+ raise ConnectionRefusedError("model_runner.user_authentication_failed")
42
43
 
43
44
  deployment_id = auth.get("deployment_id") if auth else None
44
45
 
45
46
  if deployment_id is None:
46
47
  structlogger.error("model_runner.bot_no_deployment_id", sid=sid)
47
- return False
48
+ raise ConnectionRefusedError("model_runner.bot_no_deployment_id")
48
49
 
49
50
  bot = running_bots.get(deployment_id)
50
51
  if bot is None:
51
52
  structlogger.error("model_runner.bot_not_found", deployment_id=deployment_id)
52
- return False
53
+ raise ConnectionRefusedError("model_runner.bot_not_found")
53
54
 
54
55
  if not bot.is_alive():
55
56
  structlogger.error("model_runner.bot_not_alive", deployment_id=deployment_id)
56
- return False
57
+ raise ConnectionRefusedError("model_runner.bot_not_alive")
57
58
 
58
59
  client = await create_bridge_client(sio, bot.internal_url, sid, deployment_id)
59
60
 
@@ -67,7 +68,7 @@ async def socketio_websocket_traffic_wrapper(
67
68
  structlogger.error(
68
69
  "model_runner.bot_connection_failed", deployment_id=deployment_id
69
70
  )
70
- return False
71
+ raise ConnectionRefusedError("model_runner.bot_connection_failed")
71
72
 
72
73
 
73
74
  def create_bridge_server(sio: AsyncServer, running_bots: Dict[str, BotSession]) -> None:
rasa/shared/constants.py CHANGED
@@ -83,6 +83,7 @@ ENV_LOG_LEVEL_LLM = "LOG_LEVEL_LLM"
83
83
  ENV_LOG_LEVEL_LLM_MODULE_NAMES = {
84
84
  "LLMCommandGenerator": "LOG_LEVEL_LLM_COMMAND_GENERATOR",
85
85
  "SingleStepLLMCommandGenerator": "LOG_LEVEL_LLM_COMMAND_GENERATOR",
86
+ "CompactLLMCommandGenerator": "LOG_LEVEL_LLM_COMMAND_GENERATOR",
86
87
  "MultiStepLLMCommandGenerator": "LOG_LEVEL_LLM_COMMAND_GENERATOR",
87
88
  "EnterpriseSearchPolicy": "LOG_LEVEL_LLM_ENTERPRISE_SEARCH",
88
89
  "IntentlessPolicy": "LOG_LEVEL_LLM_INTENTLESS_POLICY",
@@ -106,6 +107,7 @@ CONFIG_NAME_KEY = "name"
106
107
  CONFIG_POLICIES_KEY = "policies"
107
108
  CONFIG_PIPELINE_KEY = "pipeline"
108
109
  CONFIG_LANGUAGE_KEY = "language"
110
+ CONFIG_ADDITIONAL_LANGUAGES_KEY = "additional_languages"
109
111
  CONFIG_RECIPE_KEY = "recipe"
110
112
  CONFIG_LLM_KEY = "llm"
111
113
  CONFIG_MODEL_NAME_KEY = "model_name"
@@ -150,6 +152,8 @@ DEFAULT_MARKERS_CONFIG_PATH = "markers/config"
150
152
  DEFAULT_MARKERS_OUTPUT_PATH = "markers/output"
151
153
  DEFAULT_MARKERS_STATS_PATH = "markers/stats"
152
154
 
155
+ DEFAULT_PROMPT_PACKAGE_NAME = "rasa.dialogue_understanding.generator.prompt_templates"
156
+
153
157
  DIAGNOSTIC_DATA = "diagnostic_data"
154
158
 
155
159
  RESPONSE_CONDITION = "condition"
@@ -162,6 +166,7 @@ AZURE_AD_TOKEN_ENV_VAR = "AZURE_AD_TOKEN"
162
166
  AZURE_API_BASE_ENV_VAR = "AZURE_API_BASE"
163
167
  AZURE_API_VERSION_ENV_VAR = "AZURE_API_VERSION"
164
168
  AZURE_API_TYPE_ENV_VAR = "AZURE_API_TYPE"
169
+ AZURE_AD_SCOPES_ENV_VAR = "AZURE_AD_SCOPES"
165
170
  AZURE_SPEECH_API_KEY_ENV_VAR = "AZURE_SPEECH_API_KEY"
166
171
 
167
172
  DEEPGRAM_API_KEY_ENV_VAR = "DEEPGRAM_API_KEY"
@@ -198,7 +203,6 @@ MODEL_CONFIG_KEY = "model"
198
203
  MODEL_NAME_CONFIG_KEY = "model_name"
199
204
  PROMPT_CONFIG_KEY = "prompt"
200
205
  PROMPT_TEMPLATE_CONFIG_KEY = "prompt_template"
201
-
202
206
  STREAM_CONFIG_KEY = "stream"
203
207
  N_REPHRASES_CONFIG_KEY = "n"
204
208
  USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY = "use_chat_completions_endpoint"
@@ -232,12 +236,6 @@ LITELLM_PARAMS_KEY = "litellm_params"
232
236
  LLM_API_HEALTH_CHECK_ENV_VAR = "LLM_API_HEALTH_CHECK"
233
237
  LLM_API_HEALTH_CHECK_DEFAULT_VALUE = "false"
234
238
 
235
- AZURE_API_KEY_ENV_VAR = "AZURE_API_KEY"
236
- AZURE_AD_TOKEN_ENV_VAR = "AZURE_AD_TOKEN"
237
- AZURE_API_BASE_ENV_VAR = "AZURE_API_BASE"
238
- AZURE_API_VERSION_ENV_VAR = "AZURE_API_VERSION"
239
- AZURE_API_TYPE_ENV_VAR = "AZURE_API_TYPE"
240
-
241
239
  AWS_REGION_NAME_CONFIG_KEY = "aws_region_name"
242
240
  AWS_ACCESS_KEY_ID_CONFIG_KEY = "aws_access_key_id"
243
241
  AWS_SECRET_ACCESS_KEY_CONFIG_KEY = "aws_secret_access_key"
@@ -273,17 +271,11 @@ RASA_PROVIDER = "rasa"
273
271
  SELF_HOSTED_VLLM_PREFIX = "hosted_vllm"
274
272
  SELF_HOSTED_VLLM_API_KEY_ENV_VAR = "HOSTED_VLLM_API_KEY"
275
273
 
276
- SELF_HOSTED_VLLM_PREFIX = "hosted_vllm"
277
- SELF_HOSTED_VLLM_API_KEY_ENV_VAR = "HOSTED_VLLM_API_KEY"
278
-
279
274
  VALID_PROVIDERS_FOR_API_TYPE_CONFIG_KEY = [
280
275
  OPENAI_PROVIDER,
281
276
  AZURE_OPENAI_PROVIDER,
282
277
  ]
283
278
 
284
- SELF_HOSTED_VLLM_PREFIX = "hosted_vllm"
285
- SELF_HOSTED_VLLM_API_KEY_ENV_VAR = "HOSTED_VLLM_API_KEY"
286
-
287
279
  AZURE_API_TYPE = "azure"
288
280
  OPENAI_API_TYPE = "openai"
289
281
 
@@ -329,3 +321,13 @@ BUTTONS = "buttons"
329
321
  ATTACHMENT = "attachment"
330
322
  IMAGE = "image"
331
323
  CUSTOM = "custom"
324
+ TITLE = "title"
325
+ PAYLOAD = "payload"
326
+
327
+ # Used for LLM command generation
328
+ ROLE_USER = "user"
329
+ ROLE_SYSTEM = "system"
330
+
331
+ # Used for key values in ValidateSlotPatternFlowStackFrame
332
+ REFILL_UTTER = "refill_utter"
333
+ REJECTIONS = "rejections"
@@ -13,6 +13,7 @@ USER_INTENT_SESSION_START = "session_start"
13
13
  USER_INTENT_SESSION_END = "session_end"
14
14
  USER_INTENT_SILENCE_TIMEOUT = "silence_timeout"
15
15
  SESSION_START_METADATA_SLOT = "session_started_metadata"
16
+ LANGUAGE_SLOT = "language"
16
17
 
17
18
  DEFAULT_INTENTS = [
18
19
  USER_INTENT_RESTART,
@@ -145,6 +146,7 @@ KEY_MAPPING_TYPE = "type"
145
146
  KEY_ALLOW_NLU_CORRECTION = "allow_nlu_correction"
146
147
  KEY_ACTION = "action"
147
148
  KEY_RUN_ACTION_EVERY_TURN = "run_action_every_turn"
149
+ KEY_COEXISTENCE_SYSTEM = "coexistence_system"
148
150
 
149
151
 
150
152
  class SlotMappingType(Enum):
@@ -0,0 +1,11 @@
1
+ KEY_ID = "id"
2
+ KEY_STEPS = "steps"
3
+ KEY_NAME = "name"
4
+ KEY_DESCRIPTION = "description"
5
+ KEY_IF = "if"
6
+ KEY_ALWAYS_INCLUDE_IN_PROMPT = "always_include_in_prompt"
7
+ KEY_NLU_TRIGGER = "nlu_trigger"
8
+ KEY_FILE_PATH = "file_path"
9
+ KEY_PERSISTED_SLOTS = "persisted_slots"
10
+ KEY_RUN_PATTERN_COMPLETED = "run_pattern_completed"
11
+ KEY_TRANSLATION = "translation"
@@ -7,14 +7,29 @@ from pathlib import Path
7
7
  from typing import Any, Dict, List, Optional, Set, Text, Union
8
8
 
9
9
  import structlog
10
+ from pydantic import BaseModel
10
11
  from pypred import Predicate
11
12
 
12
13
  import rasa.shared.utils.io
14
+ from rasa.engine.language import Language
13
15
  from rasa.shared.constants import RASA_DEFAULT_FLOW_PATTERN_PREFIX
14
16
  from rasa.shared.core.constants import (
15
17
  KEY_ASK_CONFIRM_DIGRESSIONS,
16
18
  KEY_BLOCK_DIGRESSIONS,
17
19
  )
20
+ from rasa.shared.core.flows.constants import (
21
+ KEY_ALWAYS_INCLUDE_IN_PROMPT,
22
+ KEY_DESCRIPTION,
23
+ KEY_FILE_PATH,
24
+ KEY_ID,
25
+ KEY_IF,
26
+ KEY_NAME,
27
+ KEY_NLU_TRIGGER,
28
+ KEY_PERSISTED_SLOTS,
29
+ KEY_RUN_PATTERN_COMPLETED,
30
+ KEY_STEPS,
31
+ KEY_TRANSLATION,
32
+ )
18
33
  from rasa.shared.core.flows.flow_path import FlowPath, FlowPathsList, PathNode
19
34
  from rasa.shared.core.flows.flow_step import FlowStep
20
35
  from rasa.shared.core.flows.flow_step_links import (
@@ -43,6 +58,16 @@ from rasa.shared.core.slots import Slot
43
58
  structlogger = structlog.get_logger()
44
59
 
45
60
 
61
+ class FlowLanguageTranslation(BaseModel):
62
+ """Represents the translation of the flow properties in a specific language."""
63
+
64
+ name: str
65
+ """The human-readable name of the flow."""
66
+
67
+ class Config:
68
+ extra = "ignore"
69
+
70
+
46
71
  @dataclass
47
72
  class Flow:
48
73
  """Represents the configuration of a flow."""
@@ -53,6 +78,8 @@ class Flow:
53
78
  """The human-readable name of the flow."""
54
79
  description: Optional[Text] = None
55
80
  """The description of the flow."""
81
+ translation: Dict[Text, FlowLanguageTranslation] = field(default_factory=dict)
82
+ """The translation of the flow properties in different languages."""
56
83
  guard_condition: Optional[Text] = None
57
84
  """The condition that needs to be fulfilled for the flow to be startable."""
58
85
  step_sequence: FlowStepSequence = field(default_factory=FlowStepSequence.empty)
@@ -71,6 +98,8 @@ class Flow:
71
98
  """The flow ids for which the assistant should ask for confirmation."""
72
99
  block_digressions: List[str] = field(default_factory=list)
73
100
  """The flow ids that the assistant should block from digressing to."""
101
+ run_pattern_completed: bool = True
102
+ """Whether the pattern_completed flow should be run after the flow ends."""
74
103
 
75
104
  @staticmethod
76
105
  def from_json(
@@ -88,6 +117,8 @@ class Flow:
88
117
  Returns:
89
118
  A Flow object.
90
119
  """
120
+ from rasa.shared.core.flows.utils import extract_translations
121
+
91
122
  step_sequence = FlowStepSequence.from_json(flow_id, data.get("steps"))
92
123
  nlu_triggers = NLUTriggers.from_json(data.get("nlu_trigger"))
93
124
 
@@ -96,21 +127,25 @@ class Flow:
96
127
 
97
128
  return Flow(
98
129
  id=flow_id,
99
- custom_name=data.get("name"),
100
- description=data.get("description"),
101
- always_include_in_prompt=data.get("always_include_in_prompt"),
102
- # str or bool are permitted in the flow schema but internally we want a str
103
- guard_condition=str(data["if"]) if "if" in data else None,
130
+ custom_name=data.get(KEY_NAME),
131
+ description=data.get(KEY_DESCRIPTION),
132
+ always_include_in_prompt=data.get(KEY_ALWAYS_INCLUDE_IN_PROMPT),
133
+ # str or bool are permitted in the flow schema, but internally we want a str
134
+ guard_condition=str(data[KEY_IF]) if KEY_IF in data else None,
104
135
  step_sequence=Flow.resolve_default_ids(step_sequence),
105
136
  nlu_triggers=nlu_triggers,
106
137
  # If we are reading the flows in after training the file_path is part of
107
138
  # data. When the model is trained, take the provided file_path.
108
- file_path=data.get("file_path") if "file_path" in data else file_path,
109
- persisted_slots=data.get("persisted_slots", []),
139
+ file_path=data.get(KEY_FILE_PATH) if KEY_FILE_PATH in data else file_path,
140
+ persisted_slots=data.get(KEY_PERSISTED_SLOTS, []),
110
141
  ask_confirm_digressions=extract_digression_prop(
111
142
  KEY_ASK_CONFIRM_DIGRESSIONS, data
112
143
  ),
113
144
  block_digressions=extract_digression_prop(KEY_BLOCK_DIGRESSIONS, data),
145
+ run_pattern_completed=data.get(KEY_RUN_PATTERN_COMPLETED, True),
146
+ translation=extract_translations(
147
+ translation_data=data.get(KEY_TRANSLATION, {})
148
+ ),
114
149
  )
115
150
 
116
151
  def get_full_name(self) -> str:
@@ -168,33 +203,62 @@ class Flow:
168
203
  The Flow object as serialized data.
169
204
  """
170
205
  data: Dict[Text, Any] = {
171
- "id": self.id,
172
- "steps": self.step_sequence.as_json(),
206
+ KEY_ID: self.id,
207
+ KEY_STEPS: self.step_sequence.as_json(),
173
208
  }
174
209
  if self.custom_name is not None:
175
- data["name"] = self.custom_name
210
+ data[KEY_NAME] = self.custom_name
176
211
  if self.description is not None:
177
- data["description"] = self.description
212
+ data[KEY_DESCRIPTION] = self.description
178
213
  if self.guard_condition is not None:
179
- data["if"] = self.guard_condition
214
+ data[KEY_IF] = self.guard_condition
180
215
  if self.always_include_in_prompt is not None:
181
- data["always_include_in_prompt"] = self.always_include_in_prompt
216
+ data[KEY_ALWAYS_INCLUDE_IN_PROMPT] = self.always_include_in_prompt
182
217
  if self.nlu_triggers:
183
- data["nlu_trigger"] = self.nlu_triggers.as_json()
218
+ data[KEY_NLU_TRIGGER] = self.nlu_triggers.as_json()
184
219
  if self.file_path:
185
- data["file_path"] = self.file_path
220
+ data[KEY_FILE_PATH] = self.file_path
186
221
  if self.persisted_slots:
187
- data["persisted_slots"] = self.persisted_slots
222
+ data[KEY_PERSISTED_SLOTS] = self.persisted_slots
188
223
  if self.ask_confirm_digressions:
189
224
  data[KEY_ASK_CONFIRM_DIGRESSIONS] = self.ask_confirm_digressions
190
225
  if self.block_digressions:
191
226
  data[KEY_BLOCK_DIGRESSIONS] = self.block_digressions
227
+ if self.run_pattern_completed is not None:
228
+ data["run_pattern_completed"] = self.run_pattern_completed
229
+ if self.translation:
230
+ data[KEY_TRANSLATION] = {
231
+ language_code: translation.dict()
232
+ for language_code, translation in self.translation.items()
233
+ }
192
234
 
193
235
  return data
194
236
 
195
- def readable_name(self) -> str:
196
- """Returns the name of the flow or its id if no name is set."""
197
- return self.name or self.id
237
+ def localized_name(self, language: Optional[Language] = None) -> Optional[Text]:
238
+ """Returns the language specific flow name or None.
239
+
240
+ Args:
241
+ language: Preferred language code.
242
+
243
+ Returns:
244
+ Flow name in the specified language or None.
245
+ """
246
+ language_code = language.code if language else None
247
+ translation = self.translation.get(language_code)
248
+ return translation.name if translation else None
249
+
250
+ def readable_name(self, language: Optional[Language] = None) -> str:
251
+ """
252
+ Returns the flow's name in the specified language if available; otherwise
253
+ falls back to the flow's name, and finally the flow's ID.
254
+
255
+ Args:
256
+ language: Preferred language code.
257
+
258
+ Returns:
259
+ string: the localized name, the default name, or the flow's ID.
260
+ """
261
+ return self.localized_name(language) or self.name or self.id
198
262
 
199
263
  def step_by_id(self, step_id: Optional[Text]) -> Optional[FlowStep]:
200
264
  """Returns the step with the given id."""
@@ -288,9 +288,37 @@
288
288
  "additionalProperties": false,
289
289
  "schema_name": "dictionary with flow properties",
290
290
  "properties": {
291
+ "name": {
292
+ "type": "string"
293
+ },
291
294
  "description": {
292
295
  "type": "string"
293
296
  },
297
+ "translation": {
298
+ "type": "object",
299
+ "schema_name": "flow translation mapping",
300
+ "properties": {
301
+ "metadata": {
302
+ "type": "object"
303
+ }
304
+ },
305
+ "patternProperties": {
306
+ "^(?!metadata$).+": {
307
+ "type": "object",
308
+ "additionalProperties": false,
309
+ "required": [
310
+ "name"
311
+ ],
312
+ "properties": {
313
+ "name": {
314
+ "type": "string"
315
+ },
316
+ "metadata": {}
317
+ }
318
+ }
319
+ },
320
+ "additionalProperties": false
321
+ },
294
322
  "if": {
295
323
  "type": [
296
324
  "string",
@@ -300,9 +328,6 @@
300
328
  "always_include_in_prompt": {
301
329
  "type": "boolean"
302
330
  },
303
- "name": {
304
- "type": "string"
305
- },
306
331
  "nlu_trigger": {
307
332
  "$ref": "#/$defs/nlu_trigger"
308
333
  },
@@ -320,6 +345,9 @@
320
345
  },
321
346
  "block_digressions": {
322
347
  "$ref": "#/$defs/block_digressions"
348
+ },
349
+ "run_pattern_completed": {
350
+ "type": "boolean"
323
351
  }
324
352
  }
325
353
  },
@@ -10,42 +10,7 @@ from rasa.shared.core.constants import (
10
10
  )
11
11
  from rasa.shared.core.flows.flow_step import FlowStep
12
12
  from rasa.shared.core.flows.utils import extract_digression_prop
13
-
14
-
15
- @dataclass
16
- class SlotRejection:
17
- """A pair of validation condition and an utterance for the case of failure."""
18
-
19
- if_: str
20
- """The condition that should be checked."""
21
- utter: str
22
- """The utterance that should be executed if the condition is met."""
23
-
24
- @staticmethod
25
- def from_dict(data: Dict[str, Any]) -> SlotRejection:
26
- """Create a SlotRejection object from serialized data.
27
-
28
- Args:
29
- data: data for a SlotRejection object in a serialized format
30
-
31
- Returns:
32
- A SlotRejection object
33
- """
34
- return SlotRejection(
35
- if_=data["if"],
36
- utter=data["utter"],
37
- )
38
-
39
- def as_dict(self) -> Dict[str, Any]:
40
- """Serialize the SlotRejection object.
41
-
42
- Returns:
43
- the SlotRejection object as serialized data
44
- """
45
- return {
46
- "if": self.if_,
47
- "utter": self.utter,
48
- }
13
+ from rasa.shared.core.slots import SlotRejection
49
14
 
50
15
 
51
16
  @dataclass
@@ -1,17 +1,21 @@
1
- from typing import Any, Dict, List, Set
1
+ from typing import TYPE_CHECKING, Any, Dict, List, Set, Text
2
2
 
3
3
  from rasa.shared.utils.io import raise_deprecation_warning
4
4
 
5
+ if TYPE_CHECKING:
6
+ from rasa.shared.core.flows.flow import FlowLanguageTranslation
7
+
8
+
5
9
  RESET_PROPERTY_NAME = "reset_after_flow_ends"
6
10
  PERSIST_PROPERTY_NAME = "persisted_slots"
7
11
  ALL_LABEL = "ALL"
8
12
 
9
13
 
10
- def warn_deprecated_collect_step_config(flow_id: str, collect_step: str) -> None:
14
+ def warn_deprecated_collect_step_config() -> None:
11
15
  """Warns about deprecated reset_after_flow_ends usage in collect steps."""
12
16
  raise_deprecation_warning(
13
- f"Configuring '{RESET_PROPERTY_NAME}' in collect step '{collect_step}' is "
14
- f"deprecated and will be removed in Rasa Pro 4.0.0. In flow id '{flow_id}', "
17
+ f"Configuring '{RESET_PROPERTY_NAME}' in collect steps is "
18
+ f"deprecated and will be removed in Rasa Pro 4.0.0. In the parent flow, "
15
19
  f"please use the '{PERSIST_PROPERTY_NAME}' "
16
20
  "property at the flow level instead."
17
21
  )
@@ -53,3 +57,23 @@ def extract_digression_prop(prop: str, data: Dict[str, Any]) -> List[str]:
53
57
  digression_property = [ALL_LABEL] if digression_property else []
54
58
 
55
59
  return digression_property
60
+
61
+
62
+ def extract_translations(
63
+ translation_data: Dict[Text, Any],
64
+ ) -> Dict[Text, "FlowLanguageTranslation"]:
65
+ """Extracts translations from a dictionary.
66
+
67
+ Args:
68
+ translation_data: The dictionary containing the translations.
69
+
70
+ Returns:
71
+ A dictionary containing the extracted translations.
72
+ """
73
+ from rasa.shared.core.flows.flow import FlowLanguageTranslation
74
+
75
+ return {
76
+ language_code: FlowLanguageTranslation.parse_obj({**data})
77
+ for language_code, data in translation_data.items()
78
+ if language_code != "metadata"
79
+ }
@@ -723,7 +723,7 @@ def validate_slot_persistence_configuration(flow: Flow) -> None:
723
723
  flow_slots.add(step.collect)
724
724
  if not step.reset_after_flow_ends:
725
725
  collect_step = step.collect
726
- warn_deprecated_collect_step_config(flow_id, collect_step)
726
+ warn_deprecated_collect_step_config()
727
727
  if has_flow_level_persistence:
728
728
  raise DuplicateSlotPersistConfigException(flow_id, collect_step)
729
729