rasa-pro 3.13.0.dev7__py3-none-any.whl → 3.13.0.dev8__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 (150) hide show
  1. rasa/__main__.py +0 -3
  2. rasa/api.py +1 -1
  3. rasa/cli/dialogue_understanding_test.py +1 -1
  4. rasa/cli/e2e_test.py +1 -1
  5. rasa/cli/evaluate.py +1 -1
  6. rasa/cli/export.py +1 -1
  7. rasa/cli/llm_fine_tuning.py +12 -11
  8. rasa/cli/project_templates/defaults.py +133 -0
  9. rasa/cli/run.py +1 -1
  10. rasa/cli/studio/link.py +53 -0
  11. rasa/cli/studio/pull.py +78 -0
  12. rasa/cli/studio/push.py +78 -0
  13. rasa/cli/studio/studio.py +12 -0
  14. rasa/cli/studio/upload.py +8 -0
  15. rasa/cli/train.py +1 -1
  16. rasa/cli/utils.py +1 -1
  17. rasa/cli/x.py +1 -1
  18. rasa/constants.py +2 -0
  19. rasa/core/__init__.py +0 -16
  20. rasa/core/actions/action.py +5 -1
  21. rasa/core/actions/action_repeat_bot_messages.py +18 -22
  22. rasa/core/actions/action_run_slot_rejections.py +0 -1
  23. rasa/core/agent.py +16 -1
  24. rasa/core/available_endpoints.py +146 -0
  25. rasa/core/brokers/pika.py +1 -2
  26. rasa/core/channels/botframework.py +2 -2
  27. rasa/core/channels/channel.py +2 -2
  28. rasa/core/channels/hangouts.py +8 -5
  29. rasa/core/channels/mattermost.py +1 -1
  30. rasa/core/channels/rasa_chat.py +2 -4
  31. rasa/core/channels/rest.py +5 -4
  32. rasa/core/channels/studio_chat.py +3 -2
  33. rasa/core/channels/vier_cvg.py +1 -2
  34. rasa/core/channels/voice_ready/audiocodes.py +1 -8
  35. rasa/core/channels/voice_stream/audiocodes.py +7 -4
  36. rasa/core/channels/voice_stream/genesys.py +2 -2
  37. rasa/core/channels/voice_stream/twilio_media_streams.py +10 -5
  38. rasa/core/channels/voice_stream/voice_channel.py +33 -22
  39. rasa/core/http_interpreter.py +3 -7
  40. rasa/core/jobs.py +2 -1
  41. rasa/core/nlg/contextual_response_rephraser.py +34 -9
  42. rasa/core/nlg/generator.py +0 -1
  43. rasa/core/nlg/interpolator.py +2 -3
  44. rasa/core/nlg/summarize.py +39 -5
  45. rasa/core/policies/enterprise_search_policy.py +283 -62
  46. rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +63 -0
  47. rasa/core/policies/flow_policy.py +1 -1
  48. rasa/core/policies/flows/flow_executor.py +96 -17
  49. rasa/core/policies/intentless_policy.py +9 -7
  50. rasa/core/processor.py +104 -51
  51. rasa/core/run.py +33 -11
  52. rasa/core/tracker_stores/tracker_store.py +1 -1
  53. rasa/core/training/interactive.py +1 -1
  54. rasa/core/utils.py +24 -97
  55. rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
  56. rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
  57. rasa/dialogue_understanding/commands/cancel_flow_command.py +2 -0
  58. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
  59. rasa/dialogue_understanding/commands/clarify_command.py +5 -1
  60. rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
  61. rasa/dialogue_understanding/commands/human_handoff_command.py +2 -0
  62. rasa/dialogue_understanding/commands/knowledge_answer_command.py +4 -2
  63. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
  64. rasa/dialogue_understanding/commands/set_slot_command.py +11 -1
  65. rasa/dialogue_understanding/commands/skip_question_command.py +2 -0
  66. rasa/dialogue_understanding/commands/start_flow_command.py +4 -0
  67. rasa/dialogue_understanding/commands/utils.py +26 -2
  68. rasa/dialogue_understanding/generator/__init__.py +7 -1
  69. rasa/dialogue_understanding/generator/command_generator.py +4 -2
  70. rasa/dialogue_understanding/generator/command_parser.py +2 -2
  71. rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
  72. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +12 -33
  73. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +78 -0
  74. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +26 -461
  75. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
  76. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +477 -0
  77. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +8 -58
  78. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +37 -25
  79. rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
  80. rasa/dialogue_understanding/processor/command_processor.py +3 -3
  81. rasa/dialogue_understanding/processor/command_processor_component.py +3 -3
  82. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +17 -4
  83. rasa/dialogue_understanding/utils.py +68 -12
  84. rasa/dialogue_understanding_test/du_test_case.py +1 -1
  85. rasa/dialogue_understanding_test/du_test_runner.py +4 -22
  86. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -6
  87. rasa/e2e_test/e2e_test_runner.py +1 -1
  88. rasa/engine/constants.py +1 -1
  89. rasa/engine/recipes/default_recipe.py +26 -2
  90. rasa/engine/validation.py +3 -2
  91. rasa/hooks.py +0 -28
  92. rasa/llm_fine_tuning/annotation_module.py +39 -9
  93. rasa/llm_fine_tuning/conversations.py +3 -0
  94. rasa/llm_fine_tuning/llm_data_preparation_module.py +66 -49
  95. rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +52 -44
  96. rasa/llm_fine_tuning/paraphrasing_module.py +10 -12
  97. rasa/llm_fine_tuning/storage.py +4 -4
  98. rasa/llm_fine_tuning/utils.py +63 -1
  99. rasa/model_manager/model_api.py +88 -0
  100. rasa/model_manager/trainer_service.py +4 -4
  101. rasa/plugin.py +1 -11
  102. rasa/privacy/__init__.py +0 -0
  103. rasa/privacy/constants.py +83 -0
  104. rasa/privacy/event_broker_utils.py +77 -0
  105. rasa/privacy/privacy_config.py +281 -0
  106. rasa/privacy/privacy_config_schema.json +86 -0
  107. rasa/privacy/privacy_filter.py +340 -0
  108. rasa/privacy/privacy_manager.py +576 -0
  109. rasa/server.py +23 -2
  110. rasa/shared/constants.py +3 -0
  111. rasa/shared/core/constants.py +4 -3
  112. rasa/shared/core/domain.py +7 -0
  113. rasa/shared/core/events.py +37 -7
  114. rasa/shared/core/flows/flow.py +1 -2
  115. rasa/shared/core/flows/flows_yaml_schema.json +3 -0
  116. rasa/shared/core/flows/steps/collect.py +46 -2
  117. rasa/shared/core/slots.py +28 -0
  118. rasa/shared/exceptions.py +4 -0
  119. rasa/shared/utils/llm.py +161 -6
  120. rasa/shared/utils/yaml.py +32 -0
  121. rasa/studio/data_handler.py +3 -3
  122. rasa/studio/download/download.py +37 -60
  123. rasa/studio/download/flows.py +23 -31
  124. rasa/studio/link.py +200 -0
  125. rasa/studio/pull.py +94 -0
  126. rasa/studio/push.py +131 -0
  127. rasa/studio/upload.py +117 -67
  128. rasa/telemetry.py +82 -25
  129. rasa/tracing/config.py +3 -4
  130. rasa/tracing/constants.py +19 -1
  131. rasa/tracing/instrumentation/attribute_extractors.py +10 -2
  132. rasa/tracing/instrumentation/instrumentation.py +53 -2
  133. rasa/tracing/instrumentation/metrics.py +98 -15
  134. rasa/tracing/metric_instrument_provider.py +75 -3
  135. rasa/utils/common.py +1 -27
  136. rasa/utils/log_utils.py +1 -45
  137. rasa/validator.py +2 -8
  138. rasa/version.py +1 -1
  139. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev8.dist-info}/METADATA +5 -6
  140. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev8.dist-info}/RECORD +143 -129
  141. rasa/anonymization/__init__.py +0 -2
  142. rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
  143. rasa/anonymization/anonymization_pipeline.py +0 -286
  144. rasa/anonymization/anonymization_rule_executor.py +0 -266
  145. rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
  146. rasa/anonymization/schemas/config.yml +0 -47
  147. rasa/anonymization/utils.py +0 -118
  148. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev8.dist-info}/NOTICE +0 -0
  149. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev8.dist-info}/WHEEL +0 -0
  150. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev8.dist-info}/entry_points.txt +0 -0
