rasa-pro 3.12.0.dev11__py3-none-any.whl → 3.12.0.dev12__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 (71) hide show
  1. rasa/cli/inspect.py +1 -20
  2. rasa/cli/shell.py +3 -3
  3. rasa/core/actions/action.py +7 -20
  4. rasa/core/actions/forms.py +5 -10
  5. rasa/core/channels/__init__.py +0 -2
  6. rasa/core/channels/voice_ready/audiocodes.py +23 -42
  7. rasa/core/channels/voice_stream/browser_audio.py +0 -1
  8. rasa/core/channels/voice_stream/call_state.py +1 -7
  9. rasa/core/channels/voice_stream/tts/azure.py +1 -2
  10. rasa/core/channels/voice_stream/tts/cartesia.py +3 -16
  11. rasa/core/channels/voice_stream/twilio_media_streams.py +1 -2
  12. rasa/core/channels/voice_stream/voice_channel.py +1 -2
  13. rasa/core/migrate.py +2 -2
  14. rasa/core/policies/flows/flow_executor.py +42 -36
  15. rasa/core/run.py +3 -4
  16. rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -2
  17. rasa/dialogue_understanding/commands/cancel_flow_command.py +4 -62
  18. rasa/dialogue_understanding/commands/change_flow_command.py +2 -2
  19. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -2
  20. rasa/dialogue_understanding/commands/clarify_command.py +2 -2
  21. rasa/dialogue_understanding/commands/correct_slots_command.py +2 -11
  22. rasa/dialogue_understanding/commands/human_handoff_command.py +2 -2
  23. rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -2
  24. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -2
  25. rasa/dialogue_understanding/commands/set_slot_command.py +15 -7
  26. rasa/dialogue_understanding/commands/skip_question_command.py +2 -2
  27. rasa/dialogue_understanding/commands/start_flow_command.py +2 -43
  28. rasa/dialogue_understanding/commands/utils.py +1 -1
  29. rasa/dialogue_understanding/constants.py +0 -1
  30. rasa/dialogue_understanding/generator/command_generator.py +73 -110
  31. rasa/dialogue_understanding/generator/command_parser.py +1 -1
  32. rasa/dialogue_understanding/generator/llm_based_command_generator.py +3 -161
  33. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +2 -10
  34. rasa/dialogue_understanding/generator/nlu_command_adapter.py +3 -44
  35. rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2 +79 -53
  36. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +19 -11
  37. rasa/dialogue_understanding/generator/utils.py +1 -32
  38. rasa/dialogue_understanding/patterns/correction.py +1 -13
  39. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +2 -62
  40. rasa/dialogue_understanding/processor/command_processor.py +28 -115
  41. rasa/dialogue_understanding/utils.py +0 -31
  42. rasa/dialogue_understanding_test/README.md +0 -50
  43. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +3 -3
  44. rasa/model_service.py +0 -4
  45. rasa/model_training.py +27 -24
  46. rasa/shared/core/constants.py +3 -28
  47. rasa/shared/core/domain.py +20 -13
  48. rasa/shared/core/events.py +2 -13
  49. rasa/shared/core/flows/flow.py +0 -17
  50. rasa/shared/core/flows/flows_yaml_schema.json +0 -38
  51. rasa/shared/core/flows/steps/collect.py +1 -18
  52. rasa/shared/core/flows/utils.py +1 -16
  53. rasa/shared/core/slot_mappings.py +108 -144
  54. rasa/shared/core/slots.py +2 -23
  55. rasa/shared/core/trackers.py +1 -3
  56. rasa/shared/nlu/constants.py +0 -1
  57. rasa/shared/utils/llm.py +1 -1
  58. rasa/shared/utils/schemas/domain.yml +1 -0
  59. rasa/telemetry.py +13 -43
  60. rasa/utils/common.py +1 -0
  61. rasa/validator.py +82 -189
  62. rasa/version.py +1 -1
  63. {rasa_pro-3.12.0.dev11.dist-info → rasa_pro-3.12.0.dev12.dist-info}/METADATA +1 -1
  64. {rasa_pro-3.12.0.dev11.dist-info → rasa_pro-3.12.0.dev12.dist-info}/RECORD +67 -71
  65. rasa/core/actions/action_handle_digressions.py +0 -142
  66. rasa/core/channels/voice_stream/genesys.py +0 -331
  67. rasa/dialogue_understanding/commands/handle_digressions_command.py +0 -150
  68. rasa/dialogue_understanding/patterns/handle_digressions.py +0 -81
  69. {rasa_pro-3.12.0.dev11.dist-info → rasa_pro-3.12.0.dev12.dist-info}/NOTICE +0 -0
  70. {rasa_pro-3.12.0.dev11.dist-info → rasa_pro-3.12.0.dev12.dist-info}/WHEEL +0 -0
  71. {rasa_pro-3.12.0.dev11.dist-info → rasa_pro-3.12.0.dev12.dist-info}/entry_points.txt +0 -0
