rasa-pro 3.10.15__py3-none-any.whl → 3.11.0__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 (238) hide show
  1. rasa/__main__.py +31 -15
  2. rasa/api.py +12 -2
  3. rasa/cli/arguments/default_arguments.py +24 -4
  4. rasa/cli/arguments/run.py +15 -0
  5. rasa/cli/arguments/shell.py +5 -1
  6. rasa/cli/arguments/train.py +17 -9
  7. rasa/cli/evaluate.py +7 -7
  8. rasa/cli/inspect.py +19 -7
  9. rasa/cli/interactive.py +1 -0
  10. rasa/cli/project_templates/calm/config.yml +5 -7
  11. rasa/cli/project_templates/calm/endpoints.yml +15 -2
  12. rasa/cli/project_templates/tutorial/config.yml +8 -5
  13. rasa/cli/project_templates/tutorial/data/flows.yml +1 -1
  14. rasa/cli/project_templates/tutorial/data/patterns.yml +5 -0
  15. rasa/cli/project_templates/tutorial/domain.yml +14 -0
  16. rasa/cli/project_templates/tutorial/endpoints.yml +5 -0
  17. rasa/cli/run.py +7 -0
  18. rasa/cli/scaffold.py +4 -2
  19. rasa/cli/studio/upload.py +0 -15
  20. rasa/cli/train.py +14 -53
  21. rasa/cli/utils.py +14 -11
  22. rasa/cli/x.py +7 -7
  23. rasa/constants.py +3 -1
  24. rasa/core/actions/action.py +77 -33
  25. rasa/core/actions/action_hangup.py +29 -0
  26. rasa/core/actions/action_repeat_bot_messages.py +89 -0
  27. rasa/core/actions/e2e_stub_custom_action_executor.py +5 -1
  28. rasa/core/actions/http_custom_action_executor.py +4 -0
  29. rasa/core/agent.py +2 -2
  30. rasa/core/brokers/kafka.py +3 -1
  31. rasa/core/brokers/pika.py +3 -1
  32. rasa/core/channels/__init__.py +10 -6
  33. rasa/core/channels/channel.py +41 -4
  34. rasa/core/channels/development_inspector.py +150 -46
  35. rasa/core/channels/inspector/README.md +1 -1
  36. rasa/core/channels/inspector/dist/assets/{arc-b6e548fe.js → arc-bc141fb2.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-fa03ac9e.js → c4Diagram-d0fbc5ce-be2db283.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-ee67392a.js → classDiagram-936ed81e-55366915.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-9b283fae.js → classDiagram-v2-c3cb15f1-bb529518.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{createText-62fc7601-8b6fcc2a.js → createText-62fc7601-b0ec81d6.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-22e77f4f.js → edges-f2ad444c-6166330c.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-60ffc87f.js → erDiagram-9d236eb7-5ccc6a8e.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-9dd802e4.js → flowDb-1972c806-fca3bfe4.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-5fa1912f.js → flowDiagram-7ea5b25a-4739080f.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-736177bf.js +1 -0
  46. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-622a1fd2.js → flowchart-elk-definition-abe16c3d-7c1b0e0f.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-e285a63a.js → ganttDiagram-9b5ea136-772fd050.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-f237bdca.js → gitGraphDiagram-99d0ae7c-8eae1dc9.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-4b03d70e.js → index-2c4b9a3b-f55afcdf.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/index-e7cef9de.js +1317 -0
  51. rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-72a0fa5f.js → infoDiagram-736b4530-124d4a14.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-82218c41.js → journeyDiagram-df861f2b-7c4fae44.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{layout-78cff630.js → layout-b9885fb6.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{line-5038b469.js → line-7c59abb6.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{linear-c4fc4098.js → linear-4776f780.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-c33c8ea6.js → mindmap-definition-beec6740-2332c46c.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-a8d03059.js → pieDiagram-dbbf0591-8fb39303.js} +1 -1
  58. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-6a0e56b2.js → quadrantDiagram-4d7f4fd6-3c7180a2.js} +1 -1
  59. rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-2dc7c7bd.js → requirementDiagram-6fc4c22a-e910bcb8.js} +1 -1
  60. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-2360fe39.js → sankeyDiagram-8f13d901-ead16c89.js} +1 -1
  61. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-41b9f9ad.js → sequenceDiagram-b655622a-29a02a19.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-0aad326f.js → stateDiagram-59f0c015-042b3137.js} +1 -1
  63. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-9847d984.js → stateDiagram-v2-2b26beab-2178c0f3.js} +1 -1
  64. rasa/core/channels/inspector/dist/assets/{styles-080da4f6-564d890e.js → styles-080da4f6-23ffa4fc.js} +1 -1
  65. rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-38957613.js → styles-3dcbcfbf-94f59763.js} +1 -1
  66. rasa/core/channels/inspector/dist/assets/{styles-9c745c82-f0fc6921.js → styles-9c745c82-78a6bebc.js} +1 -1
  67. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-ef3c5a77.js → svgDrawCommon-4835440b-eae2a6f6.js} +1 -1
  68. rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-bf3e91c1.js → timeline-definition-5b62e21b-5c968d92.js} +1 -1
  69. rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-4d4026c0.js → xychartDiagram-2b33534f-fd3db0d5.js} +1 -1
  70. rasa/core/channels/inspector/dist/index.html +18 -15
  71. rasa/core/channels/inspector/index.html +17 -14
  72. rasa/core/channels/inspector/package.json +5 -1
  73. rasa/core/channels/inspector/src/App.tsx +118 -68
  74. rasa/core/channels/inspector/src/components/Chat.tsx +95 -0
  75. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +11 -10
  76. rasa/core/channels/inspector/src/components/DialogueStack.tsx +10 -25
  77. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +6 -3
  78. rasa/core/channels/inspector/src/helpers/audiostream.ts +165 -0
  79. rasa/core/channels/inspector/src/helpers/formatters.test.ts +10 -0
  80. rasa/core/channels/inspector/src/helpers/formatters.ts +107 -41
  81. rasa/core/channels/inspector/src/helpers/utils.ts +92 -7
  82. rasa/core/channels/inspector/src/types.ts +21 -1
  83. rasa/core/channels/inspector/yarn.lock +94 -1
  84. rasa/core/channels/rest.py +51 -46
  85. rasa/core/channels/socketio.py +28 -1
  86. rasa/core/channels/telegram.py +1 -1
  87. rasa/core/channels/twilio.py +1 -1
  88. rasa/core/channels/{audiocodes.py → voice_ready/audiocodes.py} +122 -69
  89. rasa/core/channels/{voice_aware → voice_ready}/jambonz.py +26 -8
  90. rasa/core/channels/{voice_aware → voice_ready}/jambonz_protocol.py +57 -5
  91. rasa/core/channels/{twilio_voice.py → voice_ready/twilio_voice.py} +64 -28
  92. rasa/core/channels/voice_ready/utils.py +37 -0
  93. rasa/core/channels/voice_stream/asr/__init__.py +0 -0
  94. rasa/core/channels/voice_stream/asr/asr_engine.py +89 -0
  95. rasa/core/channels/voice_stream/asr/asr_event.py +18 -0
  96. rasa/core/channels/voice_stream/asr/azure.py +129 -0
  97. rasa/core/channels/voice_stream/asr/deepgram.py +90 -0
  98. rasa/core/channels/voice_stream/audio_bytes.py +8 -0
  99. rasa/core/channels/voice_stream/browser_audio.py +107 -0
  100. rasa/core/channels/voice_stream/call_state.py +23 -0
  101. rasa/core/channels/voice_stream/tts/__init__.py +0 -0
  102. rasa/core/channels/voice_stream/tts/azure.py +106 -0
  103. rasa/core/channels/voice_stream/tts/cartesia.py +118 -0
  104. rasa/core/channels/voice_stream/tts/tts_cache.py +27 -0
  105. rasa/core/channels/voice_stream/tts/tts_engine.py +58 -0
  106. rasa/core/channels/voice_stream/twilio_media_streams.py +173 -0
  107. rasa/core/channels/voice_stream/util.py +57 -0
  108. rasa/core/channels/voice_stream/voice_channel.py +427 -0
  109. rasa/core/information_retrieval/qdrant.py +1 -0
  110. rasa/core/nlg/contextual_response_rephraser.py +45 -17
  111. rasa/{nlu → core}/persistor.py +203 -68
  112. rasa/core/policies/enterprise_search_policy.py +119 -63
  113. rasa/core/policies/flows/flow_executor.py +15 -22
  114. rasa/core/policies/intentless_policy.py +83 -28
  115. rasa/core/processor.py +25 -0
  116. rasa/core/run.py +12 -2
  117. rasa/core/secrets_manager/constants.py +4 -0
  118. rasa/core/secrets_manager/factory.py +8 -0
  119. rasa/core/secrets_manager/vault.py +11 -1
  120. rasa/core/training/interactive.py +33 -34
  121. rasa/core/utils.py +47 -21
  122. rasa/dialogue_understanding/coexistence/llm_based_router.py +41 -14
  123. rasa/dialogue_understanding/commands/__init__.py +6 -0
  124. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +60 -0
  125. rasa/dialogue_understanding/commands/session_end_command.py +61 -0
  126. rasa/dialogue_understanding/commands/user_silence_command.py +59 -0
  127. rasa/dialogue_understanding/commands/utils.py +5 -0
  128. rasa/dialogue_understanding/generator/constants.py +2 -0
  129. rasa/dialogue_understanding/generator/flow_retrieval.py +47 -9
  130. rasa/dialogue_understanding/generator/llm_based_command_generator.py +38 -15
  131. rasa/dialogue_understanding/generator/llm_command_generator.py +1 -1
  132. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +35 -13
  133. rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2 +3 -0
  134. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +60 -13
  135. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +53 -0
  136. rasa/dialogue_understanding/patterns/repeat.py +37 -0
  137. rasa/dialogue_understanding/patterns/user_silence.py +37 -0
  138. rasa/dialogue_understanding/processor/command_processor.py +21 -1
  139. rasa/e2e_test/aggregate_test_stats_calculator.py +1 -11
  140. rasa/e2e_test/assertions.py +136 -61
  141. rasa/e2e_test/assertions_schema.yml +23 -0
  142. rasa/e2e_test/e2e_test_case.py +85 -6
  143. rasa/e2e_test/e2e_test_runner.py +2 -3
  144. rasa/engine/graph.py +0 -1
  145. rasa/engine/loader.py +12 -0
  146. rasa/engine/recipes/config_files/default_config.yml +0 -3
  147. rasa/engine/recipes/default_recipe.py +0 -1
  148. rasa/engine/recipes/graph_recipe.py +0 -1
  149. rasa/engine/runner/dask.py +2 -2
  150. rasa/engine/storage/local_model_storage.py +12 -42
  151. rasa/engine/storage/storage.py +1 -5
  152. rasa/engine/validation.py +527 -74
  153. rasa/model_manager/__init__.py +0 -0
  154. rasa/model_manager/config.py +40 -0
  155. rasa/model_manager/model_api.py +559 -0
  156. rasa/model_manager/runner_service.py +286 -0
  157. rasa/model_manager/socket_bridge.py +146 -0
  158. rasa/model_manager/studio_jwt_auth.py +86 -0
  159. rasa/model_manager/trainer_service.py +325 -0
  160. rasa/model_manager/utils.py +87 -0
  161. rasa/model_manager/warm_rasa_process.py +187 -0
  162. rasa/model_service.py +112 -0
  163. rasa/model_training.py +42 -23
  164. rasa/nlu/tokenizers/whitespace_tokenizer.py +3 -14
  165. rasa/server.py +4 -2
  166. rasa/shared/constants.py +60 -8
  167. rasa/shared/core/constants.py +13 -0
  168. rasa/shared/core/domain.py +107 -50
  169. rasa/shared/core/events.py +29 -0
  170. rasa/shared/core/flows/flow.py +5 -0
  171. rasa/shared/core/flows/flows_list.py +19 -6
  172. rasa/shared/core/flows/flows_yaml_schema.json +10 -0
  173. rasa/shared/core/flows/utils.py +39 -0
  174. rasa/shared/core/flows/validation.py +121 -0
  175. rasa/shared/core/flows/yaml_flows_io.py +15 -27
  176. rasa/shared/core/slots.py +5 -0
  177. rasa/shared/importers/importer.py +59 -41
  178. rasa/shared/importers/multi_project.py +23 -11
  179. rasa/shared/importers/rasa.py +12 -3
  180. rasa/shared/importers/remote_importer.py +196 -0
  181. rasa/shared/importers/utils.py +3 -1
  182. rasa/shared/nlu/training_data/formats/rasa_yaml.py +18 -3
  183. rasa/shared/nlu/training_data/training_data.py +18 -19
  184. rasa/shared/providers/_configs/litellm_router_client_config.py +220 -0
  185. rasa/shared/providers/_configs/model_group_config.py +167 -0
  186. rasa/shared/providers/_configs/openai_client_config.py +1 -1
  187. rasa/shared/providers/_configs/rasa_llm_client_config.py +73 -0
  188. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -0
  189. rasa/shared/providers/_configs/utils.py +16 -0
  190. rasa/shared/providers/_utils.py +79 -0
  191. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +13 -29
  192. rasa/shared/providers/embedding/azure_openai_embedding_client.py +54 -21
  193. rasa/shared/providers/embedding/default_litellm_embedding_client.py +24 -0
  194. rasa/shared/providers/embedding/litellm_router_embedding_client.py +135 -0
  195. rasa/shared/providers/llm/_base_litellm_client.py +34 -22
  196. rasa/shared/providers/llm/azure_openai_llm_client.py +50 -29
  197. rasa/shared/providers/llm/default_litellm_llm_client.py +24 -0
  198. rasa/shared/providers/llm/litellm_router_llm_client.py +182 -0
  199. rasa/shared/providers/llm/rasa_llm_client.py +112 -0
  200. rasa/shared/providers/llm/self_hosted_llm_client.py +5 -29
  201. rasa/shared/providers/mappings.py +19 -0
  202. rasa/shared/providers/router/__init__.py +0 -0
  203. rasa/shared/providers/router/_base_litellm_router_client.py +183 -0
  204. rasa/shared/providers/router/router_client.py +73 -0
  205. rasa/shared/utils/common.py +40 -24
  206. rasa/shared/utils/health_check/__init__.py +0 -0
  207. rasa/shared/utils/health_check/embeddings_health_check_mixin.py +31 -0
  208. rasa/shared/utils/health_check/health_check.py +258 -0
  209. rasa/shared/utils/health_check/llm_health_check_mixin.py +31 -0
  210. rasa/shared/utils/io.py +27 -6
  211. rasa/shared/utils/llm.py +353 -43
  212. rasa/shared/utils/schemas/events.py +2 -0
  213. rasa/shared/utils/schemas/model_config.yml +0 -10
  214. rasa/shared/utils/yaml.py +181 -38
  215. rasa/studio/data_handler.py +3 -1
  216. rasa/studio/upload.py +160 -74
  217. rasa/telemetry.py +94 -17
  218. rasa/tracing/config.py +3 -1
  219. rasa/tracing/instrumentation/attribute_extractors.py +95 -18
  220. rasa/tracing/instrumentation/instrumentation.py +121 -0
  221. rasa/utils/common.py +5 -0
  222. rasa/utils/endpoints.py +27 -1
  223. rasa/utils/io.py +8 -16
  224. rasa/utils/log_utils.py +9 -2
  225. rasa/utils/sanic_error_handler.py +32 -0
  226. rasa/validator.py +110 -4
  227. rasa/version.py +1 -1
  228. {rasa_pro-3.10.15.dist-info → rasa_pro-3.11.0.dist-info}/METADATA +14 -12
  229. {rasa_pro-3.10.15.dist-info → rasa_pro-3.11.0.dist-info}/RECORD +234 -183
  230. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-1844e5a5.js +0 -1
  231. rasa/core/channels/inspector/dist/assets/index-a5d3e69d.js +0 -1040
  232. rasa/core/channels/voice_aware/utils.py +0 -20
  233. rasa/llm_fine_tuning/notebooks/unsloth_finetuning.ipynb +0 -407
  234. /rasa/core/channels/{voice_aware → voice_ready}/__init__.py +0 -0
  235. /rasa/core/channels/{voice_native → voice_stream}/__init__.py +0 -0
  236. {rasa_pro-3.10.15.dist-info → rasa_pro-3.11.0.dist-info}/NOTICE +0 -0
  237. {rasa_pro-3.10.15.dist-info → rasa_pro-3.11.0.dist-info}/WHEEL +0 -0
  238. {rasa_pro-3.10.15.dist-info → rasa_pro-3.11.0.dist-info}/entry_points.txt +0 -0
