rasa-pro 3.12.13__py3-none-any.whl → 3.12.15__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 (36) hide show
  1. rasa/cli/llm_fine_tuning.py +11 -10
  2. rasa/core/nlg/contextual_response_rephraser.py +38 -11
  3. rasa/core/nlg/summarize.py +39 -5
  4. rasa/core/policies/enterprise_search_policy.py +7 -4
  5. rasa/core/policies/intentless_policy.py +15 -9
  6. rasa/core/utils.py +4 -0
  7. rasa/dialogue_understanding/coexistence/llm_based_router.py +8 -3
  8. rasa/dialogue_understanding/commands/clarify_command.py +1 -1
  9. rasa/dialogue_understanding/commands/set_slot_command.py +1 -1
  10. rasa/dialogue_understanding/generator/constants.py +2 -2
  11. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +2 -2
  12. rasa/dialogue_understanding/processor/command_processor_component.py +2 -2
  13. rasa/dialogue_understanding_test/du_test_runner.py +3 -21
  14. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -6
  15. rasa/engine/recipes/default_recipe.py +26 -2
  16. rasa/llm_fine_tuning/annotation_module.py +39 -9
  17. rasa/llm_fine_tuning/conversations.py +3 -0
  18. rasa/llm_fine_tuning/llm_data_preparation_module.py +66 -49
  19. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +4 -2
  20. rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +52 -44
  21. rasa/llm_fine_tuning/paraphrasing_module.py +10 -12
  22. rasa/llm_fine_tuning/storage.py +4 -4
  23. rasa/llm_fine_tuning/utils.py +63 -1
  24. rasa/shared/constants.py +3 -0
  25. rasa/shared/exceptions.py +4 -0
  26. rasa/shared/providers/_configs/azure_openai_client_config.py +4 -0
  27. rasa/shared/providers/_configs/openai_client_config.py +4 -0
  28. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +3 -0
  29. rasa/shared/providers/llm/_base_litellm_client.py +5 -2
  30. rasa/shared/utils/llm.py +36 -3
  31. rasa/version.py +1 -1
  32. {rasa_pro-3.12.13.dist-info → rasa_pro-3.12.15.dist-info}/METADATA +1 -1
  33. {rasa_pro-3.12.13.dist-info → rasa_pro-3.12.15.dist-info}/RECORD +36 -36
  34. {rasa_pro-3.12.13.dist-info → rasa_pro-3.12.15.dist-info}/NOTICE +0 -0
  35. {rasa_pro-3.12.13.dist-info → rasa_pro-3.12.15.dist-info}/WHEEL +0 -0
  36. {rasa_pro-3.12.13.dist-info → rasa_pro-3.12.15.dist-info}/entry_points.txt +0 -0
@@ -208,10 +208,7 @@ def prepare_llm_fine_tuning_data(args: argparse.Namespace) -> None:
208
208
  sys.exit(0)
209
209
 
210
210
  flows = asyncio.run(e2e_test_runner.agent.processor.get_flows())
211
- llm_command_generator_config = _get_llm_command_generator_config(e2e_test_runner)
212
- llm_command_generator: Type[LLMBasedCommandGenerator] = _get_llm_command_generator(
213
- e2e_test_runner
214
- )
211
+ _validate_llm_command_generator_present(e2e_test_runner)
215
212
 
216
213
  # set up storage context
217
214
  storage_context = create_storage_context(StorageType.FILE, output_dir)
@@ -242,11 +239,11 @@ def prepare_llm_fine_tuning_data(args: argparse.Namespace) -> None:
242
239
  rephrase_config,
243
240
  args.num_rephrases,
244
241
  flows,
245
- llm_command_generator,
246
- llm_command_generator_config,
242
+ e2e_test_runner.agent,
247
243
  storage_context,
248
244
  )
249
245
  )
246
+
250
247
  statistics["num_passing_rephrased_user_messages"] = sum(
251
248
  [conversation.get_number_of_rephrases(True) for conversation in conversations]
252
249
  )
@@ -257,7 +254,11 @@ def prepare_llm_fine_tuning_data(args: argparse.Namespace) -> None:
257
254
 
258
255
  # 3. create fine-tuning dataset
259
256
  log_start_of_module("LLM Data Preparation")
