rasa-pro 3.11.0a2__py3-none-any.whl → 3.11.0a4.dev1__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 (51) hide show
  1. README.md +17 -396
  2. rasa/api.py +4 -0
  3. rasa/cli/arguments/train.py +14 -0
  4. rasa/cli/inspect.py +1 -1
  5. rasa/cli/interactive.py +1 -0
  6. rasa/cli/project_templates/calm/endpoints.yml +7 -2
  7. rasa/cli/project_templates/tutorial/endpoints.yml +7 -2
  8. rasa/cli/train.py +3 -0
  9. rasa/constants.py +2 -0
  10. rasa/core/actions/action.py +75 -33
  11. rasa/core/actions/action_repeat_bot_messages.py +72 -0
  12. rasa/core/actions/e2e_stub_custom_action_executor.py +5 -1
  13. rasa/core/actions/http_custom_action_executor.py +4 -0
  14. rasa/core/channels/socketio.py +5 -1
  15. rasa/core/channels/voice_ready/utils.py +6 -5
  16. rasa/core/channels/voice_stream/browser_audio.py +1 -1
  17. rasa/core/channels/voice_stream/twilio_media_streams.py +1 -1
  18. rasa/core/nlg/contextual_response_rephraser.py +19 -2
  19. rasa/core/persistor.py +87 -21
  20. rasa/core/utils.py +53 -22
  21. rasa/dialogue_understanding/commands/__init__.py +4 -0
  22. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +60 -0
  23. rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2 +3 -0
  24. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +19 -0
  25. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +5 -0
  26. rasa/dialogue_understanding/patterns/repeat.py +37 -0
  27. rasa/e2e_test/utils/io.py +2 -0
  28. rasa/model_manager/__init__.py +0 -0
  29. rasa/model_manager/config.py +18 -0
  30. rasa/model_manager/model_api.py +469 -0
  31. rasa/model_manager/runner_service.py +279 -0
  32. rasa/model_manager/socket_bridge.py +143 -0
  33. rasa/model_manager/studio_jwt_auth.py +86 -0
  34. rasa/model_manager/trainer_service.py +332 -0
  35. rasa/model_manager/utils.py +66 -0
  36. rasa/model_service.py +109 -0
  37. rasa/model_training.py +25 -7
  38. rasa/shared/constants.py +6 -0
  39. rasa/shared/core/constants.py +2 -0
  40. rasa/shared/providers/llm/self_hosted_llm_client.py +15 -3
  41. rasa/shared/utils/yaml.py +10 -1
  42. rasa/utils/endpoints.py +27 -1
  43. rasa/version.py +1 -1
  44. rasa_pro-3.11.0a4.dev1.dist-info/METADATA +197 -0
  45. {rasa_pro-3.11.0a2.dist-info → rasa_pro-3.11.0a4.dev1.dist-info}/RECORD +48 -38
  46. rasa/keys +0 -1
  47. rasa/llm_fine_tuning/notebooks/unsloth_finetuning.ipynb +0 -407
  48. rasa_pro-3.11.0a2.dist-info/METADATA +0 -576
  49. {rasa_pro-3.11.0a2.dist-info → rasa_pro-3.11.0a4.dev1.dist-info}/NOTICE +0 -0
  50. {rasa_pro-3.11.0a2.dist-info → rasa_pro-3.11.0a4.dev1.dist-info}/WHEEL +0 -0
  51. {rasa_pro-3.11.0a2.dist-info → rasa_pro-3.11.0a4.dev1.dist-info}/entry_points.txt +0 -0
rasa/core/utils.py CHANGED
@@ -1,8 +1,9 @@
1
+ import structlog
1
2
  import logging
2
3
  import os
3
4
  from pathlib import Path
4
5
  from socket import SOCK_DGRAM, SOCK_STREAM
5
- from typing import Any, Dict, Optional, Set, TYPE_CHECKING, Text, Tuple, Union
6
+ from typing import Any, Dict, Optional, List, Set, TYPE_CHECKING, Text, Tuple, Union
6
7
 
7
8
  import numpy as np
8
9
  from sanic import Sanic
