rasa-pro 3.13.0.dev20250612__py3-none-any.whl → 3.13.0rc1__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 (252) 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 -8
  5. rasa/cli/evaluate.py +1 -1
  6. rasa/cli/export.py +3 -1
  7. rasa/cli/llm_fine_tuning.py +12 -11
  8. rasa/cli/project_templates/defaults.py +133 -0
  9. rasa/cli/project_templates/tutorial/config.yml +1 -1
  10. rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
  11. rasa/cli/run.py +1 -1
  12. rasa/cli/studio/download.py +1 -23
  13. rasa/cli/studio/link.py +52 -0
  14. rasa/cli/studio/pull.py +79 -0
  15. rasa/cli/studio/push.py +78 -0
  16. rasa/cli/studio/studio.py +12 -0
  17. rasa/cli/studio/train.py +0 -1
  18. rasa/cli/studio/upload.py +8 -0
  19. rasa/cli/train.py +1 -1
  20. rasa/cli/utils.py +1 -1
  21. rasa/cli/x.py +1 -1
  22. rasa/constants.py +2 -0
  23. rasa/core/__init__.py +0 -16
  24. rasa/core/actions/action.py +5 -1
  25. rasa/core/actions/action_repeat_bot_messages.py +18 -22
  26. rasa/core/actions/action_run_slot_rejections.py +0 -1
  27. rasa/core/agent.py +16 -1
  28. rasa/core/available_endpoints.py +146 -0
  29. rasa/core/brokers/pika.py +1 -2
  30. rasa/core/channels/__init__.py +2 -0
  31. rasa/core/channels/botframework.py +2 -2
  32. rasa/core/channels/channel.py +2 -2
  33. rasa/core/channels/development_inspector.py +1 -1
  34. rasa/core/channels/facebook.py +1 -4
  35. rasa/core/channels/hangouts.py +8 -5
  36. rasa/core/channels/inspector/README.md +3 -3
  37. rasa/core/channels/inspector/dist/assets/{arc-c4b064fc.js → arc-371401b1.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-215b5026.js → blockDiagram-38ab4fdb-3f126156.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-2b54a0a3.js → c4Diagram-3d4e48cf-12f22eb7.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/channel-f1efda17.js +1 -0
  41. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-daacea5f.js → classDiagram-70f12bd4-03b1d386.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-930d4dc2.js → classDiagram-v2-f2320105-84f69d63.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/clone-fdf164e2.js +1 -0
  44. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-83c206ba.js → createText-2e5e7dd3-ca47fd38.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-b0eb01d0.js → edges-e0da2a9e-f837ca8a.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-17586500.js → erDiagram-9861fffd-8717ac54.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-be2a1776.js → flowDb-956e92f1-94f38b83.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-c2120ebd.js → flowDiagram-66a62f08-b616f9fb.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-7d7a1629.js +1 -0
  50. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-a6ab5c48.js → flowchart-elk-definition-4a651766-f5d24bb8.js} +1 -1
  51. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-ef613457.js → ganttDiagram-c361ad54-b43ba8d9.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-d59185b3.js → gitGraphDiagram-72cf32ee-c3aafaa5.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{graph-0f155405.js → graph-0d0a2c10.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{index-3862675e-d5f1d1b7.js → index-3862675e-58ea0305.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{index-47737d3a.js → index-cce6f8a1.js} +3 -3
  56. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-b07d141f.js → infoDiagram-f8f76790-b8f60461.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-1936d429.js → journeyDiagram-49397b02-95be5545.js} +1 -1
  58. rasa/core/channels/inspector/dist/assets/{layout-dde8d0f3.js → layout-da885b9b.js} +1 -1
  59. rasa/core/channels/inspector/dist/assets/{line-0c2c7ee0.js → line-f1c817d3.js} +1 -1
  60. rasa/core/channels/inspector/dist/assets/{linear-35dd89a4.js → linear-d42801e6.js} +1 -1
  61. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-56192851.js → mindmap-definition-fc14e90a-a38923a6.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-fc21ed78.js → pieDiagram-8a3498a8-ca6e71e9.js} +1 -1
  63. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-25e98518.js → quadrantDiagram-120e2f19-b290dae9.js} +1 -1
  64. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-546ff1f5.js → requirementDiagram-deff3bca-03f02ceb.js} +1 -1
  65. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-02d8b82d.js → sankeyDiagram-04a897e0-c49eee40.js} +1 -1
  66. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-3ca5a92e.js → sequenceDiagram-704730f1-b2cd6a3d.js} +1 -1
  67. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-128ea07c.js → stateDiagram-587899a1-e53a2028.js} +1 -1
  68. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-95f290af.js → stateDiagram-v2-d93cdb3a-e1982a03.js} +1 -1
  69. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-4984898a.js → styles-6aaf32cf-d0226ca5.js} +1 -1
  70. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-1bf266ba.js → styles-9a916d00-0e21dc00.js} +1 -1
  71. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-60521c63.js → styles-c10674c1-9588494e.js} +1 -1
  72. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-a25b6e12.js → svgDrawCommon-08f97a94-be478d4f.js} +1 -1
  73. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-0fc086bf.js → timeline-definition-85554ec2-74631749.js} +1 -1
  74. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-44ee592e.js → xychartDiagram-e933f94c-a043552f.js} +1 -1
  75. rasa/core/channels/inspector/dist/index.html +1 -1
  76. rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +1 -1
  77. rasa/core/channels/mattermost.py +1 -1
  78. rasa/core/channels/rasa_chat.py +2 -4
  79. rasa/core/channels/rest.py +5 -4
  80. rasa/core/channels/socketio.py +56 -41
  81. rasa/core/channels/studio_chat.py +314 -10
  82. rasa/core/channels/vier_cvg.py +1 -2
  83. rasa/core/channels/voice_ready/audiocodes.py +2 -9
  84. rasa/core/channels/voice_stream/asr/azure.py +9 -0
  85. rasa/core/channels/voice_stream/audiocodes.py +8 -5
  86. rasa/core/channels/voice_stream/browser_audio.py +1 -1
  87. rasa/core/channels/voice_stream/genesys.py +2 -2
  88. rasa/core/channels/voice_stream/jambonz.py +166 -0
  89. rasa/core/channels/voice_stream/tts/__init__.py +8 -0
  90. rasa/core/channels/voice_stream/twilio_media_streams.py +17 -5
  91. rasa/core/channels/voice_stream/voice_channel.py +44 -24
  92. rasa/core/exporter.py +36 -0
  93. rasa/core/http_interpreter.py +3 -7
  94. rasa/core/information_retrieval/faiss.py +18 -11
  95. rasa/core/information_retrieval/ingestion/faq_parser.py +158 -0
  96. rasa/core/jobs.py +2 -1
  97. rasa/core/nlg/contextual_response_rephraser.py +48 -12
  98. rasa/core/nlg/generator.py +0 -1
  99. rasa/core/nlg/interpolator.py +2 -3
  100. rasa/core/nlg/summarize.py +39 -5
  101. rasa/core/policies/enterprise_search_policy.py +298 -184
  102. rasa/core/policies/enterprise_search_policy_config.py +241 -0
  103. rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +64 -0
  104. rasa/core/policies/flow_policy.py +1 -1
  105. rasa/core/policies/flows/flow_executor.py +96 -17
  106. rasa/core/policies/intentless_policy.py +71 -26
  107. rasa/core/processor.py +104 -51
  108. rasa/core/run.py +33 -11
  109. rasa/core/tracker_stores/tracker_store.py +1 -1
  110. rasa/core/training/interactive.py +1 -1
  111. rasa/core/utils.py +35 -99
  112. rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
  113. rasa/dialogue_understanding/coexistence/llm_based_router.py +13 -17
  114. rasa/dialogue_understanding/commands/__init__.py +4 -0
  115. rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
  116. rasa/dialogue_understanding/commands/cancel_flow_command.py +6 -2
  117. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
  118. rasa/dialogue_understanding/commands/clarify_command.py +7 -3
  119. rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
  120. rasa/dialogue_understanding/commands/correct_slots_command.py +5 -6
  121. rasa/dialogue_understanding/commands/error_command.py +1 -1
  122. rasa/dialogue_understanding/commands/human_handoff_command.py +3 -3
  123. rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -0
  124. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
  125. rasa/dialogue_understanding/commands/set_slot_command.py +15 -5
  126. rasa/dialogue_understanding/commands/skip_question_command.py +3 -3
  127. rasa/dialogue_understanding/commands/start_flow_command.py +7 -3
  128. rasa/dialogue_understanding/commands/utils.py +26 -2
  129. rasa/dialogue_understanding/generator/__init__.py +7 -1
  130. rasa/dialogue_understanding/generator/command_generator.py +15 -3
  131. rasa/dialogue_understanding/generator/command_parser.py +2 -2
  132. rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
  133. rasa/dialogue_understanding/generator/constants.py +2 -2
  134. rasa/dialogue_understanding/generator/nlu_command_adapter.py +2 -2
  135. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +0 -2
  136. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -0
  137. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +1 -0
  138. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +79 -0
  139. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +79 -0
  140. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +28 -463
  141. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
  142. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +461 -0
  143. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +11 -64
  144. rasa/dialogue_understanding/patterns/cancel.py +1 -2
  145. rasa/dialogue_understanding/patterns/clarify.py +1 -1
  146. rasa/dialogue_understanding/patterns/correction.py +2 -2
  147. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +37 -25
  148. rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
  149. rasa/dialogue_understanding/processor/command_processor.py +11 -12
  150. rasa/dialogue_understanding/processor/command_processor_component.py +3 -3
  151. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +17 -4
  152. rasa/dialogue_understanding/stack/utils.py +3 -1
  153. rasa/dialogue_understanding/utils.py +68 -12
  154. rasa/dialogue_understanding_test/du_test_case.py +1 -1
  155. rasa/dialogue_understanding_test/du_test_runner.py +4 -22
  156. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -6
  157. rasa/e2e_test/e2e_test_coverage_report.py +1 -1
  158. rasa/e2e_test/e2e_test_runner.py +1 -1
  159. rasa/engine/constants.py +1 -1
  160. rasa/engine/graph.py +2 -2
  161. rasa/engine/recipes/default_recipe.py +26 -2
  162. rasa/engine/validation.py +3 -2
  163. rasa/hooks.py +0 -28
  164. rasa/llm_fine_tuning/annotation_module.py +39 -9
  165. rasa/llm_fine_tuning/conversations.py +3 -0
  166. rasa/llm_fine_tuning/llm_data_preparation_module.py +66 -49
  167. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +5 -7
  168. rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +52 -44
  169. rasa/llm_fine_tuning/paraphrasing_module.py +10 -12
  170. rasa/llm_fine_tuning/storage.py +4 -4
  171. rasa/llm_fine_tuning/utils.py +63 -1
  172. rasa/model_manager/model_api.py +88 -0
  173. rasa/model_manager/trainer_service.py +4 -4
  174. rasa/plugin.py +1 -11
  175. rasa/privacy/__init__.py +0 -0
  176. rasa/privacy/constants.py +83 -0
  177. rasa/privacy/event_broker_utils.py +77 -0
  178. rasa/privacy/privacy_config.py +281 -0
  179. rasa/privacy/privacy_config_schema.json +86 -0
  180. rasa/privacy/privacy_filter.py +340 -0
  181. rasa/privacy/privacy_manager.py +576 -0
  182. rasa/server.py +23 -2
  183. rasa/shared/constants.py +18 -0
  184. rasa/shared/core/command_payload_reader.py +1 -5
  185. rasa/shared/core/constants.py +4 -3
  186. rasa/shared/core/domain.py +7 -0
  187. rasa/shared/core/events.py +38 -10
  188. rasa/shared/core/flows/constants.py +2 -0
  189. rasa/shared/core/flows/flow.py +127 -14
  190. rasa/shared/core/flows/flows_list.py +18 -1
  191. rasa/shared/core/flows/flows_yaml_schema.json +3 -0
  192. rasa/shared/core/flows/steps/collect.py +46 -2
  193. rasa/shared/core/flows/steps/link.py +7 -2
  194. rasa/shared/core/flows/validation.py +25 -5
  195. rasa/shared/core/slots.py +28 -0
  196. rasa/shared/core/training_data/story_reader/yaml_story_reader.py +1 -4
  197. rasa/shared/exceptions.py +4 -0
  198. rasa/shared/providers/_configs/azure_openai_client_config.py +6 -2
  199. rasa/shared/providers/_configs/default_litellm_client_config.py +1 -1
  200. rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +1 -1
  201. rasa/shared/providers/_configs/openai_client_config.py +5 -1
  202. rasa/shared/providers/_configs/rasa_llm_client_config.py +1 -1
  203. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -1
  204. rasa/shared/providers/_configs/utils.py +0 -99
  205. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +3 -0
  206. rasa/shared/providers/llm/_base_litellm_client.py +5 -2
  207. rasa/shared/utils/common.py +1 -1
  208. rasa/shared/utils/configs.py +110 -0
  209. rasa/shared/utils/constants.py +0 -3
  210. rasa/shared/utils/llm.py +195 -9
  211. rasa/shared/utils/pykwalify_extensions.py +0 -9
  212. rasa/shared/utils/yaml.py +32 -0
  213. rasa/studio/constants.py +1 -0
  214. rasa/studio/data_handler.py +11 -4
  215. rasa/studio/download.py +167 -0
  216. rasa/studio/link.py +200 -0
  217. rasa/studio/prompts.py +223 -0
  218. rasa/studio/pull/__init__.py +0 -0
  219. rasa/studio/{download/flows.py → pull/data.py} +23 -160
  220. rasa/studio/{download → pull}/domains.py +1 -1
  221. rasa/studio/pull/pull.py +235 -0
  222. rasa/studio/push.py +136 -0
  223. rasa/studio/train.py +1 -1
  224. rasa/studio/upload.py +117 -67
  225. rasa/telemetry.py +82 -25
  226. rasa/tracing/config.py +3 -4
  227. rasa/tracing/constants.py +19 -1
  228. rasa/tracing/instrumentation/attribute_extractors.py +30 -8
  229. rasa/tracing/instrumentation/instrumentation.py +53 -2
  230. rasa/tracing/instrumentation/metrics.py +98 -15
  231. rasa/tracing/metric_instrument_provider.py +75 -3
  232. rasa/utils/common.py +7 -22
  233. rasa/utils/log_utils.py +1 -45
  234. rasa/validator.py +2 -8
  235. rasa/version.py +1 -1
  236. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/METADATA +8 -9
  237. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/RECORD +241 -220
  238. rasa/anonymization/__init__.py +0 -2
  239. rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
  240. rasa/anonymization/anonymization_pipeline.py +0 -286
  241. rasa/anonymization/anonymization_rule_executor.py +0 -266
  242. rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
  243. rasa/anonymization/schemas/config.yml +0 -47
  244. rasa/anonymization/utils.py +0 -118
  245. rasa/core/channels/inspector/dist/assets/channel-3730f5fd.js +0 -1
  246. rasa/core/channels/inspector/dist/assets/clone-e847561e.js +0 -1
  247. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-efbbfe00.js +0 -1
  248. rasa/studio/download/download.py +0 -439
  249. /rasa/{studio/download → core/information_retrieval/ingestion}/__init__.py +0 -0
  250. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/NOTICE +0 -0
  251. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/WHEEL +0 -0
  252. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/entry_points.txt +0 -0
