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.
- rasa/__main__.py +0 -3
- rasa/api.py +1 -1
- rasa/cli/dialogue_understanding_test.py +1 -1
- rasa/cli/e2e_test.py +1 -1
- rasa/cli/evaluate.py +1 -1
- rasa/cli/export.py +1 -1
- rasa/cli/llm_fine_tuning.py +12 -11
- rasa/cli/project_templates/defaults.py +133 -0
- rasa/cli/run.py +1 -1
- rasa/cli/studio/link.py +53 -0
- rasa/cli/studio/pull.py +78 -0
- rasa/cli/studio/push.py +78 -0
- rasa/cli/studio/studio.py +12 -0
- rasa/cli/studio/upload.py +8 -0
- rasa/cli/train.py +1 -1
- rasa/cli/utils.py +1 -1
- rasa/cli/x.py +1 -1
- rasa/constants.py +2 -0
- rasa/core/__init__.py +0 -16
- rasa/core/actions/action.py +5 -1
- rasa/core/actions/action_repeat_bot_messages.py +18 -22
- rasa/core/actions/action_run_slot_rejections.py +0 -1
- rasa/core/agent.py +16 -1
- rasa/core/available_endpoints.py +146 -0
- rasa/core/brokers/pika.py +1 -2
- rasa/core/channels/botframework.py +2 -2
- rasa/core/channels/channel.py +2 -2
- rasa/core/channels/hangouts.py +8 -5
- rasa/core/channels/mattermost.py +1 -1
- rasa/core/channels/rasa_chat.py +2 -4
- rasa/core/channels/rest.py +5 -4
- rasa/core/channels/studio_chat.py +3 -2
- rasa/core/channels/vier_cvg.py +1 -2
- rasa/core/channels/voice_ready/audiocodes.py +1 -8
- rasa/core/channels/voice_stream/audiocodes.py +7 -4
- rasa/core/channels/voice_stream/genesys.py +2 -2
- rasa/core/channels/voice_stream/twilio_media_streams.py +10 -5
- rasa/core/channels/voice_stream/voice_channel.py +33 -22
- rasa/core/http_interpreter.py +3 -7
- rasa/core/jobs.py +2 -1
- rasa/core/nlg/contextual_response_rephraser.py +34 -9
- rasa/core/nlg/generator.py +0 -1
- rasa/core/nlg/interpolator.py +2 -3
- rasa/core/nlg/summarize.py +39 -5
- rasa/core/policies/enterprise_search_policy.py +283 -62
- rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +63 -0
- rasa/core/policies/flow_policy.py +1 -1
- rasa/core/policies/flows/flow_executor.py +96 -17
- rasa/core/policies/intentless_policy.py +9 -7
- rasa/core/processor.py +104 -51
- rasa/core/run.py +33 -11
- rasa/core/tracker_stores/tracker_store.py +1 -1
- rasa/core/training/interactive.py +1 -1
- rasa/core/utils.py +24 -97
- rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
- rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +2 -0
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
- rasa/dialogue_understanding/commands/clarify_command.py +5 -1
- rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
- rasa/dialogue_understanding/commands/human_handoff_command.py +2 -0
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +4 -2
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
- rasa/dialogue_understanding/commands/set_slot_command.py +11 -1
- rasa/dialogue_understanding/commands/skip_question_command.py +2 -0
- rasa/dialogue_understanding/commands/start_flow_command.py +4 -0
- rasa/dialogue_understanding/commands/utils.py +26 -2
- rasa/dialogue_understanding/generator/__init__.py +7 -1
- rasa/dialogue_understanding/generator/command_generator.py +4 -2
- rasa/dialogue_understanding/generator/command_parser.py +2 -2
- rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +12 -33
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +78 -0
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +26 -461
- rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
- rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +477 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +8 -58
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +37 -25
- rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
- rasa/dialogue_understanding/processor/command_processor.py +3 -3
- rasa/dialogue_understanding/processor/command_processor_component.py +3 -3
- rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +17 -4
- rasa/dialogue_understanding/utils.py +68 -12
- rasa/dialogue_understanding_test/du_test_case.py +1 -1
- rasa/dialogue_understanding_test/du_test_runner.py +4 -22
- rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -6
- rasa/e2e_test/e2e_test_runner.py +1 -1
- rasa/engine/constants.py +1 -1
- rasa/engine/recipes/default_recipe.py +26 -2
- rasa/engine/validation.py +3 -2
- rasa/hooks.py +0 -28
- rasa/llm_fine_tuning/annotation_module.py +39 -9
- rasa/llm_fine_tuning/conversations.py +3 -0
- rasa/llm_fine_tuning/llm_data_preparation_module.py +66 -49
- rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +52 -44
- rasa/llm_fine_tuning/paraphrasing_module.py +10 -12
- rasa/llm_fine_tuning/storage.py +4 -4
- rasa/llm_fine_tuning/utils.py +63 -1
- rasa/model_manager/model_api.py +88 -0
- rasa/model_manager/trainer_service.py +4 -4
- rasa/plugin.py +1 -11
- rasa/privacy/__init__.py +0 -0
- rasa/privacy/constants.py +83 -0
- rasa/privacy/event_broker_utils.py +77 -0
- rasa/privacy/privacy_config.py +281 -0
- rasa/privacy/privacy_config_schema.json +86 -0
- rasa/privacy/privacy_filter.py +340 -0
- rasa/privacy/privacy_manager.py +576 -0
- rasa/server.py +23 -2
- rasa/shared/constants.py +3 -0
- rasa/shared/core/constants.py +4 -3
- rasa/shared/core/domain.py +7 -0
- rasa/shared/core/events.py +37 -7
- rasa/shared/core/flows/flow.py +1 -2
- rasa/shared/core/flows/flows_yaml_schema.json +3 -0
- rasa/shared/core/flows/steps/collect.py +46 -2
- rasa/shared/core/slots.py +28 -0
- rasa/shared/exceptions.py +4 -0
- rasa/shared/utils/llm.py +161 -6
- rasa/shared/utils/yaml.py +32 -0
- rasa/studio/data_handler.py +3 -3
- rasa/studio/download/download.py +37 -60
- rasa/studio/download/flows.py +23 -31
- rasa/studio/link.py +200 -0
- rasa/studio/pull.py +94 -0
- rasa/studio/push.py +131 -0
- rasa/studio/upload.py +117 -67
- rasa/telemetry.py +82 -25
- rasa/tracing/config.py +3 -4
- rasa/tracing/constants.py +19 -1
- rasa/tracing/instrumentation/attribute_extractors.py +10 -2
- rasa/tracing/instrumentation/instrumentation.py +53 -2
- rasa/tracing/instrumentation/metrics.py +98 -15
- rasa/tracing/metric_instrument_provider.py +75 -3
- rasa/utils/common.py +1 -27
- rasa/utils/log_utils.py +1 -45
- rasa/validator.py +2 -8
- rasa/version.py +1 -1
- {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev8.dist-info}/METADATA +5 -6
- {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev8.dist-info}/RECORD +143 -129
- rasa/anonymization/__init__.py +0 -2
- rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
- rasa/anonymization/anonymization_pipeline.py +0 -286
- rasa/anonymization/anonymization_rule_executor.py +0 -266
- rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
- rasa/anonymization/schemas/config.yml +0 -47
- rasa/anonymization/utils.py +0 -118
- {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev8.dist-info}/NOTICE +0 -0
- {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev8.dist-info}/WHEEL +0 -0
- {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
|
-
|
|
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
|
-
#
|
|
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
|
-
#
|
|
256
|
-
self.use_llm = self.config.get(USE_LLM_PROPERTY,
|
|
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
|
-
#
|
|
259
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
676
|
+
structlogger.error(
|
|
584
677
|
f"{logger_key}.answer_key_missing_in_metadata",
|
|
585
678
|
search_results=documents.results,
|
|
586
679
|
)
|
|
587
|
-
|
|
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=
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|