260
- llm_fine_tuning_data = convert_to_fine_tuning_data(conversations, storage_context)
257
+ llm_fine_tuning_data = asyncio.run(
258
+ convert_to_fine_tuning_data(
259
+ conversations, storage_context, e2e_test_runner.agent
260
+ )
261
+ )
261
262
  statistics["num_ft_data_points"] = len(llm_fine_tuning_data)
262
263
  log_end_of_module("LLM Data Preparation", statistics)
263
264
 
@@ -311,9 +312,9 @@ def _get_llm_command_generator_config(e2e_test_runner: E2ETestRunner) -> Dict[st
311
312
  sys.exit(1)
312
313
 
313
314
 
314
- def _get_llm_command_generator(
315
+ def _validate_llm_command_generator_present(
315
316
  e2e_test_runner: E2ETestRunner,
316
- ) -> Type[LLMBasedCommandGenerator]:
317
+ ) -> None:
317
318
  train_schema = e2e_test_runner.agent.processor.model_metadata.train_schema # type: ignore
318
319
 
319
320
  for _, node in train_schema.nodes.items():
@@ -322,7 +323,7 @@ def _get_llm_command_generator(
322
323
  ) and not node.matches_type(
323
324
  MultiStepLLMCommandGenerator, include_subtypes=True
324
325
  ):
325
- return cast(Type[LLMBasedCommandGenerator], node.uses)
326
+ return
326
327
 
327
328
  rasa.shared.utils.cli.print_error(
328
329
  "The provided model is not trained using 'SingleStepLLMCommandGenerator' or "
@@ -5,15 +5,20 @@ from jinja2 import Template
5
5
 
6
6
  from rasa import telemetry
7
7
  from rasa.core.nlg.response import TemplatedNaturalLanguageGenerator
8
- from rasa.core.nlg.summarize import summarize_conversation
8
+ from rasa.core.nlg.summarize import (
9
+ _count_multiple_utterances_as_single_turn,
10
+ summarize_conversation,
11
+ )
9
12
  from rasa.shared.constants import (
10
13
  LLM_CONFIG_KEY,
14
+ MAX_COMPLETION_TOKENS_CONFIG_KEY,
11
15
  MODEL_CONFIG_KEY,
12
16
  MODEL_GROUP_ID_CONFIG_KEY,
13
17
  MODEL_NAME_CONFIG_KEY,
14
18
  OPENAI_PROVIDER,
15
19
  PROMPT_CONFIG_KEY,
16
20
  PROVIDER_CONFIG_KEY,
21
+ TEMPERATURE_CONFIG_KEY,
17
22
  TIMEOUT_CONFIG_KEY,
18
23
  )
19
24
  from rasa.shared.core.domain import KEY_RESPONSES_TEXT, Domain
@@ -53,12 +58,13 @@ RESPONSE_SUMMARISE_CONVERSATION_KEY = "summarize_conversation"
53
58
  DEFAULT_REPHRASE_ALL = False
54
59
  DEFAULT_SUMMARIZE_HISTORY = True
55
60
  DEFAULT_MAX_HISTORICAL_TURNS = 5
61
+ DEFAULT_COUNT_MULTIPLE_UTTERANCES_AS_SINGLE_TURN = True
56
62
 
57
63
  DEFAULT_LLM_CONFIG = {
58
64
  PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
59
65
  MODEL_CONFIG_KEY: DEFAULT_OPENAI_GENERATE_MODEL_NAME,
60
- "temperature": 0.3,
61
- "max_tokens": DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
66
+ TEMPERATURE_CONFIG_KEY: 0.3,
67
+ MAX_COMPLETION_TOKENS_CONFIG_KEY: DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
62
68
  TIMEOUT_CONFIG_KEY: 5,
63
69
  }
64
70
 
@@ -70,6 +76,7 @@ its meaning. Use simple {{language}}.
70
76
  Context / previous conversation with the user:
71
77
  {{history}}
72
78
 
79
+ Last user message:
73
80
  {{current_input}}
74
81
 
75
82
  Suggested AI Response: {{suggested_response}}
@@ -122,6 +129,11 @@ class ContextualResponseRephraser(
122
129
  "max_historical_turns", DEFAULT_MAX_HISTORICAL_TURNS
123
130
  )
124
131
 
132
+ self.count_multiple_utterances_as_single_turn = self.nlg_endpoint.kwargs.get(
133
+ "count_multiple_utterances_as_single_turn",
134
+ DEFAULT_COUNT_MULTIPLE_UTTERANCES_AS_SINGLE_TURN,
135
+ )
136
+
125
137
  self.llm_config = resolve_model_client_config(
126
138
  self.nlg_endpoint.kwargs.get(LLM_CONFIG_KEY),
127
139
  ContextualResponseRephraser.__name__,
@@ -260,8 +272,16 @@ class ContextualResponseRephraser(
260
272
  Returns:
261
273
  The history for the prompt.
262
274
  """
275
+ # Count multiple utterances by bot/user as single turn in conversation history
276
+ turns_wrapper = (
277
+ _count_multiple_utterances_as_single_turn
278
+ if self.count_multiple_utterances_as_single_turn
279
+ else None
280
+ )
263
281
  llm = llm_factory(self.llm_config, DEFAULT_LLM_CONFIG)
264
- return await summarize_conversation(tracker, llm, max_turns=5)
282
+ return await summarize_conversation(
283
+ tracker, llm, max_turns=5, turns_wrapper=turns_wrapper
284
+ )
265
285
 
266
286
  async def rephrase(
267
287
  self,
@@ -283,19 +303,26 @@ class ContextualResponseRephraser(
283
303
 
284
304
  prompt_template_text = self._template_for_response_rephrasing(response)
285
305
 
286
- # Retrieve inputs for the dynamic prompt
287
- latest_message = self._last_message_if_human(tracker)
288
- current_input = f"{USER}: {latest_message}" if latest_message else ""
306
+ # Last user message (=current input) should always be in prompt if available
307
+ last_message_by_user = getattr(tracker.latest_message, "text", "")
308
+ current_input = (
309
+ f"{USER}: {last_message_by_user}" if last_message_by_user else ""
310
+ )
289
311
 
290
312
  # Only summarise conversation history if flagged
291
313
  if self.summarize_history:
292
314
  history = await self._create_history(tracker)
293
315
  else:
294
- # make sure the transcript/history contains the last user utterance
316
+ # Count multiple utterances by bot/user as single turn
317
+ turns_wrapper = (
318
+ _count_multiple_utterances_as_single_turn
319
+ if self.count_multiple_utterances_as_single_turn
320
+ else None
321
+ )
295
322
  max_turns = max(self.max_historical_turns, 1)
296
- history = tracker_as_readable_transcript(tracker, max_turns=max_turns)
297
- # the history already contains the current input
298
- current_input = ""
323
+ history = tracker_as_readable_transcript(
324
+ tracker, max_turns=max_turns, turns_wrapper=turns_wrapper
325
+ )
299
326
 
300
327
  prompt = Template(prompt_template_text).render(
301
328
  history=history,
@@ -1,4 +1,5 @@
1
- from typing import Optional
1
+ from itertools import groupby
2
+ from typing import Callable, List, Optional
2
3
 
3
4
  import structlog
4
5
  from jinja2 import Template
@@ -23,20 +24,49 @@ SUMMARY_PROMPT_TEMPLATE = Template(_DEFAULT_SUMMARIZER_TEMPLATE)
23
24
  MAX_TURNS_DEFAULT = 20
24
25
 
25
26
 
27
+ def _count_multiple_utterances_as_single_turn(transcript: List[str]) -> List[str]:
28
+ """Counts multiple utterances as a single turn.
29
+
30
+ Args:
31
+ transcript: the lines of the transcript
32
+
33
+ Returns:
34
+ transcript: with multiple utterances counted as a single turn
35
+ """
36
+ if not transcript:
37
+ return []
38
+
39
+ def get_speaker_label(line: str) -> str:
40
+ return line.partition(": ")[0] if ": " in line else ""
41
+
42
+ modified_transcript = [
43
+ f"{speaker}: {' '.join(line.partition(': ')[2] for line in group)}"
44
+ for speaker, group in groupby(transcript, key=get_speaker_label)
45
+ if speaker
46
+ ]
47
+
48
+ return modified_transcript
49
+
50
+
26
51
  def _create_summarization_prompt(
27
- tracker: DialogueStateTracker, max_turns: Optional[int]
52
+ tracker: DialogueStateTracker,
53
+ max_turns: Optional[int],
54
+ turns_wrapper: Optional[Callable[[List[str]], List[str]]],
28
55
  ) -> str:
29
56
  """Creates an LLM prompt to summarize the conversation in the tracker.
30
57
 
31
58
  Args:
32
59
  tracker: tracker of the conversation to be summarized
33
60
  max_turns: maximum number of turns to summarize
61
+ turns_wrapper: optional function to wrap the turns
34
62
 
35
63
 
36
64
  Returns:
37
65
  The prompt to summarize the conversation.
38
66
  """
39
- transcript = tracker_as_readable_transcript(tracker, max_turns=max_turns)
67
+ transcript = tracker_as_readable_transcript(
68
+ tracker, max_turns=max_turns, turns_wrapper=turns_wrapper
69
+ )
40
70
  return SUMMARY_PROMPT_TEMPLATE.render(
41
71
  conversation=transcript,
42
72
  )
@@ -46,6 +76,7 @@ async def summarize_conversation(
46
76
  tracker: DialogueStateTracker,
47
77
  llm: LLMClient,
48
78
  max_turns: Optional[int] = MAX_TURNS_DEFAULT,
79
+ turns_wrapper: Optional[Callable[[List[str]], List[str]]] = None,
49
80
  ) -> str:
50
81
  """Summarizes the dialogue using the LLM.
51
82
 
@@ -53,11 +84,12 @@ async def summarize_conversation(
53
84
  tracker: the tracker to summarize
54
85
  llm: the LLM to use for summarization
55
86
  max_turns: maximum number of turns to summarize
87
+ turns_wrapper: optional function to wrap the turns
56
88
 
57
89
  Returns:
58
90
  The summary of the dialogue.
59
91
  """
60
- prompt = _create_summarization_prompt(tracker, max_turns)
92
+ prompt = _create_summarization_prompt(tracker, max_turns, turns_wrapper)
61
93
  try:
62
94
  llm_response = await llm.acompletion(prompt)
63
95
  summarization = llm_response.choices[0].strip()
@@ -66,6 +98,8 @@ async def summarize_conversation(
66
98
  )
67
99
  return summarization
68
100
  except Exception as e:
69
- transcript = tracker_as_readable_transcript(tracker, max_turns=max_turns)
101
+ transcript = tracker_as_readable_transcript(
102
+ tracker, max_turns=max_turns, turns_wrapper=turns_wrapper
103
+ )
70
104
  structlogger.error("summarization.error", error=e)
71
105
  return transcript
@@ -46,12 +46,15 @@ from rasa.graph_components.providers.forms_provider import Forms
46
46
  from rasa.graph_components.providers.responses_provider import Responses
47
47
  from rasa.shared.constants import (
48
48
  EMBEDDINGS_CONFIG_KEY,
49
+ MAX_COMPLETION_TOKENS_CONFIG_KEY,
50
+ MAX_RETRIES_CONFIG_KEY,
49
51
  MODEL_CONFIG_KEY,
50
52
  MODEL_GROUP_ID_CONFIG_KEY,
51
53
  MODEL_NAME_CONFIG_KEY,
52
54
  OPENAI_PROVIDER,
53
55
  PROMPT_CONFIG_KEY,
54
56
  PROVIDER_CONFIG_KEY,
57
+ TEMPERATURE_CONFIG_KEY,
55
58
  TIMEOUT_CONFIG_KEY,
56
59
  )
57
60
  from rasa.shared.core.constants import (
@@ -135,14 +138,14 @@ DEFAULT_LLM_CONFIG = {
135
138
  PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
136
139
  MODEL_CONFIG_KEY: DEFAULT_OPENAI_CHAT_MODEL_NAME,
137
140
  TIMEOUT_CONFIG_KEY: 10,
138
- "temperature": 0.0,
139
- "max_tokens": 256,
140
- "max_retries": 1,
141
+ TEMPERATURE_CONFIG_KEY: 0.0,
142
+ MAX_COMPLETION_TOKENS_CONFIG_KEY: 256,
143
+ MAX_RETRIES_CONFIG_KEY: 1,
141
144
  }
142
145
 
143
146
  DEFAULT_EMBEDDINGS_CONFIG = {
144
147
  PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
145
- "model": DEFAULT_OPENAI_EMBEDDING_MODEL_NAME,
148
+ MODEL_CONFIG_KEY: DEFAULT_OPENAI_EMBEDDING_MODEL_NAME,
146
149
  }
147
150
 
148
151
  ENTERPRISE_SEARCH_PROMPT_FILE_NAME = "enterprise_search_policy_prompt.jinja2"
@@ -31,12 +31,14 @@ from rasa.graph_components.providers.responses_provider import Responses
31
31
  from rasa.shared.constants import (
32
32
  EMBEDDINGS_CONFIG_KEY,
33
33
  LLM_CONFIG_KEY,
34
+ MAX_COMPLETION_TOKENS_CONFIG_KEY,
34
35
  MODEL_CONFIG_KEY,
35
36
  MODEL_GROUP_ID_CONFIG_KEY,
36
37
  MODEL_NAME_CONFIG_KEY,
37
38
  OPENAI_PROVIDER,
38
39
  PROMPT_CONFIG_KEY,
39
40
  PROVIDER_CONFIG_KEY,
41
+ TEMPERATURE_CONFIG_KEY,
40
42
  TIMEOUT_CONFIG_KEY,
41
43
  )
42
44
  from rasa.shared.core.constants import ACTION_LISTEN_NAME
@@ -111,14 +113,14 @@ NLU_ABSTENTION_THRESHOLD = "nlu_abstention_threshold"
111
113
  DEFAULT_LLM_CONFIG = {
112
114
  PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
113
115
  MODEL_CONFIG_KEY: DEFAULT_OPENAI_CHAT_MODEL_NAME,
114
- "temperature": 0.0,
115
- "max_tokens": DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
116
+ TEMPERATURE_CONFIG_KEY: 0.0,
117
+ MAX_COMPLETION_TOKENS_CONFIG_KEY: DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
116
118
  TIMEOUT_CONFIG_KEY: 5,
117
119
  }
118
120
 
119
121
  DEFAULT_EMBEDDINGS_CONFIG = {
120
122
  PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
121
- "model": DEFAULT_OPENAI_EMBEDDING_MODEL_NAME,
123
+ MODEL_CONFIG_KEY: DEFAULT_OPENAI_EMBEDDING_MODEL_NAME,
122
124
  }
123
125
 
124
126
  DEFAULT_INTENTLESS_PROMPT_TEMPLATE = importlib.resources.open_text(
@@ -344,8 +346,6 @@ class IntentlessPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Policy):
344
346
  # ensures that the policy will not override a deterministic policy
345
347
  # which utilizes the nlu predictions confidence (e.g. Memoization).
346
348
  NLU_ABSTENTION_THRESHOLD: 0.9,
347
- LLM_CONFIG_KEY: DEFAULT_LLM_CONFIG,
348
- EMBEDDINGS_CONFIG_KEY: DEFAULT_EMBEDDINGS_CONFIG,
349
349
  PROMPT_CONFIG_KEY: DEFAULT_INTENTLESS_PROMPT_TEMPLATE,
350
350
  }
351
351
 
@@ -381,13 +381,19 @@ class IntentlessPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Policy):
381
381
  super().__init__(config, model_storage, resource, execution_context, featurizer)
382
382
 
383
383
  # Resolve LLM config
384
- self.config[LLM_CONFIG_KEY] = resolve_model_client_config(
385
- self.config.get(LLM_CONFIG_KEY), IntentlessPolicy.__name__
384
+ self.config[LLM_CONFIG_KEY] = combine_custom_and_default_config(
385
+ resolve_model_client_config(
386
+ self.config.get(LLM_CONFIG_KEY), IntentlessPolicy.__name__
387
+ ),
388
+ DEFAULT_LLM_CONFIG,
386
389
  )
387
390
 
388
391
  # Resolve embeddings config
389
- self.config[EMBEDDINGS_CONFIG_KEY] = resolve_model_client_config(
390
- self.config.get(EMBEDDINGS_CONFIG_KEY), IntentlessPolicy.__name__
392
+ self.config[EMBEDDINGS_CONFIG_KEY] = combine_custom_and_default_config(
393
+ resolve_model_client_config(
394
+ self.config.get(EMBEDDINGS_CONFIG_KEY), IntentlessPolicy.__name__
395
+ ),
396
+ DEFAULT_EMBEDDINGS_CONFIG,
391
397
  )
392
398
 
393
399
  self.nlu_abstention_threshold: float = self.config[NLU_ABSTENTION_THRESHOLD]
rasa/core/utils.py CHANGED
@@ -244,6 +244,10 @@ class AvailableEndpoints:
244
244
  cls._instance = cls.read_endpoints(endpoint_file)
245
245
  return cls._instance
246
246
 
247
+ @classmethod
248
+ def reset_instance(cls) -> None:
249
+ cls._instance = None
250
+
247
251
 
248
252
  def read_endpoints_from_path(
249
253
  endpoints_path: Optional[Union[Path, Text]] = None,
@@ -23,11 +23,14 @@ from rasa.engine.recipes.default_recipe import DefaultV1Recipe
23
23
  from rasa.engine.storage.resource import Resource
24
24
  from rasa.engine.storage.storage import ModelStorage
25
25
  from rasa.shared.constants import (
26
+ LOGIT_BIAS_CONFIG_KEY,
27
+ MAX_COMPLETION_TOKENS_CONFIG_KEY,
26
28
  MODEL_CONFIG_KEY,
27
29
  OPENAI_PROVIDER,
28
30
  PROMPT_CONFIG_KEY,
29
31
  PROVIDER_CONFIG_KEY,
30
32
  ROUTE_TO_CALM_SLOT,
33
+ TEMPERATURE_CONFIG_KEY,
31
34
  TIMEOUT_CONFIG_KEY,
32
35
  )
33
36
  from rasa.shared.core.trackers import DialogueStateTracker
@@ -66,9 +69,11 @@ DEFAULT_LLM_CONFIG = {
66
69
  PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
67
70
  MODEL_CONFIG_KEY: DEFAULT_OPENAI_CHAT_MODEL_NAME,
68
71
  TIMEOUT_CONFIG_KEY: 7,
69
- "temperature": 0.0,
70
- "max_tokens": 1,
71
- "logit_bias": {str(token_id): 100 for token_id in A_TO_C_TOKEN_IDS_CHATGPT},
72
+ TEMPERATURE_CONFIG_KEY: 0.0,
73
+ MAX_COMPLETION_TOKENS_CONFIG_KEY: 1,
74
+ LOGIT_BIAS_CONFIG_KEY: {
75
+ str(token_id): 100 for token_id in A_TO_C_TOKEN_IDS_CHATGPT
76
+ },
72
77
  }
73
78
 
74
79
  structlogger = structlog.get_logger()
@@ -119,7 +119,7 @@ class ClarifyCommand(Command):
119
119
  mapper = {
120
120
  CommandSyntaxVersion.v1: r"Clarify\(([\"\'a-zA-Z0-9_, -]*)\)",
121
121
  CommandSyntaxVersion.v2: (
122
- r"""^[\s\W\d]*disambiguate flows (["'a-zA-Z0-9_, -]*)['"`]*$"""
122
+ r"""^[\s\W\d]*disambiguate flows (["'a-zA-Z0-9_, -]*)[\W\\n]*$"""
123
123
  ),
124
124
  }
125
125
  return mapper.get(
@@ -190,7 +190,7 @@ class SetSlotCommand(Command):
190
190
  r"""SetSlot\(['"]?([a-zA-Z_][a-zA-Z0-9_-]*)['"]?, ?['"]?(.*)['"]?\)"""
191
191
  ),
192
192
  CommandSyntaxVersion.v2: (
193
- r"""^[\s\W\d]*set slot ['"`]?([a-zA-Z_][a-zA-Z0-9_-]*)['"`]? ['"`]?(.+?)['"`]*$""" # noqa: E501
193
+ r"""^[\s\W\d]*set slot ['"`]?([a-zA-Z_][a-zA-Z0-9_-]*)['"`]? ['"`]?(.+?)[\W\\n]*$""" # noqa: E501
194
194
  ),
195
195
  }
196
196
  return mapper.get(
@@ -1,5 +1,5 @@
1
1
  from rasa.shared.constants import (
2
- MAX_TOKENS_CONFIG_KEY,
2
+ MAX_COMPLETION_TOKENS_CONFIG_KEY,
3
3
  MODEL_CONFIG_KEY,
4
4
  OPENAI_PROVIDER,
5
5
  PROVIDER_CONFIG_KEY,
@@ -15,7 +15,7 @@ DEFAULT_LLM_CONFIG = {
15
15
  PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
16
16
  MODEL_CONFIG_KEY: DEFAULT_OPENAI_CHAT_MODEL_NAME_ADVANCED,
17
17
  TEMPERATURE_CONFIG_KEY: 0.0,
18
- MAX_TOKENS_CONFIG_KEY: DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
18
+ MAX_COMPLETION_TOKENS_CONFIG_KEY: DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
19
19
  TIMEOUT_CONFIG_KEY: 7,
20
20
  }
21
21
 
@@ -47,7 +47,7 @@ from rasa.shared.constants import (
47
47
  AWS_BEDROCK_PROVIDER,
48
48
  AZURE_OPENAI_PROVIDER,
49
49
  EMBEDDINGS_CONFIG_KEY,
50
- MAX_TOKENS_CONFIG_KEY,
50
+ MAX_COMPLETION_TOKENS_CONFIG_KEY,
51
51
  PROMPT_TEMPLATE_CONFIG_KEY,
52
52
  ROUTE_TO_CALM_SLOT,
53
53
  TEMPERATURE_CONFIG_KEY,
@@ -81,7 +81,7 @@ DEFAULT_LLM_CONFIG = {
81
81
  PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
82
82
  MODEL_CONFIG_KEY: MODEL_NAME_GPT_4O_2024_11_20,
83
83
  TEMPERATURE_CONFIG_KEY: 0.0,
84
- MAX_TOKENS_CONFIG_KEY: DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
84
+ MAX_COMPLETION_TOKENS_CONFIG_KEY: DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
85
85
  TIMEOUT_CONFIG_KEY: 7,
86
86
  }
87
87
 
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, Dict, List, Text
3
+ from typing import Any, Dict, List, Optional, Text
4
4
 
5
5
  import rasa.dialogue_understanding.processor.command_processor
6
6
  from rasa.engine.graph import ExecutionContext, GraphComponent
@@ -37,8 +37,8 @@ class CommandProcessorComponent(GraphComponent):
37
37
  self,
38
38
  tracker: DialogueStateTracker,
39
39
  flows: FlowsList,
40
- story_graph: StoryGraph,
41
40
  domain: Domain,
41
+ story_graph: Optional[StoryGraph] = None,
42
42
  ) -> List[Event]:
43
43
  """Execute commands to update tracker state."""
44
44
  return rasa.dialogue_understanding.processor.command_processor.execute_commands(
@@ -33,6 +33,7 @@ from rasa.e2e_test.e2e_test_runner import E2ETestRunner
33
33
  from rasa.shared.core.events import UserUttered
34
34
  from rasa.shared.core.trackers import DialogueStateTracker
35
35
  from rasa.shared.nlu.constants import PREDICTED_COMMANDS, PROMPTS
36
+ from rasa.shared.utils.llm import create_tracker_for_user_step
36
37
  from rasa.utils.endpoints import EndpointConfig
37
38
 
38
39
  structlogger = structlog.get_logger()
@@ -178,8 +179,9 @@ class DialogueUnderstandingTestRunner:
178
179
  # create and save the tracker at the time just
179
180
  # before the user message was sent
180
181
  step_sender_id = f"{sender_id}_{user_step_index}"
181
- await self._create_tracker_for_user_step(
182
+ await create_tracker_for_user_step(
182
183
  step_sender_id,
184
+ self.agent,
183
185
  test_case_tracker,
184
186
  user_uttered_event_indices[user_step_index],
185
187
  )
@@ -280,26 +282,6 @@ class DialogueUnderstandingTestRunner:
280
282
 
281
283
  return user_uttered_event
282
284
 
283
- async def _create_tracker_for_user_step(
284
- self,
285
- step_sender_id: str,
286
- test_case_tracker: DialogueStateTracker,
287
- index_user_uttered_event: int,
288
- ) -> None:
289
- """Creates a tracker for the user step."""
290
- tracker = test_case_tracker.copy()
291
- # modify the sender id so that the test case tracker is not overwritten
292
- tracker.sender_id = step_sender_id
293
-
294
- if tracker.events:
295
- # get timestamp of the event just before the user uttered event
296
- timestamp = tracker.events[index_user_uttered_event - 1].timestamp
297
- # revert the tracker to the event just before the user uttered event
298
- tracker = tracker.travel_back_in_time(timestamp)
299
-
300
- # store the tracker with the unique sender id
301
- await self.agent.tracker_store.save(tracker)
302
-
303
285
  async def _send_user_message(
304
286
  self,
305
287
  sender_id: str,
@@ -1,4 +1,3 @@
1
- from datetime import datetime
2
1
  from typing import List, Optional
3
2
 
4
3
  import structlog
@@ -24,6 +23,7 @@ from rasa.shared.core.constants import SlotMappingType
24
23
  from rasa.shared.core.events import BotUttered, SlotSet, UserUttered
25
24
  from rasa.shared.core.trackers import DialogueStateTracker
26
25
  from rasa.shared.nlu.constants import COMMANDS, ENTITIES, INTENT
26
+ from rasa.shared.utils.llm import generate_sender_id
27
27
 
28
28
  structlogger = structlog.get_logger()
29
29
 
@@ -52,7 +52,7 @@ class TestCaseTrackerSimulator:
52
52
  self.test_case = test_case
53
53
  self.output_channel = output_channel or CollectingOutputChannel()
54
54
 
55
- self.sender_id = self._generate_sender_id()
55
+ self.sender_id = generate_sender_id(self.test_case.name)
56
56
 
57
57
  async def simulate_test_case(
58
58
  self,
@@ -150,10 +150,6 @@ class TestCaseTrackerSimulator:
150
150
  user_uttered_event_indices=user_uttered_event_indices,
151
151
  )
152
152
 
153
- def _generate_sender_id(self) -> str:
154
- # add timestamp suffix to ensure sender_id is unique
155
- return f"{self.test_case.name}_{datetime.now()}"
156
-
157
153
  @staticmethod
158
154
  async def _get_latest_user_uttered_event_index(
159
155
  tracker: DialogueStateTracker, user_uttered_event_indices: List[int]
@@ -396,7 +396,9 @@ class DefaultV1Recipe(Recipe):
396
396
  return preprocessors
397
397
 
398
398
  def _get_needs_from_args(
399
- self, component: Type[GraphComponent], fn_name: str
399
+ self,
400
+ component: Type[GraphComponent],
401
+ fn_name: str,
400
402
  ) -> Dict[str, str]:
401
403
  """Get the needed arguments from the method on the component.
402
404
 
@@ -434,6 +436,7 @@ class DefaultV1Recipe(Recipe):
434
436
  parameters = {
435
437
  name
436
438
  for name, param in sig.parameters.items()
439
+ # only consider parameters which are positional or keyword
437
440
  if param.kind == param.POSITIONAL_OR_KEYWORD
438
441
  }
439
442
 
@@ -752,8 +755,28 @@ class DefaultV1Recipe(Recipe):
752
755
  predict_config, predict_nodes, train_nodes, preprocessors
753
756
  )
754
757
 
758
+ # The `story_graph_provider` is only needed if the intentless policy is used.
759
+ # If it is not used, we can remove it from the nodes as it slows down the
760
+ # loading time if users have a large number of stories.
761
+ if not self._intentless_policy_used(predict_nodes):
762
+ # Removes the `story_graph_provider` from the nodes
763
+ predict_nodes.pop("story_graph_provider", None)
764
+ if "command_processor" in predict_nodes:
765
+ # Removes story_graph from the command processor inputs
766
+ predict_nodes["command_processor"].needs.pop("story_graph", None)
767
+
755
768
  return predict_nodes
756
769
 
770
+ @staticmethod
771
+ def _intentless_policy_used(nodes: Dict[Text, SchemaNode]) -> bool:
772
+ """Checks if the intentless policy is used in the nodes."""
773
+ from rasa.core import IntentlessPolicy
774
+
775
+ for schema_node in nodes.values():
776
+ if schema_node.matches_type(IntentlessPolicy):
777
+ return True
778
+ return False
779
+
757
780
  def _add_nlu_predict_nodes(
758
781
  self,
759
782
  last_run_node: Text,
@@ -924,7 +947,8 @@ class DefaultV1Recipe(Recipe):
924
947
  predict_nodes["command_processor"] = SchemaNode(
925
948
  **DEFAULT_PREDICT_KWARGS,
926
949
  needs=self._get_needs_from_args(
927
- CommandProcessorComponent, "execute_commands"
950
+ CommandProcessorComponent,
951
+ "execute_commands",
928
952
  ),
929
953
  uses=CommandProcessorComponent,
930
954
  fn="execute_commands",