@@ -16,6 +16,7 @@ from rasa.dialogue_understanding.generator.constants import (
16
16
  LLM_CONFIG_KEY,
17
17
  FLOW_RETRIEVAL_KEY,
18
18
  FLOW_RETRIEVAL_ACTIVE_KEY,
19
+ FLOW_RETRIEVAL_FLOW_THRESHOLD,
19
20
  )
20
21
  from rasa.dialogue_understanding.generator.flow_retrieval import FlowRetrieval
21
22
  from rasa.engine.graph import GraphComponent, ExecutionContext
@@ -31,10 +32,11 @@ from rasa.shared.exceptions import ProviderClientAPIException
31
32
  from rasa.shared.nlu.constants import FLOWS_IN_PROMPT
32
33
  from rasa.shared.nlu.training_data.message import Message
33
34
  from rasa.shared.nlu.training_data.training_data import TrainingData
35
+ from rasa.shared.utils.health_check.llm_health_check_mixin import LLMHealthCheckMixin
34
36
  from rasa.shared.utils.llm import (
35
37
  allowed_values_for_slot,
36
38
  llm_factory,
37
- try_instantiate_llm_client,
39
+ resolve_model_client_config,
38
40
  )
39
41
  from rasa.utils.log_utils import log_llm
40
42
 
