rasa-pro 3.11.0rc1__py3-none-any.whl → 3.11.0rc3__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/cli/inspect.py +2 -0
- rasa/cli/studio/studio.py +18 -8
- rasa/core/actions/action_repeat_bot_messages.py +17 -0
- rasa/core/channels/channel.py +17 -0
- rasa/core/channels/development_inspector.py +4 -1
- rasa/core/channels/voice_ready/audiocodes.py +15 -4
- rasa/core/channels/voice_ready/jambonz.py +13 -2
- rasa/core/channels/voice_ready/twilio_voice.py +6 -21
- rasa/core/channels/voice_stream/asr/asr_event.py +1 -1
- rasa/core/channels/voice_stream/asr/azure.py +5 -7
- rasa/core/channels/voice_stream/asr/deepgram.py +13 -11
- rasa/core/channels/voice_stream/voice_channel.py +61 -19
- rasa/core/nlg/contextual_response_rephraser.py +20 -12
- rasa/core/policies/enterprise_search_policy.py +32 -72
- rasa/core/policies/intentless_policy.py +34 -72
- rasa/dialogue_understanding/coexistence/llm_based_router.py +18 -33
- rasa/dialogue_understanding/generator/constants.py +0 -2
- rasa/dialogue_understanding/generator/flow_retrieval.py +33 -50
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +12 -40
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +18 -20
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +19 -1
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +26 -22
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +9 -0
- rasa/dialogue_understanding/processor/command_processor.py +21 -1
- rasa/e2e_test/e2e_test_case.py +85 -6
- rasa/engine/validation.py +88 -60
- rasa/model_service.py +3 -0
- rasa/nlu/tokenizers/whitespace_tokenizer.py +3 -14
- rasa/server.py +3 -1
- rasa/shared/constants.py +5 -5
- rasa/shared/core/constants.py +1 -1
- rasa/shared/core/domain.py +0 -26
- rasa/shared/core/flows/flows_list.py +5 -1
- rasa/shared/providers/_configs/litellm_router_client_config.py +29 -9
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +6 -14
- rasa/shared/providers/embedding/litellm_router_embedding_client.py +1 -1
- rasa/shared/providers/llm/_base_litellm_client.py +32 -1
- rasa/shared/providers/llm/litellm_router_llm_client.py +56 -1
- rasa/shared/providers/llm/self_hosted_llm_client.py +4 -28
- rasa/shared/providers/router/_base_litellm_router_client.py +35 -1
- rasa/shared/utils/common.py +1 -1
- rasa/shared/utils/health_check/__init__.py +0 -0
- rasa/shared/utils/health_check/embeddings_health_check_mixin.py +31 -0
- rasa/shared/utils/health_check/health_check.py +256 -0
- rasa/shared/utils/health_check/llm_health_check_mixin.py +31 -0
- rasa/shared/utils/llm.py +5 -2
- rasa/shared/utils/yaml.py +102 -62
- rasa/studio/auth.py +3 -5
- rasa/studio/config.py +13 -4
- rasa/studio/constants.py +1 -0
- rasa/studio/data_handler.py +10 -3
- rasa/studio/upload.py +21 -10
- rasa/telemetry.py +15 -1
- rasa/tracing/config.py +3 -1
- rasa/tracing/instrumentation/attribute_extractors.py +20 -0
- rasa/tracing/instrumentation/instrumentation.py +121 -0
- rasa/utils/common.py +5 -0
- rasa/utils/io.py +8 -16
- rasa/utils/sanic_error_handler.py +32 -0
- rasa/version.py +1 -1
- {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc3.dist-info}/METADATA +3 -2
- {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc3.dist-info}/RECORD +65 -61
- rasa/shared/utils/health_check.py +0 -533
- {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc3.dist-info}/NOTICE +0 -0
- {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc3.dist-info}/WHEEL +0 -0
- {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc3.dist-info}/entry_points.txt +0 -0
|
@@ -27,12 +27,9 @@ from langchain.schema.embeddings import Embeddings
|
|
|
27
27
|
from langchain_community.vectorstores.faiss import FAISS
|
|
28
28
|
from langchain_community.vectorstores.utils import DistanceStrategy
|
|
29
29
|
|
|
30
|
-
from rasa.dialogue_understanding.generator.constants import (
|
|
31
|
-
TRAINED_EMBEDDINGS_CONFIG_KEY,
|
|
32
|
-
)
|
|
33
30
|
from rasa.engine.storage.resource import Resource
|
|
34
31
|
from rasa.engine.storage.storage import ModelStorage
|
|
35
|
-
|
|
32
|
+
import rasa.shared.utils.io
|
|
36
33
|
from rasa.shared.constants import (
|
|
37
34
|
EMBEDDINGS_CONFIG_KEY,
|
|
38
35
|
PROVIDER_CONFIG_KEY,
|
|
@@ -41,12 +38,15 @@ from rasa.shared.constants import (
|
|
|
41
38
|
from rasa.shared.core.domain import Domain
|
|
42
39
|
from rasa.shared.core.flows import FlowsList
|
|
43
40
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
44
|
-
from rasa.shared.exceptions import ProviderClientAPIException
|
|
41
|
+
from rasa.shared.exceptions import ProviderClientAPIException
|
|
45
42
|
from rasa.shared.nlu.constants import TEXT, FLOWS_FROM_SEMANTIC_SEARCH
|
|
46
43
|
from rasa.shared.nlu.training_data.message import Message
|
|
47
44
|
from rasa.shared.providers.embedding._langchain_embedding_client_adapter import (
|
|
48
45
|
_LangchainEmbeddingClientAdapter,
|
|
49
46
|
)
|
|
47
|
+
from rasa.shared.utils.health_check.embeddings_health_check_mixin import (
|
|
48
|
+
EmbeddingsHealthCheckMixin,
|
|
49
|
+
)
|
|
50
50
|
from rasa.shared.utils.llm import (
|
|
51
51
|
tracker_as_readable_transcript,
|
|
52
52
|
embedder_factory,
|
|
@@ -56,11 +56,6 @@ from rasa.shared.utils.llm import (
|
|
|
56
56
|
allowed_values_for_slot,
|
|
57
57
|
resolve_model_client_config,
|
|
58
58
|
)
|
|
59
|
-
from rasa.shared.utils.health_check import (
|
|
60
|
-
perform_training_time_embeddings_health_check,
|
|
61
|
-
perform_inference_time_embeddings_health_check,
|
|
62
|
-
)
|
|
63
|
-
from rasa.shared.utils.io import dump_obj_as_json_to_file, read_json_file
|
|
64
59
|
|
|
65
60
|
DEFAULT_FLOW_DOCUMENT_TEMPLATE = importlib.resources.read_text(
|
|
66
61
|
"rasa.dialogue_understanding.generator", "flow_document_template.jinja2"
|
|
@@ -85,7 +80,7 @@ DEFAULT_SHOULD_EMBED_SLOTS = True
|
|
|
85
80
|
structlogger = structlog.get_logger()
|
|
86
81
|
|
|
87
82
|
|
|
88
|
-
class FlowRetrieval:
|
|
83
|
+
class FlowRetrieval(EmbeddingsHealthCheckMixin):
|
|
89
84
|
@classmethod
|
|
90
85
|
def get_default_config(cls) -> Dict[str, Any]:
|
|
91
86
|
"""The default config for the flow retrieval."""
|
|
@@ -94,7 +89,6 @@ class FlowRetrieval:
|
|
|
94
89
|
MAX_FLOWS_FROM_SEMANTIC_SEARCH_KEY: DEFAULT_MAX_FLOWS_FROM_SEMANTIC_SEARCH,
|
|
95
90
|
TURNS_TO_EMBED_KEY: DEFAULT_TURNS_TO_EMBED,
|
|
96
91
|
SHOULD_EMBED_SLOTS_KEY: DEFAULT_SHOULD_EMBED_SLOTS,
|
|
97
|
-
TRAINED_EMBEDDINGS_CONFIG_KEY: None,
|
|
98
92
|
}
|
|
99
93
|
|
|
100
94
|
def __init__(
|
|
@@ -147,16 +141,6 @@ class FlowRetrieval:
|
|
|
147
141
|
|
|
148
142
|
return config
|
|
149
143
|
|
|
150
|
-
def train(self) -> None:
|
|
151
|
-
self.config[TRAINED_EMBEDDINGS_CONFIG_KEY] = (
|
|
152
|
-
perform_training_time_embeddings_health_check(
|
|
153
|
-
self.config.get(EMBEDDINGS_CONFIG_KEY),
|
|
154
|
-
DEFAULT_EMBEDDINGS_CONFIG,
|
|
155
|
-
"flow_retrieval.train",
|
|
156
|
-
FlowRetrieval.__name__,
|
|
157
|
-
)
|
|
158
|
-
)
|
|
159
|
-
|
|
160
144
|
@classmethod
|
|
161
145
|
def load(
|
|
162
146
|
cls,
|
|
@@ -166,6 +150,18 @@ class FlowRetrieval:
|
|
|
166
150
|
**kwargs: Any,
|
|
167
151
|
) -> "FlowRetrieval":
|
|
168
152
|
"""Load flow retrieval with previously populated FAISS vector store."""
|
|
153
|
+
|
|
154
|
+
# Perform health check on resolved embedding client config
|
|
155
|
+
embeddings_config = resolve_model_client_config(
|
|
156
|
+
config.get(EMBEDDINGS_CONFIG_KEY, {})
|
|
157
|
+
)
|
|
158
|
+
cls.perform_embeddings_health_check(
|
|
159
|
+
embeddings_config,
|
|
160
|
+
DEFAULT_EMBEDDINGS_CONFIG,
|
|
161
|
+
"flow_retrieval.load",
|
|
162
|
+
FlowRetrieval.__name__,
|
|
163
|
+
)
|
|
164
|
+
|
|
169
165
|
# initialize base flow retrieval
|
|
170
166
|
flow_retrieval = FlowRetrieval(config, model_storage, resource)
|
|
171
167
|
# load vector store
|
|
@@ -174,30 +170,6 @@ class FlowRetrieval:
|
|
|
174
170
|
)
|
|
175
171
|
flow_retrieval.vector_store = vector_store
|
|
176
172
|
|
|
177
|
-
persisted_config = None
|
|
178
|
-
try:
|
|
179
|
-
with model_storage.read_from(resource) as path:
|
|
180
|
-
persisted_config = read_json_file(
|
|
181
|
-
path / FLOW_RETRIEVAL_CONFIG_FILE_NAME
|
|
182
|
-
)
|
|
183
|
-
except (FileNotFoundError, FileIOException) as e:
|
|
184
|
-
structlogger.warning(
|
|
185
|
-
"flow_retrieval.load.failed", error=e, resource=resource.name
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
train_embeddings_name = (
|
|
189
|
-
persisted_config.get(TRAINED_EMBEDDINGS_CONFIG_KEY, None)
|
|
190
|
-
if persisted_config
|
|
191
|
-
else None
|
|
192
|
-
)
|
|
193
|
-
perform_inference_time_embeddings_health_check(
|
|
194
|
-
flow_retrieval.config.get(EMBEDDINGS_CONFIG_KEY),
|
|
195
|
-
DEFAULT_EMBEDDINGS_CONFIG,
|
|
196
|
-
train_embeddings_name,
|
|
197
|
-
"flow_retrieval.load",
|
|
198
|
-
FlowRetrieval.__name__,
|
|
199
|
-
)
|
|
200
|
-
|
|
201
173
|
return flow_retrieval
|
|
202
174
|
|
|
203
175
|
@classmethod
|
|
@@ -243,10 +215,7 @@ class FlowRetrieval:
|
|
|
243
215
|
|
|
244
216
|
def persist(self) -> None:
|
|
245
217
|
self._persist_vector_store()
|
|
246
|
-
|
|
247
|
-
dump_obj_as_json_to_file(
|
|
248
|
-
path / FLOW_RETRIEVAL_CONFIG_FILE_NAME, self.config
|
|
249
|
-
)
|
|
218
|
+
self._persist_config()
|
|
250
219
|
|
|
251
220
|
def _persist_vector_store(self) -> None:
|
|
252
221
|
"""Persists the FAISS vector store."""
|
|
@@ -259,6 +228,12 @@ class FlowRetrieval:
|
|
|
259
228
|
event_info="Vector store is None, not persisted.",
|
|
260
229
|
)
|
|
261
230
|
|
|
231
|
+
def _persist_config(self) -> None:
|
|
232
|
+
with self._model_storage.write_to(self._resource) as path:
|
|
233
|
+
rasa.shared.utils.io.dump_obj_as_json_to_file(
|
|
234
|
+
path / FLOW_RETRIEVAL_CONFIG_FILE_NAME, self.config
|
|
235
|
+
)
|
|
236
|
+
|
|
262
237
|
def populate(self, flows: FlowsList, domain: Domain) -> None:
|
|
263
238
|
"""Populates the vector store with embeddings generated from
|
|
264
239
|
documents based on the flow descriptions, and flow slots
|
|
@@ -268,6 +243,14 @@ class FlowRetrieval:
|
|
|
268
243
|
flows: List of flows to populate the vector store with.
|
|
269
244
|
domain: The domain containing relevant slot information.
|
|
270
245
|
"""
|
|
246
|
+
# Perform health check before populating the vector store with flows
|
|
247
|
+
self.perform_embeddings_health_check(
|
|
248
|
+
self.config.get(EMBEDDINGS_CONFIG_KEY),
|
|
249
|
+
DEFAULT_EMBEDDINGS_CONFIG,
|
|
250
|
+
"flow_retrieval.train",
|
|
251
|
+
FlowRetrieval.__name__,
|
|
252
|
+
)
|
|
253
|
+
|
|
271
254
|
flows_to_embedd = flows.exclude_link_only_flows()
|
|
272
255
|
embeddings = self._create_embedder(self.config)
|
|
273
256
|
documents = self._generate_flow_documents(flows_to_embedd, domain)
|
|
@@ -17,7 +17,6 @@ from rasa.dialogue_understanding.generator.constants import (
|
|
|
17
17
|
FLOW_RETRIEVAL_KEY,
|
|
18
18
|
FLOW_RETRIEVAL_ACTIVE_KEY,
|
|
19
19
|
FLOW_RETRIEVAL_FLOW_THRESHOLD,
|
|
20
|
-
TRAINED_MODEL_NAME_CONFIG_KEY,
|
|
21
20
|
)
|
|
22
21
|
from rasa.dialogue_understanding.generator.flow_retrieval import FlowRetrieval
|
|
23
22
|
from rasa.engine.graph import GraphComponent, ExecutionContext
|
|
@@ -33,27 +32,26 @@ from rasa.shared.exceptions import ProviderClientAPIException
|
|
|
33
32
|
from rasa.shared.nlu.constants import FLOWS_IN_PROMPT
|
|
34
33
|
from rasa.shared.nlu.training_data.message import Message
|
|
35
34
|
from rasa.shared.nlu.training_data.training_data import TrainingData
|
|
35
|
+
from rasa.shared.utils.health_check.llm_health_check_mixin import LLMHealthCheckMixin
|
|
36
36
|
from rasa.shared.utils.llm import (
|
|
37
37
|
allowed_values_for_slot,
|
|
38
38
|
llm_factory,
|
|
39
39
|
resolve_model_client_config,
|
|
40
40
|
)
|
|
41
|
-
from rasa.shared.utils.health_check import perform_training_time_llm_health_check
|
|
42
41
|
from rasa.utils.log_utils import log_llm
|
|
43
42
|
|
|
44
43
|
structlogger = structlog.get_logger()
|
|
45
44
|
|
|
46
45
|
|
|
47
|
-
LLM_BASED_COMMAND_GENERATOR_CONFIG_FILE = "config.json"
|
|
48
|
-
|
|
49
|
-
|
|
50
46
|
@DefaultV1Recipe.register(
|
|
51
47
|
[
|
|
52
48
|
DefaultV1Recipe.ComponentType.COMMAND_GENERATOR,
|
|
53
49
|
],
|
|
54
50
|
is_trainable=True,
|
|
55
51
|
)
|
|
56
|
-
class LLMBasedCommandGenerator(
|
|
52
|
+
class LLMBasedCommandGenerator(
|
|
53
|
+
LLMHealthCheckMixin, GraphComponent, CommandGenerator, ABC
|
|
54
|
+
):
|
|
57
55
|
"""An abstract class defining interface and common functionality
|
|
58
56
|
of an LLM-based command generators.
|
|
59
57
|
"""
|
|
@@ -106,11 +104,7 @@ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
|
|
|
106
104
|
@abstractmethod
|
|
107
105
|
def persist(self) -> None:
|
|
108
106
|
"""Persist the component to disk for future loading."""
|
|
109
|
-
|
|
110
|
-
with self._model_storage.write_to(self._resource) as path:
|
|
111
|
-
rasa.shared.utils.io.dump_obj_as_json_to_file(
|
|
112
|
-
path / LLM_BASED_COMMAND_GENERATOR_CONFIG_FILE, self.config
|
|
113
|
-
)
|
|
107
|
+
pass
|
|
114
108
|
|
|
115
109
|
@abstractmethod
|
|
116
110
|
async def predict_commands(
|
|
@@ -173,13 +167,11 @@ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
|
|
|
173
167
|
"""Train the llm based command generator. Stores all flows into a vector
|
|
174
168
|
store.
|
|
175
169
|
"""
|
|
176
|
-
self.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
LLMBasedCommandGenerator.__name__,
|
|
182
|
-
)
|
|
170
|
+
self.perform_llm_health_check(
|
|
171
|
+
self.config.get(LLM_CONFIG_KEY),
|
|
172
|
+
DEFAULT_LLM_CONFIG,
|
|
173
|
+
"llm_based_command_generator.train",
|
|
174
|
+
LLMBasedCommandGenerator.__name__,
|
|
183
175
|
)
|
|
184
176
|
|
|
185
177
|
if (
|
|
@@ -210,12 +202,11 @@ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
|
|
|
210
202
|
except Exception as e:
|
|
211
203
|
structlogger.error(
|
|
212
204
|
"llm_based_command_generator.train.failed",
|
|
213
|
-
event_info="Flow retrieval store
|
|
205
|
+
event_info="Flow retrieval store is inaccessible.",
|
|
214
206
|
error=e,
|
|
215
207
|
)
|
|
216
208
|
raise
|
|
217
|
-
|
|
218
|
-
self.flow_retrieval.train()
|
|
209
|
+
|
|
219
210
|
self.persist()
|
|
220
211
|
return self._resource
|
|
221
212
|
|
|
@@ -251,25 +242,6 @@ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
|
|
|
251
242
|
)
|
|
252
243
|
return None
|
|
253
244
|
|
|
254
|
-
@classmethod
|
|
255
|
-
def load_config_from_model_storage(
|
|
256
|
-
cls,
|
|
257
|
-
model_storage: ModelStorage,
|
|
258
|
-
resource: Resource,
|
|
259
|
-
) -> Optional[Text]:
|
|
260
|
-
try:
|
|
261
|
-
with model_storage.read_from(resource) as path:
|
|
262
|
-
return rasa.shared.utils.io.read_json_file(
|
|
263
|
-
path / LLM_BASED_COMMAND_GENERATOR_CONFIG_FILE
|
|
264
|
-
)
|
|
265
|
-
except (FileNotFoundError, FileIOException) as e:
|
|
266
|
-
structlogger.warning(
|
|
267
|
-
"llm_based_command_generator.load_config.failed",
|
|
268
|
-
error=e,
|
|
269
|
-
resource=resource.name,
|
|
270
|
-
)
|
|
271
|
-
return None
|
|
272
|
-
|
|
273
245
|
@classmethod
|
|
274
246
|
def load_flow_retrival(
|
|
275
247
|
cls,
|
|
@@ -24,7 +24,6 @@ from rasa.dialogue_understanding.generator.constants import (
|
|
|
24
24
|
LLM_CONFIG_KEY,
|
|
25
25
|
USER_INPUT_CONFIG_KEY,
|
|
26
26
|
FLOW_RETRIEVAL_KEY,
|
|
27
|
-
TRAINED_MODEL_NAME_CONFIG_KEY,
|
|
28
27
|
DEFAULT_LLM_CONFIG,
|
|
29
28
|
)
|
|
30
29
|
from rasa.dialogue_understanding.generator.flow_retrieval import FlowRetrieval
|
|
@@ -60,7 +59,6 @@ from rasa.shared.utils.llm import (
|
|
|
60
59
|
allowed_values_for_slot,
|
|
61
60
|
resolve_model_client_config,
|
|
62
61
|
)
|
|
63
|
-
from rasa.shared.utils.health_check import perform_inference_time_llm_health_check
|
|
64
62
|
|
|
65
63
|
# multistep template keys
|
|
66
64
|
HANDLE_FLOWS_KEY = "handle_flows"
|
|
@@ -77,6 +75,7 @@ DEFAULT_HANDLE_FLOWS_TEMPLATE = importlib.resources.read_text(
|
|
|
77
75
|
DEFAULT_FILL_SLOTS_TEMPLATE = importlib.resources.read_text(
|
|
78
76
|
"rasa.dialogue_understanding.generator.multi_step", "fill_slots_prompt.jinja2"
|
|
79
77
|
).strip()
|
|
78
|
+
MULTI_STEP_LLM_COMMAND_GENERATOR_CONFIG_FILE = "config.json"
|
|
80
79
|
|
|
81
80
|
# dictionary of template names and associated file names and default values
|
|
82
81
|
PROMPT_TEMPLATES = {
|
|
@@ -145,15 +144,18 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
|
|
|
145
144
|
**kwargs: Any,
|
|
146
145
|
) -> "MultiStepLLMCommandGenerator":
|
|
147
146
|
"""Loads trained component (see parent class for full docstring)."""
|
|
148
|
-
prompts = cls._load_prompt_templates(model_storage, resource)
|
|
149
147
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
148
|
+
# Perform health check of the LLM client config
|
|
149
|
+
llm_config = resolve_model_client_config(config.get(LLM_CONFIG_KEY, {}))
|
|
150
|
+
cls.perform_llm_health_check(
|
|
151
|
+
llm_config,
|
|
152
|
+
DEFAULT_LLM_CONFIG,
|
|
153
|
+
"multi_step_llm_command_generator.load",
|
|
154
|
+
MultiStepLLMCommandGenerator.__name__,
|
|
155
155
|
)
|
|
156
156
|
|
|
157
|
+
prompts = cls._load_prompt_templates(model_storage, resource)
|
|
158
|
+
|
|
157
159
|
# init base command generator
|
|
158
160
|
command_generator = cls(config, model_storage, resource, prompts)
|
|
159
161
|
# load flow retrieval if enabled
|
|
@@ -162,23 +164,12 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
|
|
|
162
164
|
command_generator.config, model_storage, resource
|
|
163
165
|
)
|
|
164
166
|
|
|
165
|
-
perform_inference_time_llm_health_check(
|
|
166
|
-
command_generator.config.get(LLM_CONFIG_KEY),
|
|
167
|
-
DEFAULT_LLM_CONFIG,
|
|
168
|
-
train_model_name,
|
|
169
|
-
"multi_step_llm_command_generator.load",
|
|
170
|
-
MultiStepLLMCommandGenerator.__name__,
|
|
171
|
-
)
|
|
172
|
-
|
|
173
167
|
return command_generator
|
|
174
168
|
|
|
175
169
|
def persist(self) -> None:
|
|
176
170
|
"""Persist this component to disk for future loading."""
|
|
177
|
-
super().persist()
|
|
178
|
-
|
|
179
|
-
# persist prompt template
|
|
180
171
|
self._persist_prompt_templates()
|
|
181
|
-
|
|
172
|
+
self._persist_config()
|
|
182
173
|
if self.flow_retrieval is not None:
|
|
183
174
|
self.flow_retrieval.persist()
|
|
184
175
|
|
|
@@ -411,6 +402,13 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
|
|
|
411
402
|
file_path = path / file_name
|
|
412
403
|
rasa.shared.utils.io.write_text_file(template, file_path)
|
|
413
404
|
|
|
405
|
+
def _persist_config(self) -> None:
|
|
406
|
+
"""Persist config as a source of truth for resolved clients."""
|
|
407
|
+
with self._model_storage.write_to(self._resource) as path:
|
|
408
|
+
rasa.shared.utils.io.dump_obj_as_json_to_file(
|
|
409
|
+
path / MULTI_STEP_LLM_COMMAND_GENERATOR_CONFIG_FILE, self.config
|
|
410
|
+
)
|
|
411
|
+
|
|
414
412
|
async def _predict_commands_with_multi_step(
|
|
415
413
|
self,
|
|
416
414
|
message: Message,
|
|
@@ -19,6 +19,7 @@ from rasa.engine.storage.storage import ModelStorage
|
|
|
19
19
|
from rasa.shared.constants import ROUTE_TO_CALM_SLOT
|
|
20
20
|
from rasa.shared.core.domain import Domain
|
|
21
21
|
from rasa.shared.core.flows.flows_list import FlowsList
|
|
22
|
+
from rasa.shared.core.flows.steps import CollectInformationFlowStep
|
|
22
23
|
from rasa.shared.core.slot_mappings import (
|
|
23
24
|
SlotFillingManager,
|
|
24
25
|
extract_slot_value,
|
|
@@ -217,7 +218,24 @@ def _issue_set_slot_commands(
|
|
|
217
218
|
commands: List[Command] = []
|
|
218
219
|
domain = domain if domain else Domain.empty()
|
|
219
220
|
slot_filling_manager = SlotFillingManager(domain, tracker, message)
|
|
220
|
-
|
|
221
|
+
|
|
222
|
+
# only use slots that don't have ask_before_filling set to True
|
|
223
|
+
available_slot_names = flows.available_slot_names(ask_before_filling=False)
|
|
224
|
+
|
|
225
|
+
# check if the current step is a CollectInformationFlowStep
|
|
226
|
+
# in case it has ask_before_filling set to True, we need to add the
|
|
227
|
+
# slot to the available_slot_names
|
|
228
|
+
if tracker.active_flow:
|
|
229
|
+
flow = flows.flow_by_id(tracker.active_flow)
|
|
230
|
+
step_id = tracker.current_step_id
|
|
231
|
+
if flow is not None:
|
|
232
|
+
current_step = flow.step_by_id(step_id)
|
|
233
|
+
if (
|
|
234
|
+
current_step
|
|
235
|
+
and isinstance(current_step, CollectInformationFlowStep)
|
|
236
|
+
and current_step.ask_before_filling
|
|
237
|
+
):
|
|
238
|
+
available_slot_names.add(current_step.collect)
|
|
221
239
|
|
|
222
240
|
for _, slot in tracker.slots.items():
|
|
223
241
|
# if a slot is not collected in available flows,
|
|
@@ -23,7 +23,6 @@ from rasa.dialogue_understanding.generator.constants import (
|
|
|
23
23
|
USER_INPUT_CONFIG_KEY,
|
|
24
24
|
FLOW_RETRIEVAL_KEY,
|
|
25
25
|
DEFAULT_LLM_CONFIG,
|
|
26
|
-
TRAINED_MODEL_NAME_CONFIG_KEY,
|
|
27
26
|
)
|
|
28
27
|
from rasa.dialogue_understanding.generator.flow_retrieval import (
|
|
29
28
|
FlowRetrieval,
|
|
@@ -54,7 +53,6 @@ from rasa.shared.utils.llm import (
|
|
|
54
53
|
sanitize_message_for_prompt,
|
|
55
54
|
resolve_model_client_config,
|
|
56
55
|
)
|
|
57
|
-
from rasa.shared.utils.health_check import perform_inference_time_llm_health_check
|
|
58
56
|
from rasa.utils.beta import ensure_beta_feature_is_enabled, BetaNotEnabledException
|
|
59
57
|
from rasa.utils.log_utils import log_llm
|
|
60
58
|
|
|
@@ -64,6 +62,7 @@ DEFAULT_COMMAND_PROMPT_TEMPLATE = importlib.resources.read_text(
|
|
|
64
62
|
"rasa.dialogue_understanding.generator.single_step",
|
|
65
63
|
"command_prompt_template.jinja2",
|
|
66
64
|
)
|
|
65
|
+
SINGLE_STEP_LLM_COMMAND_GENERATOR_CONFIG_FILE = "config.json"
|
|
67
66
|
|
|
68
67
|
structlogger = structlog.get_logger()
|
|
69
68
|
|
|
@@ -114,6 +113,7 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
|
|
|
114
113
|
)
|
|
115
114
|
|
|
116
115
|
self.trace_prompt_tokens = self.config.get("trace_prompt_tokens", False)
|
|
116
|
+
self.repeat_command_enabled = self.is_repeat_command_enabled()
|
|
117
117
|
|
|
118
118
|
### Implementations of LLMBasedCommandGenerator parent
|
|
119
119
|
@staticmethod
|
|
@@ -137,6 +137,16 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
|
|
|
137
137
|
**kwargs: Any,
|
|
138
138
|
) -> "SingleStepLLMCommandGenerator":
|
|
139
139
|
"""Loads trained component (see parent class for full docstring)."""
|
|
140
|
+
|
|
141
|
+
# Perform health check of the LLM API endpoint
|
|
142
|
+
llm_config = resolve_model_client_config(config.get(LLM_CONFIG_KEY, {}))
|
|
143
|
+
cls.perform_llm_health_check(
|
|
144
|
+
llm_config,
|
|
145
|
+
DEFAULT_LLM_CONFIG,
|
|
146
|
+
"single_step_llm_command_generator.load",
|
|
147
|
+
SingleStepLLMCommandGenerator.__name__,
|
|
148
|
+
)
|
|
149
|
+
|
|
140
150
|
# load prompt template from the model storage.
|
|
141
151
|
prompt_template = cls.load_prompt_template_from_model_storage(
|
|
142
152
|
model_storage, resource, COMMAND_PROMPT_FILE_NAME
|
|
@@ -150,34 +160,28 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
|
|
|
150
160
|
command_generator.config, model_storage, resource
|
|
151
161
|
)
|
|
152
162
|
|
|
153
|
-
persisted_config = cls.load_config_from_model_storage(model_storage, resource)
|
|
154
|
-
train_model_name = (
|
|
155
|
-
persisted_config.get(TRAINED_MODEL_NAME_CONFIG_KEY, None)
|
|
156
|
-
if persisted_config
|
|
157
|
-
else None
|
|
158
|
-
)
|
|
159
|
-
perform_inference_time_llm_health_check(
|
|
160
|
-
command_generator.config.get(LLM_CONFIG_KEY),
|
|
161
|
-
DEFAULT_LLM_CONFIG,
|
|
162
|
-
train_model_name,
|
|
163
|
-
"single_step_llm_command_generator.load",
|
|
164
|
-
SingleStepLLMCommandGenerator.__name__,
|
|
165
|
-
)
|
|
166
|
-
|
|
167
163
|
return command_generator
|
|
168
164
|
|
|
169
165
|
def persist(self) -> None:
|
|
170
166
|
"""Persist this component to disk for future loading."""
|
|
171
|
-
|
|
172
|
-
|
|
167
|
+
self._persist_prompt_template()
|
|
168
|
+
self._persist_config()
|
|
169
|
+
if self.flow_retrieval is not None:
|
|
170
|
+
self.flow_retrieval.persist()
|
|
173
171
|
|
|
172
|
+
def _persist_prompt_template(self) -> None:
|
|
173
|
+
"""Persist prompt template for future loading."""
|
|
174
174
|
with self._model_storage.write_to(self._resource) as path:
|
|
175
175
|
rasa.shared.utils.io.write_text_file(
|
|
176
176
|
self.prompt_template, path / COMMAND_PROMPT_FILE_NAME
|
|
177
177
|
)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
|
|
179
|
+
def _persist_config(self) -> None:
|
|
180
|
+
"""Persist config as a source of truth for resolved clients."""
|
|
181
|
+
with self._model_storage.write_to(self._resource) as path:
|
|
182
|
+
rasa.shared.utils.io.dump_obj_as_json_to_file(
|
|
183
|
+
path / SINGLE_STEP_LLM_COMMAND_GENERATOR_CONFIG_FILE, self.config
|
|
184
|
+
)
|
|
181
185
|
|
|
182
186
|
async def predict_commands(
|
|
183
187
|
self,
|
|
@@ -455,7 +459,7 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
|
|
|
455
459
|
"current_slot": current_slot,
|
|
456
460
|
"current_slot_description": current_slot_description,
|
|
457
461
|
"user_message": latest_user_message,
|
|
458
|
-
"is_repeat_command_enabled": self.
|
|
462
|
+
"is_repeat_command_enabled": self.repeat_command_enabled,
|
|
459
463
|
}
|
|
460
464
|
|
|
461
465
|
return self.compile_template(self.prompt_template).render(**inputs)
|
|
@@ -111,6 +111,15 @@ slots:
|
|
|
111
111
|
type: bool
|
|
112
112
|
mappings:
|
|
113
113
|
- type: from_llm
|
|
114
|
+
silence_timeout:
|
|
115
|
+
type: float
|
|
116
|
+
initial_value: 6.0
|
|
117
|
+
max_value: 1000000
|
|
118
|
+
consecutive_silence_timeouts:
|
|
119
|
+
type: float
|
|
120
|
+
initial_value: 0.0
|
|
121
|
+
max_value: 1000000
|
|
122
|
+
|
|
114
123
|
|
|
115
124
|
flows:
|
|
116
125
|
pattern_cancel_flow:
|
|
@@ -8,6 +8,7 @@ from rasa.dialogue_understanding.commands import (
|
|
|
8
8
|
Command,
|
|
9
9
|
CorrectSlotsCommand,
|
|
10
10
|
CorrectedSlot,
|
|
11
|
+
RepeatBotMessagesCommand,
|
|
11
12
|
SetSlotCommand,
|
|
12
13
|
StartFlowCommand,
|
|
13
14
|
FreeFormAnswerCommand,
|
|
@@ -422,6 +423,9 @@ def clean_up_commands(
|
|
|
422
423
|
elif not tracker.has_coexistence_routing_slot and len(clean_commands) > 1:
|
|
423
424
|
clean_commands = filter_cannot_handle_command_for_skipped_slots(clean_commands)
|
|
424
425
|
|
|
426
|
+
clean_commands = ensure_max_number_of_command_type(
|
|
427
|
+
clean_commands, RepeatBotMessagesCommand, 1
|
|
428
|
+
)
|
|
425
429
|
structlogger.debug(
|
|
426
430
|
"command_processor.clean_up_commands.final_commands",
|
|
427
431
|
command=clean_commands,
|
|
@@ -430,6 +434,22 @@ def clean_up_commands(
|
|
|
430
434
|
return clean_commands
|
|
431
435
|
|
|
432
436
|
|
|
437
|
+
def ensure_max_number_of_command_type(
|
|
438
|
+
commands: List[Command], command_type: Type[Command], n: int
|
|
439
|
+
) -> List[Command]:
|
|
440
|
+
"""Ensures that for a given command type only the first n stay in the list."""
|
|
441
|
+
filtered: List[Command] = []
|
|
442
|
+
count = 0
|
|
443
|
+
for c in commands:
|
|
444
|
+
if isinstance(c, command_type):
|
|
445
|
+
if count >= n:
|
|
446
|
+
continue
|
|
447
|
+
else:
|
|
448
|
+
count += 1
|
|
449
|
+
filtered.append(c)
|
|
450
|
+
return filtered
|
|
451
|
+
|
|
452
|
+
|
|
433
453
|
def clean_up_clarify_command(
|
|
434
454
|
commands_so_far: List[Command],
|
|
435
455
|
all_commands: List[Command],
|
|
@@ -452,7 +472,7 @@ def clean_up_clarify_command(
|
|
|
452
472
|
if not (isinstance(c, SetSlotCommand) and c.name == ROUTE_TO_CALM_SLOT)
|
|
453
473
|
]
|
|
454
474
|
|
|
455
|
-
# if
|
|
475
|
+
# if all commands are clarify commands, add the first one only, otherwise add none
|
|
456
476
|
if all(
|
|
457
477
|
isinstance(c, ClarifyCommand) for c in commands_without_route_to_calm_set_slot
|
|
458
478
|
):
|