rasa-pro 3.12.6.dev2__py3-none-any.whl → 3.13.0.dev2__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 (92) hide show
  1. rasa/__init__.py +0 -6
  2. rasa/cli/scaffold.py +1 -1
  3. rasa/core/actions/action.py +38 -34
  4. rasa/core/actions/action_run_slot_rejections.py +1 -1
  5. rasa/core/channels/studio_chat.py +16 -43
  6. rasa/core/channels/voice_ready/audiocodes.py +46 -17
  7. rasa/core/information_retrieval/faiss.py +68 -7
  8. rasa/core/information_retrieval/information_retrieval.py +40 -2
  9. rasa/core/information_retrieval/milvus.py +7 -2
  10. rasa/core/information_retrieval/qdrant.py +7 -2
  11. rasa/core/nlg/contextual_response_rephraser.py +11 -27
  12. rasa/core/nlg/generator.py +5 -21
  13. rasa/core/nlg/response.py +6 -43
  14. rasa/core/nlg/summarize.py +1 -15
  15. rasa/core/nlg/translate.py +0 -8
  16. rasa/core/policies/enterprise_search_policy.py +64 -316
  17. rasa/core/policies/flows/flow_executor.py +3 -38
  18. rasa/core/policies/intentless_policy.py +4 -17
  19. rasa/core/policies/policy.py +0 -2
  20. rasa/core/processor.py +27 -6
  21. rasa/core/utils.py +53 -0
  22. rasa/dialogue_understanding/coexistence/llm_based_router.py +4 -18
  23. rasa/dialogue_understanding/commands/cancel_flow_command.py +4 -59
  24. rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -2
  25. rasa/dialogue_understanding/commands/start_flow_command.py +0 -41
  26. rasa/dialogue_understanding/generator/command_generator.py +67 -0
  27. rasa/dialogue_understanding/generator/command_parser.py +1 -1
  28. rasa/dialogue_understanding/generator/llm_based_command_generator.py +7 -23
  29. rasa/dialogue_understanding/generator/llm_command_generator.py +1 -3
  30. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +1 -1
  31. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -1
  32. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +24 -2
  33. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +8 -12
  34. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +0 -61
  35. rasa/dialogue_understanding/processor/command_processor.py +7 -65
  36. rasa/dialogue_understanding/stack/utils.py +0 -38
  37. rasa/dialogue_understanding_test/command_metric_calculation.py +7 -40
  38. rasa/dialogue_understanding_test/command_metrics.py +38 -0
  39. rasa/dialogue_understanding_test/du_test_case.py +58 -25
  40. rasa/dialogue_understanding_test/du_test_result.py +228 -132
  41. rasa/dialogue_understanding_test/du_test_runner.py +10 -1
  42. rasa/dialogue_understanding_test/io.py +48 -16
  43. rasa/document_retrieval/__init__.py +0 -0
  44. rasa/document_retrieval/constants.py +32 -0
  45. rasa/document_retrieval/document_post_processor.py +351 -0
  46. rasa/document_retrieval/document_post_processor_prompt_template.jinja2 +0 -0
  47. rasa/document_retrieval/document_retriever.py +333 -0
  48. rasa/document_retrieval/knowledge_base_connectors/__init__.py +0 -0
  49. rasa/document_retrieval/knowledge_base_connectors/api_connector.py +39 -0
  50. rasa/document_retrieval/knowledge_base_connectors/knowledge_base_connector.py +34 -0
  51. rasa/document_retrieval/knowledge_base_connectors/vector_store_connector.py +226 -0
  52. rasa/document_retrieval/query_rewriter.py +234 -0
  53. rasa/document_retrieval/query_rewriter_prompt_template.jinja2 +8 -0
  54. rasa/engine/recipes/default_components.py +2 -0
  55. rasa/hooks.py +0 -55
  56. rasa/model_manager/model_api.py +1 -1
  57. rasa/model_manager/socket_bridge.py +0 -7
  58. rasa/shared/constants.py +0 -5
  59. rasa/shared/core/constants.py +0 -8
  60. rasa/shared/core/domain.py +12 -3
  61. rasa/shared/core/flows/flow.py +0 -17
  62. rasa/shared/core/flows/flows_yaml_schema.json +3 -38
  63. rasa/shared/core/flows/steps/collect.py +5 -18
  64. rasa/shared/core/flows/utils.py +1 -16
  65. rasa/shared/core/slot_mappings.py +11 -5
  66. rasa/shared/core/slots.py +1 -1
  67. rasa/shared/core/trackers.py +4 -10
  68. rasa/shared/nlu/constants.py +0 -1
  69. rasa/shared/providers/constants.py +0 -9
  70. rasa/shared/providers/llm/_base_litellm_client.py +4 -14
  71. rasa/shared/providers/llm/default_litellm_llm_client.py +2 -2
  72. rasa/shared/providers/llm/litellm_router_llm_client.py +7 -17
  73. rasa/shared/providers/llm/llm_client.py +15 -24
  74. rasa/shared/providers/llm/self_hosted_llm_client.py +2 -10
  75. rasa/shared/utils/common.py +11 -1
  76. rasa/shared/utils/health_check/health_check.py +1 -7
  77. rasa/shared/utils/llm.py +1 -1
  78. rasa/tracing/instrumentation/attribute_extractors.py +50 -17
  79. rasa/tracing/instrumentation/instrumentation.py +12 -12
  80. rasa/tracing/instrumentation/intentless_policy_instrumentation.py +1 -2
  81. rasa/utils/licensing.py +0 -15
  82. rasa/validator.py +1 -123
  83. rasa/version.py +1 -1
  84. {rasa_pro-3.12.6.dev2.dist-info → rasa_pro-3.13.0.dev2.dist-info}/METADATA +2 -3
  85. {rasa_pro-3.12.6.dev2.dist-info → rasa_pro-3.13.0.dev2.dist-info}/RECORD +88 -80
  86. rasa/core/actions/action_handle_digressions.py +0 -164
  87. rasa/dialogue_understanding/commands/handle_digressions_command.py +0 -144
  88. rasa/dialogue_understanding/patterns/handle_digressions.py +0 -81
  89. rasa/monkey_patches.py +0 -91
  90. {rasa_pro-3.12.6.dev2.dist-info → rasa_pro-3.13.0.dev2.dist-info}/NOTICE +0 -0
  91. {rasa_pro-3.12.6.dev2.dist-info → rasa_pro-3.13.0.dev2.dist-info}/WHEEL +0 -0
  92. {rasa_pro-3.12.6.dev2.dist-info → rasa_pro-3.13.0.dev2.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,39 @@
1
+ from typing import Any, Dict, Optional
2
+
3
+ from rasa.core.information_retrieval import SearchResultList
4
+ from rasa.document_retrieval.knowledge_base_connectors.knowledge_base_connector import (
5
+ KnowledgeBaseConnector,
6
+ )
7
+ from rasa.engine.storage.resource import Resource
8
+ from rasa.engine.storage.storage import ModelStorage
9
+ from rasa.shared.core.trackers import DialogueStateTracker
10
+
11
+
12
+ class APIConnector(KnowledgeBaseConnector):
13
+ def __init__(self, config: Dict[str, Any]) -> None:
14
+ self.config = config
15
+
16
+ @classmethod
17
+ def load(
18
+ cls,
19
+ config: Dict[str, Any],
20
+ model_storage: ModelStorage,
21
+ resource: Resource,
22
+ **kwargs: Any,
23
+ ) -> "APIConnector":
24
+ # TODO implement
25
+ return APIConnector(config)
26
+
27
+ async def retrieve_documents(
28
+ self,
29
+ search_query: str,
30
+ k: int,
31
+ threshold: float,
32
+ tracker: Optional[DialogueStateTracker],
33
+ ) -> Optional[SearchResultList]:
34
+ # TODO implement
35
+ return SearchResultList(results=[], metadata={})
36
+
37
+ def connect_or_raise(self) -> None:
38
+ # TODO implement
39
+ return None
@@ -0,0 +1,34 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, Dict, Optional
3
+
4
+ from rasa.core.information_retrieval import SearchResultList
5
+ from rasa.engine.storage.resource import Resource
6
+ from rasa.engine.storage.storage import ModelStorage
7
+ from rasa.shared.core.trackers import DialogueStateTracker
8
+
9
+
10
+ class KnowledgeBaseConnector(ABC):
11
+ @abstractmethod
12
+ def connect_or_raise(self) -> None:
13
+ pass
14
+
15
+ @abstractmethod
16
+ async def retrieve_documents(
17
+ self,
18
+ search_query: str,
19
+ k: int,
20
+ threshold: float,
21
+ tracker: Optional[DialogueStateTracker],
22
+ ) -> Optional[SearchResultList]:
23
+ pass
24
+
25
+ @classmethod
26
+ @abstractmethod
27
+ def load(
28
+ cls,
29
+ config: Dict[str, Any],
30
+ model_storage: ModelStorage,
31
+ resource: Resource,
32
+ **kwargs: Any,
33
+ ) -> "KnowledgeBaseConnector":
34
+ pass
@@ -0,0 +1,226 @@
1
+ import copy
2
+ from enum import Enum
3
+ from typing import TYPE_CHECKING, Any, Dict, Optional
4
+
5
+ import structlog
6
+
7
+ from rasa.core.information_retrieval import (
8
+ InformationRetrieval,
9
+ InformationRetrievalException,
10
+ SearchResultList,
11
+ create_from_endpoint_config,
12
+ )
13
+ from rasa.core.information_retrieval.faiss import FAISS_Store
14
+ from rasa.document_retrieval.constants import (
15
+ DEFAULT_EMBEDDINGS_CONFIG,
16
+ DEFAULT_VECTOR_STORE,
17
+ DEFAULT_VECTOR_STORE_TYPE,
18
+ VECTOR_STORE_CONFIG_KEY,
19
+ VECTOR_STORE_TYPE_CONFIG_KEY,
20
+ )
21
+ from rasa.document_retrieval.knowledge_base_connectors.knowledge_base_connector import (
22
+ KnowledgeBaseConnector,
23
+ )
24
+ from rasa.engine.storage.resource import Resource
25
+ from rasa.engine.storage.storage import ModelStorage
26
+ from rasa.shared.constants import EMBEDDINGS_CONFIG_KEY
27
+ from rasa.shared.core.trackers import DialogueStateTracker, EventVerbosity
28
+ from rasa.shared.exceptions import RasaException
29
+ from rasa.shared.providers.embedding._langchain_embedding_client_adapter import (
30
+ _LangchainEmbeddingClientAdapter,
31
+ )
32
+ from rasa.shared.utils.health_check.embeddings_health_check_mixin import (
33
+ EmbeddingsHealthCheckMixin,
34
+ )
35
+ from rasa.shared.utils.health_check.health_check import perform_embeddings_health_check
36
+ from rasa.shared.utils.llm import embedder_factory, resolve_model_client_config
37
+
38
+ if TYPE_CHECKING:
39
+ from langchain.schema.embeddings import Embeddings
40
+
41
+
42
+ structlogger = structlog.get_logger()
43
+
44
+
45
+ class VectorStoreConnectionError(RasaException):
46
+ """Exception raised for errors in connecting to the vector store."""
47
+
48
+
49
+ class VectorStoreConfigurationError(RasaException):
50
+ """Exception raised for errors in vector store configuration."""
51
+
52
+
53
+ class VectorStoreType(Enum):
54
+ FAISS = "FAISS"
55
+ QDRANT = "QDRANT"
56
+ MILVUS = "MILVUS"
57
+
58
+ def __str__(self) -> str:
59
+ return self.value
60
+
61
+
62
+ class VectorStoreConnector(KnowledgeBaseConnector, EmbeddingsHealthCheckMixin):
63
+ def __init__(
64
+ self,
65
+ config: Dict[str, Any],
66
+ model_storage: ModelStorage,
67
+ resource: Resource,
68
+ vector_store: Optional[InformationRetrieval] = None,
69
+ ) -> None:
70
+ self.config = config
71
+ self.vector_store_type = config.get(VECTOR_STORE_CONFIG_KEY, {}).get(
72
+ VECTOR_STORE_TYPE_CONFIG_KEY
73
+ )
74
+
75
+ # Vector store object and configuration
76
+ self.vector_store = vector_store
77
+ self.vector_store_config = self.config.get(
78
+ VECTOR_STORE_CONFIG_KEY, DEFAULT_VECTOR_STORE
79
+ )
80
+
81
+ # Embeddings configuration for encoding the search query
82
+ self.embeddings_config = (
83
+ self.config[EMBEDDINGS_CONFIG_KEY] or DEFAULT_EMBEDDINGS_CONFIG
84
+ )
85
+
86
+ self._model_storage = model_storage
87
+ self._resource = resource
88
+
89
+ @classmethod
90
+ def _create_plain_embedder(cls, config: Dict[str, Any]) -> "Embeddings":
91
+ """Creates an embedder based on the given configuration.
92
+
93
+ Returns:
94
+ The embedder.
95
+ """
96
+ # Copy the config so original config is not modified
97
+ config = copy.deepcopy(config)
98
+ # Resolve config and instantiate the embedding client
99
+ config[EMBEDDINGS_CONFIG_KEY] = resolve_model_client_config(
100
+ config.get(EMBEDDINGS_CONFIG_KEY), VectorStoreConnector.__name__
101
+ )
102
+ client = embedder_factory(
103
+ config.get(EMBEDDINGS_CONFIG_KEY), DEFAULT_EMBEDDINGS_CONFIG
104
+ )
105
+ # Wrap the embedding client in the adapter
106
+ return _LangchainEmbeddingClientAdapter(client)
107
+
108
+ @classmethod
109
+ def load(
110
+ cls,
111
+ config: Dict[str, Any],
112
+ model_storage: ModelStorage,
113
+ resource: Resource,
114
+ **kwargs: Any,
115
+ ) -> "VectorStoreConnector":
116
+ # Perform health check on the resolved embeddings client config
117
+ embedding_config = resolve_model_client_config(
118
+ config.get(EMBEDDINGS_CONFIG_KEY, {})
119
+ )
120
+ perform_embeddings_health_check(
121
+ embedding_config,
122
+ DEFAULT_EMBEDDINGS_CONFIG,
123
+ "vector_store_connector.load",
124
+ VectorStoreConnector.__name__,
125
+ )
126
+
127
+ store_type = config.get(VECTOR_STORE_CONFIG_KEY, {}).get(
128
+ VECTOR_STORE_TYPE_CONFIG_KEY
129
+ )
130
+ embeddings = cls._create_plain_embedder(config)
131
+
132
+ structlogger.info("vector_store_connector.load", config=config)
133
+ if store_type == VectorStoreType.FAISS.value:
134
+ # if a vector store is not specified,
135
+ # default to using FAISS with the index stored in the model
136
+ # TODO figure out a way to get path without context manager
137
+ with model_storage.read_from(resource) as path:
138
+ vector_store = FAISS_Store(
139
+ embeddings=embeddings,
140
+ index_path=path,
141
+ docs_folder=None,
142
+ create_index=False,
143
+ )
144
+ else:
145
+ vector_store = create_from_endpoint_config(
146
+ config_type=store_type,
147
+ embeddings=embeddings,
148
+ ) # type: ignore
149
+
150
+ return cls(
151
+ config=config,
152
+ model_storage=model_storage,
153
+ resource=resource,
154
+ vector_store=vector_store,
155
+ )
156
+
157
+ def connect_or_raise(self) -> None:
158
+ """Connects to the vector store or raises an exception.
159
+
160
+ Raise exceptions for the following cases:
161
+ - The configuration is not specified
162
+ - Unable to connect to the vector store
163
+
164
+ Args:
165
+ endpoints: Endpoints configuration.
166
+ """
167
+ if self.vector_store_type == VectorStoreType.FAISS.value:
168
+ return
169
+ from rasa.core.utils import AvailableEndpoints
170
+
171
+ endpoints = AvailableEndpoints.get_instance()
172
+
173
+ config = endpoints.vector_store if endpoints else None
174
+ store_type = self.config.get(VECTOR_STORE_CONFIG_KEY, {}).get(
175
+ VECTOR_STORE_TYPE_CONFIG_KEY
176
+ )
177
+
178
+ if config is None and store_type != DEFAULT_VECTOR_STORE_TYPE:
179
+ structlogger.error("vector_store_connector._connect_or_raise.no_config")
180
+ raise VectorStoreConfigurationError(
181
+ """No vector store specified. Please specify a vector
182
+ store in the endpoints configuration."""
183
+ )
184
+ try:
185
+ self.vector_store.connect(config) # type: ignore
186
+ except Exception as e:
187
+ structlogger.error(
188
+ "vector_store_connector._connect_or_raise.connect_error",
189
+ error=e,
190
+ config=config,
191
+ )
192
+ raise VectorStoreConnectionError(
193
+ f"Unable to connect to the vector store. Error: {e}"
194
+ )
195
+
196
+ async def retrieve_documents(
197
+ self,
198
+ search_query: str,
199
+ k: int,
200
+ threshold: float,
201
+ tracker: Optional[DialogueStateTracker],
202
+ ) -> Optional[SearchResultList]:
203
+ if self.vector_store is None:
204
+ return None
205
+
206
+ try:
207
+ self.connect_or_raise()
208
+ except (VectorStoreConfigurationError, VectorStoreConnectionError) as e:
209
+ structlogger.error("vector_store_connector.connection_error", error=e)
210
+ return None
211
+
212
+ if tracker is not None:
213
+ tracker_state = tracker.current_state(EventVerbosity.AFTER_RESTART)
214
+ else:
215
+ tracker_state = {}
216
+
217
+ try:
218
+ return await self.vector_store.search(
219
+ query=search_query,
220
+ threshold=threshold,
221
+ tracker_state=tracker_state,
222
+ k=k,
223
+ )
224
+ except InformationRetrievalException as e:
225
+ structlogger.error("vector_store.search_error", error=e)
226
+ return None
@@ -0,0 +1,234 @@
1
+ import importlib.resources
2
+ from enum import Enum
3
+ from typing import Any, Dict, Optional
4
+
5
+ import structlog
6
+ from jinja2 import Template
7
+
8
+ import rasa.shared.utils.io
9
+ from rasa.engine.storage.resource import Resource
10
+ from rasa.engine.storage.storage import ModelStorage
11
+ from rasa.shared.constants import (
12
+ LLM_CONFIG_KEY,
13
+ MODEL_CONFIG_KEY,
14
+ OPENAI_PROVIDER,
15
+ PROMPT_TEMPLATE_CONFIG_KEY,
16
+ PROVIDER_CONFIG_KEY,
17
+ TEXT,
18
+ TIMEOUT_CONFIG_KEY,
19
+ )
20
+ from rasa.shared.core.trackers import DialogueStateTracker
21
+ from rasa.shared.exceptions import FileIOException, ProviderClientAPIException
22
+ from rasa.shared.nlu.training_data.message import Message
23
+ from rasa.shared.providers.llm.llm_client import LLMClient
24
+ from rasa.shared.providers.llm.llm_response import LLMResponse
25
+ from rasa.shared.utils.health_check.health_check import perform_llm_health_check
26
+ from rasa.shared.utils.health_check.llm_health_check_mixin import (
27
+ LLMHealthCheckMixin,
28
+ )
29
+ from rasa.shared.utils.llm import (
30
+ AI,
31
+ DEFAULT_OPENAI_GENERATE_MODEL_NAME,
32
+ DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
33
+ USER,
34
+ get_prompt_template,
35
+ llm_factory,
36
+ resolve_model_client_config,
37
+ sanitize_message_for_prompt,
38
+ tracker_as_readable_transcript,
39
+ )
40
+
41
+ QUERY_REWRITER_PROMPT_FILE_NAME = "query_rewriter_prompt_template.jinja2"
42
+ MAX_TURNS = "max_turns"
43
+ DEFAULT_LLM_CONFIG = {
44
+ PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
45
+ MODEL_CONFIG_KEY: DEFAULT_OPENAI_GENERATE_MODEL_NAME,
46
+ "temperature": 0.3,
47
+ "max_tokens": DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
48
+ TIMEOUT_CONFIG_KEY: 5,
49
+ }
50
+ DEFAULT_QUERY_REWRITER_PROMPT_TEMPLATE = importlib.resources.read_text(
51
+ "rasa.document_retrieval",
52
+ "query_rewriter_prompt_template.jinja2",
53
+ )
54
+
55
+ TYPE_CONFIG_KEY = "type"
56
+
57
+ structlogger = structlog.get_logger()
58
+
59
+
60
+ class QueryRewritingType(Enum):
61
+ PLAIN = "PLAIN"
62
+ CONCATENATED_TURNS = "CONCATENATED_TURNS"
63
+ REPHRASE = "REPHRASE"
64
+ KEYWORD_EXTRACTION = "KEYWORD_EXTRACTION"
65
+
66
+ def __str__(self) -> str:
67
+ return self.value
68
+
69
+
70
+ class QueryRewriter(LLMHealthCheckMixin):
71
+ @classmethod
72
+ def get_default_config(cls) -> Dict[str, Any]:
73
+ """The default config for the query rewriter."""
74
+ return {
75
+ TYPE_CONFIG_KEY: QueryRewritingType.PLAIN,
76
+ MAX_TURNS: 0,
77
+ LLM_CONFIG_KEY: DEFAULT_LLM_CONFIG,
78
+ PROMPT_TEMPLATE_CONFIG_KEY: DEFAULT_QUERY_REWRITER_PROMPT_TEMPLATE,
79
+ }
80
+
81
+ def __init__(
82
+ self,
83
+ config: Dict[str, Any],
84
+ model_storage: ModelStorage,
85
+ resource: Resource,
86
+ prompt_template: Optional[str] = None,
87
+ ):
88
+ self.config = {**self.get_default_config(), **config}
89
+ self.config[LLM_CONFIG_KEY] = resolve_model_client_config(
90
+ self.config.get(LLM_CONFIG_KEY), QueryRewriter.__name__
91
+ )
92
+ self.prompt_template = prompt_template or get_prompt_template(
93
+ config.get(PROMPT_TEMPLATE_CONFIG_KEY),
94
+ DEFAULT_QUERY_REWRITER_PROMPT_TEMPLATE,
95
+ )
96
+
97
+ self._model_storage = model_storage
98
+ self._resource = resource
99
+
100
+ @classmethod
101
+ def load(
102
+ cls,
103
+ config: Dict[str, Any],
104
+ model_storage: ModelStorage,
105
+ resource: Resource,
106
+ **kwargs: Any,
107
+ ) -> "QueryRewriter":
108
+ """Load query rewriter."""
109
+ llm_config = resolve_model_client_config(config.get(LLM_CONFIG_KEY, {}))
110
+ perform_llm_health_check(
111
+ llm_config,
112
+ DEFAULT_LLM_CONFIG,
113
+ "query_rewriter.load",
114
+ QueryRewriter.__name__,
115
+ )
116
+
117
+ # load prompt template
118
+ prompt_template = None
119
+ try:
120
+ with model_storage.read_from(resource) as path:
121
+ prompt_template = rasa.shared.utils.io.read_file(
122
+ path / QUERY_REWRITER_PROMPT_FILE_NAME
123
+ )
124
+ except (FileNotFoundError, FileIOException) as e:
125
+ structlogger.warning(
126
+ "query_rewriter.load_prompt_template.failed",
127
+ error=e,
128
+ resource=resource.name,
129
+ )
130
+
131
+ return QueryRewriter(config, model_storage, resource, prompt_template)
132
+
133
+ def persist(self) -> None:
134
+ with self._model_storage.write_to(self._resource) as path:
135
+ rasa.shared.utils.io.write_text_file(
136
+ self.prompt_template, path / QUERY_REWRITER_PROMPT_FILE_NAME
137
+ )
138
+
139
+ @staticmethod
140
+ def _concatenate_turns(
141
+ message: Message, tracker: DialogueStateTracker, max_turns: int
142
+ ) -> str:
143
+ transcript = tracker_as_readable_transcript(tracker, max_turns=max_turns)
144
+ transcript += "\nUSER: " + message.get(TEXT)
145
+ return transcript
146
+
147
+ @staticmethod
148
+ async def _invoke_llm(prompt: str, llm: LLMClient) -> Optional[LLMResponse]:
149
+ try:
150
+ return await llm.acompletion(prompt)
151
+ except Exception as e:
152
+ # unfortunately, langchain does not wrap LLM exceptions which means
153
+ # we have to catch all exceptions here
154
+ structlogger.error("query_rewriter.llm.error", error=e)
155
+ raise ProviderClientAPIException(
156
+ message="LLM call exception", original_exception=e
157
+ )
158
+
159
+ async def _rephrase_message(
160
+ self, message: Message, tracker: DialogueStateTracker, max_turns: int = 5
161
+ ) -> str:
162
+ llm = llm_factory(self.config.get(LLM_CONFIG_KEY), DEFAULT_LLM_CONFIG)
163
+
164
+ transcript = tracker_as_readable_transcript(
165
+ tracker, max_turns=max_turns, ai_prefix="ASSISTANT"
166
+ )
167
+
168
+ inputs = {
169
+ "conversation": transcript,
170
+ "user_message": message.get(TEXT),
171
+ }
172
+
173
+ prompt = Template(self.prompt_template).render(**inputs)
174
+ llm_response = await self._invoke_llm(prompt, llm)
175
+ llm_response = LLMResponse.ensure_llm_response(llm_response)
176
+
177
+ return llm_response.choices[0]
178
+
179
+ @staticmethod
180
+ def _keyword_extraction(
181
+ message: Message, tracker: DialogueStateTracker, max_turns: int = 5
182
+ ) -> str:
183
+ import spacy
184
+
185
+ nlp = spacy.load("en_core_web_md")
186
+
187
+ transcript = tracker_as_readable_transcript(tracker, max_turns=max_turns)
188
+ transcript = transcript.replace(USER, "")
189
+ transcript = transcript.replace(AI, "")
190
+
191
+ doc = nlp(transcript)
192
+
193
+ keywords = set()
194
+ for token in doc:
195
+ # Extract nouns and proper nouns
196
+ if token.pos_ in ["NOUN", "PROPN"]:
197
+ keywords.add(token.lemma_)
198
+
199
+ for ent in doc.ents:
200
+ # Add named entities as keywords
201
+ keywords.add(ent.text)
202
+
203
+ # Remove stop words and punctuation
204
+ keywords = {
205
+ word
206
+ for word in keywords
207
+ if word.lower() not in nlp.Defaults.stop_words and word.isalpha()
208
+ }
209
+
210
+ if keywords:
211
+ return message.get(TEXT) + " " + " ".join(keywords)
212
+ else:
213
+ return message.get(TEXT)
214
+
215
+ async def prepare_search_query(
216
+ self, message: Message, tracker: DialogueStateTracker
217
+ ) -> str:
218
+ query_rewriting_type = self.config[TYPE_CONFIG_KEY]
219
+ max_turns: int = self.config[MAX_TURNS]
220
+
221
+ query: str
222
+
223
+ if query_rewriting_type == QueryRewritingType.CONCATENATED_TURNS.value:
224
+ query = self._concatenate_turns(message, tracker, max_turns)
225
+ elif query_rewriting_type == QueryRewritingType.KEYWORD_EXTRACTION.value:
226
+ query = self._keyword_extraction(message, tracker, max_turns)
227
+ elif query_rewriting_type == QueryRewritingType.REPHRASE.value:
228
+ query = await self._rephrase_message(message, tracker, max_turns)
229
+ elif query_rewriting_type == QueryRewritingType.PLAIN.value:
230
+ query = message.get(TEXT)
231
+ else:
232
+ raise ValueError(f"Invalid query rewriting type: {query_rewriting_type}")
233
+
234
+ return sanitize_message_for_prompt(query)
@@ -0,0 +1,8 @@
1
+ Conversation Context:
2
+ {{conversation}}
3
+
4
+ User's Latest Request: {{user_message}}
5
+
6
+ Instruction: Rephrase the user's latest request into a concise search query for a knowledge base.
7
+
8
+ Search Query:
@@ -13,6 +13,7 @@ from rasa.dialogue_understanding.generator import (
13
13
  LLMCommandGenerator,
14
14
  )
15
15
  from rasa.dialogue_understanding.generator.nlu_command_adapter import NLUCommandAdapter
16
+ from rasa.document_retrieval.document_retriever import DocumentRetriever
16
17
  from rasa.nlu.classifiers.diet_classifier import DIETClassifier
17
18
  from rasa.nlu.classifiers.fallback_classifier import FallbackClassifier
18
19
  from rasa.nlu.classifiers.keyword_intent_classifier import KeywordIntentClassifier
@@ -92,4 +93,5 @@ DEFAULT_COMPONENTS = [
92
93
  FlowPolicy,
93
94
  EnterpriseSearchPolicy,
94
95
  IntentlessPolicy,
96
+ DocumentRetriever,
95
97
  ]
rasa/hooks.py CHANGED
@@ -1,20 +1,8 @@
1
1
  import argparse
2
2
  import logging
3
- import os
4
3
  from typing import TYPE_CHECKING, List, Optional, Text, Union
5
4
 
6
- import litellm
7
5
  import pluggy
8
- import structlog
9
-
10
- from rasa.shared.providers.constants import (
11
- LANGFUSE_CALLBACK_NAME,
12
- LANGFUSE_HOST_ENV_VAR,
13
- LANGFUSE_PROJECT_ID_ENV_VAR,
14
- LANGFUSE_PUBLIC_KEY_ENV_VAR,
15
- LANGFUSE_SECRET_KEY_ENV_VAR,
16
- RASA_LANGFUSE_INTEGRATION_ENABLED_ENV_VAR,
17
- )
18
6
 
19
7
  # IMPORTANT: do not import anything from rasa here - use scoped imports
20
8
  # this avoids circular imports, as the hooks are used in different places
@@ -30,7 +18,6 @@ if TYPE_CHECKING:
30
18
 
31
19
  hookimpl = pluggy.HookimplMarker("rasa")
32
20
  logger = logging.getLogger(__name__)
33
- structlogger = structlog.get_logger()
34
21
 
35
22
 
36
23
  @hookimpl # type: ignore[misc]
@@ -70,8 +57,6 @@ def configure_commandline(cmdline_arguments: argparse.Namespace) -> Optional[Tex
70
57
  config.configure_tracing(tracer_provider)
71
58
  config.configure_metrics(endpoints_file)
72
59
 
73
- _init_langfuse_integration()
74
-
75
60
  return endpoints_file
76
61
 
77
62
 
@@ -130,43 +115,3 @@ def after_server_stop() -> None:
130
115
 
131
116
  if anon_pipeline is not None:
132
117
  anon_pipeline.stop()
133
-
134
-
135
- def _is_langfuse_integration_enabled() -> bool:
136
- return (
137
- os.environ.get(RASA_LANGFUSE_INTEGRATION_ENABLED_ENV_VAR, "false").lower()
138
- == "true"
139
- )
140
-
141
-
142
- def _init_langfuse_integration() -> None:
143
- if not _is_langfuse_integration_enabled():
144
- structlogger.info(
145
- "hooks._init_langfuse_integration.disabled",
146
- event_info="Langfuse integration is disabled.",
147
- )
148
- return
149
-
150
- if (
151
- not os.environ.get(LANGFUSE_HOST_ENV_VAR)
152
- or not os.environ.get(LANGFUSE_PROJECT_ID_ENV_VAR)
153
- or not os.environ.get(LANGFUSE_PUBLIC_KEY_ENV_VAR)
154
- or not os.environ.get(LANGFUSE_SECRET_KEY_ENV_VAR)
155
- ):
156
- structlogger.warning(
157
- "hooks._init_langfuse_integration.missing_langfuse_keys",
158
- event_info=(
159
- "Langfuse integration is enabled, but some environment variables"
160
- "are missing. Please set LANGFUSE_HOST, LANGFUSE_PROJECT_ID, "
161
- "LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment "
162
- "variables to use Langfuse integration."
163
- ),
164
- )
165
- return
166
-
167
- litellm.success_callback = [LANGFUSE_CALLBACK_NAME]
168
- litellm.failure_callback = [LANGFUSE_CALLBACK_NAME]
169
- structlogger.info(
170
- "hooks.langfuse_callbacks_initialized",
171
- event_info="Langfuse integration initialized.",
172
- )
@@ -483,7 +483,7 @@ def external_blueprint() -> Blueprint:
483
483
  """Create a blueprint for the model manager API."""
484
484
  from rasa.core.channels.socketio import SocketBlueprint
485
485
 
486
- sio = AsyncServer(async_mode="sanic", cors_allowed_origins="*")
486
+ sio = AsyncServer(async_mode="sanic", cors_allowed_origins=[])
487
487
  bp = SocketBlueprint(sio, "", "model_api_external")
488
488
 
489
489
  create_bridge_server(sio, running_bots)
@@ -131,13 +131,6 @@ async def create_bridge_client(
131
131
  structlogger.debug("model_runner.bot_message", deployment_id=deployment_id)
132
132
  await sio.emit("bot_message", data, room=sid)
133
133
 
134
- @client.event # type: ignore[misc]
135
- async def error(data: Dict[str, Any]) -> None:
136
- structlogger.debug(
137
- "model_runner.bot_error", deployment_id=deployment_id, data=data
138
- )
139
- await sio.emit("error", data, room=sid)
140
-
141
134
  @client.event # type: ignore[misc]
142
135
  async def tracker(data: Dict[str, Any]) -> None:
143
136
  await sio.emit("tracker", json.loads(data), room=sid)
rasa/shared/constants.py CHANGED
@@ -338,8 +338,3 @@ ROLE_SYSTEM = "system"
338
338
  # Used for key values in ValidateSlotPatternFlowStackFrame
339
339
  REFILL_UTTER = "refill_utter"
340
340
  REJECTIONS = "rejections"
341
-
342
- LANGFUSE_METADATA_USER_ID = "trace_user_id"
343
- LANGFUSE_METADATA_SESSION_ID = "session_id"
344
- LANGFUSE_CUSTOM_METADATA_DICT = "trace_metadata"
345
- LANGFUSE_TAGS = "tags"