@@ -47,7 +49,9 @@ structlogger = structlog.get_logger()
47
49
  ],
48
50
  is_trainable=True,
49
51
  )
50
- class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
52
+ class LLMBasedCommandGenerator(
53
+ LLMHealthCheckMixin, GraphComponent, CommandGenerator, ABC
54
+ ):
51
55
  """An abstract class defining interface and common functionality
52
56
  of an LLM-based command generators.
53
57
  """
@@ -61,6 +65,9 @@ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
61
65
  ) -> None:
62
66
  super().__init__(config)
63
67
  self.config = {**self.get_default_config(), **config}
68
+ self.config[LLM_CONFIG_KEY] = resolve_model_client_config(
69
+ self.config.get(LLM_CONFIG_KEY), LLMBasedCommandGenerator.__name__
70
+ )
64
71
  self._model_storage = model_storage
65
72
  self._resource = resource
66
73
  self.flow_retrieval: Optional[FlowRetrieval]
@@ -70,17 +77,9 @@ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
70
77
  self.config[FLOW_RETRIEVAL_KEY], model_storage, resource
71
78
  )
72
79
  structlogger.info("llm_based_command_generator.flow_retrieval.enabled")
80
+ self.config[FLOW_RETRIEVAL_KEY] = self.flow_retrieval.config
73
81
  else:
