rasa-pro 3.9.18__py3-none-any.whl → 3.10.4__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 (190) hide show
  1. README.md +26 -57
  2. rasa/__init__.py +1 -2
  3. rasa/__main__.py +5 -0
  4. rasa/anonymization/anonymization_rule_executor.py +2 -2
  5. rasa/api.py +26 -22
  6. rasa/cli/arguments/data.py +27 -2
  7. rasa/cli/arguments/default_arguments.py +25 -3
  8. rasa/cli/arguments/run.py +9 -9
  9. rasa/cli/arguments/train.py +2 -0
  10. rasa/cli/data.py +70 -8
  11. rasa/cli/e2e_test.py +108 -433
  12. rasa/cli/interactive.py +1 -0
  13. rasa/cli/llm_fine_tuning.py +395 -0
  14. rasa/cli/project_templates/calm/endpoints.yml +1 -1
  15. rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
  16. rasa/cli/run.py +14 -13
  17. rasa/cli/scaffold.py +10 -8
  18. rasa/cli/train.py +8 -7
  19. rasa/cli/utils.py +15 -0
  20. rasa/constants.py +7 -1
  21. rasa/core/actions/action.py +98 -49
  22. rasa/core/actions/action_run_slot_rejections.py +4 -1
  23. rasa/core/actions/custom_action_executor.py +9 -6
  24. rasa/core/actions/direct_custom_actions_executor.py +80 -0
  25. rasa/core/actions/e2e_stub_custom_action_executor.py +68 -0
  26. rasa/core/actions/grpc_custom_action_executor.py +2 -2
  27. rasa/core/actions/http_custom_action_executor.py +6 -5
  28. rasa/core/agent.py +21 -17
  29. rasa/core/channels/__init__.py +2 -0
  30. rasa/core/channels/audiocodes.py +1 -16
  31. rasa/core/channels/inspector/dist/index.html +0 -2
  32. rasa/core/channels/inspector/index.html +0 -2
  33. rasa/core/channels/voice_aware/__init__.py +0 -0
  34. rasa/core/channels/voice_aware/jambonz.py +103 -0
  35. rasa/core/channels/voice_aware/jambonz_protocol.py +344 -0
  36. rasa/core/channels/voice_aware/utils.py +20 -0
  37. rasa/core/channels/voice_native/__init__.py +0 -0
  38. rasa/core/constants.py +6 -1
  39. rasa/core/featurizers/single_state_featurizer.py +1 -22
  40. rasa/core/featurizers/tracker_featurizers.py +18 -115
  41. rasa/core/information_retrieval/faiss.py +7 -4
  42. rasa/core/information_retrieval/information_retrieval.py +8 -0
  43. rasa/core/information_retrieval/milvus.py +9 -2
  44. rasa/core/information_retrieval/qdrant.py +1 -1
  45. rasa/core/nlg/contextual_response_rephraser.py +32 -10
  46. rasa/core/nlg/summarize.py +4 -3
  47. rasa/core/policies/enterprise_search_policy.py +100 -44
  48. rasa/core/policies/flows/flow_executor.py +130 -94
  49. rasa/core/policies/intentless_policy.py +52 -28
  50. rasa/core/policies/ted_policy.py +33 -58
  51. rasa/core/policies/unexpected_intent_policy.py +7 -15
  52. rasa/core/processor.py +20 -53
  53. rasa/core/run.py +5 -4
  54. rasa/core/tracker_store.py +8 -4
  55. rasa/core/utils.py +45 -56
  56. rasa/dialogue_understanding/coexistence/llm_based_router.py +45 -12
  57. rasa/dialogue_understanding/commands/__init__.py +4 -0
  58. rasa/dialogue_understanding/commands/change_flow_command.py +0 -6
  59. rasa/dialogue_understanding/commands/session_start_command.py +59 -0
  60. rasa/dialogue_understanding/commands/set_slot_command.py +1 -5
  61. rasa/dialogue_understanding/commands/utils.py +38 -0
  62. rasa/dialogue_understanding/generator/constants.py +10 -3
  63. rasa/dialogue_understanding/generator/flow_retrieval.py +14 -5
  64. rasa/dialogue_understanding/generator/llm_based_command_generator.py +12 -2
  65. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +106 -87
  66. rasa/dialogue_understanding/generator/nlu_command_adapter.py +28 -6
  67. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +90 -37
  68. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +15 -15
  69. rasa/dialogue_understanding/patterns/session_start.py +37 -0
  70. rasa/dialogue_understanding/processor/command_processor.py +13 -14
  71. rasa/e2e_test/aggregate_test_stats_calculator.py +124 -0
  72. rasa/e2e_test/assertions.py +1181 -0
  73. rasa/e2e_test/assertions_schema.yml +106 -0
  74. rasa/e2e_test/constants.py +20 -0
  75. rasa/e2e_test/e2e_config.py +220 -0
  76. rasa/e2e_test/e2e_config_schema.yml +26 -0
  77. rasa/e2e_test/e2e_test_case.py +131 -8
  78. rasa/e2e_test/e2e_test_converter.py +363 -0
  79. rasa/e2e_test/e2e_test_converter_prompt.jinja2 +70 -0
  80. rasa/e2e_test/e2e_test_coverage_report.py +364 -0
  81. rasa/e2e_test/e2e_test_result.py +26 -6
  82. rasa/e2e_test/e2e_test_runner.py +491 -72
  83. rasa/e2e_test/e2e_test_schema.yml +96 -0
  84. rasa/e2e_test/pykwalify_extensions.py +39 -0
  85. rasa/e2e_test/stub_custom_action.py +70 -0
  86. rasa/e2e_test/utils/__init__.py +0 -0
  87. rasa/e2e_test/utils/e2e_yaml_utils.py +55 -0
  88. rasa/e2e_test/utils/io.py +596 -0
  89. rasa/e2e_test/utils/validation.py +80 -0
  90. rasa/engine/recipes/default_components.py +0 -2
  91. rasa/engine/storage/local_model_storage.py +0 -1
  92. rasa/env.py +9 -0
  93. rasa/keys +1 -0
  94. rasa/llm_fine_tuning/__init__.py +0 -0
  95. rasa/llm_fine_tuning/annotation_module.py +241 -0
  96. rasa/llm_fine_tuning/conversations.py +144 -0
  97. rasa/llm_fine_tuning/llm_data_preparation_module.py +178 -0
  98. rasa/llm_fine_tuning/notebooks/unsloth_finetuning.ipynb +407 -0
  99. rasa/llm_fine_tuning/paraphrasing/__init__.py +0 -0
  100. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +281 -0
  101. rasa/llm_fine_tuning/paraphrasing/default_rephrase_prompt_template.jina2 +44 -0
  102. rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +121 -0
  103. rasa/llm_fine_tuning/paraphrasing/rephrased_user_message.py +10 -0
  104. rasa/llm_fine_tuning/paraphrasing_module.py +128 -0
  105. rasa/llm_fine_tuning/storage.py +174 -0
  106. rasa/llm_fine_tuning/train_test_split_module.py +441 -0
  107. rasa/model_training.py +48 -16
  108. rasa/nlu/classifiers/diet_classifier.py +25 -38
  109. rasa/nlu/classifiers/logistic_regression_classifier.py +9 -44
  110. rasa/nlu/classifiers/sklearn_intent_classifier.py +16 -37
  111. rasa/nlu/extractors/crf_entity_extractor.py +50 -93
  112. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +45 -78
  113. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +17 -52
  114. rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +3 -5
  115. rasa/nlu/persistor.py +129 -32
  116. rasa/server.py +45 -10
  117. rasa/shared/constants.py +63 -15
  118. rasa/shared/core/domain.py +15 -12
  119. rasa/shared/core/events.py +28 -2
  120. rasa/shared/core/flows/flow.py +208 -13
  121. rasa/shared/core/flows/flow_path.py +84 -0
  122. rasa/shared/core/flows/flows_list.py +28 -10
  123. rasa/shared/core/flows/flows_yaml_schema.json +269 -193
  124. rasa/shared/core/flows/validation.py +112 -25
  125. rasa/shared/core/flows/yaml_flows_io.py +149 -10
  126. rasa/shared/core/trackers.py +6 -0
  127. rasa/shared/core/training_data/visualization.html +2 -2
  128. rasa/shared/exceptions.py +4 -0
  129. rasa/shared/importers/importer.py +60 -11
  130. rasa/shared/importers/remote_importer.py +196 -0
  131. rasa/shared/nlu/constants.py +2 -0
  132. rasa/shared/nlu/training_data/features.py +2 -120
  133. rasa/shared/providers/_configs/__init__.py +0 -0
  134. rasa/shared/providers/_configs/azure_openai_client_config.py +181 -0
  135. rasa/shared/providers/_configs/client_config.py +57 -0
  136. rasa/shared/providers/_configs/default_litellm_client_config.py +130 -0
  137. rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +234 -0
  138. rasa/shared/providers/_configs/openai_client_config.py +175 -0
  139. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +171 -0
  140. rasa/shared/providers/_configs/utils.py +101 -0
  141. rasa/shared/providers/_ssl_verification_utils.py +124 -0
  142. rasa/shared/providers/embedding/__init__.py +0 -0
  143. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +254 -0
  144. rasa/shared/providers/embedding/_langchain_embedding_client_adapter.py +74 -0
  145. rasa/shared/providers/embedding/azure_openai_embedding_client.py +277 -0
  146. rasa/shared/providers/embedding/default_litellm_embedding_client.py +102 -0
  147. rasa/shared/providers/embedding/embedding_client.py +90 -0
  148. rasa/shared/providers/embedding/embedding_response.py +41 -0
  149. rasa/shared/providers/embedding/huggingface_local_embedding_client.py +191 -0
  150. rasa/shared/providers/embedding/openai_embedding_client.py +172 -0
  151. rasa/shared/providers/llm/__init__.py +0 -0
  152. rasa/shared/providers/llm/_base_litellm_client.py +227 -0
  153. rasa/shared/providers/llm/azure_openai_llm_client.py +338 -0
  154. rasa/shared/providers/llm/default_litellm_llm_client.py +84 -0
  155. rasa/shared/providers/llm/llm_client.py +76 -0
  156. rasa/shared/providers/llm/llm_response.py +50 -0
  157. rasa/shared/providers/llm/openai_llm_client.py +155 -0
  158. rasa/shared/providers/llm/self_hosted_llm_client.py +169 -0
  159. rasa/shared/providers/mappings.py +75 -0
  160. rasa/shared/utils/cli.py +30 -0
  161. rasa/shared/utils/io.py +65 -3
  162. rasa/shared/utils/llm.py +223 -200
  163. rasa/shared/utils/yaml.py +122 -7
  164. rasa/studio/download.py +19 -13
  165. rasa/studio/train.py +2 -3
  166. rasa/studio/upload.py +2 -3
  167. rasa/telemetry.py +113 -58
  168. rasa/tracing/config.py +2 -3
  169. rasa/tracing/instrumentation/attribute_extractors.py +29 -17
  170. rasa/tracing/instrumentation/instrumentation.py +4 -47
  171. rasa/utils/common.py +18 -19
  172. rasa/utils/endpoints.py +7 -4
  173. rasa/utils/io.py +66 -0
  174. rasa/utils/json_utils.py +60 -0
  175. rasa/utils/licensing.py +9 -1
  176. rasa/utils/ml_utils.py +4 -2
  177. rasa/utils/tensorflow/model_data.py +193 -2
  178. rasa/validator.py +196 -1
  179. rasa/version.py +1 -1
  180. {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.4.dist-info}/METADATA +47 -72
  181. {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.4.dist-info}/RECORD +186 -121
  182. rasa/nlu/classifiers/llm_intent_classifier.py +0 -519
  183. rasa/shared/providers/openai/clients.py +0 -43
  184. rasa/shared/providers/openai/session_handler.py +0 -110
  185. rasa/utils/tensorflow/feature_array.py +0 -366
  186. /rasa/{shared/providers/openai → cli/project_templates/tutorial/actions}/__init__.py +0 -0
  187. /rasa/cli/project_templates/tutorial/{actions.py → actions/actions.py} +0 -0
  188. {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.4.dist-info}/NOTICE +0 -0
  189. {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.4.dist-info}/WHEEL +0 -0
  190. {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.4.dist-info}/entry_points.txt +0 -0