@@ -19,14 +20,18 @@ from rasa.core.constants import (
19
20
  from rasa.core.lock_store import LockStore, RedisLockStore, InMemoryLockStore
20
21
  from rasa.shared.constants import DEFAULT_ENDPOINTS_PATH, TCP_PROTOCOL
21
22
  from rasa.shared.core.trackers import DialogueStateTracker
22
- from rasa.utils.endpoints import EndpointConfig, read_endpoint_config
23
+ from rasa.utils.endpoints import (
24
+ EndpointConfig,
25
+ read_endpoint_config,
26
+ read_property_config_from_endpoints_file,
27
+ )
23
28
  from rasa.utils.io import write_yaml
24
29
 
25
30
  if TYPE_CHECKING:
26
31
  from rasa.core.nlg import NaturalLanguageGenerator
27
32
  from rasa.shared.core.domain import Domain
28
33
 
29
- logger = logging.getLogger(__name__)
34
+ structlogger = structlog.get_logger()
30
35
 
31
36
 
32
37
  def configure_file_logging(
@@ -124,15 +129,17 @@ def list_routes(app: Sanic) -> Dict[Text, Text]:
124
129
  for arg in route._params:
125
130
  options[arg] = f"[{arg}]"
126
131
 
127
- handlers = [(next(iter(route.methods)), route.name.replace("rasa_server.", ""))]
132
+ name = route.name.replace("rasa_server.", "")
133
+ methods = ",".join(route.methods)
128
134
 
129
- for method, name in handlers:
130
- full_endpoint = "/" + "/".join(endpoint)
131
- line = unquote(f"{full_endpoint:50s} {method:30s} {name}")
132
- output[name] = line
135
+ full_endpoint = "/" + "/".join(endpoint)
136
+ line = unquote(f"{full_endpoint:50s} {methods:30s} {name}")
137
+ output[name] = line
133
138
 
134
139
  url_table = "\n".join(output[url] for url in sorted(output))
135
- logger.debug(f"Available web server routes: \n{url_table}")
140
+ structlogger.debug(
141
+ "server.routes", event_info=f"Available web server routes: \n{url_table}"
142
+ )
136
143
 
137
144
  return output
138
145
 
@@ -171,6 +178,8 @@ def is_limit_reached(num_messages: int, limit: Optional[int]) -> bool:
171
178
  class AvailableEndpoints:
172
179
  """Collection of configured endpoints."""
173
180
 
181
+ _instance = None
182
+
174
183
  @classmethod
175
184
  def read_endpoints(cls, endpoint_file: Text) -> "AvailableEndpoints":
176
185
  """Read the different endpoints from a yaml file."""
@@ -184,6 +193,9 @@ class AvailableEndpoints:
184
193
  lock_store = read_endpoint_config(endpoint_file, endpoint_type="lock_store")
185
194
  event_broker = read_endpoint_config(endpoint_file, endpoint_type="event_broker")
186
195
  vector_store = read_endpoint_config(endpoint_file, endpoint_type="vector_store")
196
+ model_groups = read_property_config_from_endpoints_file(
197
+ endpoint_file, property_name="model_groups"
198
+ )
187
199
 
188
200
  return cls(
189
201
  nlg,
@@ -194,6 +206,7 @@ class AvailableEndpoints:
194
206
  lock_store,
195
207
  event_broker,
196
208
  vector_store,
209
+ model_groups,
197
210
  )
198
211
 
199
212
  def __init__(
@@ -206,6 +219,7 @@ class AvailableEndpoints:
206
219
  lock_store: Optional[EndpointConfig] = None,
207
220
  event_broker: Optional[EndpointConfig] = None,
208
221
  vector_store: Optional[EndpointConfig] = None,
222
+ model_groups: Optional[List[Dict[str, Any]]] = None,
209
223
  ) -> None:
210
224
  """Create an `AvailableEndpoints` object."""
211
225
  self.model = model
@@ -216,6 +230,15 @@ class AvailableEndpoints:
216
230
  self.lock_store = lock_store
217
231
  self.event_broker = event_broker
218
232
  self.vector_store = vector_store
233
+ self.model_groups = model_groups
234
+
235
+ @classmethod
236
+ def get_instance(cls, endpoint_file: Optional[Text] = None) -> "AvailableEndpoints":
237
+ """Get the singleton instance of AvailableEndpoints."""
238
+ # Ensure that the instance is initialized only once.
239
+ if cls._instance is None:
240
+ cls._instance = cls.read_endpoints(endpoint_file)
241
+ return cls._instance
219
242
 
220
243
 
221
244
  def read_endpoints_from_path(
@@ -234,7 +257,7 @@ def read_endpoints_from_path(
234
257
  endpoints_config_path = cli_utils.get_validated_path(
235
258
  endpoints_path, "endpoints", DEFAULT_ENDPOINTS_PATH, True
236
259
  )
237
- return AvailableEndpoints.read_endpoints(endpoints_config_path)
260
+ return AvailableEndpoints.get_instance(endpoints_config_path)
238
261
 
239
262
 
240
263
  def _lock_store_is_multi_worker_compatible(
@@ -263,17 +286,22 @@ def number_of_sanic_workers(lock_store: Union[EndpointConfig, LockStore, None])
263
286
  """
264
287
 
265
288
  def _log_and_get_default_number_of_workers() -> int:
266
- logger.debug(
267
- f"Using the default number of Sanic workers ({DEFAULT_SANIC_WORKERS})."
289
+ structlogger.debug(
290
+ "server.worker.set_count",
291
+ number_of_workers=DEFAULT_SANIC_WORKERS,
292
+ event_info=f"Using the default number of Sanic workers "
293
+ f"({DEFAULT_SANIC_WORKERS}).",
268
294
  )
269
295
  return DEFAULT_SANIC_WORKERS
270
296
 
271
297
  try:
272
298
  env_value = int(os.environ.get(ENV_SANIC_WORKERS, DEFAULT_SANIC_WORKERS))
273
299
  except ValueError:
274
- logger.error(
275
- f"Cannot convert environment variable `{ENV_SANIC_WORKERS}` "
276
- f"to int ('{os.environ[ENV_SANIC_WORKERS]}')."
300
+ structlogger.error(
301
+ "server.worker.set_count.error",
302
+ number_of_workers=os.environ[ENV_SANIC_WORKERS],
303
+ event_info=f"Cannot convert environment variable `{ENV_SANIC_WORKERS}` "
304
+ f"to int ('{os.environ[ENV_SANIC_WORKERS]}').",
277
305
  )
278
306
  return _log_and_get_default_number_of_workers()
279
307
 
@@ -281,20 +309,23 @@ def number_of_sanic_workers(lock_store: Union[EndpointConfig, LockStore, None])
281
309
  return _log_and_get_default_number_of_workers()
282
310
 
283
311
  if env_value < 1:
284
- logger.debug(
285
- f"Cannot set number of Sanic workers to the desired value "
286
- f"({env_value}). The number of workers must be at least 1."
312
+ structlogger.debug(
313
+ "server.worker.set_count.error_less_than_one",
314
+ number_of_workers=env_value,
315
+ event_info=f"Cannot set number of Sanic workers to the desired value "
316
+ f"({env_value}). The number of workers must be at least 1.",
287
317
  )
288
318
  return _log_and_get_default_number_of_workers()
289
319
 
290
320
  if _lock_store_is_multi_worker_compatible(lock_store):
291
- logger.debug(f"Using {env_value} Sanic workers.")
321
+ structlogger.debug(f"Using {env_value} Sanic workers.")
292
322
  return env_value
293
323
 
294
- logger.debug(
295
- f"Unable to assign desired number of Sanic workers ({env_value}) as "
324
+ structlogger.debug(
325
+ "server.worker.set_count.error_no_lock_store",
326
+ event_info=f"Unable to assign desired number of Sanic workers ({env_value}) as "
296
327
  f"no `RedisLockStore` or custom `LockStore` endpoint "
297
- f"configuration has been found."
328
+ f"configuration has been found.",
298
329
  )
299
330
  return _log_and_get_default_number_of_workers()
300
331
 
@@ -33,6 +33,9 @@ from rasa.dialogue_understanding.commands.session_start_command import (
33
33
  SessionStartCommand,
34
34
  )
35
35
  from rasa.dialogue_understanding.commands.session_end_command import SessionEndCommand
36
+ from rasa.dialogue_understanding.commands.repeat_bot_messages_command import (
37
+ RepeatBotMessagesCommand,
38
+ )
36
39
 
37
40
  __all__ = [
38
41
  "Command",
@@ -53,5 +56,6 @@ __all__ = [
53
56
  "ChangeFlowCommand",
54
57
  "SessionStartCommand",
55
58
  "SessionEndCommand",
59
+ "RepeatBotMessagesCommand",
56
60
  "RestartCommand",
57
61
  ]
@@ -0,0 +1,60 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Dict, List
5
+ from rasa.dialogue_understanding.commands import Command
6
+ from rasa.dialogue_understanding.patterns.repeat import (
7
+ RepeatBotMessagesPatternFlowStackFrame,
8
+ )
9
+ from rasa.shared.core.events import Event
10
+ from rasa.shared.core.flows import FlowsList
11
+ from rasa.shared.core.trackers import DialogueStateTracker
12
+
13
+
14
+ @dataclass
15
+ class RepeatBotMessagesCommand(Command):
16
+ """A command to indicate that the bot should repeat its last messages."""
17
+
18
+ @classmethod
19
+ def command(cls) -> str:
20
+ """Returns the command type."""
21
+ return "repeat"
22
+
23
+ @classmethod
24
+ def from_dict(cls, data: Dict[str, Any]) -> RepeatBotMessagesCommand:
25
+ """Converts the dictionary to a command.
26
+
27
+ Returns:
28
+ The converted dictionary.
29
+ """
30
+ return RepeatBotMessagesCommand()
31
+
32
+ def run_command_on_tracker(
33
+ self,
34
+ tracker: DialogueStateTracker,
35
+ all_flows: FlowsList,
36
+ original_tracker: DialogueStateTracker,
37
+ ) -> List[Event]:
38
+ """Runs the command on the tracker.
39
+ Get all the bot utterances until last user utterance and repeat them.
40
+
41
+ Args:
42
+ tracker: The tracker to run the command on.
43
+ all_flows: All flows in the assistant.
44
+ original_tracker: The tracker before any command was executed.
45
+
46
+ Returns:
47
+ The events to apply to the tracker.
48
+ """
49
+ stack = tracker.stack
50
+ stack.push(RepeatBotMessagesPatternFlowStackFrame())
51
+ return tracker.create_stack_updated_events(stack)
52
+
53
+ def __hash__(self) -> int:
54
+ return hash(self.command())
55
+
56
+ def __eq__(self, other: object) -> bool:
57
+ if not isinstance(other, RepeatBotMessagesCommand):
58
+ return False
59
+
60
+ return True
@@ -41,6 +41,9 @@ Based on this information generate a list of actions you want to take. Your job
41
41
  * Responding to knowledge-oriented user messages, described by "SearchAndReply()"
42
42
  * Responding to a casual, non-task-oriented user message, described by "ChitChat()".
43
43
  * Handing off to a human, in case the user seems frustrated or explicitly asks to speak to one, described by "HumanHandoff()".
44
+ {% if is_repeat_command_enabled %}
45
+ * Repeat the last bot messages, described by "RepeatLastBotMessages()". This is useful when the user asks to repeat the last bot messages.
46
+ {% endif %}
44
47
 
45
48
  ===
46
49
  Write out the actions you want to take, one per line, in the order they should take place.
@@ -16,6 +16,7 @@ from rasa.dialogue_understanding.commands import (
16
16
  KnowledgeAnswerCommand,
17
17
  ClarifyCommand,
18
18
  CannotHandleCommand,
19
+ RepeatBotMessagesCommand,
19
20
  )
20
21
  from rasa.dialogue_understanding.generator.constants import (
21
22
  LLM_CONFIG_KEY,
@@ -50,6 +51,7 @@ from rasa.shared.utils.llm import (
50
51
  sanitize_message_for_prompt,
51
52
  )
52
53
  from rasa.utils.log_utils import log_llm
54
+ from rasa.utils.beta import ensure_beta_feature_is_enabled, BetaNotEnabledException
53
55
 
54
56
  COMMAND_PROMPT_FILE_NAME = "command_prompt.jinja2"
55
57
 
@@ -293,6 +295,7 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
293
295
  knowledge_re = re.compile(r"SearchAndReply\(\)")
294
296
  humand_handoff_re = re.compile(r"HumanHandoff\(\)")
295
297
  clarify_re = re.compile(r"Clarify\(([a-zA-Z0-9_, ]+)\)")
298
+ repeat_re = re.compile(r"RepeatLastBotMessages\(\)")
296
299
 
297
300
  for action in actions.strip().splitlines():
298
301
  if match := slot_set_re.search(action):
@@ -319,6 +322,8 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
319
322
  commands.append(KnowledgeAnswerCommand())
320
323
  elif humand_handoff_re.search(action):
321
324
  commands.append(HumanHandoffCommand())
325
+ elif repeat_re.search(action):
326
+ commands.append(RepeatBotMessagesCommand())
322
327
  elif match := clarify_re.search(action):
323
328
  options = sorted([opt.strip() for opt in match.group(1).split(",")])
324
329
  valid_options = [
@@ -393,6 +398,20 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
393
398
  "current_slot": current_slot,
394
399
  "current_slot_description": current_slot_description,
395
400
  "user_message": latest_user_message,
401
+ "is_repeat_command_enabled": self.is_repeat_command_enabled(),
396
402
  }
397
403
 
398
404
  return self.compile_template(self.prompt_template).render(**inputs)
405
+
406
+ def is_repeat_command_enabled(self) -> bool:
407
+ """Check for feature flag"""
408
+ RASA_PRO_BETA_REPEAT_COMMAND_ENV_VAR_NAME = "RASA_PRO_BETA_REPEAT_COMMAND"
409
+ try:
410
+ ensure_beta_feature_is_enabled(
411
+ "Repeat Command",
412
+ env_flag=RASA_PRO_BETA_REPEAT_COMMAND_ENV_VAR_NAME,
413
+ )
414
+ except BetaNotEnabledException:
415
+ return False
416
+
417
+ return True
@@ -217,6 +217,11 @@ flows:
217
217
  - action: utter_internal_error_rasa
218
218
  next: END
219
219
 
220
+ pattern_repeat_bot_messages:
221
+ description: Voice conversation repair pattern to repeat bot messages
222
+ name: pattern repeat bot messages
223
+ steps:
224
+ - action: action_repeat_bot_messages
220
225
 
221
226
  pattern_restart:
222
227
  description: Flow for restarting the conversation
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Dict
5
+
6
+ from rasa.dialogue_understanding.stack.frames import PatternFlowStackFrame
7
+ from rasa.shared.constants import RASA_DEFAULT_FLOW_PATTERN_PREFIX
8
+
9
+ FLOW_PATTERN_REPEAT = RASA_DEFAULT_FLOW_PATTERN_PREFIX + "repeat_bot_messages"
10
+
11
+
12
+ @dataclass
13
+ class RepeatBotMessagesPatternFlowStackFrame(PatternFlowStackFrame):
14
+ """A flow stack frame that can get added when bot messages should be repeated"""
15
+
16
+ flow_id: str = FLOW_PATTERN_REPEAT
17
+ """The ID of the flow."""
18
+
19
+ @classmethod
20
+ def type(cls) -> str:
21
+ """Returns the type of the frame."""
22
+ return FLOW_PATTERN_REPEAT
23
+
24
+ @staticmethod
25
+ def from_dict(data: Dict[str, Any]) -> RepeatBotMessagesPatternFlowStackFrame:
26
+ """Creates a `DialogueStackFrame` from a dictionary.
27
+
28
+ Args:
29
+ data: The dictionary to create the `DialogueStackFrame` from.
30
+
31
+ Returns:
32
+ The created `DialogueStackFrame`.
33
+ """
34
+ return RepeatBotMessagesPatternFlowStackFrame(
35
+ frame_id=data["frame_id"],
36
+ step_id=data["step_id"],
37
+ )
rasa/e2e_test/utils/io.py CHANGED
@@ -506,6 +506,8 @@ def transform_results_output_to_yaml(yaml_string: str) -> str:
506
506
  result.append(s)
507
507
  elif s.startswith("\n"):
508
508
  result.append(s.strip())
509
+ elif s.strip().startswith("#"):
510
+ continue
509
511
  else:
510
512
  result.append(s)
511
513
  return "".join(result)
File without changes
@@ -0,0 +1,18 @@
1
+ import sys
2
+ import os
3
+
4
+ SERVER_BASE_WORKING_DIRECTORY = os.environ.get(
5
+ "RASA_MODEL_SERVER_BASE_DIRECTORY", "working-data"
6
+ )
7
+
8
+ SERVER_BASE_URL = os.environ.get("RASA_MODEL_SERVER_BASE_URL", None)
9
+
10
+ # defaults to storing on the local hard drive
11
+ SERVER_MODEL_REMOTE_STORAGE = os.environ.get("RASA_REMOTE_STORAGE", None)
12
+
13
+ # The path to the python executable that is running this script
14
+ # we will use the same python to run training / bots
15
+ RASA_PYTHON_PATH = sys.executable
16
+
17
+ # the max limit for parallel training and bot run requests
18
+ MAX_PARALLEL_TRAININGS = os.getenv("MAX_PARALLEL_TRAININGS", 10)