74
82
  self.flow_retrieval = None
75
- structlogger.warn(
76
- "llm_based_command_generator.flow_retrieval.disabled",
77
- event_info=(
78
- "Disabling flow retrieval can cause issues when there are a "
79
- "large number of flows to be included in the prompt. For more"
80
- "information see:\n"
81
- "https://rasa.com/docs/rasa-pro/concepts/dialogue-understanding#how-the-llmcommandgenerator-works"
82
- ),
83
- )
84
83
 
85
84
  ### Abstract methods
86
85
  @staticmethod
@@ -168,14 +167,34 @@ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
168
167
  """Train the llm based command generator. Stores all flows into a vector
169
168
  store.
170
169
  """
171
- # Validate llm configuration
172
- try_instantiate_llm_client(
170
+ self.perform_llm_health_check(
173
171
  self.config.get(LLM_CONFIG_KEY),
174
172
  DEFAULT_LLM_CONFIG,
175
173
  "llm_based_command_generator.train",
176
- "LLMBasedCommandGenerator",
174
+ LLMBasedCommandGenerator.__name__,
177
175
  )
178
176
 
177
+ if (
178
+ self.flow_retrieval is None
179
+ and len(flows.user_flows) > FLOW_RETRIEVAL_FLOW_THRESHOLD
180
+ ):
181
+ structlogger.warn(
182
+ "llm_based_command_generator.flow_retrieval.disabled",
183
+ event_info=(
184
+ f"You have {len(flows.user_flows)} user flows but flow "
185
+ f"retrieval is disabled. "
186
+ f"It is recommended to enable flow retrieval if the "
187
+ f"total number of user flows exceed "
188
+ f"{FLOW_RETRIEVAL_FLOW_THRESHOLD}. "
189
+ f"Keeping it disabled can result in deterioration of "
190
+ f"command generator's functional "
191
+ f"performance and higher costs because of increased "
192
+ f"number of tokens in the prompt. For more"
193
+ "information see:\n"
194
+ "https://rasa.com/docs/rasa-pro/concepts/dialogue-understanding#how-the-llmcommandgenerator-works"
195
+ ),
196
+ )
197
+
179
198
  # flow retrieval is populated with only user-defined flows
180
199
  try:
181
200
  if self.flow_retrieval is not None and not flows.is_empty():
@@ -187,6 +206,7 @@ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
187
206
  error=e,
188
207
  )
