rasa-pro 3.11.0a4.dev3__py3-none-any.whl → 3.11.0rc2__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 +22 -12
- rasa/api.py +1 -1
- rasa/cli/arguments/default_arguments.py +1 -2
- rasa/cli/arguments/shell.py +5 -1
- rasa/cli/e2e_test.py +1 -1
- rasa/cli/evaluate.py +8 -8
- rasa/cli/inspect.py +6 -4
- rasa/cli/llm_fine_tuning.py +1 -1
- rasa/cli/project_templates/calm/config.yml +5 -7
- rasa/cli/project_templates/calm/endpoints.yml +8 -0
- rasa/cli/project_templates/tutorial/config.yml +8 -5
- rasa/cli/project_templates/tutorial/data/flows.yml +1 -1
- rasa/cli/project_templates/tutorial/data/patterns.yml +5 -0
- rasa/cli/project_templates/tutorial/domain.yml +14 -0
- rasa/cli/project_templates/tutorial/endpoints.yml +7 -7
- rasa/cli/run.py +1 -1
- rasa/cli/scaffold.py +4 -2
- rasa/cli/studio/studio.py +18 -8
- rasa/cli/utils.py +5 -0
- rasa/cli/x.py +8 -8
- rasa/constants.py +1 -1
- rasa/core/actions/action_repeat_bot_messages.py +17 -0
- rasa/core/channels/channel.py +20 -0
- rasa/core/channels/inspector/dist/assets/{arc-6852c607.js → arc-bc141fb2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-acc952b2.js → c4Diagram-d0fbc5ce-be2db283.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-848a7597.js → classDiagram-936ed81e-55366915.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-a73d3e68.js → classDiagram-v2-c3cb15f1-bb529518.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{createText-62fc7601-e5ee049d.js → createText-62fc7601-b0ec81d6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-771e517e.js → edges-f2ad444c-6166330c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-aa347178.js → erDiagram-9d236eb7-5ccc6a8e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-651fc57d.js → flowDb-1972c806-fca3bfe4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-ca67804f.js → flowDiagram-7ea5b25a-4739080f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-736177bf.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-2dbc568d.js → flowchart-elk-definition-abe16c3d-7c1b0e0f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-25a65bd8.js → ganttDiagram-9b5ea136-772fd050.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-fdc7378d.js → gitGraphDiagram-99d0ae7c-8eae1dc9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-6f1fd606.js → index-2c4b9a3b-f55afcdf.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-efdd30c1.js → index-e7cef9de.js} +68 -68
- rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-cb1a041a.js → infoDiagram-736b4530-124d4a14.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-14609879.js → journeyDiagram-df861f2b-7c4fae44.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-2490f52b.js → layout-b9885fb6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-40186f1f.js → line-7c59abb6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-08814e93.js → linear-4776f780.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-1a534584.js → mindmap-definition-beec6740-2332c46c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-72397b61.js → pieDiagram-dbbf0591-8fb39303.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-3bb0b6a3.js → quadrantDiagram-4d7f4fd6-3c7180a2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-57334f61.js → requirementDiagram-6fc4c22a-e910bcb8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-111e1297.js → sankeyDiagram-8f13d901-ead16c89.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-10bcfe62.js → sequenceDiagram-b655622a-29a02a19.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-acaf7513.js → stateDiagram-59f0c015-042b3137.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-3ec2a235.js → stateDiagram-v2-2b26beab-2178c0f3.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-080da4f6-62730289.js → styles-080da4f6-23ffa4fc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-5284ee76.js → styles-3dcbcfbf-94f59763.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9c745c82-642435e3.js → styles-9c745c82-78a6bebc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-b250a350.js → svgDrawCommon-4835440b-eae2a6f6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-c2b147ed.js → timeline-definition-5b62e21b-5c968d92.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-f92cfea9.js → xychartDiagram-2b33534f-fd3db0d5.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/src/App.tsx +1 -1
- rasa/core/channels/inspector/src/helpers/audiostream.ts +77 -16
- rasa/core/channels/socketio.py +2 -1
- rasa/core/channels/telegram.py +1 -1
- rasa/core/channels/twilio.py +1 -1
- rasa/core/channels/voice_ready/audiocodes.py +12 -0
- rasa/core/channels/voice_ready/jambonz.py +15 -4
- rasa/core/channels/voice_ready/twilio_voice.py +6 -21
- rasa/core/channels/voice_stream/asr/asr_event.py +5 -0
- rasa/core/channels/voice_stream/asr/azure.py +122 -0
- rasa/core/channels/voice_stream/asr/deepgram.py +16 -6
- rasa/core/channels/voice_stream/audio_bytes.py +1 -0
- rasa/core/channels/voice_stream/browser_audio.py +31 -8
- rasa/core/channels/voice_stream/call_state.py +23 -0
- rasa/core/channels/voice_stream/tts/azure.py +6 -2
- rasa/core/channels/voice_stream/tts/cartesia.py +10 -6
- rasa/core/channels/voice_stream/tts/tts_engine.py +1 -0
- rasa/core/channels/voice_stream/twilio_media_streams.py +27 -18
- rasa/core/channels/voice_stream/util.py +4 -4
- rasa/core/channels/voice_stream/voice_channel.py +189 -39
- rasa/core/featurizers/single_state_featurizer.py +22 -1
- rasa/core/featurizers/tracker_featurizers.py +115 -18
- rasa/core/nlg/contextual_response_rephraser.py +32 -30
- rasa/core/persistor.py +86 -39
- rasa/core/policies/enterprise_search_policy.py +119 -60
- rasa/core/policies/flows/flow_executor.py +7 -4
- rasa/core/policies/intentless_policy.py +78 -22
- rasa/core/policies/ted_policy.py +58 -33
- rasa/core/policies/unexpected_intent_policy.py +15 -7
- rasa/core/processor.py +25 -0
- rasa/core/training/interactive.py +34 -35
- rasa/core/utils.py +8 -3
- rasa/dialogue_understanding/coexistence/llm_based_router.py +39 -12
- rasa/dialogue_understanding/commands/change_flow_command.py +6 -0
- rasa/dialogue_understanding/commands/user_silence_command.py +59 -0
- rasa/dialogue_understanding/commands/utils.py +5 -0
- rasa/dialogue_understanding/generator/constants.py +2 -0
- rasa/dialogue_understanding/generator/flow_retrieval.py +49 -4
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +37 -23
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +57 -10
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +19 -1
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +71 -11
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +39 -0
- rasa/dialogue_understanding/patterns/user_silence.py +37 -0
- rasa/dialogue_understanding/processor/command_processor.py +21 -1
- rasa/e2e_test/e2e_test_case.py +85 -6
- rasa/e2e_test/e2e_test_runner.py +4 -2
- rasa/e2e_test/utils/io.py +1 -1
- rasa/engine/validation.py +316 -10
- rasa/model_manager/config.py +15 -3
- rasa/model_manager/model_api.py +15 -7
- rasa/model_manager/runner_service.py +8 -6
- rasa/model_manager/socket_bridge.py +6 -3
- rasa/model_manager/trainer_service.py +7 -5
- rasa/model_manager/utils.py +28 -7
- rasa/model_service.py +9 -2
- rasa/model_training.py +2 -0
- rasa/nlu/classifiers/diet_classifier.py +38 -25
- rasa/nlu/classifiers/logistic_regression_classifier.py +22 -9
- rasa/nlu/classifiers/sklearn_intent_classifier.py +37 -16
- rasa/nlu/extractors/crf_entity_extractor.py +93 -50
- rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +45 -16
- rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +52 -17
- rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +5 -3
- rasa/nlu/tokenizers/whitespace_tokenizer.py +3 -14
- rasa/server.py +3 -1
- rasa/shared/constants.py +36 -3
- rasa/shared/core/constants.py +7 -0
- rasa/shared/core/domain.py +26 -0
- rasa/shared/core/flows/flow.py +5 -0
- rasa/shared/core/flows/flows_list.py +5 -1
- rasa/shared/core/flows/flows_yaml_schema.json +10 -0
- rasa/shared/core/flows/utils.py +39 -0
- rasa/shared/core/flows/validation.py +96 -0
- rasa/shared/core/slots.py +5 -0
- rasa/shared/nlu/training_data/features.py +120 -2
- rasa/shared/providers/_configs/azure_openai_client_config.py +5 -3
- rasa/shared/providers/_configs/litellm_router_client_config.py +200 -0
- rasa/shared/providers/_configs/model_group_config.py +167 -0
- rasa/shared/providers/_configs/openai_client_config.py +1 -1
- rasa/shared/providers/_configs/rasa_llm_client_config.py +73 -0
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -0
- rasa/shared/providers/_configs/utils.py +16 -0
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +18 -29
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +54 -21
- rasa/shared/providers/embedding/litellm_router_embedding_client.py +135 -0
- rasa/shared/providers/llm/_base_litellm_client.py +37 -31
- rasa/shared/providers/llm/azure_openai_llm_client.py +50 -29
- rasa/shared/providers/llm/litellm_router_llm_client.py +127 -0
- rasa/shared/providers/llm/rasa_llm_client.py +112 -0
- rasa/shared/providers/llm/self_hosted_llm_client.py +1 -1
- rasa/shared/providers/mappings.py +19 -0
- rasa/shared/providers/router/__init__.py +0 -0
- rasa/shared/providers/router/_base_litellm_router_client.py +149 -0
- rasa/shared/providers/router/router_client.py +73 -0
- rasa/shared/utils/common.py +8 -0
- 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/io.py +28 -6
- rasa/shared/utils/llm.py +353 -46
- rasa/shared/utils/yaml.py +111 -73
- 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 +81 -26
- rasa/telemetry.py +92 -17
- rasa/tracing/config.py +2 -0
- rasa/tracing/instrumentation/attribute_extractors.py +94 -17
- rasa/tracing/instrumentation/instrumentation.py +121 -0
- rasa/utils/common.py +5 -0
- rasa/utils/io.py +7 -81
- rasa/utils/log_utils.py +9 -2
- rasa/utils/sanic_error_handler.py +32 -0
- rasa/utils/tensorflow/feature_array.py +366 -0
- rasa/utils/tensorflow/model_data.py +2 -193
- rasa/validator.py +70 -0
- rasa/version.py +1 -1
- {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/METADATA +11 -10
- {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/RECORD +183 -163
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-587d82d8.js +0 -1
- {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/NOTICE +0 -0
- {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/WHEEL +0 -0
- {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/entry_points.txt +0 -0
|
@@ -4,6 +4,11 @@ responses:
|
|
|
4
4
|
utter_ask_rephrase:
|
|
5
5
|
- text: I’m sorry I am unable to understand you, could you please rephrase?
|
|
6
6
|
|
|
7
|
+
utter_ask_still_there:
|
|
8
|
+
- text: "Hello, are you still there?"
|
|
9
|
+
metadata:
|
|
10
|
+
rephrase: True
|
|
11
|
+
|
|
7
12
|
utter_boolean_slot_rejection:
|
|
8
13
|
- text: "Sorry, the value you provided, `{{value}}`, is not valid. Please respond with a valid value."
|
|
9
14
|
metadata:
|
|
@@ -75,6 +80,11 @@ responses:
|
|
|
75
80
|
metadata:
|
|
76
81
|
rephrase: True
|
|
77
82
|
|
|
83
|
+
utter_inform_hangup:
|
|
84
|
+
- text: It seems you are not there anymore. I will hang up shortly.
|
|
85
|
+
metadata:
|
|
86
|
+
rephrase: True
|
|
87
|
+
|
|
78
88
|
utter_internal_error_rasa:
|
|
79
89
|
- text: Sorry, I am having trouble with that. Please try again in a few minutes.
|
|
80
90
|
|
|
@@ -251,3 +261,32 @@ flows:
|
|
|
251
261
|
name: pattern skip question
|
|
252
262
|
steps:
|
|
253
263
|
- action: utter_skip_question_answer
|
|
264
|
+
|
|
265
|
+
pattern_user_silence:
|
|
266
|
+
description: Reacting to user silence in voice bots
|
|
267
|
+
name: pattern react to silence
|
|
268
|
+
nlu_trigger:
|
|
269
|
+
- intent: silence_timeout
|
|
270
|
+
persisted_slots:
|
|
271
|
+
- consecutive_silence_timeouts
|
|
272
|
+
steps:
|
|
273
|
+
- noop: true
|
|
274
|
+
next:
|
|
275
|
+
- if: "slots.consecutive_silence_timeouts = 0.0"
|
|
276
|
+
then:
|
|
277
|
+
- set_slots:
|
|
278
|
+
- consecutive_silence_timeouts: 1.0
|
|
279
|
+
- action: action_repeat_bot_messages
|
|
280
|
+
next: END
|
|
281
|
+
- if: "slots.consecutive_silence_timeouts = 1.0"
|
|
282
|
+
then:
|
|
283
|
+
- set_slots:
|
|
284
|
+
- consecutive_silence_timeouts: 2.0
|
|
285
|
+
- action: utter_ask_still_there
|
|
286
|
+
next: END
|
|
287
|
+
- if: "slots.consecutive_silence_timeouts > 1.0"
|
|
288
|
+
then:
|
|
289
|
+
- action: utter_inform_hangup
|
|
290
|
+
- action: action_hangup
|
|
291
|
+
next: END
|
|
292
|
+
- else: END
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Any, Dict
|
|
4
|
+
|
|
5
|
+
from rasa.dialogue_understanding.stack.frames import PatternFlowStackFrame
|
|
6
|
+
from rasa.shared.constants import RASA_DEFAULT_FLOW_PATTERN_PREFIX
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
FLOW_PATTERN_USER_SILENCE = RASA_DEFAULT_FLOW_PATTERN_PREFIX + "user_silence"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class UserSilencePatternFlowStackFrame(PatternFlowStackFrame):
|
|
14
|
+
"""A flow stack frame that can get added when the silence timeout trips."""
|
|
15
|
+
|
|
16
|
+
flow_id: str = FLOW_PATTERN_USER_SILENCE
|
|
17
|
+
"""The ID of the flow."""
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def type(cls) -> str:
|
|
21
|
+
"""Returns the type of the frame."""
|
|
22
|
+
return FLOW_PATTERN_USER_SILENCE
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def from_dict(data: Dict[str, Any]) -> UserSilencePatternFlowStackFrame:
|
|
26
|
+
"""Creates a `DialogueStackFrame` from a dictionary.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
data: The dictionary to create the `DialogueStackFrame` from.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
The created `DialogueStackFrame`.
|
|
33
|
+
"""
|
|
34
|
+
return UserSilencePatternFlowStackFrame(
|
|
35
|
+
frame_id=data["frame_id"],
|
|
36
|
+
step_id=data["step_id"],
|
|
37
|
+
)
|
|
@@ -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
|
):
|
rasa/e2e_test/e2e_test_case.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import logging
|
|
2
1
|
from collections import OrderedDict
|
|
2
|
+
from collections import defaultdict
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from typing import Any, Dict, List, Optional, Text, Union
|
|
5
5
|
|
|
6
|
+
import structlog
|
|
7
|
+
|
|
6
8
|
from rasa.e2e_test.assertions import Assertion
|
|
7
9
|
from rasa.e2e_test.constants import (
|
|
8
10
|
KEY_ASSERTIONS,
|
|
@@ -20,10 +22,11 @@ from rasa.e2e_test.constants import (
|
|
|
20
22
|
KEY_USER_INPUT,
|
|
21
23
|
)
|
|
22
24
|
from rasa.e2e_test.stub_custom_action import StubCustomAction
|
|
25
|
+
from rasa.shared.constants import DOCS_BASE_URL
|
|
23
26
|
from rasa.shared.core.events import BotUttered, SlotSet, UserUttered
|
|
24
27
|
from rasa.shared.exceptions import RasaException
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
structlogger = structlog.get_logger(__name__)
|
|
27
30
|
|
|
28
31
|
|
|
29
32
|
@dataclass(frozen=True)
|
|
@@ -343,9 +346,10 @@ class ActualStepOutput:
|
|
|
343
346
|
try:
|
|
344
347
|
return self.user_uttered_events[0]
|
|
345
348
|
except IndexError:
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
f"
|
|
349
|
+
structlogger.debug(
|
|
350
|
+
"e2e_test_case.get_user_uttered_event.no_user_uttered_event",
|
|
351
|
+
event_info=f"Could not find `UserUttered` event in the "
|
|
352
|
+
f"ActualStepOutput: {self}",
|
|
349
353
|
)
|
|
350
354
|
return None
|
|
351
355
|
return None
|
|
@@ -395,7 +399,7 @@ class TestCase:
|
|
|
395
399
|
else:
|
|
396
400
|
steps.append(TestStep.from_dict(step))
|
|
397
401
|
|
|
398
|
-
|
|
402
|
+
test_case = TestCase(
|
|
399
403
|
name=input_test_case.get(KEY_TEST_CASE, "default"),
|
|
400
404
|
steps=steps,
|
|
401
405
|
file=file,
|
|
@@ -405,6 +409,81 @@ class TestCase:
|
|
|
405
409
|
fixture_names=input_test_case.get(KEY_FIXTURES),
|
|
406
410
|
metadata_name=input_test_case.get(KEY_METADATA),
|
|
407
411
|
)
|
|
412
|
+
test_case.validate()
|
|
413
|
+
return test_case
|
|
414
|
+
|
|
415
|
+
def validate(self) -> None:
|
|
416
|
+
"""Validates the test case.
|
|
417
|
+
|
|
418
|
+
This method calls all validation methods required for the test case.
|
|
419
|
+
"""
|
|
420
|
+
if self.uses_assertions():
|
|
421
|
+
self.validate_duplicate_user_messages_metadata()
|
|
422
|
+
|
|
423
|
+
def validate_duplicate_user_messages_metadata(self) -> None:
|
|
424
|
+
"""Validates that duplicate user messages use metadata correctly.
|
|
425
|
+
|
|
426
|
+
Ensures that each duplicate user message uses unique metadata.
|
|
427
|
+
|
|
428
|
+
Raises warnings if any issues are found.
|
|
429
|
+
"""
|
|
430
|
+
docs_link = (
|
|
431
|
+
f"{DOCS_BASE_URL}/testing/e2e-testing-assertions/assertions-how-to-guide/"
|
|
432
|
+
"#how-to-handle-duplicate-user-text-messages-in-the-same-test-case"
|
|
433
|
+
)
|
|
434
|
+
no_metadata_event_info = (
|
|
435
|
+
"Test case '{name}' has duplicate user steps with text '{text}', "
|
|
436
|
+
"and user step at line {line} lacks metadata. When using "
|
|
437
|
+
"duplicate user messages, metadata should be set on each step to ensure "
|
|
438
|
+
f"correct processing. Please refer to the documentation: {docs_link}"
|
|
439
|
+
)
|
|
440
|
+
non_unique_metadata_event_info = (
|
|
441
|
+
"Test case '{name}' has duplicate user steps with text '{text}', "
|
|
442
|
+
"and user step at line {line} has duplicate metadata "
|
|
443
|
+
"name '{metadata_name}'. Metadata names should be unique for each user "
|
|
444
|
+
"step among duplicates. This may cause issues in processing "
|
|
445
|
+
f"user messages. Please refer to the documentation: {docs_link}"
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
# Use dict[str, list] structure to group steps by user message text to easily
|
|
449
|
+
# identify and validate instances with duplicate messages and their metadata.
|
|
450
|
+
message_steps = defaultdict(list)
|
|
451
|
+
|
|
452
|
+
# Collect user steps by text
|
|
453
|
+
for step in self.steps:
|
|
454
|
+
if step.actor == KEY_USER_INPUT and step.text:
|
|
455
|
+
message_steps[step.text].append(step)
|
|
456
|
+
|
|
457
|
+
# Check for duplicate messages
|
|
458
|
+
for text, steps in message_steps.items():
|
|
459
|
+
if len(steps) <= 1:
|
|
460
|
+
continue
|
|
461
|
+
|
|
462
|
+
metadata_names_used = set()
|
|
463
|
+
for step in steps:
|
|
464
|
+
if not step.metadata_name:
|
|
465
|
+
structlogger.warning(
|
|
466
|
+
"e2e_test_case.validate_duplicate_user_messages_metadata.no_metadata",
|
|
467
|
+
event_info=no_metadata_event_info.format(
|
|
468
|
+
name=self.name,
|
|
469
|
+
text=text,
|
|
470
|
+
line=step.line,
|
|
471
|
+
),
|
|
472
|
+
)
|
|
473
|
+
break
|
|
474
|
+
elif step.metadata_name in metadata_names_used:
|
|
475
|
+
structlogger.warning(
|
|
476
|
+
"e2e_test_case.validate_duplicate_user_messages_metadata.non_unique_metadata",
|
|
477
|
+
event_info=non_unique_metadata_event_info.format(
|
|
478
|
+
name=self.name,
|
|
479
|
+
text=text,
|
|
480
|
+
line=step.line,
|
|
481
|
+
metadata_name=step.metadata_name,
|
|
482
|
+
),
|
|
483
|
+
)
|
|
484
|
+
break
|
|
485
|
+
else:
|
|
486
|
+
metadata_names_used.add(step.metadata_name)
|
|
408
487
|
|
|
409
488
|
def as_dict(self) -> Dict[Text, Any]:
|
|
410
489
|
"""Returns the test case as a dictionary."""
|
rasa/e2e_test/e2e_test_runner.py
CHANGED
|
@@ -190,11 +190,11 @@ class E2ETestRunner:
|
|
|
190
190
|
error=f"Message handling timed out for user message '{step.text}'.",
|
|
191
191
|
exc_info=True,
|
|
192
192
|
)
|
|
193
|
-
except Exception:
|
|
193
|
+
except Exception as exc:
|
|
194
194
|
structlogger.error(
|
|
195
195
|
"e2e_test_runner.run_prediction_loop",
|
|
196
196
|
error=f"An exception occurred while handling "
|
|
197
|
-
f"user message '{step.text}'.",
|
|
197
|
+
f"user message '{step.text}'. Error: {exc}",
|
|
198
198
|
)
|
|
199
199
|
tracker = await self.agent.tracker_store.retrieve(sender_id) # type: ignore[assignment]
|
|
200
200
|
turns[position], event_cursor = self.get_actual_step_output(
|
|
@@ -1155,6 +1155,8 @@ class E2ETestRunner:
|
|
|
1155
1155
|
flow_paths_stack
|
|
1156
1156
|
and self.agent.domain
|
|
1157
1157
|
and self.agent.domain.is_custom_action(event.action_name)
|
|
1158
|
+
and STEP_ID_METADATA_KEY in event.metadata
|
|
1159
|
+
and ACTIVE_FLOW_METADATA_KEY in event.metadata
|
|
1158
1160
|
):
|
|
1159
1161
|
flow_paths_stack[-1].nodes.append(self._create_path_node(event))
|
|
1160
1162
|
|
rasa/e2e_test/utils/io.py
CHANGED
|
@@ -346,7 +346,7 @@ def read_test_cases(path: str) -> TestSuite:
|
|
|
346
346
|
beta_flag_verified = False
|
|
347
347
|
|
|
348
348
|
for test_file in test_files:
|
|
349
|
-
test_file_content = parse_raw_yaml(Path(test_file).read_text())
|
|
349
|
+
test_file_content = parse_raw_yaml(Path(test_file).read_text(encoding="utf-8"))
|
|
350
350
|
|
|
351
351
|
validate_yaml_data_using_schema_with_assertions(
|
|
352
352
|
yaml_data=test_file_content, schema_content=e2e_test_schema
|
rasa/engine/validation.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
import inspect
|
|
3
|
-
import re
|
|
4
3
|
import logging
|
|
4
|
+
import re
|
|
5
5
|
import sys
|
|
6
6
|
import typing
|
|
7
7
|
from typing import (
|
|
@@ -18,11 +18,10 @@ from typing import (
|
|
|
18
18
|
List,
|
|
19
19
|
)
|
|
20
20
|
|
|
21
|
+
import rasa.utils.common
|
|
21
22
|
import structlog
|
|
22
23
|
import typing_utils
|
|
23
|
-
|
|
24
|
-
import rasa.utils.common
|
|
25
|
-
from rasa.core import IntentlessPolicy
|
|
24
|
+
from rasa.core import IntentlessPolicy, ContextualResponseRephraser
|
|
26
25
|
from rasa.core.policies.policy import PolicyPrediction
|
|
27
26
|
from rasa.core.utils import AvailableEndpoints
|
|
28
27
|
from rasa.dialogue_understanding.coexistence.constants import (
|
|
@@ -31,9 +30,17 @@ from rasa.dialogue_understanding.coexistence.constants import (
|
|
|
31
30
|
STICKY,
|
|
32
31
|
NON_STICKY,
|
|
33
32
|
)
|
|
33
|
+
from rasa.dialogue_understanding.coexistence.intent_based_router import (
|
|
34
|
+
IntentBasedRouter,
|
|
35
|
+
)
|
|
36
|
+
from rasa.dialogue_understanding.coexistence.llm_based_router import LLMBasedRouter
|
|
34
37
|
from rasa.dialogue_understanding.generator import (
|
|
35
38
|
LLMBasedCommandGenerator,
|
|
36
39
|
)
|
|
40
|
+
from rasa.dialogue_understanding.generator.constants import (
|
|
41
|
+
LLM_CONFIG_KEY,
|
|
42
|
+
FLOW_RETRIEVAL_KEY,
|
|
43
|
+
)
|
|
37
44
|
from rasa.dialogue_understanding.patterns.chitchat import FLOW_PATTERN_CHITCHAT
|
|
38
45
|
from rasa.engine.constants import RESERVED_PLACEHOLDERS
|
|
39
46
|
from rasa.engine.exceptions import GraphSchemaValidationException
|
|
@@ -47,18 +54,32 @@ from rasa.engine.graph import (
|
|
|
47
54
|
from rasa.engine.storage.resource import Resource
|
|
48
55
|
from rasa.engine.storage.storage import ModelStorage
|
|
49
56
|
from rasa.engine.training.fingerprinting import Fingerprintable
|
|
50
|
-
from rasa.shared.constants import
|
|
57
|
+
from rasa.shared.constants import (
|
|
58
|
+
DOCS_URL_GRAPH_COMPONENTS,
|
|
59
|
+
ROUTE_TO_CALM_SLOT,
|
|
60
|
+
EMBEDDINGS_CONFIG_KEY,
|
|
61
|
+
API_BASE_CONFIG_KEY,
|
|
62
|
+
DEPLOYMENT_CONFIG_KEY,
|
|
63
|
+
API_VERSION_CONFIG_KEY,
|
|
64
|
+
API_KEY,
|
|
65
|
+
AWS_REGION_NAME_CONFIG_KEY,
|
|
66
|
+
MODEL_GROUP_ID_CONFIG_KEY,
|
|
67
|
+
ROUTER_CONFIG_KEY,
|
|
68
|
+
MODELS_CONFIG_KEY,
|
|
69
|
+
MODEL_GROUP_CONFIG_KEY,
|
|
70
|
+
ROUTER_STRATEGY_CONFIG_KEY,
|
|
71
|
+
VALID_ROUTER_STRATEGIES,
|
|
72
|
+
ROUTER_STRATEGIES_REQUIRING_REDIS_CACHE,
|
|
73
|
+
ROUTER_STRATEGIES_NOT_REQUIRING_CACHE,
|
|
74
|
+
REDIS_HOST_CONFIG_KEY,
|
|
75
|
+
)
|
|
51
76
|
from rasa.shared.core.constants import ACTION_RESET_ROUTING, ACTION_TRIGGER_CHITCHAT
|
|
52
77
|
from rasa.shared.core.domain import Domain
|
|
53
78
|
from rasa.shared.core.flows import FlowsList, Flow
|
|
54
79
|
from rasa.shared.core.slots import Slot
|
|
55
80
|
from rasa.shared.exceptions import RasaException
|
|
56
81
|
from rasa.shared.nlu.training_data.message import Message
|
|
57
|
-
|
|
58
|
-
from rasa.dialogue_understanding.coexistence.intent_based_router import (
|
|
59
|
-
IntentBasedRouter,
|
|
60
|
-
)
|
|
61
|
-
from rasa.dialogue_understanding.coexistence.llm_based_router import LLMBasedRouter
|
|
82
|
+
from rasa.shared.utils.cli import print_error_and_exit
|
|
62
83
|
|
|
63
84
|
TypeAnnotation = Union[TypeVar, Text, Type, Optional[AvailableEndpoints]]
|
|
64
85
|
|
|
@@ -845,6 +866,291 @@ def validate_coexistance_routing_setup(
|
|
|
845
866
|
)
|
|
846
867
|
|
|
847
868
|
|
|
869
|
+
def _validate_component_model_client_config(
|
|
870
|
+
component_config: Dict[str, Any],
|
|
871
|
+
key: str,
|
|
872
|
+
model_group_syntax_used: List[bool],
|
|
873
|
+
model_group_ids: List[str],
|
|
874
|
+
component_name: Optional[str] = None,
|
|
875
|
+
) -> None:
|
|
876
|
+
"""Validate the LLM configuration of a component.
|
|
877
|
+
|
|
878
|
+
Checks if the llm is defined using the new syntax or the old syntax.
|
|
879
|
+
If the new syntax is used, it checks that no other parameters are present.
|
|
880
|
+
|
|
881
|
+
Args:
|
|
882
|
+
component_config: The config of the component
|
|
883
|
+
key: either 'llm' or 'embeddings'
|
|
884
|
+
model_group_syntax_used:
|
|
885
|
+
list of booleans indicating whether the new syntax is used
|
|
886
|
+
model_group_ids: list of model group ids
|
|
887
|
+
component_name: the name of the component
|
|
888
|
+
"""
|
|
889
|
+
if key not in component_config:
|
|
890
|
+
# no llm configuration present
|
|
891
|
+
return
|
|
892
|
+
|
|
893
|
+
if MODEL_GROUP_CONFIG_KEY in component_config[key]:
|
|
894
|
+
model_group_syntax_used.append(True)
|
|
895
|
+
model_group_ids.append(component_config[key][MODEL_GROUP_CONFIG_KEY])
|
|
896
|
+
|
|
897
|
+
if len(component_config[key]) > 1:
|
|
898
|
+
print_error_and_exit(
|
|
899
|
+
f"You specified a '{MODEL_GROUP_CONFIG_KEY}' for the '{key}' "
|
|
900
|
+
f"config key for the component "
|
|
901
|
+
f"'{component_name or component_config['name']}'. "
|
|
902
|
+
"No other parameters are allowed under the "
|
|
903
|
+
f"'{key}' key in that case. Please update your config."
|
|
904
|
+
)
|
|
905
|
+
else:
|
|
906
|
+
model_group_syntax_used.append(False)
|
|
907
|
+
|
|
908
|
+
# check that api_key is not set in config
|
|
909
|
+
if API_KEY in component_config[key]:
|
|
910
|
+
print_error_and_exit(
|
|
911
|
+
f"You specified '{API_KEY}' in the config for "
|
|
912
|
+
f"'{component_name or component_config['name']}', which is not allowed."
|
|
913
|
+
" Set API keys through environment variables."
|
|
914
|
+
)
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
def validate_model_client_configuration_setup(config: Dict[str, Any]) -> None:
|
|
918
|
+
"""Validates the model client configuration setup.
|
|
919
|
+
|
|
920
|
+
Checks the model configuration of the components in the pipeline.
|
|
921
|
+
Validation fails, if
|
|
922
|
+
- the LLM/embeddings is/are defined using the old and the new syntax at
|
|
923
|
+
the same time (either at component level itself or across different components)
|
|
924
|
+
- the LLM/embeddings is/are defined using the new syntax, but no model
|
|
925
|
+
group is defined or the referenced model group does not exist
|
|
926
|
+
|
|
927
|
+
Args:
|
|
928
|
+
config: The config dictionary
|
|
929
|
+
"""
|
|
930
|
+
|
|
931
|
+
def is_uniform_bool_list(bool_list: List[bool]) -> bool:
|
|
932
|
+
# check if list contains only True or False
|
|
933
|
+
return all(bool_list) or not any(bool_list)
|
|
934
|
+
|
|
935
|
+
model_group_syntax_used: List[bool] = []
|
|
936
|
+
model_group_ids: List[str] = []
|
|
937
|
+
|
|
938
|
+
for outer_key in ["pipeline", "policies"]:
|
|
939
|
+
if outer_key not in config or config[outer_key] is None:
|
|
940
|
+
continue
|
|
941
|
+
|
|
942
|
+
for component in config[outer_key]:
|
|
943
|
+
for key in [LLM_CONFIG_KEY, EMBEDDINGS_CONFIG_KEY]:
|
|
944
|
+
_validate_component_model_client_config(
|
|
945
|
+
component, key, model_group_syntax_used, model_group_ids
|
|
946
|
+
)
|
|
947
|
+
|
|
948
|
+
# as flow retrieval is not a component itself, we need to
|
|
949
|
+
# check it separately
|
|
950
|
+
if FLOW_RETRIEVAL_KEY in component:
|
|
951
|
+
if EMBEDDINGS_CONFIG_KEY in component[FLOW_RETRIEVAL_KEY]:
|
|
952
|
+
_validate_component_model_client_config(
|
|
953
|
+
component[FLOW_RETRIEVAL_KEY],
|
|
954
|
+
EMBEDDINGS_CONFIG_KEY,
|
|
955
|
+
model_group_syntax_used,
|
|
956
|
+
model_group_ids,
|
|
957
|
+
component["name"] + "." + FLOW_RETRIEVAL_KEY,
|
|
958
|
+
)
|
|
959
|
+
|
|
960
|
+
# also include the ContextualResponseRephraser component
|
|
961
|
+
endpoints = AvailableEndpoints.get_instance()
|
|
962
|
+
if endpoints.nlg is not None:
|
|
963
|
+
_validate_component_model_client_config(
|
|
964
|
+
endpoints.nlg.kwargs,
|
|
965
|
+
LLM_CONFIG_KEY,
|
|
966
|
+
model_group_syntax_used,
|
|
967
|
+
model_group_ids,
|
|
968
|
+
ContextualResponseRephraser.__name__,
|
|
969
|
+
)
|
|
970
|
+
|
|
971
|
+
if not is_uniform_bool_list(model_group_syntax_used):
|
|
972
|
+
print_error_and_exit(
|
|
973
|
+
"Some of your components refer to an LLM using the "
|
|
974
|
+
f"'{MODEL_GROUP_CONFIG_KEY}' parameter, other components directly"
|
|
975
|
+
f" define the LLM under the '{LLM_CONFIG_KEY}' or the "
|
|
976
|
+
f"'{EMBEDDINGS_CONFIG_KEY}' key. You cannot use"
|
|
977
|
+
" both types of definitions. Please chose one syntax "
|
|
978
|
+
"and update your config."
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
# Print a deprecation warning in case the old syntax is used.
|
|
982
|
+
if len(model_group_syntax_used) > 0 and model_group_syntax_used[0] is False:
|
|
983
|
+
structlogger.warning(
|
|
984
|
+
"validate_llm_configuration_setup",
|
|
985
|
+
event_info=(
|
|
986
|
+
"Defining the LLM configuration in the config.yml file itself is"
|
|
987
|
+
" deprecated and will be removed in Rasa 4.0.0. "
|
|
988
|
+
"Please use the new syntax and define your LLM configuration"
|
|
989
|
+
"in the endpoints.yml file."
|
|
990
|
+
),
|
|
991
|
+
)
|
|
992
|
+
|
|
993
|
+
endpoints = AvailableEndpoints.get_instance()
|
|
994
|
+
if len(model_group_ids) > 0 and endpoints.model_groups is None:
|
|
995
|
+
print_error_and_exit(
|
|
996
|
+
"You are referring to (a) model group(s) in your "
|
|
997
|
+
"config.yml file, but no model group was defined in "
|
|
998
|
+
"the endpoints.yml file. Please define the model "
|
|
999
|
+
"group(s)."
|
|
1000
|
+
)
|
|
1001
|
+
|
|
1002
|
+
if endpoints.model_groups is None:
|
|
1003
|
+
return
|
|
1004
|
+
|
|
1005
|
+
existing_model_group_ids = [
|
|
1006
|
+
model_group[MODEL_GROUP_ID_CONFIG_KEY] for model_group in endpoints.model_groups
|
|
1007
|
+
]
|
|
1008
|
+
|
|
1009
|
+
for model_group_id in model_group_ids:
|
|
1010
|
+
if model_group_id not in existing_model_group_ids:
|
|
1011
|
+
print_error_and_exit(
|
|
1012
|
+
"One of your components is referring to the model group "
|
|
1013
|
+
f"'{model_group_id}', but this model group does not exist in the "
|
|
1014
|
+
f"endpoints.yml file. Please chose one of the existing "
|
|
1015
|
+
f"model groups ({existing_model_group_ids}) or define "
|
|
1016
|
+
f"the a model group for '{model_group_id}'."
|
|
1017
|
+
)
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
def _validate_unique_model_group_ids(model_groups: List[Dict[str, Any]]) -> None:
|
|
1021
|
+
# Each model id must be unique within the model_groups
|
|
1022
|
+
model_ids = [model_group[MODEL_GROUP_ID_CONFIG_KEY] for model_group in model_groups]
|
|
1023
|
+
if len(model_ids) != len(set(model_ids)):
|
|
1024
|
+
print_error_and_exit(
|
|
1025
|
+
"Each model group id must be unique. Please make sure that "
|
|
1026
|
+
"the model group ids are unique in your endpoints.yml file."
|
|
1027
|
+
)
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
def _validate_model_group_with_multiple_models(
|
|
1031
|
+
model_groups: List[Dict[str, Any]],
|
|
1032
|
+
) -> None:
|
|
1033
|
+
# You cannot define multiple models within a model group, when no router is defined.
|
|
1034
|
+
for model_group in model_groups:
|
|
1035
|
+
if (
|
|
1036
|
+
len(model_group[MODELS_CONFIG_KEY]) > 1
|
|
1037
|
+
and ROUTER_CONFIG_KEY not in model_group
|
|
1038
|
+
):
|
|
1039
|
+
print_error_and_exit(
|
|
1040
|
+
f"You defined multiple models for the model group "
|
|
1041
|
+
f"'{model_group[MODEL_GROUP_ID_CONFIG_KEY]}', but no router. "
|
|
1042
|
+
f"If a model group contains "
|
|
1043
|
+
f"multiple models, a router must be defined. Please define a router "
|
|
1044
|
+
f"for the model group '{model_group[MODEL_GROUP_ID_CONFIG_KEY]}'."
|
|
1045
|
+
)
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
def _validate_model_group_router_setting(
|
|
1049
|
+
model_groups: List[Dict[str, Any]],
|
|
1050
|
+
) -> None:
|
|
1051
|
+
# You cannot define multiple models within a model group, when no router is defined.
|
|
1052
|
+
for model_group in model_groups:
|
|
1053
|
+
if ROUTER_CONFIG_KEY not in model_group:
|
|
1054
|
+
continue
|
|
1055
|
+
|
|
1056
|
+
router_config = model_group[ROUTER_CONFIG_KEY]
|
|
1057
|
+
if ROUTER_STRATEGY_CONFIG_KEY in router_config:
|
|
1058
|
+
router_strategy = router_config.get(ROUTER_STRATEGY_CONFIG_KEY)
|
|
1059
|
+
if router_strategy and router_strategy not in VALID_ROUTER_STRATEGIES:
|
|
1060
|
+
print_error_and_exit(
|
|
1061
|
+
f"The router strategy you defined for the model group "
|
|
1062
|
+
f"'{model_group[MODEL_GROUP_ID_CONFIG_KEY]}' is not valid. "
|
|
1063
|
+
f"Valid router strategies are categorized as follows:\n"
|
|
1064
|
+
f"- Strategies requiring Redis caching: "
|
|
1065
|
+
f"{', '.join(ROUTER_STRATEGIES_REQUIRING_REDIS_CACHE)}\n"
|
|
1066
|
+
f"- Strategies not requiring caching: "
|
|
1067
|
+
f"{', '.join(ROUTER_STRATEGIES_NOT_REQUIRING_CACHE)}"
|
|
1068
|
+
)
|
|
1069
|
+
if (
|
|
1070
|
+
router_strategy in ROUTER_STRATEGIES_REQUIRING_REDIS_CACHE
|
|
1071
|
+
and REDIS_HOST_CONFIG_KEY not in router_config
|
|
1072
|
+
):
|
|
1073
|
+
structlogger.warning(
|
|
1074
|
+
"validation.router_strategy.redis_host_not_defined",
|
|
1075
|
+
event_info=(
|
|
1076
|
+
f"The router strategy '{router_strategy}' requires a Redis host"
|
|
1077
|
+
f" to be defined. Without a Redis host, the system defaults to "
|
|
1078
|
+
f"'in-memory' caching. Please add the '{REDIS_HOST_CONFIG_KEY}'"
|
|
1079
|
+
f" to the router configuration for the model group "
|
|
1080
|
+
f"'{model_group[MODEL_GROUP_ID_CONFIG_KEY]}'."
|
|
1081
|
+
),
|
|
1082
|
+
)
|
|
1083
|
+
|
|
1084
|
+
|
|
1085
|
+
def _validate_usage_of_environment_variables_in_model_group_config(
|
|
1086
|
+
model_groups: List[Dict[str, Any]],
|
|
1087
|
+
) -> None:
|
|
1088
|
+
# Limit the use of ${env_var} in the model_groups config to the following variables:
|
|
1089
|
+
# deployment, api_base, api_key, api_version, aws_region_name
|
|
1090
|
+
allowed_env_vars = {
|
|
1091
|
+
DEPLOYMENT_CONFIG_KEY,
|
|
1092
|
+
API_BASE_CONFIG_KEY,
|
|
1093
|
+
API_KEY,
|
|
1094
|
+
API_VERSION_CONFIG_KEY,
|
|
1095
|
+
AWS_REGION_NAME_CONFIG_KEY,
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
for model_group in model_groups:
|
|
1099
|
+
for model_config in model_group[MODELS_CONFIG_KEY]:
|
|
1100
|
+
for key, value in model_config.items():
|
|
1101
|
+
if isinstance(value, str):
|
|
1102
|
+
if re.match(r"\${(\w+)}", value) and key not in allowed_env_vars:
|
|
1103
|
+
print_error_and_exit(
|
|
1104
|
+
f"You defined '{key}' as environment variable in model "
|
|
1105
|
+
f"group '{model_group[MODEL_GROUP_ID_CONFIG_KEY]}', "
|
|
1106
|
+
f"which is not allowed. "
|
|
1107
|
+
f"You can only use environment variables for the following "
|
|
1108
|
+
f"keys: {', '.join(allowed_env_vars)}. "
|
|
1109
|
+
f"Please update your config."
|
|
1110
|
+
)
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
def _validate_api_key_is_an_environment_variable(
|
|
1114
|
+
model_groups: List[Dict[str, Any]],
|
|
1115
|
+
) -> None:
|
|
1116
|
+
# the api key can only be set as an environment variable
|
|
1117
|
+
for model_group in model_groups:
|
|
1118
|
+
for model_config in model_group[MODELS_CONFIG_KEY]:
|
|
1119
|
+
for key, value in model_config.items():
|
|
1120
|
+
if key == API_KEY:
|
|
1121
|
+
if isinstance(value, str):
|
|
1122
|
+
if not re.match(r"\${(\w+)}", value):
|
|
1123
|
+
print_error_and_exit(
|
|
1124
|
+
f"You defined the '{API_KEY}' in model group "
|
|
1125
|
+
f"'{model_group[MODEL_GROUP_ID_CONFIG_KEY]}' as a "
|
|
1126
|
+
f"string. The '{API_KEY}' must be set as an environment"
|
|
1127
|
+
f" variable. Please update your config."
|
|
1128
|
+
)
|
|
1129
|
+
else:
|
|
1130
|
+
print_error_and_exit(
|
|
1131
|
+
f"You should define the '{API_KEY}' in model group "
|
|
1132
|
+
f"'{model_group[MODEL_GROUP_ID_CONFIG_KEY]}' using the "
|
|
1133
|
+
f"environment variable syntax - ${{ENV_VARIABLE_NAME}}. "
|
|
1134
|
+
f"Please update your config."
|
|
1135
|
+
)
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
def validate_model_group_configuration_setup() -> None:
|
|
1139
|
+
"""Validates the model group configuration setup in endpoints.yml."""
|
|
1140
|
+
endpoints = AvailableEndpoints.get_instance()
|
|
1141
|
+
|
|
1142
|
+
if endpoints.model_groups is None:
|
|
1143
|
+
return
|
|
1144
|
+
|
|
1145
|
+
_validate_unique_model_group_ids(endpoints.model_groups)
|
|
1146
|
+
_validate_model_group_with_multiple_models(endpoints.model_groups)
|
|
1147
|
+
_validate_usage_of_environment_variables_in_model_group_config(
|
|
1148
|
+
endpoints.model_groups
|
|
1149
|
+
)
|
|
1150
|
+
_validate_api_key_is_an_environment_variable(endpoints.model_groups)
|
|
1151
|
+
_validate_model_group_router_setting(endpoints.model_groups)
|
|
1152
|
+
|
|
1153
|
+
|
|
848
1154
|
def validate_command_generator_exclusivity(schema: GraphSchema) -> None:
|
|
849
1155
|
"""Validate that multiple command generators are not defined at same time."""
|
|
850
1156
|
from rasa.dialogue_understanding.generator import (
|