rasa/cli/inspect.py CHANGED
@@ -9,10 +9,6 @@ from rasa import telemetry
9
9
  from rasa.cli import SubParsersAction
10
10
  from rasa.cli.arguments import shell as arguments
11
11
  from rasa.core import constants
12
- from rasa.engine.storage.local_model_storage import LocalModelStorage
13
- from rasa.exceptions import ModelNotFound
14
- from rasa.model import get_local_model
15
- from rasa.shared.utils.cli import print_error
16
12
  from rasa.utils.cli import remove_argument_from_parser
17
13
 
18
14
 
@@ -59,8 +55,6 @@ async def open_inspector_in_browser(server_url: Text, voice: bool = False) -> No
59
55
  def inspect(args: argparse.Namespace) -> None:
60
56
  """Inspect the bot using the most recent model."""
61
57
  import rasa.cli.run
62
- from rasa.cli.utils import get_validated_path
63
- from rasa.shared.constants import DEFAULT_MODELS_PATH
64
58
 
65
59
  async def after_start_hook_open_inspector(_: Sanic, __: AbstractEventLoop) -> None:
66
60
  """Hook to open the browser on server start."""
@@ -77,18 +71,5 @@ def inspect(args: argparse.Namespace) -> None:
77
71
  args.credentials = None
78
72
  args.server_listeners = [(after_start_hook_open_inspector, "after_server_start")]
79
73
 
80
- model = get_validated_path(args.model, "model", DEFAULT_MODELS_PATH)
81
-
82
- try:
83
- model = get_local_model(model)
84
- except ModelNotFound:
85
- print_error(
86
- "No model found. Train a model before running the "
87
- "server using `rasa train`."
88
- )
89
- return
90
-
91
- metadata = LocalModelStorage.metadata_from_archive(model)
92
-
93
- telemetry.track_inspect_started(args.connector, metadata.assistant_id)
74
+ telemetry.track_inspect_started(args.connector)
94
75
  rasa.cli.run.run(args)
rasa/cli/shell.py CHANGED
@@ -95,7 +95,7 @@ def shell_nlu(args: argparse.Namespace) -> None:
95
95
  )
96
96
  return
97
97
 
98
- telemetry.track_shell_started("nlu", metadata.assistant_id)
98
+ telemetry.track_shell_started("nlu")
99
99
  rasa.nlu.run.run_cmdline(model)
100
100
 
101
101
 
@@ -129,12 +129,12 @@ def shell(args: argparse.Namespace) -> None:
129
129
  if metadata.training_type == TrainingType.NLU:
130
130
  import rasa.nlu.run
131
131
 
132
- telemetry.track_shell_started("nlu", metadata.assistant_id)
132
+ telemetry.track_shell_started("nlu")
133
133
 
134
134
  rasa.nlu.run.run_cmdline(model)
135
135
  else:
136
136
  import rasa.cli.run
137
137
 
138
- telemetry.track_shell_started("rasa", metadata.assistant_id)
138
+ telemetry.track_shell_started("rasa")
139
139
 
140
140
  rasa.cli.run.run(args)
@@ -73,9 +73,9 @@ from rasa.shared.core.constants import (
73
73
  ACTION_VALIDATE_SLOT_MAPPINGS,
74
74
  DEFAULT_SLOT_NAMES,
75
75
  KNOWLEDGE_BASE_SLOT_NAMES,
76
+ MAPPING_TYPE,
76
77
  REQUESTED_SLOT,
77
78
  USER_INTENT_OUT_OF_SCOPE,
78
- SetSlotExtractor,
79
79
  SlotMappingType,
80
80
  )