rasa/studio/download.py CHANGED
@@ -210,8 +210,8 @@ def _handle_download_no_overwrite(
210
210
  if domain_path.is_dir():
211
211
  studio_domain_path = domain_path / STUDIO_DOMAIN_FILENAME
212
212
  new_domain_data = data_handler.combine_domains(
213
- data_from_studio.get_domain().as_dict(),
214
- data_original.get_domain().as_dict(),
213
+ data_from_studio.get_user_domain().as_dict(),
214
+ data_original.get_user_domain().as_dict(),
215
215
  )
216
216
  studio_domain = Domain.from_dict(new_domain_data)
217
217
  if not studio_domain.is_empty():
@@ -219,7 +219,9 @@ def _handle_download_no_overwrite(
219
219
  else:
220
220
  logger.warning("No additional domain data found.")
221
221
  elif domain_path.is_file():
222
- domain_merged = data_original.get_domain().merge(data_from_studio.get_domain())
222
+ domain_merged = data_original.get_user_domain().merge(
223
+ data_from_studio.get_user_domain()
224
+ )
223
225
  domain_merged.persist(domain_path)
224
226
 
225
227
  if len(data_paths) == 1 and data_paths[0].is_file():
@@ -230,7 +232,9 @@ def _handle_download_no_overwrite(
230
232
  )
231
233
  data_nlu.persist_nlu(data_path)
232
234
  if handler.has_flows():
233
- data_flows = data_original.get_flows().merge(data_from_studio.get_flows())
235
+ data_flows = data_original.get_user_flows().merge(
236
+ data_from_studio.get_user_flows()
237
+ )
234
238
  YamlFlowsWriter.dump(data_flows.underlying_flows, data_path)
235
239
 
236
240
  elif len(data_paths) == 1 and data_paths[0].is_dir():
@@ -276,8 +280,8 @@ def _persist_flows_diff(
276
280
  ) -> None:
277
281
  """Creates a new flows file from the diff of original and studio data."""
278
282
  new_flows_data = data_handler.create_new_flows_from_diff(
279
- data_from_studio.get_flows().underlying_flows,
280
- data_original.get_flows().underlying_flows,
283
+ data_from_studio.get_user_flows().underlying_flows,
284
+ data_original.get_user_flows().underlying_flows,
281
285
  )
282
286
  if new_flows_data:
283
287
  YamlFlowsWriter.dump(new_flows_data, data_path)
@@ -317,11 +321,13 @@ def _handle_download_with_overwrite(
317
321
  )
318
322
 
319
323
  if domain_path.is_file():
320
- domain_merged = data_from_studio.get_domain().merge(data_original.get_domain())
324
+ domain_merged = data_from_studio.get_user_domain().merge(
325
+ data_original.get_user_domain()
326
+ )
321
327
  domain_merged.persist(domain_path)
322
328
  elif domain_path.is_dir():
323
329
  default = domain_path / Path(STUDIO_DOMAIN_FILENAME)
324
- studio_domain = data_from_studio.get_domain()
330
+ studio_domain = data_from_studio.get_user_domain()
325
331
 
326
332
  paths = get_domain_path(domain_path, data_from_studio, mapper)
327
333
 
@@ -358,8 +364,8 @@ def _handle_download_with_overwrite(
358
364
  )
359
365
  nlu_data_merged.persist_nlu(data_paths[0])
360
366
  if handler.has_flows():
361
- flows_data_merged = data_from_studio.get_flows().merge(
362
- data_original.get_flows()
367
+ flows_data_merged = data_from_studio.get_user_flows().merge(
368
+ data_original.get_user_flows()
363
369
  )
364
370
  YamlFlowsWriter.dump(flows_data_merged.underlying_flows, data_paths[0])
365
371
  elif len(data_paths) == 1 and data_paths[0].is_dir():
@@ -374,12 +380,12 @@ def _handle_download_with_overwrite(
374
380
  nlu_data = nlu_data.merge(nlu_file.get_nlu_data())
375
381
  pretty_write_nlu_yaml(read_yaml(nlu_data.nlu_as_yaml()), paths["nlu_path"])
376
382
  if handler.has_flows():
377
- flows_data = data_from_studio.get_flows()
383
+ flows_data = data_from_studio.get_user_flows()
378
384
  if paths["flow_path"].exists():
379
385
  flows_file = TrainingDataImporter.load_from_dict(
380
386
  training_data_paths=[str(paths["flow_path"])]
381
387
  )
382
- flows_data = flows_data.merge(flows_file.get_flows())
388
+ flows_data = flows_data.merge(flows_file.get_user_flows())
383
389
  YamlFlowsWriter.dump(flows_data.underlying_flows, paths["flow_path"])
384
390
  else:
385
391
  # TODO: we are not handling the case of multiple data paths?
@@ -407,7 +413,7 @@ def get_training_path(
407
413
  for intent in data_original.get_nlu_data().intents:
408
414
  for path in mapper.get_file(intent, "intents").get("training", []):
409
415
  nlu_paths.add(path)
410
- flows = [flow.id for flow in data_original.get_flows().underlying_flows]
416
+ flows = [flow.id for flow in data_original.get_user_flows().underlying_flows]
411
417
  for flow in flows:
412
418
  for path in mapper.get_file(flow, "flows").get("training", []):
413
419
  flow_paths.add(path)
rasa/studio/train.py CHANGED
@@ -20,19 +20,18 @@ from rasa.shared.core.flows.yaml_flows_io import YamlFlowsWriter
20
20
  from rasa.shared.importers.importer import TrainingDataImporter
21
21
  from rasa.shared.utils.yaml import read_yaml, write_yaml
22
22
  from rasa.studio import data_handler
23
- from rasa.utils.common import get_temp_dir_name
24
-
25
23
  from rasa.studio.config import StudioConfig
26
24
  from rasa.studio.data_handler import (
27
25
  StudioDataHandler,
28
26
  import_data_from_studio,
29
27
  )
28
+ from rasa.utils.common import get_temp_dir_name
30
29
 
31
30
  logger = logging.getLogger(__name__)
32
31
 
33
32
 
34
33
  def handle_train(args: argparse.Namespace) -> Optional[str]:
35
- from rasa import train as train_all
34
+ from rasa.api import train as train_all
36
35
 
37
36
  handler = StudioDataHandler(
38
37
  studio_config=StudioConfig.read_config(), assistant_name=args.assistant_name[0]
rasa/studio/upload.py CHANGED
@@ -151,7 +151,7 @@ def upload_calm_assistant(args: argparse.Namespace, endpoint: str) -> StudioResu
151
151
 
152
152
  # Prepare config and domain
153
153
  config = importer.get_config()
154
- domain_from_files = importer.get_domain().as_dict()
154
+ domain_from_files = importer.get_user_domain().as_dict()
155
155
  endpoints_from_files = read_yaml_file(args.endpoints)
156
156
  config_from_files = read_yaml_file(args.config)
157
157
 
@@ -188,8 +188,7 @@ def upload_calm_assistant(args: argparse.Namespace, endpoint: str) -> StudioResu
188
188
  training_data_paths=training_data_paths
189
189
  )
190
190
 
191
- user_flows = flow_importer.get_flows().user_flows
192
- flows = list(user_flows)
191
+ flows = list(flow_importer.get_user_flows())
193
192
 
194
193
  # We instantiate the TrainingDataImporter again on purpose to avoid
195
194
  # adding patterns to domain's actions. More info https://t.ly/W8uuc
rasa/telemetry.py CHANGED
@@ -122,8 +122,6 @@ TELEMETRY_INTENTLESS_POLICY_TRAINING_COMPLETED_EVENT = (
122
122
  "Intentless Policy Training Completed"
123
123
  )
124
124
  TELEMETRY_INTENTLESS_POLICY_PREDICT_EVENT = "Intentless Policy Predicted"
125
- TELEMETRY_LLM_INTENT_PREDICT_EVENT = "LLM Intent Predicted"
126
- TELEMETRY_LLM_INTENT_TRAIN_COMPLETED_EVENT = "LLM Intent Training Completed"
127
125
  TELEMETRY_E2E_TEST_RUN_STARTED_EVENT = "E2E Test Run Started"
128
126
  TELEMETRY_ENTERPRISE_SEARCH_POLICY_TRAINING_STARTED_EVENT = (
129
127
  "Enterprise Search Policy Training Started"
@@ -172,6 +170,11 @@ TRACING_BACKEND = "tracing_backend"
172
170
  METRICS_BACKEND = "metrics_backend"
173
171
  VERSION = "version"
174
172
 
173
+ # E2E test conversion
174
+ TELEMETRY_E2E_TEST_CONVERSION_EVENT = "E2E Test Conversion Completed"
175
+ E2E_TEST_CONVERSION_FILE_TYPE = "file_type"
176
+ E2E_TEST_CONVERSION_TEST_CASE_COUNT = "test_case_count"
177
+
175
178
 
176
179
  def print_telemetry_reporting_info() -> None:
177
180
  """Print telemetry information to std out."""
@@ -1078,6 +1081,7 @@ def _get_llm_command_generator_config(config: Dict[str, Any]) -> Optional[Dict]:
1078
1081
  retrieval is enabled, and flow retrieval embedding model.
1079
1082
  """
1080
1083
  from rasa.shared.constants import (
1084
+ EMBEDDINGS_CONFIG_KEY,
1081
1085
  MODEL_CONFIG_KEY,
1082
1086
  MODEL_NAME_CONFIG_KEY,
1083
1087
  )
@@ -1116,13 +1120,17 @@ def _get_llm_command_generator_config(config: Dict[str, Any]) -> Optional[Dict]:
1116
1120
  llm_model_name = (
1117
1121
  llm_config.get(MODEL_CONFIG_KEY)
1118
1122
  or llm_config.get(MODEL_NAME_CONFIG_KEY)
1119
- or DEFAULT_LLM_CONFIG[MODEL_NAME_CONFIG_KEY]
1123
+ or DEFAULT_LLM_CONFIG[MODEL_CONFIG_KEY]
1120
1124
  )
1121
1125
  flow_retrieval_config = component.get(FLOW_RETRIEVAL_KEY, {})
1122
1126
  flow_retrieval_enabled = flow_retrieval_config.get("active", True)
1127
+ flow_retrieval_embeddings_config = flow_retrieval_config.get(
1128
+ EMBEDDINGS_CONFIG_KEY, DEFAULT_EMBEDDINGS_CONFIG
1129
+ )
1123
1130
  flow_retrieval_embedding_model_name = (
1124
- flow_retrieval_config.get("embeddings", DEFAULT_EMBEDDINGS_CONFIG).get(
1125
- "model"
1131
+ (
1132
+ flow_retrieval_embeddings_config.get(MODEL_NAME_CONFIG_KEY)
1133
+ or flow_retrieval_embeddings_config.get(MODEL_CONFIG_KEY)
1126
1134
  )
1127
1135
  if flow_retrieval_enabled
1128
1136
  else None
@@ -1439,6 +1447,76 @@ def track_markers_parsed_count(
1439
1447
  )
1440
1448
 
1441
1449
 
1450
+ def extract_assertion_type_counts(
1451
+ input_test_cases: List["TestCase"],
1452
+ ) -> typing.Tuple[bool, Dict[str, Any]]:
1453
+ """Extracts the total count of different assertion types from the test cases."""
1454
+ from rasa.e2e_test.assertions import AssertionType
1455
+
1456
+ uses_assertions = False
1457
+
1458
+ flow_started_count = 0
1459
+ flow_completed_count = 0
1460
+ flow_cancelled_count = 0
1461
+ pattern_clarification_contains_count = 0
1462
+ action_executed_count = 0
1463
+ slot_was_set_count = 0
1464
+ slot_was_not_set_count = 0
1465
+ bot_uttered_count = 0
1466
+ generative_response_is_relevant_count = 0
1467
+ generative_response_is_grounded_count = 0
1468
+
1469
+ for test_case in input_test_cases:
1470
+ for step in test_case.steps:
1471
+ assertions = step.assertions if step.assertions else []
1472
+ for assertion in assertions:
1473
+ if assertion.type == AssertionType.ACTION_EXECUTED.value:
1474
+ action_executed_count += 1
1475
+ elif assertion.type == AssertionType.SLOT_WAS_SET.value:
1476
+ slot_was_set_count += 1
1477
+ elif assertion.type == AssertionType.SLOT_WAS_NOT_SET.value:
1478
+ slot_was_not_set_count += 1
1479
+ elif assertion.type == AssertionType.BOT_UTTERED.value:
1480
+ bot_uttered_count += 1
1481
+ elif (
1482
+ assertion.type
1483
+ == AssertionType.GENERATIVE_RESPONSE_IS_RELEVANT.value
1484
+ ):
1485
+ generative_response_is_relevant_count += 1
1486
+ elif (
1487
+ assertion.type
1488
+ == AssertionType.GENERATIVE_RESPONSE_IS_GROUNDED.value
1489
+ ):
1490
+ generative_response_is_grounded_count += 1
1491
+ elif assertion.type == AssertionType.FLOW_STARTED.value:
1492
+ flow_started_count += 1
1493
+ elif assertion.type == AssertionType.FLOW_COMPLETED.value:
1494
+ flow_completed_count += 1
1495
+ elif assertion.type == AssertionType.FLOW_CANCELLED.value:
1496
+ flow_cancelled_count += 1
1497
+ elif (
1498
+ assertion.type == AssertionType.PATTERN_CLARIFICATION_CONTAINS.value
1499
+ ):
1500
+ pattern_clarification_contains_count += 1
1501
+
1502
+ uses_assertions = True
1503
+
1504
+ result = {
1505
+ "flow_started_count": flow_started_count,
1506
+ "flow_completed_count": flow_completed_count,
1507
+ "flow_cancelled_count": flow_cancelled_count,
1508
+ "pattern_clarification_contains_count": pattern_clarification_contains_count,
1509
+ "action_executed_count": action_executed_count,
1510
+ "slot_was_set_count": slot_was_set_count,
1511
+ "slot_was_not_set_count": slot_was_not_set_count,
1512
+ "bot_uttered_count": bot_uttered_count,
1513
+ "generative_response_is_relevant_count": generative_response_is_relevant_count,
1514
+ "generative_response_is_grounded_count": generative_response_is_grounded_count,
1515
+ }
1516
+
1517
+ return uses_assertions, result
1518
+
1519
+
1442
1520
  @ensure_telemetry_enabled
1443
1521
  def track_e2e_test_run(
1444
1522
  input_test_cases: List["TestCase"],
@@ -1446,15 +1524,26 @@ def track_e2e_test_run(
1446
1524
  input_metadata: List["Metadata"],
1447
1525
  ) -> None:
1448
1526
  """Track an end-to-end test run."""
1527
+ properties = {
1528
+ "number_of_test_cases": len(input_test_cases),
1529
+ "number_of_fixtures": len(input_fixtures),
1530
+ "uses_fixtures": len(input_fixtures) > 0,
1531
+ "uses_metadata": len(input_metadata) > 0,
1532
+ "number_of_metadata": len(input_metadata),
1533
+ }
1534
+
1535
+ uses_assertions, assertion_type_counts = extract_assertion_type_counts(
1536
+ input_test_cases
1537
+ )
1538
+
1539
+ properties.update({"uses_assertions": uses_assertions})
1540
+
1541
+ if uses_assertions:
1542
+ properties.update(assertion_type_counts)
1543
+
1449
1544
  _track(
1450
1545
  TELEMETRY_E2E_TEST_RUN_STARTED_EVENT,
1451
- {
1452
- "number_of_test_cases": len(input_test_cases),
1453
- "number_of_fixtures": len(input_fixtures),
1454
- "uses_fixtures": len(input_fixtures) > 0,
1455
- "uses_metadata": len(input_metadata) > 0,
1456
- "number_of_metadata": len(input_metadata),
1457
- },
1546
+ properties,
1458
1547
  )
1459
1548
 
1460
1549
 
@@ -1523,52 +1612,6 @@ def track_intentless_policy_predict(
1523
1612
  )
1524
1613
 
1525
1614
 
1526
- @ensure_telemetry_enabled
1527
- def track_llm_intent_predict(
1528
- embeddings_type: Optional[str],
1529
- embeddings_model: Optional[str],
1530
- llm_type: Optional[str],
1531
- llm_model: Optional[str],
1532
- ) -> None:
1533
- """Track when a user predicts an intent using the llm intent classifier."""
1534
- _track(
1535
- TELEMETRY_LLM_INTENT_PREDICT_EVENT,
1536
- {
1537
- "embeddings_type": embeddings_type,
1538
- "embeddings_model": embeddings_model,
1539
- "llm_type": llm_type,
1540
- "llm_model": llm_model,
1541
- },
1542
- )
1543
-
1544
-
1545
- @ensure_telemetry_enabled
1546
- def track_llm_intent_train_completed(
1547
- embeddings_type: Optional[str],
1548
- embeddings_model: Optional[str],
1549
- llm_type: Optional[str],
1550
- llm_model: Optional[str],
1551
- fallback_intent: Optional[str],
1552
- custom_prompt_template: Optional[str],
1553
- number_of_examples: int,
1554
- number_of_available_intents: int,
1555
- ) -> None:
1556
- """Track when a user trains the llm intent classifier."""
1557
- _track(
1558
- TELEMETRY_LLM_INTENT_TRAIN_COMPLETED_EVENT,
1559
- {
1560
- "embeddings_type": embeddings_type,
1561
- "embeddings_model": embeddings_model,
1562
- "llm_type": llm_type,
1563
- "llm_model": llm_model,
1564
- "fallback_intent": fallback_intent,
1565
- "custom_prompt_template": custom_prompt_template,
1566
- "number_of_examples": number_of_examples,
1567
- "number_of_available_intents": number_of_available_intents,
1568
- },
1569
- )
1570
-
1571
-
1572
1615
  @ensure_telemetry_enabled
1573
1616
  def identify_endpoint_config_traits(
1574
1617
  endpoints_file: Optional[Text],
@@ -1735,3 +1778,15 @@ def track_conversation_count(conversation_count: int, tracked_month: datetime) -
1735
1778
  "month": tracked_month.month,
1736
1779
  },
1737
1780
  )
1781
+
1782
+
1783
+ @ensure_telemetry_enabled
1784
+ def track_e2e_test_conversion_completed(file_type: str, test_case_count: int) -> None:
1785
+ """Track the used input file type for E2E test conversion."""
1786
+ _track(
1787
+ TELEMETRY_E2E_TEST_CONVERSION_EVENT,
1788
+ {
1789
+ E2E_TEST_CONVERSION_FILE_TYPE: file_type,
1790
+ E2E_TEST_CONVERSION_TEST_CASE_COUNT: test_case_count,
1791
+ },
1792
+ )
rasa/tracing/config.py CHANGED
@@ -20,7 +20,6 @@ from rasa.core.actions.custom_action_executor import (
20
20
  CustomActionExecutor,
21
21
  RetryCustomActionExecutor,
22
22
  )
23
- from rasa.core.actions.grpc_custom_action_executor import GRPCCustomActionExecutor
24
23
  from rasa.core.agent import Agent
25
24
  from rasa.core.processor import MessageProcessor
26
25
  from rasa.core.tracker_store import TrackerStore
@@ -45,10 +44,11 @@ from rasa.tracing.constants import (
45
44
  ENDPOINTS_ROOT_CERTIFICATES_KEY,
46
45
  ENDPOINTS_METRICS_KEY,
47
46
  )
48
- from rasa.tracing.instrumentation import instrumentation
49
47
  from rasa.tracing.metric_instrument_provider import MetricInstrumentProvider
50
48
  from rasa.utils.endpoints import EndpointConfig, read_endpoint_config
51
49
 
50
+ from rasa.tracing.instrumentation import instrumentation
51
+
52
52
  TRACING_SERVICE_NAME = os.environ.get("TRACING_SERVICE_NAME", "rasa")
53
53
 
54
54
  logger = logging.getLogger(__name__)
@@ -107,7 +107,6 @@ def configure_tracing(tracer_provider: Optional[TracerProvider]) -> None:
107
107
  vector_store_subclasses=vector_store_subclasses,
108
108
  nlu_command_adapter_class=NLUCommandAdapter,
109
109
  endpoint_config_class=EndpointConfig,
110
- grpc_custom_action_executor_class=GRPCCustomActionExecutor,
111
110
  single_step_llm_command_generator_class=SingleStepLLMCommandGenerator,
112
111
  multi_step_llm_command_generator_class=MultiStepLLMCommandGenerator,
113
112
  custom_action_executor_subclasses=custom_action_executor_subclasses,
@@ -7,6 +7,7 @@ import tiktoken
7
7
  from numpy import ndarray
8
8
  from rasa_sdk.grpc_py import action_webhook_pb2
9
9
 
10
+ from rasa.core.actions.action import DirectCustomActionExecutor
10
11
  from rasa.core.actions.grpc_custom_action_executor import GRPCCustomActionExecutor
11
12
  from rasa.core.actions.http_custom_action_executor import HTTPCustomActionExecutor
12
13
  from rasa.core.agent import Agent
@@ -20,6 +21,13 @@ from rasa.dialogue_understanding.commands import Command
20
21
  from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack
21
22
  from rasa.engine.graph import ExecutionContext, GraphModelConfiguration, GraphNode
22
23
  from rasa.engine.training.graph_trainer import GraphTrainer
24
+ from rasa.shared.constants import (
25
+ EMBEDDINGS_CONFIG_KEY,
26
+ MODEL_CONFIG_KEY,
27
+ PROVIDER_CONFIG_KEY,
28
+ TIMEOUT_CONFIG_KEY,
29
+ DEPLOYMENT_CONFIG_KEY,
30
+ )
23
31
  from rasa.shared.core.constants import REQUESTED_SLOT
24
32
  from rasa.shared.core.domain import Domain
25
33
  from rasa.shared.core.events import DialogueStackUpdated, Event
@@ -27,7 +35,9 @@ from rasa.shared.core.flows import Flow, FlowsList, FlowStep
27
35
  from rasa.shared.core.trackers import DialogueStateTracker
28
36
  from rasa.shared.importers.importer import TrainingDataImporter
29
37
  from rasa.shared.nlu.constants import INTENT_NAME_KEY, SET_SLOT_COMMAND
30
- from rasa.shared.utils.llm import combine_custom_and_default_config
38
+ from rasa.shared.utils.llm import (
39
+ combine_custom_and_default_config,
40
+ )
31
41
  from rasa.tracing.constants import (
32
42
  PROMPT_TOKEN_LENGTH_ATTRIBUTE_NAME,
33
43
  REQUEST_BODY_SIZE_IN_BYTES_ATTRIBUTE_NAME,
@@ -249,10 +259,7 @@ def extract_attrs_for_graph_trainer(
249
259
  }
250
260
 
251
261
 
252
- def extract_headers(
253
- message: UserMessage,
254
- **kwargs: Any,
255
- ) -> Any:
262
+ def extract_headers(message: UserMessage, **kwargs: Any) -> Any:
256
263
  """Extract the headers from the `UserMessage`."""
257
264
  if message.headers:
258
265
  return message.headers
@@ -304,18 +311,15 @@ def extract_llm_config(self: Any, default_llm_config: Dict[str, Any]) -> Dict[st
304
311
 
305
312
  attributes = {
306
313
  "class_name": self.__class__.__name__,
307
- "llm_model": str(config.get("model", llm_property.get("model_name"))),
308
- "llm_type": str(llm_property.get("_type")),
309
- "embeddings": json.dumps(config.get("embeddings", {})),
314
+ "llm_model": str(llm_property.get(MODEL_CONFIG_KEY)),
315
+ "llm_type": str(llm_property.get(PROVIDER_CONFIG_KEY)),
316
+ "embeddings": json.dumps(config.get(EMBEDDINGS_CONFIG_KEY, {})),
310
317
  "llm_temperature": str(llm_property.get("temperature")),
311
- "request_timeout": str(llm_property.get("request_timeout")),
318
+ "request_timeout": str(llm_property.get(TIMEOUT_CONFIG_KEY)),
312
319
  }
313
320
 
314
- if "model" in llm_property:
315
- attributes["llm_model"] = str(llm_property.get("model"))
316
-
317
- if "engine" in llm_property:
318
- attributes["llm_engine"] = str(llm_property.get("engine"))
321
+ if DEPLOYMENT_CONFIG_KEY in llm_property:
322
+ attributes["llm_engine"] = str(llm_property.get(DEPLOYMENT_CONFIG_KEY))
319
323
 
320
324
  return attributes
321
325
 
@@ -643,16 +647,24 @@ def extend_attributes_with_prompt_tokens_length(
643
647
 
644
648
 
645
649
  def extract_attrs_for_custom_action_executor_run(
646
- self: Union[HTTPCustomActionExecutor, GRPCCustomActionExecutor],
650
+ self: Union[
651
+ HTTPCustomActionExecutor, GRPCCustomActionExecutor, DirectCustomActionExecutor
652
+ ],
647
653
  tracker: DialogueStateTracker,
648
654
  domain: Domain,
649
655
  include_domain: bool = False,
650
656
  ) -> Dict[str, Any]:
657
+ actions_module, url = None, None
658
+ if hasattr(self, "action_endpoint"):
659
+ url = self.action_endpoint.url
660
+ actions_module = self.action_endpoint.actions_module
661
+
651
662
  attrs: Dict[str, Any] = {
652
663
  "class_name": self.__class__.__name__,
653
- "action_name": self.action_name if hasattr(self, "action_name") else "None",
664
+ "action_name": self.action_name,
654
665
  "sender_id": tracker.sender_id,
655
- "url": self.action_endpoint.url if hasattr(self, "action_endpoint") else "None",
666
+ "url": str(url),
667
+ "actions_module": str(actions_module),
656
668
  }
657
669
  return attrs
658
670
 
@@ -20,10 +20,10 @@ from typing import (
20
20
 
21
21
  from multidict import MultiDict
22
22
  from opentelemetry.context import Context
23
+
23
24
  from opentelemetry.sdk.trace import TracerProvider
24
25
  from opentelemetry.trace import SpanKind, Tracer
25
26
  from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
26
-
27
27
  from rasa.core.actions.action import Action, RemoteAction, CustomActionExecutor
28
28
  from rasa.core.actions.custom_action_executor import RetryCustomActionExecutor
29
29
  from rasa.core.actions.grpc_custom_action_executor import GRPCCustomActionExecutor
@@ -48,13 +48,11 @@ from rasa.dialogue_understanding.generator import (
48
48
  from rasa.dialogue_understanding.generator.nlu_command_adapter import NLUCommandAdapter
49
49
  from rasa.engine.graph import GraphNode
50
50
  from rasa.engine.training.graph_trainer import GraphTrainer
51
- from rasa.shared.core.domain import Domain
52
51
  from rasa.shared.core.flows import FlowsList
53
52
  from rasa.shared.core.trackers import DialogueStateTracker
54
53
  from rasa.shared.nlu.constants import SET_SLOT_COMMAND
55
54
  from rasa.shared.nlu.training_data.message import Message
56
55
  from rasa.tracing.constants import REQUEST_BODY_SIZE_IN_BYTES_ATTRIBUTE_NAME
57
- from rasa.tracing.instrumentation import attribute_extractors
58
56
  from rasa.tracing.instrumentation.intentless_policy_instrumentation import (
59
57
  _instrument_extract_ai_responses,
60
58
  _instrument_generate_answer,
@@ -70,6 +68,9 @@ from rasa.tracing.instrumentation.metrics import (
70
68
  )
71
69
  from rasa.utils.endpoints import concat_url, EndpointConfig
72
70
 
71
+ from rasa.tracing.instrumentation import attribute_extractors
72
+
73
+
73
74
  # The `TypeVar` representing the return type for a function to be wrapped.
74
75
  S = TypeVar("S")
75
76
  # The `TypeVar` representing the type of the argument passed to the function to be
@@ -307,7 +308,6 @@ def instrument(
307
308
  vector_store_subclasses: Optional[List[Type[InformationRetrievalType]]] = None,
308
309
  nlu_command_adapter_class: Optional[Type[NLUCommandAdapterType]] = None,
309
310
  endpoint_config_class: Optional[Type[EndpointConfigType]] = None,
310
- grpc_custom_action_executor_class: Optional[Type[GRPCCustomActionExecutor]] = None,
311
311
  single_step_llm_command_generator_class: Optional[
312
312
  Type[SingleStepLLMCommandGeneratorType]
313
313
  ] = None,
@@ -354,9 +354,6 @@ def instrument(
354
354
  `None` is given, no `NLUCommandAdapter` will be instrumented.
355
355
  :param endpoint_config_class: The `EndpointConfig` to be instrumented. If
356
356
  `None` is given, no `EndpointConfig` will be instrumented.
357
- :param grpc_custom_action_executor_class: The `GRPCCustomActionExecution` to be
358
- instrumented. If `None` is given, no `GRPCCustomActionExecution`
359
- will be instrumented.
360
357
  :param single_step_llm_command_generator_class: The `SingleStepLLMCommandGenerator`
361
358
  to be instrumented. If `None` is given, no `SingleStepLLMCommandGenerator` will
362
359
  be instrumented.
@@ -584,14 +581,6 @@ def instrument(
584
581
  endpoint_config_class,
585
582
  )
586
583
 
587
- if grpc_custom_action_executor_class is not None and not class_is_instrumented(
588
- grpc_custom_action_executor_class
589
- ):
590
- _instrument_grpc_custom_action_executor(
591
- tracer_provider.get_tracer(grpc_custom_action_executor_class.__module__),
592
- grpc_custom_action_executor_class,
593
- )
594
-
595
584
  if custom_action_executor_subclasses:
596
585
  for custom_action_executor_subclass in custom_action_executor_subclasses:
597
586
  if (
@@ -1107,38 +1096,6 @@ def _instrument_endpoint_config(
1107
1096
  logger.debug(f"Instrumented '{endpoint_config_class.__name__}.request'.")
1108
1097
 
1109
1098
 
1110
- def _instrument_grpc_custom_action_executor(
1111
- tracer: Tracer, grpc_custom_action_executor_class: Type[GRPCCustomActionExecutor]
1112
- ) -> None:
1113
- """Instrument the `run` method of the `GRPCCustomActionExecutor` class.
1114
-
1115
- Args:
1116
- tracer: The `Tracer` that shall be used for tracing.
1117
- grpc_custom_action_executor_class: The `GRPCCustomActionExecutor` to
1118
- be instrumented.
1119
- """
1120
-
1121
- def tracing_grpc_custom_action_executor_wrapper(fn: Callable) -> Callable:
1122
- @functools.wraps(fn)
1123
- async def wrapper(
1124
- self: Type[GRPCCustomActionExecutor],
1125
- tracker: Type[DialogueStateTracker],
1126
- domain: Type[Domain],
1127
- include_domain: bool = False,
1128
- ) -> bool:
1129
- TraceContextTextMapPropagator().inject(self.action_endpoint.headers)
1130
- result = await fn(self, tracker, domain, include_domain)
1131
- return result
1132
-
1133
- return wrapper
1134
-
1135
- grpc_custom_action_executor_class.run = tracing_grpc_custom_action_executor_wrapper( # type: ignore[assignment]
1136
- grpc_custom_action_executor_class.run
1137
- )
1138
-
1139
- logger.debug(f"Instrumented '{grpc_custom_action_executor_class.__name__}.run.")
1140
-
1141
-
1142
1099
  def _mangled_instrumented_boolean_attribute_name(instrumented_class: Type) -> Text:
1143
1100
  # see https://peps.python.org/pep-0008/#method-names-and-instance-variables
1144
1101
  # and https://stackoverflow.com/a/50401073