189
208
  raise
209
+
190
210
  self.persist()
191
211
  return self._resource
192
212
 
@@ -224,7 +244,10 @@ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
224
244
 
225
245
  @classmethod
226
246
  def load_flow_retrival(
227
- cls, config: Dict[Text, Any], model_storage: ModelStorage, resource: Resource
247
+ cls,
248
+ config: Dict[str, Any],
249
+ model_storage: ModelStorage,
250
+ resource: Resource,
228
251
  ) -> Optional[FlowRetrieval]:
229
252
  """Load the FlowRetrieval component if it is enabled in the configuration."""
230
253
  enable_flow_retrieval = config.get(FLOW_RETRIEVAL_KEY, {}).get(
@@ -1,4 +1,4 @@
1
- from typing import Dict, Any, Optional, Text
1
+ from typing import Any, Dict, Optional, Text
2
2
 
3
3
  import structlog
4
4
  from deprecated import deprecated # type: ignore[import]
@@ -40,7 +40,10 @@ from rasa.engine.graph import ExecutionContext
40
40
  from rasa.engine.recipes.default_recipe import DefaultV1Recipe
41
41
  from rasa.engine.storage.resource import Resource
42
42
  from rasa.engine.storage.storage import ModelStorage
43
- from rasa.shared.constants import RASA_PATTERN_CANNOT_HANDLE_NOT_SUPPORTED
43
+ from rasa.shared.constants import (
44
+ RASA_PATTERN_CANNOT_HANDLE_NOT_SUPPORTED,
45
+ EMBEDDINGS_CONFIG_KEY,
46
+ )
44
47
  from rasa.shared.constants import ROUTE_TO_CALM_SLOT
45
48
  from rasa.shared.core.flows import FlowStep, Flow, FlowsList
46
49
  from rasa.shared.core.flows.steps.collect import CollectInformationFlowStep
@@ -54,7 +57,7 @@ from rasa.shared.utils.llm import (
54
57
  tracker_as_readable_transcript,
55
58
  sanitize_message_for_prompt,
56
59
  allowed_values_for_slot,
57
- try_instantiate_llm_client,
60
+ resolve_model_client_config,
58
61
  )
59
62
 
60
63
  # multistep template keys
@@ -72,6 +75,7 @@ DEFAULT_HANDLE_FLOWS_TEMPLATE = importlib.resources.read_text(
72
75
  DEFAULT_FILL_SLOTS_TEMPLATE = importlib.resources.read_text(
73
76
  "rasa.dialogue_understanding.generator.multi_step", "fill_slots_prompt.jinja2"
74
77
  ).strip()
78
+ MULTI_STEP_LLM_COMMAND_GENERATOR_CONFIG_FILE = "config.json"
75
79
 
76
80
  # dictionary of template names and associated file names and default values
77
81
  PROMPT_TEMPLATES = {
@@ -140,27 +144,32 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
140
144
  **kwargs: Any,
141
145
  ) -> "MultiStepLLMCommandGenerator":
142
146
  """Loads trained component (see parent class for full docstring)."""
143
- prompts = cls._load_prompt_templates(model_storage, resource)
144
- # init base command generator
145
- command_generator = cls(config, model_storage, resource, prompts)
146
- try_instantiate_llm_client(
147
- command_generator.config.get(LLM_CONFIG_KEY),
147
+
148
+ # Perform health check of the LLM client config
149
+ llm_config = resolve_model_client_config(config.get(LLM_CONFIG_KEY, {}))
150
+ cls.perform_llm_health_check(
151
+ llm_config,
148
152
  DEFAULT_LLM_CONFIG,
149
153
  "multi_step_llm_command_generator.load",
150
154
  MultiStepLLMCommandGenerator.__name__,
151
155
  )
156
+
157
+ prompts = cls._load_prompt_templates(model_storage, resource)
158
+
159
+ # init base command generator
160
+ command_generator = cls(config, model_storage, resource, prompts)
152
161
  # load flow retrieval if enabled
153
162
  if command_generator.enabled_flow_retrieval:
154
163
  command_generator.flow_retrieval = cls.load_flow_retrival(
155
164
  command_generator.config, model_storage, resource
156
165
  )
166
+
157
167
  return command_generator
158
168
 
159
169
  def persist(self) -> None:
160
170
  """Persist this component to disk for future loading."""
161
- # persist prompt template
162
171
  self._persist_prompt_templates()
163
- # persist flow retrieval
172
+ self._persist_config()
164
173
  if self.flow_retrieval is not None:
165
174
  self.flow_retrieval.persist()
166
175
 
@@ -393,6 +402,13 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
393
402
  file_path = path / file_name
394
403
  rasa.shared.utils.io.write_text_file(template, file_path)
395
404
 
405
+ def _persist_config(self) -> None:
406
+ """Persist config as a source of truth for resolved clients."""
407
+ with self._model_storage.write_to(self._resource) as path:
408
+ rasa.shared.utils.io.dump_obj_as_json_to_file(
409
+ path / MULTI_STEP_LLM_COMMAND_GENERATOR_CONFIG_FILE, self.config
410
+ )
411
+
396
412
  async def _predict_commands_with_multi_step(
397
413
  self,
398
414
  message: Message,
@@ -786,11 +802,17 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
786
802
  .get(FILE_PATH_KEY),
787
803
  DEFAULT_FILL_SLOTS_TEMPLATE,
788
804
  )
805
+
806
+ llm_config = resolve_model_client_config(
807
+ config.get(LLM_CONFIG_KEY), MultiStepLLMCommandGenerator.__name__
808
+ )
809
+ embedding_config = resolve_model_client_config(
810
+ config.get(FLOW_RETRIEVAL_KEY, {}).get(EMBEDDINGS_CONFIG_KEY),
811
+ FlowRetrieval.__name__,
812
+ )
813
+
789
814
  return deep_container_fingerprint(
790
- [
791
- handle_flows_template,
792
- fill_slots_template,
793
- ]
815
+ [handle_flows_template, fill_slots_template, llm_config, embedding_config]
794
816
  )
795
817
 
796
818
  @staticmethod
@@ -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,
@@ -38,6 +39,7 @@ from rasa.shared.constants import (
38
39
  ROUTE_TO_CALM_SLOT,
39
40
  PROMPT_CONFIG_KEY,
40
41
  PROMPT_TEMPLATE_CONFIG_KEY,
42
+ EMBEDDINGS_CONFIG_KEY,
41
43
  )
42
44
  from rasa.shared.core.flows import FlowsList
43
45
  from rasa.shared.core.trackers import DialogueStateTracker
@@ -49,8 +51,9 @@ from rasa.shared.utils.llm import (
49
51
  get_prompt_template,
50
52
  tracker_as_readable_transcript,
51
53
  sanitize_message_for_prompt,
52
- try_instantiate_llm_client,
54
+ resolve_model_client_config,
53
55
  )
56
+ from rasa.utils.beta import ensure_beta_feature_is_enabled, BetaNotEnabledException
54
57
  from rasa.utils.log_utils import log_llm
55
58
 
56
59
  COMMAND_PROMPT_FILE_NAME = "command_prompt.jinja2"
@@ -59,6 +62,7 @@ DEFAULT_COMMAND_PROMPT_TEMPLATE = importlib.resources.read_text(
59
62
  "rasa.dialogue_understanding.generator.single_step",
60
63
  "command_prompt_template.jinja2",
61
64
  )
65
+ SINGLE_STEP_LLM_COMMAND_GENERATOR_CONFIG_FILE = "config.json"
62
66
 
63
67
  structlogger = structlog.get_logger()
64
68
 
@@ -109,6 +113,7 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
109
113
  )
110
114
 
111
115
  self.trace_prompt_tokens = self.config.get("trace_prompt_tokens", False)
116
+ self.repeat_command_enabled = self.is_repeat_command_enabled()
112
117
 
113
118
  ### Implementations of LLMBasedCommandGenerator parent
114
119
  @staticmethod
@@ -132,35 +137,51 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
132
137
  **kwargs: Any,
133
138
  ) -> "SingleStepLLMCommandGenerator":
134
139
  """Loads trained component (see parent class for full docstring)."""
140
+
141
+ # Perform health check of the LLM API endpoint
142
+ llm_config = resolve_model_client_config(config.get(LLM_CONFIG_KEY, {}))
143
+ cls.perform_llm_health_check(
144
+ llm_config,
145
+ DEFAULT_LLM_CONFIG,
146
+ "single_step_llm_command_generator.load",
147
+ SingleStepLLMCommandGenerator.__name__,
148
+ )
149
+
135
150
  # load prompt template from the model storage.
136
151
  prompt_template = cls.load_prompt_template_from_model_storage(
137
152
  model_storage, resource, COMMAND_PROMPT_FILE_NAME
138
153
  )
154
+
139
155
  # init base command generator
140
156
  command_generator = cls(config, model_storage, resource, prompt_template)
141
- try_instantiate_llm_client(
142
- command_generator.config.get(LLM_CONFIG_KEY),
143
- DEFAULT_LLM_CONFIG,
144
- "single_step_llm_command_generator.load",
145
- SingleStepLLMCommandGenerator.__name__,
146
- )
147
157
  # load flow retrieval if enabled
148
158
  if command_generator.enabled_flow_retrieval:
149
159
  command_generator.flow_retrieval = cls.load_flow_retrival(
150
160
  command_generator.config, model_storage, resource
151
161
  )
162
+
152
163
  return command_generator
153
164
 
154
165
  def persist(self) -> None:
155
166
  """Persist this component to disk for future loading."""
156
- # persist prompt template
167
+ self._persist_prompt_template()
168
+ self._persist_config()
169
+ if self.flow_retrieval is not None:
170
+ self.flow_retrieval.persist()
171
+
172
+ def _persist_prompt_template(self) -> None:
173
+ """Persist prompt template for future loading."""
157
174
  with self._model_storage.write_to(self._resource) as path:
158
175
  rasa.shared.utils.io.write_text_file(
159
176
  self.prompt_template, path / COMMAND_PROMPT_FILE_NAME
160
177
  )
161
- # persist flow retrieval
162
- if self.flow_retrieval is not None:
163
- self.flow_retrieval.persist()
178
+
179
+ def _persist_config(self) -> None:
180
+ """Persist config as a source of truth for resolved clients."""
181
+ with self._model_storage.write_to(self._resource) as path:
182
+ rasa.shared.utils.io.dump_obj_as_json_to_file(
183
+ path / SINGLE_STEP_LLM_COMMAND_GENERATOR_CONFIG_FILE, self.config
184
+ )
164
185
 
165
186
  async def predict_commands(
166
187
  self,
@@ -309,6 +330,7 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
309
330
  knowledge_re = re.compile(r"SearchAndReply\(\)")
310
331
  humand_handoff_re = re.compile(r"HumanHandoff\(\)")
311
332
  clarify_re = re.compile(r"Clarify\(([\"\'a-zA-Z0-9_, ]+)\)")
333
+ repeat_re = re.compile(r"RepeatLastBotMessages\(\)")
312
334
 
313
335
  for action in actions.strip().splitlines():
314
336
  if match := slot_set_re.search(action):
@@ -335,6 +357,8 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
335
357
  commands.append(KnowledgeAnswerCommand())
336
358
  elif humand_handoff_re.search(action):
337
359
  commands.append(HumanHandoffCommand())
360
+ elif repeat_re.search(action):
361
+ commands.append(RepeatBotMessagesCommand())
338
362
  elif match := clarify_re.search(action):
339
363
  options = sorted([opt.strip() for opt in match.group(1).split(",")])
340
364
  # Remove surrounding quotes if present
@@ -366,7 +390,7 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
366
390
 
367
391
  @classmethod
368
392
  def fingerprint_addon(cls: Any, config: Dict[str, Any]) -> Optional[str]:
369
- """Add a fingerprint of the knowledge base for the graph."""
393
+ """Add a fingerprint for the graph."""
370
394
  config_prompt = (
371
395
  config.get(PROMPT_CONFIG_KEY)
372
396
  or config.get(PROMPT_TEMPLATE_CONFIG_KEY)
@@ -376,7 +400,16 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
376
400
  config_prompt,
377
401
  DEFAULT_COMMAND_PROMPT_TEMPLATE,
378
402
  )
379
- return deep_container_fingerprint(prompt_template)
403
+ llm_config = resolve_model_client_config(
404
+ config.get(LLM_CONFIG_KEY), SingleStepLLMCommandGenerator.__name__
405
+ )
406
+ embedding_config = resolve_model_client_config(
407
+ config.get(FLOW_RETRIEVAL_KEY, {}).get(EMBEDDINGS_CONFIG_KEY),
408
+ FlowRetrieval.__name__,
409
+ )
410
+ return deep_container_fingerprint(
411
+ [prompt_template, llm_config, embedding_config]
412
+ )
380
413
 
381
414
  ### Helper methods
382
415
  def render_template(
@@ -426,6 +459,20 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
426
459
  "current_slot": current_slot,
427
460
  "current_slot_description": current_slot_description,
428
461
  "user_message": latest_user_message,
462
+ "is_repeat_command_enabled": self.repeat_command_enabled,
429
463
  }
430
464
 
431
465
  return self.compile_template(self.prompt_template).render(**inputs)
466
+
467
+ def is_repeat_command_enabled(self) -> bool:
468
+ """Check for feature flag"""
469
+ RASA_PRO_BETA_REPEAT_COMMAND_ENV_VAR_NAME = "RASA_PRO_BETA_REPEAT_COMMAND"
470
+ try:
471
+ ensure_beta_feature_is_enabled(
472
+ "Repeat Command",
473
+ env_flag=RASA_PRO_BETA_REPEAT_COMMAND_ENV_VAR_NAME,
474
+ )
475
+ except BetaNotEnabledException:
476
+ return False
477
+
478
+ return True
@@ -4,6 +4,11 @@ responses:
4
4
  utter_ask_rephrase:
5
5
  - text: I’m sorry I am unable to understand you, could you please rephrase?
6
6
 
7
+ utter_ask_still_there:
8
+ - text: "Hello, are you still there?"
9
+ metadata:
10
+ rephrase: True
11
+
7
12
  utter_boolean_slot_rejection:
8
13
  - text: "Sorry, the value you provided, `{{value}}`, is not valid. Please respond with a valid value."
9
14
  metadata:
@@ -75,6 +80,11 @@ responses:
75
80
  metadata:
76
81
  rephrase: True
77
82
 
83
+ utter_inform_hangup:
84
+ - text: It seems you are not there anymore. I will hang up shortly.
85
+ metadata:
86
+ rephrase: True
87
+
78
88
  utter_internal_error_rasa:
79
89
  - text: Sorry, I am having trouble with that. Please try again in a few minutes.
80
90
 
@@ -101,6 +111,15 @@ slots:
101
111
  type: bool
102
112
  mappings:
103
113
  - type: from_llm
114
+ silence_timeout:
115
+ type: float
116
+ initial_value: 6.0
117
+ max_value: 1000000
118
+ consecutive_silence_timeouts:
119
+ type: float
120
+ initial_value: 0.0
121
+ max_value: 1000000
122
+
104
123
 
105
124
  flows:
106
125
  pattern_cancel_flow:
@@ -217,6 +236,11 @@ flows:
217
236
  - action: utter_internal_error_rasa
218
237
  next: END
219
238
 
239
+ pattern_repeat_bot_messages:
240
+ description: Voice conversation repair pattern to repeat bot messages
241
+ name: pattern repeat bot messages
242
+ steps:
243
+ - action: action_repeat_bot_messages
220
244
 
221
245
  pattern_restart:
222
246
  description: Flow for restarting the conversation
@@ -246,3 +270,32 @@ flows:
246
270
  name: pattern skip question
247
271
  steps:
248
272
  - action: utter_skip_question_answer
273
+
274
+ pattern_user_silence:
275
+ description: Reacting to user silence in voice bots
276
+ name: pattern react to silence
277
+ nlu_trigger:
278
+ - intent: silence_timeout
279
+ persisted_slots:
280
+ - consecutive_silence_timeouts
281
+ steps:
282
+ - noop: true
283
+ next:
284
+ - if: "slots.consecutive_silence_timeouts = 0.0"
285
+ then:
286
+ - set_slots:
287
+ - consecutive_silence_timeouts: 1.0
288
+ - action: action_repeat_bot_messages
289
+ next: END
290
+ - if: "slots.consecutive_silence_timeouts = 1.0"
291
+ then:
292
+ - set_slots:
293
+ - consecutive_silence_timeouts: 2.0
294
+ - action: utter_ask_still_there
295
+ next: END
296
+ - if: "slots.consecutive_silence_timeouts > 1.0"
297
+ then:
298
+ - action: utter_inform_hangup
299
+ - action: action_hangup
300
+ next: END
301
+ - else: END
@@ -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
+ )
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ from typing import Any, Dict
4
+
5
+ from rasa.dialogue_understanding.stack.frames import PatternFlowStackFrame
6
+ from rasa.shared.constants import RASA_DEFAULT_FLOW_PATTERN_PREFIX
7
+
8
+
9
+ FLOW_PATTERN_USER_SILENCE = RASA_DEFAULT_FLOW_PATTERN_PREFIX + "user_silence"
10
+
11
+
12
+ @dataclass
13
+ class UserSilencePatternFlowStackFrame(PatternFlowStackFrame):
14
+ """A flow stack frame that can get added when the silence timeout trips."""
15
+
16
+ flow_id: str = FLOW_PATTERN_USER_SILENCE
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_USER_SILENCE
23
+
24
+ @staticmethod
25
+ def from_dict(data: Dict[str, Any]) -> UserSilencePatternFlowStackFrame:
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 UserSilencePatternFlowStackFrame(
35
+ frame_id=data["frame_id"],
36
+ step_id=data["step_id"],
37
+ )
@@ -8,6 +8,7 @@ from rasa.dialogue_understanding.commands import (
8
8
  Command,
9
9
  CorrectSlotsCommand,
10
10
  CorrectedSlot,
11
+ RepeatBotMessagesCommand,
11
12
  SetSlotCommand,
12
13
  StartFlowCommand,
13
14
  FreeFormAnswerCommand,
@@ -422,6 +423,9 @@ def clean_up_commands(
422
423
  elif not tracker.has_coexistence_routing_slot and len(clean_commands) > 1:
423
424
  clean_commands = filter_cannot_handle_command_for_skipped_slots(clean_commands)
424
425
 
426
+ clean_commands = ensure_max_number_of_command_type(
427
+ clean_commands, RepeatBotMessagesCommand, 1
428
+ )
425
429
  structlogger.debug(
426
430
  "command_processor.clean_up_commands.final_commands",
427
431
  command=clean_commands,
@@ -430,6 +434,22 @@ def clean_up_commands(
430
434
  return clean_commands
431
435
 
432
436
 
437
+ def ensure_max_number_of_command_type(
438
+ commands: List[Command], command_type: Type[Command], n: int
439
+ ) -> List[Command]:
440
+ """Ensures that for a given command type only the first n stay in the list."""
441
+ filtered: List[Command] = []
442
+ count = 0
443
+ for c in commands:
444
+ if isinstance(c, command_type):
445
+ if count >= n:
446
+ continue
447
+ else:
448
+ count += 1
449
+ filtered.append(c)
450
+ return filtered
451
+
452
+
433
453
  def clean_up_clarify_command(
434
454
  commands_so_far: List[Command],
435
455
  all_commands: List[Command],
@@ -452,7 +472,7 @@ def clean_up_clarify_command(
452
472
  if not (isinstance(c, SetSlotCommand) and c.name == ROUTE_TO_CALM_SLOT)
453
473
  ]
454
474
 
455
- # if there are multiple clarify commands, do add the first one
475
+ # if all commands are clarify commands, add the first one only, otherwise add none
456
476
  if all(
457
477
  isinstance(c, ClarifyCommand) for c in commands_without_route_to_calm_set_slot
458
478
  ):
@@ -35,7 +35,6 @@ class AggregateTestStatsCalculator:
35
35
  self.test_cases = test_cases
36
36
 
37
37
  self.failed_assertion_set: Set["Assertion"] = set()
38
- self.failed_test_cases_without_assertion_failure: Set[str] = set()
39
38
  self.passed_count_mapping = {
40
39
  subclass_type: 0
41
40
  for subclass_type in _get_all_assertion_subclasses().keys()
@@ -90,14 +89,8 @@ class AggregateTestStatsCalculator:
90
89
  passed_test_case_names = [
91
90
  passed.test_case.name for passed in self.passed_results
92
91
  ]
93
- # We filter out test cases that failed without an assertion failure
94
- filtered_test_cases = [
95
- test_case
96
- for test_case in self.test_cases
97
- if test_case.name not in self.failed_test_cases_without_assertion_failure
98
- ]
99
92
 
100
- for test_case in filtered_test_cases:
93
+ for test_case in self.test_cases:
101
94
  if test_case.name in passed_test_case_names:
102
95
  for step in test_case.steps:
103
96
  if step.assertions is None:
@@ -125,9 +118,6 @@ class AggregateTestStatsCalculator:
125
118
  "no_assertion_failure_in_failed_result",
126
119
  test_case=failed.test_case.name,
127
120
  )
128
- self.failed_test_cases_without_assertion_failure.add(
129
- failed.test_case.name
130
- )
131
121
  continue
132
122
 
133
123
  self.failed_assertion_set.add(failed.assertion_failure.assertion)