81
81
  from rasa.shared.core.domain import Domain
@@ -111,7 +111,6 @@ if TYPE_CHECKING:
111
111
  from rasa.core.channels.channel import OutputChannel
112
112
  from rasa.core.nlg import NaturalLanguageGenerator
113
113
  from rasa.shared.core.events import IntentPrediction
114
- from rasa.shared.core.slot_mappings import SlotMapping
115
114
 
116
115
  logger = logging.getLogger(__name__)
117
116
 
@@ -119,10 +118,6 @@ logger = logging.getLogger(__name__)
119
118
  def default_actions(action_endpoint: Optional[EndpointConfig] = None) -> List["Action"]:
120
119
  """List default actions."""
121
120
  from rasa.core.actions.action_clean_stack import ActionCleanStack
122
- from rasa.core.actions.action_handle_digressions import (
123
- ActionBlockDigressions,
124
- ActionContinueDigression,
125
- )
126
121
  from rasa.core.actions.action_hangup import ActionHangup
127
122
  from rasa.core.actions.action_repeat_bot_messages import ActionRepeatBotMessages
128
123
  from rasa.core.actions.action_run_slot_rejections import ActionRunSlotRejections
@@ -157,8 +152,6 @@ def default_actions(action_endpoint: Optional[EndpointConfig] = None) -> List["A
157
152
  ActionResetRouting(),
158
153
  ActionHangup(),
159
154
  ActionRepeatBotMessages(),
160
- ActionBlockDigressions(),
161
- ActionContinueDigression(),
162
155
  ]
163
156
 
164
157
 
@@ -947,14 +940,7 @@ class RemoteAction(Action):
947
940
  )
948
941
 
949
942
  events = rasa.shared.core.events.deserialise_events(events_json)
950
-
951
- processed_events = []
952
- for event in events:
953
- if isinstance(event, SlotSet) and event.filled_by is None:
954
- event.filled_by = SetSlotExtractor.CUSTOM.value
955
- processed_events.append(event)
956
-
957
- return cast(List[Event], bot_messages) + processed_events
943
+ return cast(List[Event], bot_messages) + events
958
944
 
959
945
  def name(self) -> Text:
960
946
  return self._name
@@ -1222,7 +1208,7 @@ class ActionExtractSlots(Action):
1222
1208
 
1223
1209
  async def _execute_custom_action(
1224
1210
  self,
1225
- mapping: "SlotMapping",
1211
+ mapping: Dict[Text, Any],
1226
1212
  executed_custom_actions: Set[Text],
1227
1213
  output_channel: "OutputChannel",
1228
1214
  nlg: "NaturalLanguageGenerator",
@@ -1230,7 +1216,7 @@ class ActionExtractSlots(Action):
1230
1216
  domain: "Domain",
1231
1217
  calm_custom_action_names: Optional[Set[str]] = None,
1232
1218
  ) -> Tuple[List[Event], Set[Text]]:
1233
- custom_action = mapping.run_action_every_turn
1219
+ custom_action = mapping.get("action")
1234
1220
 
1235
1221
  if not custom_action or custom_action in executed_custom_actions:
1236
1222
  return [], executed_custom_actions
@@ -1331,9 +1317,10 @@ class ActionExtractSlots(Action):
1331
1317
  slot_events.append(SlotSet(slot.name, slot_value))
1332
1318
 
1333
1319
  for mapping in slot.mappings:
1334
- should_fill_controlled_slot = mapping.type == SlotMappingType.CONTROLLED
1320
+ mapping_type = SlotMappingType(mapping.get(MAPPING_TYPE))
1321
+ should_fill_custom_slot = mapping_type == SlotMappingType.CUSTOM
1335
1322
 