@@ -1,7 +1,8 @@
1
+ import dataclasses
1
2
  import importlib.resources
2
3
  import json
3
4
  import re
4
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Text
5
+ from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Text
5
6
 
6
7
  import dotenv
7
8
  import structlog
@@ -9,6 +10,7 @@ from jinja2 import Template
9
10
  from pydantic import ValidationError
10
11
 
11
12
  import rasa.shared.utils.io
13
+ from rasa.core.available_endpoints import AvailableEndpoints
12
14
  from rasa.core.constants import (
13
15
  POLICY_MAX_HISTORY,
14
16
  POLICY_PRIORITY,
@@ -23,7 +25,6 @@ from rasa.core.information_retrieval import (
23
25
  )
24
26
  from rasa.core.information_retrieval.faiss import FAISS_Store
25
27
  from rasa.core.policies.policy import Policy, PolicyPrediction
26
- from rasa.core.utils import AvailableEndpoints
27
28
  from rasa.dialogue_understanding.generator.constants import (
28
29
  LLM_CONFIG_KEY,
29
30
  )
@@ -53,7 +54,9 @@ from rasa.shared.constants import (
53
54
  MODEL_NAME_CONFIG_KEY,
54
55
  OPENAI_PROVIDER,
55
56
  PROMPT_CONFIG_KEY,
57
+ PROMPT_TEMPLATE_CONFIG_KEY,
56
58
  PROVIDER_CONFIG_KEY,
59
+ RASA_PATTERN_CANNOT_HANDLE_NO_RELEVANT_ANSWER,
57
60
  TEMPERATURE_CONFIG_KEY,
58
61
  TIMEOUT_CONFIG_KEY,
59
62
  )
@@ -78,7 +81,6 @@ from rasa.shared.nlu.training_data.training_data import TrainingData
78
81
  from rasa.shared.providers.embedding._langchain_embedding_client_adapter import (
79
82
  _LangchainEmbeddingClientAdapter,
80
83
  )
81
- from rasa.shared.providers.llm.llm_client import LLMClient
82
84
  from rasa.shared.providers.llm.llm_response import LLMResponse, measure_llm_latency
83
85
  from rasa.shared.utils.cli import print_error_and_exit
84
86
  from rasa.shared.utils.constants import (
@@ -113,7 +115,7 @@ if TYPE_CHECKING:
113
115
 
114
116
  from rasa.utils.log_utils import log_llm
115
117
 
116
- logger = structlog.get_logger()
118
+ structlogger = structlog.get_logger()
117
119
 
118
120
  dotenv.load_dotenv("./.env")
119
121
 
@@ -124,6 +126,7 @@ VECTOR_STORE_THRESHOLD_PROPERTY = "threshold"
124
126
  TRACE_TOKENS_PROPERTY = "trace_prompt_tokens"
125
127
  CITATION_ENABLED_PROPERTY = "citation_enabled"
126
128
  USE_LLM_PROPERTY = "use_generative_llm"
129
+ CHECK_RELEVANCY_PROPERTY = "check_relevancy"
127
130
  MAX_MESSAGES_IN_QUERY_KEY = "max_messages_in_query"
128
131
 
129
132
  DEFAULT_VECTOR_STORE_TYPE = "faiss"
@@ -134,6 +137,10 @@ DEFAULT_VECTOR_STORE = {
134
137
  VECTOR_STORE_THRESHOLD_PROPERTY: DEFAULT_VECTOR_STORE_THRESHOLD,
135
138
  }
136
139
 
140
+ DEFAULT_CHECK_RELEVANCY_PROPERTY = False
141
+ DEFAULT_USE_LLM_PROPERTY = True
142
+ DEFAULT_CITATION_ENABLED_PROPERTY = False
143
+
137
144
  DEFAULT_LLM_CONFIG = {
138
145
  PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
139
146
  MODEL_CONFIG_KEY: DEFAULT_OPENAI_CHAT_MODEL_NAME,
@@ -162,6 +169,18 @@ DEFAULT_ENTERPRISE_SEARCH_PROMPT_WITH_CITATION_TEMPLATE = importlib.resources.re
162
169
  "rasa.core.policies", "enterprise_search_prompt_with_citation_template.jinja2"
163
170
  )
164
171
 
172
+ DEFAULT_ENTERPRISE_SEARCH_PROMPT_WITH_RELEVANCY_CHECK_AND_CITATION_TEMPLATE = (
173
+ importlib.resources.read_text(
174
+ "rasa.core.policies",
175
+ "enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2",
176
+ )
177
+ )
178
+
179
+ # TODO: Update this pattern once the experiments are done
180
+ _ENTERPRISE_SEARCH_ANSWER_NOT_RELEVANT_PATTERN = re.compile(
181
+ r"\[NO_RELEVANT_ANSWER_FOUND\]"
182
+ )
183
+
165
184
 
166
185
  class VectorStoreConnectionError(RasaException):
167
186
  """Exception raised for errors in connecting to the vector store."""
@@ -171,6 +190,12 @@ class VectorStoreConfigurationError(RasaException):
171
190
  """Exception raised for errors in vector store configuration."""
172
191
 
173
192
 
193
+ @dataclasses.dataclass
194
+ class _RelevancyCheckResponse:
195
+ answer: Optional[str]
196
+ relevant: bool
197
+
198
+
174
199
  @DefaultV1Recipe.register(
175
200
  DefaultV1Recipe.ComponentType.POLICY_WITH_END_TO_END_SUPPORT, is_trainable=True
176
201
  )
@@ -220,6 +245,11 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
220
245
  """Constructs a new Policy object."""
221
246
  super().__init__(config, model_storage, resource, execution_context, featurizer)
222
247
 
248
+ # Check for deprecated keys and issue a warning if those are used
249
+ self._check_config_keys_and_warn_if_deprecated()
250
+ # Check for mutual exclusivity of extractive and generative search
251
+ self._check_and_warn_mutual_exclusivity_of_extractive_and_generative_search()
252
+
223
253
  # Resolve LLM config
224
254
  self.config[LLM_CONFIG_KEY] = resolve_model_client_config(
225
255
  self.config.get(LLM_CONFIG_KEY), EnterpriseSearchPolicy.__name__
@@ -234,6 +264,9 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
234
264
  self.vector_store_config = self.config.get(
235
265
  VECTOR_STORE_PROPERTY, DEFAULT_VECTOR_STORE
236
266
  )
267
+ self.vector_search_threshold = self.vector_store_config.get(
268
+ VECTOR_STORE_THRESHOLD_PROPERTY, DEFAULT_VECTOR_STORE_THRESHOLD
269
+ )
237
270
 
238
271
  # Embeddings configuration for encoding the search query
239
272
  self.embeddings_config = (
@@ -249,30 +282,77 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
249
282
  # Maximum number of messages to include in the search query
250
283
  self.max_messages_in_query = self.config.get(MAX_MESSAGES_IN_QUERY_KEY, 2)
251
284
 
252
- # boolean to enable/disable tracing of prompt tokens
285
+ # Boolean to enable/disable tracing of prompt tokens
253
286
  self.trace_prompt_tokens = self.config.get(TRACE_TOKENS_PROPERTY, False)
254
287
 
255
- # boolean to enable/disable the use of LLM for response generation
256
- self.use_llm = self.config.get(USE_LLM_PROPERTY, True)
288
+ # Boolean to enable/disable the use of LLM for response generation
289
+ self.use_llm = self.config.get(USE_LLM_PROPERTY, DEFAULT_USE_LLM_PROPERTY)
257
290
 
258
- # boolean to enable/disable citation generation
259
- self.citation_enabled = self.config.get(CITATION_ENABLED_PROPERTY, False)
291
+ # Boolean to enable/disable citation generation. This flag enables citation
292
+ # logic, but it only takes effect if `use_llm` is True.
293
+ self.citation_enabled = self.config.get(
294
+ CITATION_ENABLED_PROPERTY, DEFAULT_CITATION_ENABLED_PROPERTY
295
+ )
260
296
 
261
- self.prompt_template = prompt_template or get_prompt_template(
262
- self.config.get(PROMPT_CONFIG_KEY),
263
- DEFAULT_ENTERPRISE_SEARCH_PROMPT_TEMPLATE,
264
- log_source_component=EnterpriseSearchPolicy.__name__,
265
- log_source_method=LOG_COMPONENT_SOURCE_METHOD_INIT,
297
+ # Boolean to enable/disable the use of relevancy check alongside answer
298
+ # generation. This flag enables citation logic, but it only takes effect if
299
+ # `use_llm` is True.
300
+ self.relevancy_check_enabled = self.config.get(
301
+ CHECK_RELEVANCY_PROPERTY, DEFAULT_CHECK_RELEVANCY_PROPERTY
266
302
  )
267
- self.citation_prompt_template = get_prompt_template(
268
- self.config.get(PROMPT_CONFIG_KEY),
269
- DEFAULT_ENTERPRISE_SEARCH_PROMPT_WITH_CITATION_TEMPLATE,
270
- log_source_component=EnterpriseSearchPolicy.__name__,
271
- log_source_method=LOG_COMPONENT_SOURCE_METHOD_INIT,
303
+
304
+ # Resolve the prompt template. The prompt will only be used if the 'use_llm' is
305
+ # set to True.
306
+ self.prompt_template = prompt_template or self._resolve_prompt_template(
307
+ self.config, LOG_COMPONENT_SOURCE_METHOD_INIT
272
308
  )
273
- # If citation is enabled, use the citation prompt template
274
- if self.citation_enabled:
275
- self.prompt_template = self.citation_prompt_template
309
+
310
+ def _check_config_keys_and_warn_if_deprecated(self) -> None:
311
+ """Checks and warns about deprecated config parameters."""
312
+ if (
313
+ PROMPT_CONFIG_KEY in self.config
314
+ and PROMPT_TEMPLATE_CONFIG_KEY in self.config
315
+ ):
316
+ structlogger.warning(
317
+ "enterprise_search_policy.init"
318
+ ".both_deprecated_and_non_deprecated_config_keys_used_at_the_same_time",
319
+ event_info=(
320
+ f"Both '{PROMPT_CONFIG_KEY}' and '{PROMPT_TEMPLATE_CONFIG_KEY}' "
321
+ f"are present in the config. '{PROMPT_CONFIG_KEY}' will be ignored "
322
+ f"in favor of {PROMPT_TEMPLATE_CONFIG_KEY}."
323
+ ),
324
+ )
325
+
326
+ # 'prompt' config key is deprecated in favor of 'prompt_template'
327
+ if PROMPT_CONFIG_KEY in self.config:
328
+ structlogger.warning(
329
+ "enterprise_search_policy.init.deprecated_config_key",
330
+ event_info=(
331
+ f"The config parameter '{PROMPT_CONFIG_KEY}' is deprecated "
332
+ "and will be removed in Rasa 4.0.0. "
333
+ f"Please use the config parameter '{PROMPT_TEMPLATE_CONFIG_KEY}'"
334
+ f"instead. "
335
+ ),
336
+ )
337
+
338
+ def _check_and_warn_mutual_exclusivity_of_extractive_and_generative_search(
339
+ self,
340
+ ) -> None:
341
+ if self.config.get(
342
+ CHECK_RELEVANCY_PROPERTY, DEFAULT_CHECK_RELEVANCY_PROPERTY
343
+ ) and not self.config.get(USE_LLM_PROPERTY, DEFAULT_USE_LLM_PROPERTY):
344
+ structlogger.warning(
345
+ "enterprise_search_policy.init"
346
+ ".relevancy_check_enabled_with_disabled_generative_search",
347
+ event_info=(
348
+ f"The config parameter '{CHECK_RELEVANCY_PROPERTY}' is set to"
349
+ f"'True', but the generative search is disabled (config"
350
+ f"parameter '{USE_LLM_PROPERTY}' is set to 'False'). As a result, "
351
+ "the relevancy check for the generative search will be disabled. "
352
+ f"To use this check, set the config parameter '{USE_LLM_PROPERTY}' "
353
+ f"to `True`."
354
+ ),
355
+ )
276
356
 
277
357
  @classmethod
278
358
  def _create_plain_embedder(cls, config: Dict[Text, Any]) -> "Embeddings":
@@ -366,7 +446,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
366
446
  try:
367
447
  embeddings = self._create_plain_embedder(self.config)
368
448
  except (ValidationError, Exception) as e:
369
- logger.error(
449
+ structlogger.error(
370
450
  "enterprise_search_policy.train.embedder_instantiation_failed",
371
451
  message="Unable to instantiate the embedding client.",
372
452
  error=e,
@@ -377,7 +457,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
377
457
  )
378
458
 
379
459
  if store_type == DEFAULT_VECTOR_STORE_TYPE:
380
- logger.info("enterprise_search_policy.train.faiss")
460
+ structlogger.info("enterprise_search_policy.train.faiss")
381
461
  with self._model_storage.write_to(self._resource) as path:
382
462
  self.vector_store = FAISS_Store(
383
463
  docs_folder=self.vector_store_config.get(SOURCE_PROPERTY),
@@ -386,7 +466,9 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
386
466
  create_index=True,
387
467
  )
388
468
  else:
389
- logger.info("enterprise_search_policy.train.custom", store_type=store_type)
469
+ structlogger.info(
470
+ "enterprise_search_policy.train.custom", store_type=store_type
471
+ )
390
472
 
391
473
  # telemetry call to track training completion
392
474
  track_enterprise_search_policy_train_completed(
@@ -402,6 +484,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
402
484
  or self.llm_config.get(MODEL_NAME_CONFIG_KEY),
403
485
  llm_model_group_id=self.llm_config.get(MODEL_GROUP_ID_CONFIG_KEY),
404
486
  citation_enabled=self.citation_enabled,
487
+ relevancy_check_enabled=self.relevancy_check_enabled,
405
488
  )
406
489
  self.persist()
407
490
  return self._resource
@@ -454,7 +537,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
454
537
  config = endpoints.vector_store if endpoints else None
455
538
  store_type = self.vector_store_config.get(VECTOR_STORE_TYPE_PROPERTY)
456
539
  if config is None and store_type != DEFAULT_VECTOR_STORE_TYPE:
457
- logger.error(
540
+ structlogger.error(
458
541
  "enterprise_search_policy._connect_vector_store_or_raise.no_config"
459
542
  )
460
543
  raise VectorStoreConfigurationError(
@@ -464,7 +547,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
464
547
  try:
465
548
  self.vector_store.connect(config) # type: ignore
466
549
  except Exception as e:
467
- logger.error(
550
+ structlogger.error(
468
551
  "enterprise_search_policy._connect_vector_store_or_raise.connect_error",
469
552
  error=e,
470
553
  config=config,
@@ -490,14 +573,14 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
490
573
  transcript.append(sanitize_message_for_prompt(event.text))
491
574
 
492
575
  search_query = " ".join(transcript[-history:][::-1])
493
- logger.debug("search_query", search_query=search_query)
576
+ structlogger.debug("search_query", search_query=search_query)
494
577
  return search_query
495
578
 
496
579
  async def predict_action_probabilities( # type: ignore[override]
497
580
  self,
498
581
  tracker: DialogueStateTracker,
499
582
  domain: Domain,
500
- endpoints: Optional[AvailableEndpoints],
583
+ endpoints: Optional[AvailableEndpoints] = None,
501
584
  rule_only_data: Optional[Dict[Text, Any]] = None,
502
585
  **kwargs: Any,
503
586
  ) -> PolicyPrediction:
@@ -516,23 +599,20 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
516
599
  The prediction.
517
600
  """
518
601
  logger_key = "enterprise_search_policy.predict_action_probabilities"
519
- vector_search_threshold = self.vector_store_config.get(
520
- VECTOR_STORE_THRESHOLD_PROPERTY, DEFAULT_VECTOR_STORE_THRESHOLD
521
- )
522
- llm = llm_factory(self.config.get(LLM_CONFIG_KEY), DEFAULT_LLM_CONFIG)
602
+
523
603
  if not self.supports_current_stack_frame(
524
604
  tracker, False, False
525
605
  ) or self.should_abstain_in_coexistence(tracker, True):
526
606
  return self._prediction(self._default_predictions(domain))
527
607
 
528
608
  if not self.vector_store:
529
- logger.error(f"{logger_key}.no_vector_store")
609
+ structlogger.error(f"{logger_key}.no_vector_store")
530
610
  return self._create_prediction_internal_error(domain, tracker)
531
611
 
532
612
  try:
533
613
  self._connect_vector_store_or_raise(endpoints)
534
614
  except (VectorStoreConfigurationError, VectorStoreConnectionError) as e:
535
- logger.error(f"{logger_key}.connection_error", error=e)
615
+ structlogger.error(f"{logger_key}.connection_error", error=e)
536
616
  return self._create_prediction_internal_error(domain, tracker)
537
617
 
538
618
  search_query = self._prepare_search_query(
@@ -544,20 +624,19 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
544
624
  documents = await self.vector_store.search(
545
625
  query=search_query,
546
626
  tracker_state=tracker_state,
547
- threshold=vector_search_threshold,
627
+ threshold=self.vector_search_threshold,
548
628
  )
549
629
  except InformationRetrievalException as e:
550
- logger.error(f"{logger_key}.search_error", error=e)
630
+ structlogger.error(f"{logger_key}.search_error", error=e)
551
631
  return self._create_prediction_internal_error(domain, tracker)
552
632
 
553
633
  if not documents.results:
554
- logger.info(f"{logger_key}.no_documents")
634
+ structlogger.info(f"{logger_key}.no_documents")
555
635
  return self._create_prediction_cannot_handle(domain, tracker)
556
636
 
557
637
  if self.use_llm:
558
638
  prompt = self._render_prompt(tracker, documents.results)
559
- llm_response = await self._generate_llm_answer(llm, prompt)
560
- llm_response = LLMResponse.ensure_llm_response(llm_response)
639
+ llm_response = await self._invoke_llm(prompt)
561
640
 
562
641
  self._add_prompt_and_llm_response_to_latest_message(
563
642
  tracker=tracker,
@@ -567,24 +646,38 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
567
646
  )
568
647
 
569
648
  if llm_response is None or not llm_response.choices:
570
- logger.debug(f"{logger_key}.no_llm_response")
649
+ structlogger.debug(f"{logger_key}.no_llm_response")
571
650
  response = None
572
651
  else:
573
652
  llm_answer = llm_response.choices[0]
574
653
 
654
+ if self.relevancy_check_enabled:
655
+ relevancy_response = self._parse_llm_relevancy_check_response(
656
+ llm_answer
657
+ )
658
+ if not relevancy_response.relevant:
659
+ structlogger.debug(f"{logger_key}.answer_not_relevant")
660
+ return self._create_prediction_cannot_handle(
661
+ domain,
662
+ tracker,
663
+ RASA_PATTERN_CANNOT_HANDLE_NO_RELEVANT_ANSWER,
664
+ )
665
+
575
666
  if self.citation_enabled:
576
667
  llm_answer = self.post_process_citations(llm_answer)
577
668
 
578
- logger.debug(f"{logger_key}.llm_answer", llm_answer=llm_answer)
669
+ structlogger.debug(
670
+ f"{logger_key}.llm_answer", prompt=prompt, llm_answer=llm_answer
671
+ )
579
672
  response = llm_answer
580
673
  else:
581
674
  response = documents.results[0].metadata.get("answer", None)
582
675
  if not response:
583
- logger.error(
676
+ structlogger.error(
584
677
  f"{logger_key}.answer_key_missing_in_metadata",
585
678
  search_results=documents.results,
586
679
  )
587
- logger.debug(
680
+ structlogger.debug(
588
681
  "enterprise_search_policy.predict_action_probabilities.no_llm",
589
682
  search_results=documents,
590
683
  )
@@ -616,6 +709,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
616
709
  or self.llm_config.get(MODEL_NAME_CONFIG_KEY),
617
710
  llm_model_group_id=self.llm_config.get(MODEL_GROUP_ID_CONFIG_KEY),
618
711
  citation_enabled=self.citation_enabled,
712
+ relevancy_check_enabled=self.relevancy_check_enabled,
619
713
  )
620
714
  return self._create_prediction(
621
715
  domain=domain, tracker=tracker, action_metadata=action_metadata
@@ -639,11 +733,12 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
639
733
  ),
640
734
  "docs": documents,
641
735
  "slots": self._prepare_slots_for_template(tracker),
736
+ "check_relevancy": self.relevancy_check_enabled,
642
737
  "citation_enabled": self.citation_enabled,
643
738
  }
644
739
  prompt = Template(self.prompt_template).render(**inputs)
645
740
  log_llm(
646
- logger=logger,
741
+ logger=structlogger,
647
742
  log_module="EnterpriseSearchPolicy",
648
743
  log_event="enterprise_search_policy._render_prompt.prompt_rendered",
649
744
  prompt=prompt,
@@ -651,9 +746,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
651
746
  return prompt
652
747
 
653
748
  @measure_llm_latency
654
- async def _generate_llm_answer(
655
- self, llm: LLMClient, prompt: Text
656
- ) -> Optional[LLMResponse]:
749
+ async def _invoke_llm(self, prompt: Text) -> Optional[LLMResponse]:
657
750
  """Fetches an LLM completion for the provided prompt.
658
751
 
659
752
  Args:
@@ -663,17 +756,32 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
663
756
  Returns:
664
757
  An LLMResponse object, or None if the call fails.
665
758
  """
759
+ llm = llm_factory(self.config.get(LLM_CONFIG_KEY), DEFAULT_LLM_CONFIG)
666
760
  try:
667
- return await llm.acompletion(prompt)
761
+ response = await llm.acompletion(prompt)
762
+ return LLMResponse.ensure_llm_response(response)
668
763
  except Exception as e:
669
764
  # unfortunately, langchain does not wrap LLM exceptions which means
670
765
  # we have to catch all exceptions here
671
- logger.error(
766
+ structlogger.error(
672
767
  "enterprise_search_policy._generate_llm_answer.llm_error",
673
768
  error=e,
674
769
  )
675
770
  return None
676
771
 
772
+ def _parse_llm_relevancy_check_response(
773
+ self, llm_answer: str
774
+ ) -> _RelevancyCheckResponse:
775
+ """Checks if the LLM response is relevant by parsing it."""
776
+ answer_relevant = not _ENTERPRISE_SEARCH_ANSWER_NOT_RELEVANT_PATTERN.search(
777
+ llm_answer
778
+ )
779
+ structlogger.debug("")
780
+ return _RelevancyCheckResponse(
781
+ answer=llm_answer if answer_relevant else None,
782
+ relevant=answer_relevant,
783
+ )
784
+
677
785
  def _create_prediction(
678
786
  self,
679
787
  domain: Domain,
@@ -708,10 +816,18 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
708
816
  )
709
817
 
710
818
  def _create_prediction_cannot_handle(
711
- self, domain: Domain, tracker: DialogueStateTracker
819
+ self,
820
+ domain: Domain,
821
+ tracker: DialogueStateTracker,
822
+ reason: Optional[str] = None,
712
823
  ) -> PolicyPrediction:
824
+ cannot_handle_stack_frame = (
825
+ CannotHandlePatternFlowStackFrame(reason=reason)
826
+ if reason is not None
827
+ else CannotHandlePatternFlowStackFrame()
828
+ )
713
829
  return self._create_prediction_for_pattern(
714
- domain, tracker, CannotHandlePatternFlowStackFrame()
830
+ domain, tracker, cannot_handle_stack_frame
715
831
  )
716
832
 
717
833
  def _create_prediction_for_pattern(
@@ -780,7 +896,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
780
896
  path / ENTERPRISE_SEARCH_PROMPT_FILE_NAME
781
897
  )
782
898
  except (FileNotFoundError, FileIOException) as e:
783
- logger.warning(
899
+ structlogger.warning(
784
900
  "enterprise_search_policy.load.failed", error=e, resource=resource.name
785
901
  )
786
902
 
@@ -790,7 +906,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
790
906
 
791
907
  embeddings = cls._create_plain_embedder(config)
792
908
 
793
- logger.info("enterprise_search_policy.load", config=config)
909
+ structlogger.info("enterprise_search_policy.load", config=config)
794
910
  if store_type == DEFAULT_VECTOR_STORE_TYPE:
795
911
  # if a vector store is not specified,
796
912
  # default to using FAISS with the index stored in the model
@@ -849,15 +965,12 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
849
965
  @classmethod
850
966
  def fingerprint_addon(cls, config: Dict[str, Any]) -> Optional[str]:
851
967
  """Add a fingerprint of enterprise search policy for the graph."""
852
- local_knowledge_data = cls._get_local_knowledge_data(config)
853
-
854
- prompt_template = get_prompt_template(
855
- config.get(PROMPT_CONFIG_KEY),
856
- DEFAULT_ENTERPRISE_SEARCH_PROMPT_TEMPLATE,
857
- log_source_component=EnterpriseSearchPolicy.__name__,
858
- log_source_method=LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON,
968
+ prompt_template = cls._resolve_prompt_template(
969
+ config, LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON
859
970
  )
860
971
 
972
+ local_knowledge_data = cls._get_local_knowledge_data(config)
973
+
861
974
  llm_config = resolve_model_client_config(
862
975
  config.get(LLM_CONFIG_KEY), EnterpriseSearchPolicy.__name__
863
976
  )
@@ -881,7 +994,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
881
994
  Returns:
882
995
  The post-processed LLM answer.
883
996
  """
884
- logger.debug(
997
+ structlogger.debug(
885
998
  "enterprise_search_policy.post_process_citations", llm_answer=llm_answer
886
999
  )
887
1000
 
@@ -982,3 +1095,111 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
982
1095
  log_source_method,
983
1096
  EnterpriseSearchPolicy.__name__,
984
1097
  )
1098
+
1099
+ @classmethod
1100
+ def get_system_default_prompt_based_on_config(cls, config: Dict[str, Any]) -> str:
1101
+ """
1102
+ Resolves the default prompt template for Enterprise Search Policy based on
1103
+ the component's configuration.
1104
+
1105
+ - The old prompt is selected when both citation and relevancy check are either
1106
+ disabled or not set in the configuration.
1107
+ - The citation prompt is used when citation is enabled and relevancy check is
1108
+ either disabled or not set in the configuration.
1109
+ - The relevancy check prompt is only used when relevancy check is enabled.
1110
+
1111
+ Args:
1112
+ config: The component's configuration.
1113
+
1114
+ Returns:
1115
+ The resolved jinja prompt template as a string.
1116
+ """
1117
+
1118
+ # Get the feature flags
1119
+ citation_enabled = config.get(
1120
+ CITATION_ENABLED_PROPERTY, DEFAULT_CITATION_ENABLED_PROPERTY
1121
+ )
1122
+ relevancy_check_enabled = config.get(
1123
+ CHECK_RELEVANCY_PROPERTY, DEFAULT_CHECK_RELEVANCY_PROPERTY
1124
+ )
1125
+
1126
+ # Based on the enabled features (citation, relevancy check) fetch the
1127
+ # appropriate default prompt
1128
+ default_prompt = cls._select_default_prompt_template_based_on_features(
1129
+ relevancy_check_enabled, citation_enabled
1130
+ )
1131
+
1132
+ return default_prompt
1133
+
1134
+ @classmethod
1135
+ def _resolve_prompt_template(
1136
+ cls,
1137
+ config: dict,
1138
+ log_source_method: Literal["init", "fingerprint"],
1139
+ ) -> str:
1140
+ """
1141
+ Resolves the prompt template to use for the Enterprise Search Policy's
1142
+ generative search.
1143
+
1144
+ Checks if a custom template is provided via component's configuration. If not,
1145
+ it selects the appropriate default template based on the enabled features
1146
+ (citation and relevancy check).
1147
+
1148
+ Args:
1149
+ config: The component's configuration.
1150
+ log_source_method: The name of the method or function emitting the log for
1151
+ better traceability.
1152
+ Returns:
1153
+ The resolved jinja prompt template as a string.
1154
+ """
1155
+
1156
+ # Read the template path from the configuration if available.
1157
+ # The deprecated 'prompt' has a lower priority compared to 'prompt_template'
1158
+ config_defined_prompt = (
1159
+ config.get(PROMPT_TEMPLATE_CONFIG_KEY)
1160
+ or config.get(PROMPT_CONFIG_KEY)
1161
+ or None
1162
+ )
1163
+ # Select the default prompt based on the features set in the config.
1164
+ default_prompt = cls.get_system_default_prompt_based_on_config(config)
1165
+
1166
+ return get_prompt_template(
1167
+ config_defined_prompt,
1168
+ default_prompt,
1169
+ log_source_component=EnterpriseSearchPolicy.__name__,
1170
+ log_source_method=log_source_method,
1171
+ )
1172
+
1173
+ @classmethod
1174
+ def _select_default_prompt_template_based_on_features(
1175
+ cls,
1176
+ relevancy_check_enabled: bool,
1177
+ citation_enabled: bool,
1178
+ ) -> str:
1179
+ """
1180
+ Returns the appropriate default prompt template based on the feature flags.
1181
+
1182
+ The selection follows this priority:
1183
+ 1. If relevancy check is enabled, return the prompt that includes both relevancy
1184
+ and citation blocks.
1185
+ 2. If only citation is enabled, return the prompt with citation blocks.
1186
+ 3. Otherwise, fall back to the legacy default prompt template.
1187
+
1188
+ Args:
1189
+ relevancy_check_enabled: Whether the LLM-generated answer should undergo
1190
+ relevancy evaluation.
1191
+ citation_enabled: Whether citations should be included in the generated
1192
+ answer.
1193
+
1194
+ Returns:
1195
+ The default prompt template corresponding to the enabled features.
1196
+ """
1197
+ if relevancy_check_enabled:
1198
+ # ES prompt that has relevancy check and citations blocks
1199
+ return DEFAULT_ENTERPRISE_SEARCH_PROMPT_WITH_RELEVANCY_CHECK_AND_CITATION_TEMPLATE # noqa: E501
1200
+ elif citation_enabled:
1201
+ # ES prompt with citation's block - backward compatibility
1202
+ return DEFAULT_ENTERPRISE_SEARCH_PROMPT_WITH_CITATION_TEMPLATE
1203
+ else:
1204
+ # Legacy ES prompt - backward compatibility
1205
+ return DEFAULT_ENTERPRISE_SEARCH_PROMPT_TEMPLATE