@@ -1,3 +1,4 @@
1
+ import dataclasses
1
2
  import importlib.resources
2
3
  import json
3
4
  import re
@@ -9,10 +10,8 @@ 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
- POLICY_MAX_HISTORY,
14
- POLICY_PRIORITY,
15
- SEARCH_POLICY_PRIORITY,
16
15
  UTTER_SOURCE_METADATA_KEY,
17
16
  )
18
17
  from rasa.core.information_retrieval import (
@@ -22,8 +21,15 @@ from rasa.core.information_retrieval import (
22
21
  create_from_endpoint_config,
23
22
  )
24
23
  from rasa.core.information_retrieval.faiss import FAISS_Store
24
+ from rasa.core.policies.enterprise_search_policy_config import (
25
+ DEFAULT_EMBEDDINGS_CONFIG,
26
+ DEFAULT_ENTERPRISE_SEARCH_CONFIG,
27
+ DEFAULT_LLM_CONFIG,
28
+ DEFAULT_VECTOR_STORE_TYPE,
29
+ SOURCE_PROPERTY,
30
+ EnterpriseSearchPolicyConfig,
31
+ )
25
32
  from rasa.core.policies.policy import Policy, PolicyPrediction
26
- from rasa.core.utils import AvailableEndpoints
27
33
  from rasa.dialogue_understanding.generator.constants import (
28
34
  LLM_CONFIG_KEY,
29
35
  )
@@ -49,10 +55,8 @@ from rasa.shared.constants import (
49
55
  MODEL_CONFIG_KEY,
50
56
  MODEL_GROUP_ID_CONFIG_KEY,
51
57
  MODEL_NAME_CONFIG_KEY,
52
- OPENAI_PROVIDER,
53
- PROMPT_CONFIG_KEY,
54
58
  PROVIDER_CONFIG_KEY,
55
- TIMEOUT_CONFIG_KEY,
59
+ RASA_PATTERN_CANNOT_HANDLE_NO_RELEVANT_ANSWER,
56
60
  )
57
61
  from rasa.shared.core.constants import (
58
62
  ACTION_CANCEL_FLOW,
@@ -75,7 +79,6 @@ from rasa.shared.nlu.training_data.training_data import TrainingData
75
79
  from rasa.shared.providers.embedding._langchain_embedding_client_adapter import (
76
80
  _LangchainEmbeddingClientAdapter,
77
81
  )
78
- from rasa.shared.providers.llm.llm_client import LLMClient
79
82
  from rasa.shared.providers.llm.llm_response import LLMResponse, measure_llm_latency
80
83
  from rasa.shared.utils.cli import print_error_and_exit
81
84
  from rasa.shared.utils.constants import (
@@ -88,12 +91,9 @@ from rasa.shared.utils.health_check.embeddings_health_check_mixin import (
88
91
  from rasa.shared.utils.health_check.llm_health_check_mixin import LLMHealthCheckMixin
89
92
  from rasa.shared.utils.io import deep_container_fingerprint
90
93
  from rasa.shared.utils.llm import (
91
- DEFAULT_OPENAI_CHAT_MODEL_NAME,
92
- DEFAULT_OPENAI_EMBEDDING_MODEL_NAME,
93
94
  embedder_factory,
94
95
  get_prompt_template,
95
96
  llm_factory,
96
- resolve_model_client_config,
97
97
  sanitize_message_for_prompt,
98
98
  tracker_as_readable_transcript,
99
99
  )
@@ -110,41 +110,10 @@ if TYPE_CHECKING:
110
110
 
111
111
  from rasa.utils.log_utils import log_llm
112
112
 
113
- logger = structlog.get_logger()
113
+ structlogger = structlog.get_logger()
114
114
 
115
115
  dotenv.load_dotenv("./.env")
116
116
 
117
- SOURCE_PROPERTY = "source"
118
- VECTOR_STORE_TYPE_PROPERTY = "type"
119
- VECTOR_STORE_PROPERTY = "vector_store"
120
- VECTOR_STORE_THRESHOLD_PROPERTY = "threshold"
121
- TRACE_TOKENS_PROPERTY = "trace_prompt_tokens"
122
- CITATION_ENABLED_PROPERTY = "citation_enabled"
123
- USE_LLM_PROPERTY = "use_generative_llm"
124
- MAX_MESSAGES_IN_QUERY_KEY = "max_messages_in_query"
125
-
126
- DEFAULT_VECTOR_STORE_TYPE = "faiss"
127
- DEFAULT_VECTOR_STORE_THRESHOLD = 0.0
128
- DEFAULT_VECTOR_STORE = {
129
- VECTOR_STORE_TYPE_PROPERTY: DEFAULT_VECTOR_STORE_TYPE,
130
- SOURCE_PROPERTY: "./docs",
131
- VECTOR_STORE_THRESHOLD_PROPERTY: DEFAULT_VECTOR_STORE_THRESHOLD,
132
- }
133
-
134
- DEFAULT_LLM_CONFIG = {
135
- PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
136
- MODEL_CONFIG_KEY: DEFAULT_OPENAI_CHAT_MODEL_NAME,
137
- TIMEOUT_CONFIG_KEY: 10,
138
- "temperature": 0.0,
139
- "max_tokens": 256,
140
- "max_retries": 1,
141
- }
142
-
143
- DEFAULT_EMBEDDINGS_CONFIG = {
144
- PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
145
- "model": DEFAULT_OPENAI_EMBEDDING_MODEL_NAME,
146
- }
147
-
148
117
  ENTERPRISE_SEARCH_PROMPT_FILE_NAME = "enterprise_search_policy_prompt.jinja2"
149
118
  ENTERPRISE_SEARCH_CONFIG_FILE_NAME = "config.json"
150
119
 
@@ -159,6 +128,15 @@ DEFAULT_ENTERPRISE_SEARCH_PROMPT_WITH_CITATION_TEMPLATE = importlib.resources.re
159
128
  "rasa.core.policies", "enterprise_search_prompt_with_citation_template.jinja2"
160
129
  )
161
130
 
131
+ DEFAULT_ENTERPRISE_SEARCH_PROMPT_WITH_RELEVANCY_CHECK_AND_CITATION_TEMPLATE = (
132
+ importlib.resources.read_text(
133
+ "rasa.core.policies",
134
+ "enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2",
135
+ )
136
+ )
137
+
138
+ _ENTERPRISE_SEARCH_ANSWER_NOT_RELEVANT_PATTERN = re.compile(r"\[NO_RAG_ANSWER\]")
139
+
162
140
 
163
141
  class VectorStoreConnectionError(RasaException):
164
142
  """Exception raised for errors in connecting to the vector store."""
@@ -168,6 +146,12 @@ class VectorStoreConfigurationError(RasaException):
168
146
  """Exception raised for errors in vector store configuration."""
169
147
 
170
148
 
149
+ @dataclasses.dataclass
150
+ class _RelevancyCheckResponse:
151
+ answer: Optional[str]
152
+ relevant: bool
153
+
154
+
171
155
  @DefaultV1Recipe.register(
172
156
  DefaultV1Recipe.ComponentType.POLICY_WITH_END_TO_END_SUPPORT, is_trainable=True
173
157
  )
@@ -199,10 +183,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
199
183
  @staticmethod
200
184
  def get_default_config() -> Dict[str, Any]:
201
185
  """Returns the default config of the policy."""
202
- return {
203
- POLICY_PRIORITY: SEARCH_POLICY_PRIORITY,
204
- VECTOR_STORE_PROPERTY: DEFAULT_VECTOR_STORE,
205
- }
186
+ return DEFAULT_ENTERPRISE_SEARCH_CONFIG
206
187
 
207
188
  def __init__(
208
189
  self,
@@ -217,76 +198,71 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
217
198
  """Constructs a new Policy object."""
218
199
  super().__init__(config, model_storage, resource, execution_context, featurizer)
219
200
 
220
- # Resolve LLM config
221
- self.config[LLM_CONFIG_KEY] = resolve_model_client_config(
222
- self.config.get(LLM_CONFIG_KEY), EnterpriseSearchPolicy.__name__
223
- )
224
- # Resolve embeddings config
225
- self.config[EMBEDDINGS_CONFIG_KEY] = resolve_model_client_config(
226
- self.config.get(EMBEDDINGS_CONFIG_KEY), EnterpriseSearchPolicy.__name__
227
- )
201
+ parsed_config = EnterpriseSearchPolicyConfig.from_dict(config)
228
202
 
229
203
  # Vector store object and configuration
230
204
  self.vector_store = vector_store
231
- self.vector_store_config = self.config.get(
232
- VECTOR_STORE_PROPERTY, DEFAULT_VECTOR_STORE
233
- )
205
+ self.vector_store_config = parsed_config.vector_store_config
206
+ self.vector_search_threshold = parsed_config.vector_store_threshold
207
+ self.vector_store_type = parsed_config.vector_store_type
234
208
 
235
- # Embeddings configuration for encoding the search query
236
- self.embeddings_config = (
237
- self.config[EMBEDDINGS_CONFIG_KEY] or DEFAULT_EMBEDDINGS_CONFIG
238
- )
209
+ # Resolved embeddings configuration for encoding the search query
210
+ self.embeddings_config = parsed_config.embeddings_config
239
211
 
240
- # LLM Configuration for response generation
241
- self.llm_config = self.config[LLM_CONFIG_KEY] or DEFAULT_LLM_CONFIG
212
+ # Resolved LLM Configuration for response generation
213
+ self.llm_config = parsed_config.llm_config
242
214
 
243
215
  # Maximum number of turns to include in the prompt
244
- self.max_history = self.config.get(POLICY_MAX_HISTORY)
216
+ self.max_history = parsed_config.max_history
245
217
 
246
218
  # Maximum number of messages to include in the search query
247
- self.max_messages_in_query = self.config.get(MAX_MESSAGES_IN_QUERY_KEY, 2)
219
+ self.max_messages_in_query = parsed_config.max_messages_in_query
220
+
221
+ # Boolean to enable/disable tracing of prompt tokens
222
+ self.trace_prompt_tokens = parsed_config.trace_prompt_tokens
248
223
 
249
- # boolean to enable/disable tracing of prompt tokens
250
- self.trace_prompt_tokens = self.config.get(TRACE_TOKENS_PROPERTY, False)
224
+ # Boolean to enable/disable the use of LLM for response generation
225
+ self.use_llm = parsed_config.use_generative_llm
251
226
 
252
- # boolean to enable/disable the use of LLM for response generation
253
- self.use_llm = self.config.get(USE_LLM_PROPERTY, True)
227
+ # Boolean to enable/disable citation generation. This flag enables citation
228
+ # logic, but it only takes effect if `use_llm` is True.
229
+ self.citation_enabled = parsed_config.enable_citation
254
230
 
255
- # boolean to enable/disable citation generation
256
- self.citation_enabled = self.config.get(CITATION_ENABLED_PROPERTY, False)
231
+ # Boolean to enable/disable the use of relevancy check alongside answer
232
+ # generation. This flag enables citation logic, but it only takes effect if
233
+ # `use_llm` is True.
234
+ self.relevancy_check_enabled = parsed_config.check_relevancy
257
235
 
236
+ # Resolve the prompt template. The prompt will only be used if the 'use_llm' is
237
+ # set to True.
258
238
  self.prompt_template = prompt_template or get_prompt_template(
259
- self.config.get(PROMPT_CONFIG_KEY),
260
- DEFAULT_ENTERPRISE_SEARCH_PROMPT_TEMPLATE,
261
- log_source_component=EnterpriseSearchPolicy.__name__,
262
- log_source_method=LOG_COMPONENT_SOURCE_METHOD_INIT,
263
- )
264
- self.citation_prompt_template = get_prompt_template(
265
- self.config.get(PROMPT_CONFIG_KEY),
266
- DEFAULT_ENTERPRISE_SEARCH_PROMPT_WITH_CITATION_TEMPLATE,
239
+ jinja_file_path=parsed_config.prompt_template,
240
+ default_prompt_template=self._select_default_prompt_template_based_on_features(
241
+ parsed_config.check_relevancy, parsed_config.enable_citation
242
+ ),
267
243
  log_source_component=EnterpriseSearchPolicy.__name__,
268
244
  log_source_method=LOG_COMPONENT_SOURCE_METHOD_INIT,
269
245
  )
270
- # If citation is enabled, use the citation prompt template
271
- if self.citation_enabled:
272
- self.prompt_template = self.citation_prompt_template
273
246
 
274
247
  @classmethod
275
- def _create_plain_embedder(cls, config: Dict[Text, Any]) -> "Embeddings":
248
+ def _create_plain_embedder(cls, embeddings_config: Dict[Text, Any]) -> "Embeddings":
276
249
  """Creates an embedder based on the given configuration.
277
250
 
251
+ Args:
252
+ embeddings_config: A resolved embeddings configuration. Resolved means the
253
+ configuration is either:
254
+ - A reference to a model group that has already been expanded into
255
+ its corresponding configuration using the information from
256
+ `endpoints.yml`, or
257
+ - A full configuration for the embedder defined directly (i.e. not
258
+ relying on model groups or indirections).
259
+
278
260
  Returns:
279
- The embedder.
261
+ The embedder.
280
262
  """
281
263
  # Copy the config so original config is not modified
282
- config = config.copy()
283
- # Resolve config and instantiate the embedding client
284
- config[EMBEDDINGS_CONFIG_KEY] = resolve_model_client_config(
285
- config.get(EMBEDDINGS_CONFIG_KEY), EnterpriseSearchPolicy.__name__
286
- )
287
- client = embedder_factory(
288
- config.get(EMBEDDINGS_CONFIG_KEY), DEFAULT_EMBEDDINGS_CONFIG
289
- )
264
+ embeddings_config = embeddings_config.copy()
265
+ client = embedder_factory(embeddings_config, DEFAULT_EMBEDDINGS_CONFIG)
290
266
  # Wrap the embedding client in the adapter
291
267
  return _LangchainEmbeddingClientAdapter(client)
292
268
 
@@ -352,18 +328,18 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
352
328
  can load the policy from the resource.
353
329
  """
354
330
  # Perform health checks for both LLM and embeddings client configs
355
- self._perform_health_checks(self.config, "enterprise_search_policy.train")
356
-
357
- store_type = self.vector_store_config.get(VECTOR_STORE_TYPE_PROPERTY)
331
+ self._perform_health_checks(
332
+ self.llm_config, self.embeddings_config, "enterprise_search_policy.train"
333
+ )
358
334
 
359
335
  # telemetry call to track training start
360
336
  track_enterprise_search_policy_train_started()
361
337
 
362
338
  # validate embedding configuration
363
339
  try:
364
- embeddings = self._create_plain_embedder(self.config)
340
+ embeddings = self._create_plain_embedder(self.embeddings_config)
365
341
  except (ValidationError, Exception) as e:
366
- logger.error(
342
+ structlogger.error(
367
343
  "enterprise_search_policy.train.embedder_instantiation_failed",
368
344
  message="Unable to instantiate the embedding client.",
369
345
  error=e,
@@ -373,21 +349,25 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
373
349
  f"required environment variables. Error: {e}"
374
350
  )
375
351
 
376
- if store_type == DEFAULT_VECTOR_STORE_TYPE:
377
- logger.info("enterprise_search_policy.train.faiss")
352
+ if self.vector_store_type == DEFAULT_VECTOR_STORE_TYPE:
353
+ structlogger.info("enterprise_search_policy.train.faiss")
378
354
  with self._model_storage.write_to(self._resource) as path:
379
355
  self.vector_store = FAISS_Store(
380
356
  docs_folder=self.vector_store_config.get(SOURCE_PROPERTY),
381
357
  embeddings=embeddings,
382
358
  index_path=path,
383
359
  create_index=True,
360
+ parse_as_faq_pairs=not self.use_llm,
384
361
  )
385
362
  else:
386
- logger.info("enterprise_search_policy.train.custom", store_type=store_type)
363
+ structlogger.info(
364
+ "enterprise_search_policy.train.custom",
365
+ store_type=self.vector_store_type,
366
+ )
387
367
 
388
368
  # telemetry call to track training completion
389
369
  track_enterprise_search_policy_train_completed(
390
- vector_store_type=store_type,
370
+ vector_store_type=self.vector_store_type,
391
371
  embeddings_type=self.embeddings_config.get(PROVIDER_CONFIG_KEY),
392
372
  embeddings_model=self.embeddings_config.get(MODEL_CONFIG_KEY)
393
373
  or self.embeddings_config.get(MODEL_NAME_CONFIG_KEY),
@@ -399,6 +379,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
399
379
  or self.llm_config.get(MODEL_NAME_CONFIG_KEY),
400
380
  llm_model_group_id=self.llm_config.get(MODEL_GROUP_ID_CONFIG_KEY),
401
381
  citation_enabled=self.citation_enabled,
382
+ relevancy_check_enabled=self.relevancy_check_enabled,
402
383
  )
403
384
  self.persist()
404
385
  return self._resource
@@ -409,8 +390,11 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
409
390
  rasa.shared.utils.io.write_text_file(
410
391
  self.prompt_template, path / ENTERPRISE_SEARCH_PROMPT_FILE_NAME
411
392
  )
393
+ config = self.config.copy()
394
+ config[LLM_CONFIG_KEY] = self.llm_config
395
+ config[EMBEDDINGS_CONFIG_KEY] = self.embeddings_config
412
396
  rasa.shared.utils.io.dump_obj_as_json_to_file(
413
- path / ENTERPRISE_SEARCH_CONFIG_FILE_NAME, self.config
397
+ path / ENTERPRISE_SEARCH_CONFIG_FILE_NAME, config
414
398
  )
415
399
 
416
400
  def _prepare_slots_for_template(
@@ -449,9 +433,8 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
449
433
  endpoints: Endpoints configuration.
450
434
  """
451
435
  config = endpoints.vector_store if endpoints else None
452
- store_type = self.vector_store_config.get(VECTOR_STORE_TYPE_PROPERTY)
453
- if config is None and store_type != DEFAULT_VECTOR_STORE_TYPE:
454
- logger.error(
436
+ if config is None and self.vector_store_type != DEFAULT_VECTOR_STORE_TYPE:
437
+ structlogger.error(
455
438
  "enterprise_search_policy._connect_vector_store_or_raise.no_config"
456
439
  )
457
440
  raise VectorStoreConfigurationError(
@@ -461,7 +444,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
461
444
  try:
462
445
  self.vector_store.connect(config) # type: ignore
463
446
  except Exception as e:
464
- logger.error(
447
+ structlogger.error(
465
448
  "enterprise_search_policy._connect_vector_store_or_raise.connect_error",
466
449
  error=e,
467
450
  config=config,
@@ -487,14 +470,14 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
487
470
  transcript.append(sanitize_message_for_prompt(event.text))
488
471
 
489
472
  search_query = " ".join(transcript[-history:][::-1])
490
- logger.debug("search_query", search_query=search_query)
473
+ structlogger.debug("search_query", search_query=search_query)
491
474
  return search_query
492
475
 
493
476
  async def predict_action_probabilities( # type: ignore[override]
494
477
  self,
495
478
  tracker: DialogueStateTracker,
496
479
  domain: Domain,
497
- endpoints: Optional[AvailableEndpoints],
480
+ endpoints: Optional[AvailableEndpoints] = None,
498
481
  rule_only_data: Optional[Dict[Text, Any]] = None,
499
482
  **kwargs: Any,
500
483
  ) -> PolicyPrediction:
@@ -513,23 +496,20 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
513
496
  The prediction.
514
497
  """
515
498
  logger_key = "enterprise_search_policy.predict_action_probabilities"
516
- vector_search_threshold = self.vector_store_config.get(
517
- VECTOR_STORE_THRESHOLD_PROPERTY, DEFAULT_VECTOR_STORE_THRESHOLD
518
- )
519
- llm = llm_factory(self.config.get(LLM_CONFIG_KEY), DEFAULT_LLM_CONFIG)
499
+
520
500
  if not self.supports_current_stack_frame(
521
501
  tracker, False, False
522
502
  ) or self.should_abstain_in_coexistence(tracker, True):
523
503
  return self._prediction(self._default_predictions(domain))
524
504
 
525
505
  if not self.vector_store:
526
- logger.error(f"{logger_key}.no_vector_store")
506
+ structlogger.error(f"{logger_key}.no_vector_store")
527
507
  return self._create_prediction_internal_error(domain, tracker)
528
508
 
529
509
  try:
530
510
  self._connect_vector_store_or_raise(endpoints)
531
511
  except (VectorStoreConfigurationError, VectorStoreConnectionError) as e:
532
- logger.error(f"{logger_key}.connection_error", error=e)
512
+ structlogger.error(f"{logger_key}.connection_error", error=e)
533
513
  return self._create_prediction_internal_error(domain, tracker)
534
514
 
535
515
  search_query = self._prepare_search_query(
@@ -541,20 +521,19 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
541
521
  documents = await self.vector_store.search(
542
522
  query=search_query,
543
523
  tracker_state=tracker_state,
544
- threshold=vector_search_threshold,
524
+ threshold=self.vector_search_threshold,
545
525
  )
546
526
  except InformationRetrievalException as e:
547
- logger.error(f"{logger_key}.search_error", error=e)
527
+ structlogger.error(f"{logger_key}.search_error", error=e)
548
528
  return self._create_prediction_internal_error(domain, tracker)
549
529
 
550
530
  if not documents.results:
551
- logger.info(f"{logger_key}.no_documents")
531
+ structlogger.info(f"{logger_key}.no_documents")
552
532
  return self._create_prediction_cannot_handle(domain, tracker)
553
533
 
554
534
  if self.use_llm:
555
535
  prompt = self._render_prompt(tracker, documents.results)
556
- llm_response = await self._generate_llm_answer(llm, prompt)
557
- llm_response = LLMResponse.ensure_llm_response(llm_response)
536
+ llm_response = await self._invoke_llm(prompt)
558
537
 
559
538
  self._add_prompt_and_llm_response_to_latest_message(
560
539
  tracker=tracker,
@@ -564,24 +543,38 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
564
543
  )
565
544
 
566
545
  if llm_response is None or not llm_response.choices:
567
- logger.debug(f"{logger_key}.no_llm_response")
546
+ structlogger.debug(f"{logger_key}.no_llm_response")
568
547
  response = None
569
548
  else:
570
549
  llm_answer = llm_response.choices[0]
571
550
 
551
+ if self.relevancy_check_enabled:
552
+ relevancy_response = self._parse_llm_relevancy_check_response(
553
+ llm_answer
554
+ )
555
+ if not relevancy_response.relevant:
556
+ structlogger.debug(f"{logger_key}.answer_not_relevant")
557
+ return self._create_prediction_cannot_handle(
558
+ domain,
559
+ tracker,
560
+ RASA_PATTERN_CANNOT_HANDLE_NO_RELEVANT_ANSWER,
561
+ )
562
+
572
563
  if self.citation_enabled:
573
564
  llm_answer = self.post_process_citations(llm_answer)
574
565
 
575
- logger.debug(f"{logger_key}.llm_answer", llm_answer=llm_answer)
566
+ structlogger.debug(
567
+ f"{logger_key}.llm_answer", prompt=prompt, llm_answer=llm_answer
568
+ )
576
569
  response = llm_answer
577
570
  else:
578
571
  response = documents.results[0].metadata.get("answer", None)
579
572
  if not response:
580
- logger.error(
573
+ structlogger.error(
581
574
  f"{logger_key}.answer_key_missing_in_metadata",
582
575
  search_results=documents.results,
583
576
  )
584
- logger.debug(
577
+ structlogger.debug(
585
578
  "enterprise_search_policy.predict_action_probabilities.no_llm",
586
579
  search_results=documents,
587
580
  )
@@ -601,7 +594,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
601
594
 
602
595
  # telemetry call to track policy prediction
603
596
  track_enterprise_search_policy_predict(
604
- vector_store_type=self.vector_store_config.get(VECTOR_STORE_TYPE_PROPERTY),
597
+ vector_store_type=self.vector_store_type,
605
598
  embeddings_type=self.embeddings_config.get(PROVIDER_CONFIG_KEY),
606
599
  embeddings_model=self.embeddings_config.get(MODEL_CONFIG_KEY)
607
600
  or self.embeddings_config.get(MODEL_NAME_CONFIG_KEY),
@@ -613,6 +606,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
613
606
  or self.llm_config.get(MODEL_NAME_CONFIG_KEY),
614
607
  llm_model_group_id=self.llm_config.get(MODEL_GROUP_ID_CONFIG_KEY),
615
608
  citation_enabled=self.citation_enabled,
609
+ relevancy_check_enabled=self.relevancy_check_enabled,
616
610
  )
617
611
  return self._create_prediction(
618
612
  domain=domain, tracker=tracker, action_metadata=action_metadata
@@ -636,11 +630,12 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
636
630
  ),
637
631
  "docs": documents,
638
632
  "slots": self._prepare_slots_for_template(tracker),
633
+ "check_relevancy": self.relevancy_check_enabled,
639
634
  "citation_enabled": self.citation_enabled,
640
635
  }
641
636
  prompt = Template(self.prompt_template).render(**inputs)
642
637
  log_llm(
643
- logger=logger,
638
+ logger=structlogger,
644
639
  log_module="EnterpriseSearchPolicy",
645
640
  log_event="enterprise_search_policy._render_prompt.prompt_rendered",
646
641
  prompt=prompt,
@@ -648,9 +643,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
648
643
  return prompt
649
644
 
650
645
  @measure_llm_latency
651
- async def _generate_llm_answer(
652
- self, llm: LLMClient, prompt: Text
653
- ) -> Optional[LLMResponse]:
646
+ async def _invoke_llm(self, prompt: Text) -> Optional[LLMResponse]:
654
647
  """Fetches an LLM completion for the provided prompt.
655
648
 
656
649
  Args:
@@ -660,17 +653,32 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
660
653
  Returns:
661
654
  An LLMResponse object, or None if the call fails.
662
655
  """
656
+ llm = llm_factory(self.llm_config, DEFAULT_LLM_CONFIG)
663
657
  try:
664
- return await llm.acompletion(prompt)
658
+ response = await llm.acompletion(prompt)
659
+ return LLMResponse.ensure_llm_response(response)
665
660
  except Exception as e:
666
661
  # unfortunately, langchain does not wrap LLM exceptions which means
667
662
  # we have to catch all exceptions here
668
- logger.error(
663
+ structlogger.error(
669
664
  "enterprise_search_policy._generate_llm_answer.llm_error",
670
665
  error=e,
671
666
  )
672
667
  return None
673
668
 
669
+ def _parse_llm_relevancy_check_response(
670
+ self, llm_answer: str
671
+ ) -> _RelevancyCheckResponse:
672
+ """Checks if the LLM response is relevant by parsing it."""
673
+ answer_relevant = not _ENTERPRISE_SEARCH_ANSWER_NOT_RELEVANT_PATTERN.search(
674
+ llm_answer
675
+ )
676
+ structlogger.debug("")
677
+ return _RelevancyCheckResponse(
678
+ answer=llm_answer if answer_relevant else None,
679
+ relevant=answer_relevant,
680
+ )
681
+
674
682
  def _create_prediction(
675
683
  self,
676
684
  domain: Domain,
@@ -705,10 +713,18 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
705
713
  )
706
714
 
707
715
  def _create_prediction_cannot_handle(
708
- self, domain: Domain, tracker: DialogueStateTracker
716
+ self,
717
+ domain: Domain,
718
+ tracker: DialogueStateTracker,
719
+ reason: Optional[str] = None,
709
720
  ) -> PolicyPrediction:
721
+ cannot_handle_stack_frame = (
722
+ CannotHandlePatternFlowStackFrame(reason=reason)
723
+ if reason is not None
724
+ else CannotHandlePatternFlowStackFrame()
725
+ )
710
726
  return self._create_prediction_for_pattern(
711
- domain, tracker, CannotHandlePatternFlowStackFrame()
727
+ domain, tracker, cannot_handle_stack_frame
712
728
  )
713
729
 
714
730
  def _create_prediction_for_pattern(
@@ -767,70 +783,88 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
767
783
  **kwargs: Any,
768
784
  ) -> "EnterpriseSearchPolicy":
769
785
  """Loads a trained policy (see parent class for full docstring)."""
786
+ parsed_config = EnterpriseSearchPolicyConfig.from_dict(config)
787
+
770
788
  # Perform health checks for both LLM and embeddings client configs
771
- cls._perform_health_checks(config, "enterprise_search_policy.load")
789
+ cls._perform_health_checks(
790
+ parsed_config.llm_config,
791
+ parsed_config.embeddings_config,
792
+ "enterprise_search_policy.load",
793
+ )
772
794
 
773
- prompt_template = None
795
+ prompt_template = cls._load_prompt_template(model_storage, resource)
796
+ embeddings = cls._create_plain_embedder(parsed_config.embeddings_config)
797
+ vector_store = cls._load_vector_store(
798
+ embeddings,
799
+ parsed_config.vector_store_type,
800
+ parsed_config.use_generative_llm,
801
+ model_storage,
802
+ resource,
803
+ )
804
+
805
+ structlogger.info("enterprise_search_policy.load", config=config)
806
+
807
+ return cls(
808
+ config,
809
+ model_storage,
810
+ resource,
811
+ execution_context,
812
+ vector_store=vector_store,
813
+ prompt_template=prompt_template,
814
+ )
815
+
816
+ @classmethod
817
+ def _load_prompt_template(
818
+ cls, model_storage: ModelStorage, resource: Resource
819
+ ) -> Optional[str]:
774
820
  try:
775
821
  with model_storage.read_from(resource) as path:
776
- prompt_template = rasa.shared.utils.io.read_file(
822
+ return rasa.shared.utils.io.read_file(
777
823
  path / ENTERPRISE_SEARCH_PROMPT_FILE_NAME
778
824
  )
779
825
  except (FileNotFoundError, FileIOException) as e:
780
- logger.warning(
826
+ structlogger.warning(
781
827
  "enterprise_search_policy.load.failed", error=e, resource=resource.name
782
828
  )
829
+ return None
783
830
 
784
- store_type = config.get(VECTOR_STORE_PROPERTY, {}).get(
785
- VECTOR_STORE_TYPE_PROPERTY
786
- )
787
-
788
- embeddings = cls._create_plain_embedder(config)
789
-
790
- logger.info("enterprise_search_policy.load", config=config)
831
+ @classmethod
832
+ def _load_vector_store(
833
+ cls,
834
+ embeddings: "Embeddings",
835
+ store_type: str,
836
+ use_generative_llm: bool,
837
+ model_storage: ModelStorage,
838
+ resource: Resource,
839
+ ) -> InformationRetrieval:
791
840
  if store_type == DEFAULT_VECTOR_STORE_TYPE:
792
841
  # if a vector store is not specified,
793
842
  # default to using FAISS with the index stored in the model
794
843
  # TODO figure out a way to get path without context manager
795
844
  with model_storage.read_from(resource) as path:
796
- vector_store = FAISS_Store(
845
+ return FAISS_Store(
797
846
  embeddings=embeddings,
798
847
  index_path=path,
799
848
  docs_folder=None,
800
849
  create_index=False,
850
+ parse_as_faq_pairs=not use_generative_llm,
801
851
  )
802
852
  else:
803
- vector_store = create_from_endpoint_config(
853
+ return create_from_endpoint_config(
804
854
  config_type=store_type,
805
855
  embeddings=embeddings,
806
- ) # type: ignore
807
-
808
- return cls(
809
- config,
810
- model_storage,
811
- resource,
812
- execution_context,
813
- vector_store=vector_store,
814
- prompt_template=prompt_template,
815
- )
856
+ )
816
857
 
817
858
  @classmethod
818
- def _get_local_knowledge_data(cls, config: Dict[str, Any]) -> Optional[List[str]]:
859
+ def _get_local_knowledge_data(
860
+ cls, store_type: str, source: Optional[str] = None
861
+ ) -> Optional[List[str]]:
819
862
  """This is required only for local knowledge base types.
820
863
 
821
864
  e.g. FAISS, to ensure that the graph component is retrained when the knowledge
822
865
  base is updated.
823
866
  """
824
- merged_config = {**cls.get_default_config(), **config}
825
-
826
- store_type = merged_config.get(VECTOR_STORE_PROPERTY, {}).get(
827
- VECTOR_STORE_TYPE_PROPERTY
828
- )
829
- if store_type != DEFAULT_VECTOR_STORE_TYPE:
830
- return None
831
-
832
- source = merged_config.get(VECTOR_STORE_PROPERTY, {}).get(SOURCE_PROPERTY)
833
- if not source:
867
+ if store_type != DEFAULT_VECTOR_STORE_TYPE or not source:
834
868
  return None
835
869
 
836
870
  docs = FAISS_Store.load_documents(source)
@@ -846,21 +880,28 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
846
880
  @classmethod
847
881
  def fingerprint_addon(cls, config: Dict[str, Any]) -> Optional[str]:
848
882
  """Add a fingerprint of enterprise search policy for the graph."""
849
- local_knowledge_data = cls._get_local_knowledge_data(config)
883
+ parsed_config = EnterpriseSearchPolicyConfig.from_dict(config)
850
884
 
885
+ # Resolve the prompt template
886
+ default_prompt_template = cls._select_default_prompt_template_based_on_features(
887
+ parsed_config.check_relevancy, parsed_config.enable_citation
888
+ )
851
889
  prompt_template = get_prompt_template(
852
- config.get(PROMPT_CONFIG_KEY),
853
- DEFAULT_ENTERPRISE_SEARCH_PROMPT_TEMPLATE,
890
+ jinja_file_path=parsed_config.prompt_template,
891
+ default_prompt_template=default_prompt_template,
854
892
  log_source_component=EnterpriseSearchPolicy.__name__,
855
893
  log_source_method=LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON,
856
894
  )
857
895
 
858
- llm_config = resolve_model_client_config(
859
- config.get(LLM_CONFIG_KEY), EnterpriseSearchPolicy.__name__
860
- )
861
- embedding_config = resolve_model_client_config(
862
- config.get(EMBEDDINGS_CONFIG_KEY), EnterpriseSearchPolicy.__name__
896
+ # Fetch the local knowledge data in case FAISS is used
897
+ local_knowledge_data = cls._get_local_knowledge_data(
898
+ parsed_config.vector_store_type, parsed_config.vector_store_source
863
899
  )
900
+
901
+ # Get the resolved LLM and embeddings configurations
902
+ llm_config = parsed_config.llm_config
903
+ embedding_config = parsed_config.embeddings_config
904
+
864
905
  return deep_container_fingerprint(
865
906
  [prompt_template, local_knowledge_data, llm_config, embedding_config]
866
907
  )
@@ -878,7 +919,7 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
878
919
  Returns:
879
920
  The post-processed LLM answer.
880
921
  """
881
- logger.debug(
922
+ structlogger.debug(
882
923
  "enterprise_search_policy.post_process_citations", llm_answer=llm_answer
883
924
  )
884
925
 
@@ -958,24 +999,97 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
958
999
 
959
1000
  @classmethod
960
1001
  def _perform_health_checks(
961
- cls, config: Dict[Text, Any], log_source_method: str
1002
+ cls,
1003
+ llm_config: Dict[Text, Any],
1004
+ embeddings_config: Dict[Text, Any],
1005
+ log_source_method: str,
962
1006
  ) -> None:
963
- # Perform health check of the LLM client config
964
- llm_config = resolve_model_client_config(config.get(LLM_CONFIG_KEY, {}))
1007
+ """
1008
+ Perform the health checks using resolved LLM and embeddings configurations.
1009
+ Resolved means the configuration is either:
1010
+ - A reference to a model group that has already been expanded into
1011
+ its corresponding configuration using the information from
1012
+ `endpoints.yml`, or
1013
+ - A full configuration for the embedder defined directly (i.e. not
1014
+ relying on model groups or indirections).
1015
+
1016
+ Args:
1017
+ llm_config: A resolved LLM configuration.
1018
+ embeddings_config: A resolved embeddings configuration.
1019
+ log_source_method: The method health checks has been called from.
1020
+
1021
+ """
965
1022
  cls.perform_llm_health_check(
966
1023
  llm_config,
967
1024
  DEFAULT_LLM_CONFIG,
968
1025
  log_source_method,
969
1026
  EnterpriseSearchPolicy.__name__,
970
1027
  )
971
-
972
- # Perform health check of the embeddings client config
973
- embeddings_config = resolve_model_client_config(
974
- config.get(EMBEDDINGS_CONFIG_KEY, {})
975
- )
976
1028
  cls.perform_embeddings_health_check(
977
1029
  embeddings_config,
978
1030
  DEFAULT_EMBEDDINGS_CONFIG,
979
1031
  log_source_method,
980
1032
  EnterpriseSearchPolicy.__name__,
981
1033
  )
1034
+
1035
+ @classmethod
1036
+ def get_system_default_prompt_based_on_config(cls, config: Dict[str, Any]) -> str:
1037
+ """
1038
+ Resolves the default prompt template for Enterprise Search Policy based on
1039
+ the component's configuration.
1040
+
1041
+ - The old prompt is selected when both citation and relevancy check are either
1042
+ disabled or not set in the configuration.
1043
+ - The citation prompt is used when citation is enabled and relevancy check is
1044
+ either disabled or not set in the configuration.
1045
+ - The relevancy check prompt is only used when relevancy check is enabled.
1046
+
1047
+ Args:
1048
+ config: The component's configuration.
1049
+
1050
+ Returns:
1051
+ The resolved jinja prompt template as a string.
1052
+ """
1053
+ # Get the feature flags
1054
+ parsed_config = EnterpriseSearchPolicyConfig.from_dict(config)
1055
+ # Based on the enabled features (citation, relevancy check) fetch the
1056
+ # appropriate default prompt
1057
+ default_prompt = cls._select_default_prompt_template_based_on_features(
1058
+ parsed_config.check_relevancy, parsed_config.enable_citation
1059
+ )
1060
+
1061
+ return default_prompt
1062
+
1063
+ @classmethod
1064
+ def _select_default_prompt_template_based_on_features(
1065
+ cls,
1066
+ relevancy_check_enabled: bool,
1067
+ citation_enabled: bool,
1068
+ ) -> str:
1069
+ """
1070
+ Returns the appropriate default prompt template based on the feature flags.
1071
+
1072
+ The selection follows this priority:
1073
+ 1. If relevancy check is enabled, return the prompt that includes both relevancy
1074
+ and citation blocks.
1075
+ 2. If only citation is enabled, return the prompt with citation blocks.
1076
+ 3. Otherwise, fall back to the legacy default prompt template.
1077
+
1078
+ Args:
1079
+ relevancy_check_enabled: Whether the LLM-generated answer should undergo
1080
+ relevancy evaluation.
1081
+ citation_enabled: Whether citations should be included in the generated
1082
+ answer.
1083
+
1084
+ Returns:
1085
+ The default prompt template corresponding to the enabled features.
1086
+ """
1087
+ if relevancy_check_enabled:
1088
+ # ES prompt that has relevancy check and citations blocks
1089
+ return DEFAULT_ENTERPRISE_SEARCH_PROMPT_WITH_RELEVANCY_CHECK_AND_CITATION_TEMPLATE # noqa: E501
1090
+ elif citation_enabled:
1091
+ # ES prompt with citation's block - backward compatibility
1092
+ return DEFAULT_ENTERPRISE_SEARCH_PROMPT_WITH_CITATION_TEMPLATE
1093
+ else:
1094
+ # Legacy ES prompt - backward compatibility
1095
+ return DEFAULT_ENTERPRISE_SEARCH_PROMPT_TEMPLATE