1336
- if should_fill_controlled_slot:
1323
+ if should_fill_custom_slot:
1337
1324
  (
1338
1325
  custom_evts,
1339
1326
  executed_custom_actions,
@@ -2,7 +2,7 @@ import copy
2
2
  import itertools
3
3
  import json
4
4
  import logging
5
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Text, Union
5
+ from typing import Any, Dict, List, Optional, Set, Text, Union
6
6
 
7
7
  import structlog
8
8
 
@@ -16,7 +16,7 @@ from rasa.shared.constants import UTTER_PREFIX
16
16
  from rasa.shared.core.constants import (
17
17
  ACTION_EXTRACT_SLOTS,
18
18
  ACTION_LISTEN_NAME,
19
- KEY_MAPPING_TYPE,
19
+ MAPPING_TYPE,
20
20
  REQUESTED_SLOT,
21
21
  SLOT_MAPPINGS,
22
22
  SlotMappingType,
@@ -35,9 +35,6 @@ from rasa.shared.core.slots import ListSlot
35
35
  from rasa.shared.core.trackers import DialogueStateTracker
36
36
  from rasa.utils.endpoints import EndpointConfig
37
37
 
38
- if TYPE_CHECKING:
39
- from rasa.shared.core.slot_mappings import SlotMapping
40
-
41
38
  logger = logging.getLogger(__name__)
42
39
  structlogger = structlog.get_logger()
43
40
 
@@ -161,9 +158,7 @@ class FormAction(LoopAction):
161
158
  domain_slots = domain.as_dict().get(KEY_SLOTS, {})
162
159
  for slot in domain.required_slots_for_form(self.name()):
163
160
  for slot_mapping in domain_slots.get(slot, {}).get(SLOT_MAPPINGS, []):
164
- if slot_mapping.get(KEY_MAPPING_TYPE) == str(
165
- SlotMappingType.FROM_ENTITY
166
- ):
161
+ if slot_mapping.get(MAPPING_TYPE) == str(SlotMappingType.FROM_ENTITY):
167
162
  mapping_as_string = json.dumps(slot_mapping, sort_keys=True)
168
163
  if mapping_as_string in unique_entity_slot_mappings:
169
164
  unique_entity_slot_mappings.remove(mapping_as_string)
@@ -174,7 +169,7 @@ class FormAction(LoopAction):
174
169
  return unique_entity_slot_mappings
175
170
 
176
171
  def entity_mapping_is_unique(
177
- self, slot_mapping: "SlotMapping", domain: Domain
172
+ self, slot_mapping: Dict[Text, Any], domain: Domain
178
173
  ) -> bool:
179
174
  """Verifies if the from_entity mapping is unique."""
180
175
  if not self._have_unique_entity_mappings_been_initialized:
@@ -182,7 +177,7 @@ class FormAction(LoopAction):
182
177
  self._unique_entity_mappings = self._create_unique_entity_mappings(domain)
183
178
  self._have_unique_entity_mappings_been_initialized = True
184
179
 
185
- mapping_as_string = json.dumps(slot_mapping.as_dict(), sort_keys=True)
180
+ mapping_as_string = json.dumps(slot_mapping, sort_keys=True)
186
181
  return mapping_as_string in self._unique_entity_mappings
187
182
 
188
183
  @staticmethod
@@ -32,7 +32,6 @@ from rasa.core.channels.vier_cvg import CVGInput
32
32
  from rasa.core.channels.voice_stream.twilio_media_streams import (
33
33
  TwilioMediaStreamsInputChannel,
34
34
  )
35
- from rasa.core.channels.voice_stream.genesys import GenesysInputChannel
36
35
  from rasa.core.channels.studio_chat import StudioChatInput
37
36
 
38
37
  input_channel_classes: List[Type[InputChannel]] = [
@@ -56,7 +55,6 @@ input_channel_classes: List[Type[InputChannel]] = [
56
55
  JambonzVoiceReadyInput,
57
56
  TwilioMediaStreamsInputChannel,
58
57
  BrowserAudioInputChannel,
59
- GenesysInputChannel,
60
58
  StudioChatInput,
61
59
  ]
62
60
 
@@ -1,11 +1,9 @@
1
- import asyncio
2
1
  import copy
3
2
  import json
4
3
  import uuid
5
- from collections import defaultdict
6
4
  from dataclasses import asdict
7
5
  from datetime import datetime, timedelta, timezone
8
- from typing import Any, Awaitable, Callable, Dict, List, Optional, Set, Text, Union
6
+ from typing import Any, Awaitable, Callable, Dict, List, Optional, Text, Union
9
7
 
10
8
  import structlog
11
9
  from jsonschema import ValidationError, validate
@@ -225,16 +223,6 @@ class AudiocodesInput(InputChannel):
225
223
  self.scheduler_job = None
226
224
  self.keep_alive = keep_alive
227
225
  self.keep_alive_expiration_factor = keep_alive_expiration_factor
228
- self.background_tasks: Dict[Text, Set[asyncio.Task]] = defaultdict(set)
229
-
230
- def _create_task(self, conversation_id: Text, coro: Awaitable[Any]) -> asyncio.Task:
231
- """Create and track an asyncio task for a conversation."""
232
- task: asyncio.Task = asyncio.create_task(coro)
233
- self.background_tasks[conversation_id].add(task)
234
- task.add_done_callback(
235
- lambda t: self.background_tasks[conversation_id].discard(t)
236
- )
237
- return task
238
226
 
239
227
  async def _set_scheduler_job(self) -> None:
240
228
  if self.scheduler_job:
@@ -263,20 +251,11 @@ class AudiocodesInput(InputChannel):
263
251
  )
264
252
  now = datetime.now(timezone.utc)
265
253
  delta = timedelta(seconds=self.keep_alive * self.keep_alive_expiration_factor)
266
-
267
- # clean up conversations
268
- inactive = [
269
- conv_id
270
- for conv_id, conv in self.conversations.items()
271
- if not conv.is_active_conversation(now, delta)
272
- ]
273
-
274
- # cancel tasks and remove conversations
275
- for conv_id in inactive:
276
- for task in self.background_tasks[conv_id]:
277
- task.cancel()
278
- self.background_tasks.pop(conv_id, None)
279
- self.conversations.pop(conv_id, None)
254
+ self.conversations = {
255
+ k: v
256
+ for k, v in self.conversations.items()
257
+ if v.is_active_conversation(now, delta)
258
+ }
280
259
 
281
260
  def handle_start_conversation(self, body: Dict[Text, Any]) -> Dict[Text, Any]:
282
261
  conversation_id = body["conversation"]
@@ -368,29 +347,31 @@ class AudiocodesInput(InputChannel):
368
347
  structlogger.debug("audiocodes.on_activities", conversation=conversation_id)
369
348
  conversation = self._get_conversation(request.token, conversation_id)
370
349
  if conversation is None:
371
- structlogger.warning(
372
- "audiocodes.on_activities.no_conversation", request=request.json
373
- )
374
350
  return response.json({})
375
351
  elif conversation.ws:
376
352
  ac_output: Union[WebsocketOutput, AudiocodesOutput] = WebsocketOutput(
377
353
  conversation.ws, conversation_id
378
354
  )
379
- response_json = {}
355
+ await conversation.handle_activities(
356
+ request.json,
357
+ output_channel=ac_output,
358
+ on_new_message=on_new_message,
359
+ )
360
+ return response.json({})
380
361
  else:
381
362
  # handle non websocket case where messages get returned in json
382
363
  ac_output = AudiocodesOutput()
383
- response_json = {
384
- "conversation": conversation_id,
385
- "activities": ac_output.messages,
386
- }
387
-
388
- # start a background task to handle activities
389
- self._create_task(
390
- conversation_id,
391
- conversation.handle_activities(request.json, ac_output, on_new_message),
392
- )
393
- return response.json(response_json)
364
+ await conversation.handle_activities(
365
+ request.json,
366
+ output_channel=ac_output,
367
+ on_new_message=on_new_message,
368
+ )
369
+ return response.json(
370
+ {
371
+ "conversation": conversation_id,
372
+ "activities": ac_output.messages,
373
+ }
374
+ )
394
375
 
395
376
  @ac_webhook.route(
396
377
  "/conversation/<conversation_id>/disconnect", methods=["POST"]
@@ -65,7 +65,6 @@ class BrowserAudioInputChannel(VoiceInputChannel):
65
65
  def map_input_message(
66
66
  self,
67
67
  message: Any,
68
- ws: Websocket,
69
68
  ) -> VoiceChannelAction:
70
69
  data = json.loads(message)
71
70
  if "audio" in data:
@@ -1,6 +1,6 @@
1
1
  import asyncio
2
2
  from contextvars import ContextVar
3
- from dataclasses import dataclass, field
3
+ from dataclasses import dataclass
4
4
  from typing import Optional
5
5
 
6
6
  from werkzeug.local import LocalProxy
@@ -19,12 +19,6 @@ class CallState:
19
19
  should_hangup: bool = False
20
20
  connection_failed: bool = False
21
21
 
22
- # Genesys requires the server and client each maintain a
23
- # monotonically increasing message sequence number.
24
- client_sequence_number: int = 0
25
- server_sequence_number: int = 0
26
- audio_buffer: bytearray = field(default_factory=bytearray)
27
-
28
22
 
29
23
  _call_state: ContextVar[CallState] = ContextVar("call_state")
30
24
  call_state = LocalProxy(_call_state)
@@ -81,8 +81,7 @@ class AzureTTS(TTSEngine[AzureTTSConfig]):
81
81
  @staticmethod
82
82
  def create_request_body(text: str, conf: AzureTTSConfig) -> str:
83
83
  return f"""
84
- <speak version='1.0' xml:lang='{conf.language}' xmlns:mstts='http://www.w3.org/2001/mstts'
85
- xmlns='http://www.w3.org/2001/10/synthesis'>
84
+ <speak version='1.0' xml:lang='{conf.language}'>
86
85
  <voice xml:lang='{conf.language}' name='{conf.voice}'>
87
86
  {text}
88
87
  </voice>
@@ -1,5 +1,3 @@
1
- import base64
2
- import json
3
1
  import os
4
2
  from dataclasses import dataclass
5
3
  from typing import AsyncIterator, Dict, Optional
@@ -41,7 +39,7 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
41
39
  @staticmethod
42
40
  def get_tts_endpoint() -> str:
43
41
  """Create the endpoint string for cartesia."""
44
- return "https://api.cartesia.ai/tts/sse"
42
+ return "https://api.cartesia.ai/tts/bytes"
45
43
 
46
44
  @staticmethod
47
45
  def get_request_body(text: str, config: CartesiaTTSConfig) -> Dict:
@@ -87,19 +85,8 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
87
85
  url, headers=headers, json=payload, chunked=True
88
86
  ) as response:
89
87
  if 200 <= response.status < 300:
90
- async for chunk in response.content:
91
- # we are looking for chunks in the response that look like
92
- # b"data: {..., data: <base64 encoded audio bytes> ...}"
93
- # and extract the audio bytes from that
94
- if chunk.startswith(b"data: "):
95
- json_bytes = chunk[5:-1]
96
- json_data = json.loads(json_bytes.decode())
97
- if "data" in json_data:
98
- base64_encoded_bytes = json_data["data"]
99
- channel_bytes = base64.b64decode(base64_encoded_bytes)
100
- yield self.engine_bytes_to_rasa_audio_bytes(
101
- channel_bytes
102
- )
88
+ async for data in response.content.iter_chunked(1024):
89
+ yield self.engine_bytes_to_rasa_audio_bytes(data)
103
90
  return
104
91
  else:
105
92
  structlogger.error(
@@ -98,7 +98,6 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
98
98
  def map_input_message(
99
99
  self,
100
100
  message: Any,
101
- ws: Websocket,
102
101
  ) -> VoiceChannelAction:
103
102
  data = json.loads(message)
104
103
  if data["event"] == "media":
@@ -143,7 +142,7 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
143
142
  def blueprint(
144
143
  self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
145
144
  ) -> Blueprint:
146
- """Defines a Sanic blueprint for the voice input channel."""
145
+ """Defines a Sanic bluelogger.debug."""
147
146
  blueprint = Blueprint("twilio_media_streams", __name__)
148
147
 
149
148
  @blueprint.route("/", methods=["GET"])
@@ -315,7 +315,6 @@ class VoiceInputChannel(InputChannel):
315
315
  def map_input_message(
316
316
  self,
317
317
  message: Any,
318
- ws: Websocket,
319
318
  ) -> VoiceChannelAction:
320
319
  """Map a channel input message to a voice channel action."""
321
320
  raise NotImplementedError
@@ -341,7 +340,7 @@ class VoiceInputChannel(InputChannel):
341
340
  async def consume_audio_bytes() -> None:
342
341
  async for message in channel_websocket:
343
342
  is_bot_speaking_before = call_state.is_bot_speaking
344
- channel_action = self.map_input_message(message, channel_websocket)
343
+ channel_action = self.map_input_message(message)
345
344
  is_bot_speaking_after = call_state.is_bot_speaking
346
345
 
347
346
  if not is_bot_speaking_before and is_bot_speaking_after:
rasa/core/migrate.py CHANGED
@@ -14,7 +14,7 @@ from rasa.shared.constants import (
14
14
  )
15
15
  from rasa.shared.core.constants import (
16
16
  ACTIVE_LOOP,
17
- KEY_MAPPING_TYPE,
17
+ MAPPING_TYPE,
18
18
  REQUESTED_SLOT,
19
19
  SLOT_MAPPINGS,
20
20
  SlotMappingType,
@@ -43,7 +43,7 @@ def _create_back_up(domain_file: Path, backup_location: Path) -> Dict[Text, Any]
43
43
  def _get_updated_mapping_condition(
44
44
  condition: Dict[Text, Text], mapping: Dict[Text, Any], slot_name: Text
45
45
  ) -> Dict[Text, Text]:
46
- if mapping.get(KEY_MAPPING_TYPE) not in [
46
+ if mapping.get(MAPPING_TYPE) not in [
47
47
  str(SlotMappingType.FROM_ENTITY),
48
48
  str(SlotMappingType.FROM_TRIGGER_INTENT),
49
49
  ]:
@@ -23,7 +23,6 @@ from rasa.core.policies.flows.flow_step_result import (
23
23
  )
24
24
  from rasa.dialogue_understanding.commands import CancelFlowCommand
25
25
  from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame
26
- from rasa.dialogue_understanding.patterns.clarify import ClarifyPatternFlowStackFrame
27
26
  from rasa.dialogue_understanding.patterns.collect_information import (
28
27
  CollectInformationPatternFlowStackFrame,
29
28
  )
@@ -51,12 +50,9 @@ from rasa.dialogue_understanding.stack.frames.flow_stack_frame import (
51
50
  )
52
51
  from rasa.dialogue_understanding.stack.utils import (
53
52
  top_user_flow_frame,
54
- user_flows_on_the_stack,
55
53
  )
56
54
  from rasa.shared.constants import RASA_PATTERN_HUMAN_HANDOFF
57
- from rasa.shared.core.constants import (
58
- ACTION_LISTEN_NAME,
59
- )
55
+ from rasa.shared.core.constants import ACTION_LISTEN_NAME, SlotMappingType
60
56
  from rasa.shared.core.events import (
61
57
  Event,
62
58
  FlowCompleted,
@@ -276,28 +272,6 @@ def trigger_pattern_continue_interrupted(
276
272
  return events
277
273
 
278
274
 
279
- def trigger_pattern_clarification(
280
- current_frame: DialogueStackFrame, stack: DialogueStack, flows: FlowsList
281
- ) -> None:
282
- """Trigger the pattern to clarify which topic to continue if needed."""
283
- if not isinstance(current_frame, UserFlowStackFrame):
284
- return None
285
-
286
- if current_frame.frame_type == FlowStackFrameType.CALL:
287
- # we want to return to the flow that called the current flow
288
- return None
289
-
290
- pending_flows = [
291
- flows.flow_by_id(frame.flow_id)
292
- for frame in stack.frames
293
- if isinstance(frame, UserFlowStackFrame)
294
- and frame.flow_id != current_frame.flow_id
295
- ]
296
-
297
- flow_names = [flow.readable_name() for flow in pending_flows if flow is not None]
298
- stack.push(ClarifyPatternFlowStackFrame(names=flow_names))
299
-
300
-
301
275
  def trigger_pattern_completed(
302
276
  current_frame: DialogueStackFrame, stack: DialogueStack, flows: FlowsList
303
277
  ) -> None:
@@ -566,6 +540,38 @@ def cancel_flow_and_push_internal_error(stack: DialogueStack, flow_name: str) ->
566
540
  stack.push(InternalErrorPatternFlowStackFrame())
567
541
 
568
542
 
543
+ def validate_custom_slot_mappings(
544
+ step: CollectInformationFlowStep,
545
+ stack: DialogueStack,
546
+ tracker: DialogueStateTracker,
547
+ available_actions: List[str],
548
+ flow_name: str,
549
+ ) -> bool:
550
+ """Validate a slot with custom mappings.
551
+
552
+ If invalid, trigger pattern_internal_error and return False.
553
+ """
554
+ slot = tracker.slots.get(step.collect, None)
555
+ slot_mappings = slot.mappings if slot else []
556
+ for mapping in slot_mappings:
557
+ if (
558
+ mapping.get("type") == SlotMappingType.CUSTOM.value
559
+ and mapping.get("action") is None
560
+ ):
561
+ # this is a slot that must be filled by a custom action
562
+ # check if collect_action exists
563
+ if step.collect_action not in available_actions:
564
+ structlogger.error(
565
+ "flow.step.run.collect_action_not_found_for_custom_slot_mapping",
566
+ action=step.collect_action,
567
+ collect=step.collect,
568
+ )
569
+ cancel_flow_and_push_internal_error(stack, flow_name)
570
+ return False
571
+
572
+ return True
573
+
574
+
569
575
  def attach_stack_metadata_to_events(
570
576
  step_id: str,
571
577
  flow_id: str,
@@ -663,15 +669,7 @@ def _run_end_step(
663
669
  structlogger.debug("flow.step.run.flow_end")
664
670
  current_frame = stack.pop()
665
671
  trigger_pattern_completed(current_frame, stack, flows)
666
- resumed_events = []
667
- if len(user_flows_on_the_stack(stack)) > 1:
668
- # if there are more user flows on the stack,
669
- # we need to trigger the pattern clarify
670
- trigger_pattern_clarification(current_frame, stack, flows)
671
- else:
672
- resumed_events = trigger_pattern_continue_interrupted(
673
- current_frame, stack, flows
674
- )
672
+ resumed_events = trigger_pattern_continue_interrupted(current_frame, stack, flows)
675
673
  reset_events: List[Event] = reset_scoped_slots(current_frame, flow, tracker)
676
674
  return ContinueFlowWithNextStep(
677
675
  events=initial_events + reset_events + resumed_events, has_flow_ended=True
@@ -762,6 +760,14 @@ def _run_collect_information_step(
762
760
  # if we return any other FlowStepResult, the assistant will stay silent
763
761
  # instead of triggering the internal error pattern
764
762
  return ContinueFlowWithNextStep(events=initial_events)
763
+ is_mapping_valid = validate_custom_slot_mappings(
764
+ step, stack, tracker, available_actions, flow_name
765
+ )
766
+
767
+ if not is_mapping_valid:
768
+ # if we return any other FlowStepResult, the assistant will stay silent
769
+ # instead of triggering the internal error pattern
770
+ return ContinueFlowWithNextStep(events=initial_events)
765
771
 
766
772
  structlogger.debug("flow.step.run.collect")
767
773
  trigger_pattern_ask_collect_information(
rasa/core/run.py CHANGED
@@ -283,10 +283,9 @@ def serve_application(
283
283
  endpoints.lock_store if endpoints else None
284
284
  )
285
285
 
286
- if not inspect:
287
- telemetry.track_server_start(
288
- input_channels, endpoints, model_path, number_of_workers, enable_api
289
- )
286
+ telemetry.track_server_start(
287
+ input_channels, endpoints, model_path, number_of_workers, enable_api
288
+ )
290
289
 
291
290
  rasa.utils.common.update_sanic_log_level(
292
291
  log_file, use_syslog, syslog_address, syslog_port, syslog_protocol
@@ -74,7 +74,7 @@ class CannotHandleCommand(Command):
74
74
 
75
75
  def to_dsl(self) -> str:
76
76
  """Converts the command to a DSL string."""
77
- return "CannotHandle()"
77
+ return "cannot handle"
78
78
 
79
79
  @classmethod
80
80
  def from_dsl(cls, match: re.Match, **kwargs: Any) -> CannotHandleCommand:
@@ -86,4 +86,4 @@ class CannotHandleCommand(Command):
86
86
 
87
87
  @staticmethod
88
88
  def regex_pattern() -> str:
89
- return r"CannotHandle\(\)"
89
+ return r"^cannot handle$"