rasa-pro 3.14.0rc4__py3-none-any.whl → 3.15.0a1__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/agents/agent_manager.py +7 -5
- rasa/agents/protocol/a2a/a2a_agent.py +13 -11
- rasa/agents/protocol/mcp/mcp_base_agent.py +49 -11
- rasa/agents/validation.py +4 -2
- rasa/builder/config.py +4 -0
- rasa/builder/copilot/copilot.py +28 -9
- rasa/builder/copilot/copilot_templated_message_provider.py +1 -1
- rasa/builder/copilot/models.py +171 -4
- rasa/builder/document_retrieval/inkeep_document_retrieval.py +2 -0
- rasa/builder/download.py +1 -1
- rasa/builder/service.py +101 -24
- rasa/builder/telemetry/__init__.py +0 -0
- rasa/builder/telemetry/copilot_langfuse_telemetry.py +384 -0
- rasa/builder/{copilot/telemetry.py → telemetry/copilot_segment_telemetry.py} +21 -3
- rasa/builder/validation_service.py +4 -0
- rasa/cli/arguments/data.py +9 -0
- rasa/cli/data.py +72 -6
- rasa/cli/interactive.py +3 -0
- rasa/cli/llm_fine_tuning.py +1 -0
- rasa/cli/project_templates/defaults.py +1 -0
- rasa/cli/validation/bot_config.py +2 -0
- rasa/constants.py +2 -1
- rasa/core/actions/action_exceptions.py +1 -1
- rasa/core/agent.py +4 -1
- rasa/core/available_agents.py +1 -1
- rasa/core/exceptions.py +1 -1
- rasa/core/featurizers/tracker_featurizers.py +3 -2
- rasa/core/persistor.py +7 -7
- rasa/core/policies/flows/agent_executor.py +84 -4
- rasa/core/policies/flows/flow_exceptions.py +5 -2
- rasa/core/policies/flows/flow_executor.py +23 -8
- rasa/core/policies/flows/mcp_tool_executor.py +7 -1
- rasa/core/policies/rule_policy.py +1 -1
- rasa/core/run.py +15 -4
- rasa/dialogue_understanding/commands/cancel_flow_command.py +1 -1
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +1 -1
- rasa/e2e_test/e2e_config.py +4 -3
- rasa/engine/recipes/default_components.py +16 -6
- rasa/graph_components/validators/default_recipe_validator.py +10 -4
- rasa/model_manager/runner_service.py +1 -1
- rasa/nlu/classifiers/diet_classifier.py +2 -0
- rasa/privacy/privacy_config.py +1 -1
- rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +4 -7
- rasa/shared/core/slots.py +55 -24
- rasa/shared/core/training_data/story_reader/story_reader.py +1 -1
- rasa/shared/exceptions.py +23 -2
- rasa/shared/providers/llm/litellm_router_llm_client.py +2 -2
- rasa/shared/utils/common.py +9 -1
- rasa/shared/utils/llm.py +21 -4
- rasa/shared/utils/mcp/server_connection.py +7 -4
- rasa/studio/download.py +3 -0
- rasa/studio/prompts.py +1 -0
- rasa/studio/upload.py +4 -0
- rasa/utils/common.py +9 -0
- rasa/utils/endpoints.py +2 -0
- rasa/utils/installation_utils.py +111 -0
- rasa/utils/log_utils.py +20 -1
- rasa/utils/tensorflow/callback.py +2 -0
- rasa/utils/train_utils.py +2 -0
- rasa/version.py +1 -1
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/METADATA +4 -2
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/RECORD +65 -62
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/NOTICE +0 -0
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/WHEEL +0 -0
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/entry_points.txt +0 -0
rasa/core/run.py
CHANGED
|
@@ -328,10 +328,21 @@ def serve_application(
|
|
|
328
328
|
|
|
329
329
|
logger.info(f"Starting Rasa server on {protocol}://{interface}:{port}")
|
|
330
330
|
|
|
331
|
-
app
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
331
|
+
async def load_agent_and_check_failure(app: Sanic, loop: AbstractEventLoop) -> None:
|
|
332
|
+
"""Load agent and exit if it fails in non-debug mode."""
|
|
333
|
+
try:
|
|
334
|
+
await load_agent_on_start(
|
|
335
|
+
model_path, endpoints, remote_storage, sub_agents, app, loop
|
|
336
|
+
)
|
|
337
|
+
except Exception as e:
|
|
338
|
+
is_debug = logger.isEnabledFor(logging.DEBUG)
|
|
339
|
+
if is_debug:
|
|
340
|
+
raise e # show traceback in debug
|
|
341
|
+
# non-debug: log and exit without starting server
|
|
342
|
+
logger.error(f"Failed to load agent: {e}")
|
|
343
|
+
os._exit(1) # Any other exit method would show a traceback.
|
|
344
|
+
|
|
345
|
+
app.register_listener(load_agent_and_check_failure, "before_server_start")
|
|
335
346
|
|
|
336
347
|
app.register_listener(
|
|
337
348
|
licensing.validate_limited_server_license, "after_server_start"
|
|
@@ -56,7 +56,7 @@ class CancelFlowCommand(Command):
|
|
|
56
56
|
Returns:
|
|
57
57
|
The frames that were canceled.
|
|
58
58
|
"""
|
|
59
|
-
canceled_frames = []
|
|
59
|
+
canceled_frames: List[str] = []
|
|
60
60
|
# we need to go through the original stack dump in reverse order
|
|
61
61
|
# to find the frames that were canceled. we cancel everything from
|
|
62
62
|
# the top of the stack until we hit the user flow that was canceled.
|
|
@@ -233,7 +233,7 @@ flows:
|
|
|
233
233
|
collect: interrupted_flow_to_continue
|
|
234
234
|
description: "Fill this slot with the name of the flow the user wants to continue. If the user does not want to continue any of the interrupted flows, fill this slot with 'none'."
|
|
235
235
|
next:
|
|
236
|
-
- if: slots.interrupted_flow_to_continue
|
|
236
|
+
- if: slots.interrupted_flow_to_continue is not "none"
|
|
237
237
|
then:
|
|
238
238
|
- action: action_continue_interrupted_flow
|
|
239
239
|
next: END
|
rasa/e2e_test/e2e_config.py
CHANGED
|
@@ -72,9 +72,10 @@ class LLMJudgeConfig(BaseModel):
|
|
|
72
72
|
|
|
73
73
|
llm_config = resolve_model_client_config(llm_config)
|
|
74
74
|
llm_config, llm_extra_parameters = cls.extract_attributes(llm_config)
|
|
75
|
-
|
|
76
|
-
llm_config
|
|
77
|
-
|
|
75
|
+
if not llm_config:
|
|
76
|
+
llm_config = combine_custom_and_default_config(
|
|
77
|
+
llm_config, cls.get_default_llm_config()
|
|
78
|
+
)
|
|
78
79
|
embeddings_config = resolve_model_client_config(embeddings)
|
|
79
80
|
embeddings_config, embeddings_extra_parameters = cls.extract_attributes(
|
|
80
81
|
embeddings_config
|
|
@@ -27,22 +27,32 @@ from rasa.shared.utils.common import conditional_import
|
|
|
27
27
|
|
|
28
28
|
# components dependent on tensorflow
|
|
29
29
|
TEDPolicy, TED_POLICY_AVAILABLE = conditional_import(
|
|
30
|
-
"rasa.core.policies.ted_policy", "TEDPolicy"
|
|
30
|
+
"rasa.core.policies.ted_policy", "TEDPolicy", check_installation_setup=True
|
|
31
31
|
)
|
|
32
32
|
UnexpecTEDIntentPolicy, UNEXPECTED_INTENT_POLICY_AVAILABLE = conditional_import(
|
|
33
|
-
"rasa.core.policies.unexpected_intent_policy",
|
|
33
|
+
"rasa.core.policies.unexpected_intent_policy",
|
|
34
|
+
"UnexpecTEDIntentPolicy",
|
|
35
|
+
check_installation_setup=True,
|
|
34
36
|
)
|
|
35
37
|
DIETClassifier, DIET_CLASSIFIER_AVAILABLE = conditional_import(
|
|
36
|
-
"rasa.nlu.classifiers.diet_classifier",
|
|
38
|
+
"rasa.nlu.classifiers.diet_classifier",
|
|
39
|
+
"DIETClassifier",
|
|
40
|
+
check_installation_setup=True,
|
|
37
41
|
)
|
|
38
42
|
ConveRTFeaturizer, CONVERT_FEATURIZER_AVAILABLE = conditional_import(
|
|
39
|
-
"rasa.nlu.featurizers.dense_featurizer.convert_featurizer",
|
|
43
|
+
"rasa.nlu.featurizers.dense_featurizer.convert_featurizer",
|
|
44
|
+
"ConveRTFeaturizer",
|
|
45
|
+
check_installation_setup=True,
|
|
40
46
|
)
|
|
41
47
|
LanguageModelFeaturizer, LANGUAGE_MODEL_FEATURIZER_AVAILABLE = conditional_import(
|
|
42
|
-
"rasa.nlu.featurizers.dense_featurizer.lm_featurizer",
|
|
48
|
+
"rasa.nlu.featurizers.dense_featurizer.lm_featurizer",
|
|
49
|
+
"LanguageModelFeaturizer",
|
|
50
|
+
check_installation_setup=True,
|
|
43
51
|
)
|
|
44
52
|
ResponseSelector, RESPONSE_SELECTOR_AVAILABLE = conditional_import(
|
|
45
|
-
"rasa.nlu.selectors.response_selector",
|
|
53
|
+
"rasa.nlu.selectors.response_selector",
|
|
54
|
+
"ResponseSelector",
|
|
55
|
+
check_installation_setup=True,
|
|
46
56
|
)
|
|
47
57
|
|
|
48
58
|
# components dependent on skops
|
|
@@ -40,16 +40,22 @@ from rasa.shared.utils.common import conditional_import
|
|
|
40
40
|
|
|
41
41
|
# Conditional imports for TensorFlow-dependent components
|
|
42
42
|
TEDPolicy, TED_POLICY_AVAILABLE = conditional_import(
|
|
43
|
-
"rasa.core.policies.ted_policy", "TEDPolicy"
|
|
43
|
+
"rasa.core.policies.ted_policy", "TEDPolicy", check_installation_setup=True
|
|
44
44
|
)
|
|
45
45
|
UnexpecTEDIntentPolicy, UNEXPECTED_INTENT_POLICY_AVAILABLE = conditional_import(
|
|
46
|
-
"rasa.core.policies.unexpected_intent_policy",
|
|
46
|
+
"rasa.core.policies.unexpected_intent_policy",
|
|
47
|
+
"UnexpecTEDIntentPolicy",
|
|
48
|
+
check_installation_setup=True,
|
|
47
49
|
)
|
|
48
50
|
DIETClassifier, DIET_CLASSIFIER_AVAILABLE = conditional_import(
|
|
49
|
-
"rasa.nlu.classifiers.diet_classifier",
|
|
51
|
+
"rasa.nlu.classifiers.diet_classifier",
|
|
52
|
+
"DIETClassifier",
|
|
53
|
+
check_installation_setup=True,
|
|
50
54
|
)
|
|
51
55
|
ResponseSelector, RESPONSE_SELECTOR_AVAILABLE = conditional_import(
|
|
52
|
-
"rasa.nlu.selectors.response_selector",
|
|
56
|
+
"rasa.nlu.selectors.response_selector",
|
|
57
|
+
"ResponseSelector",
|
|
58
|
+
check_installation_setup=True,
|
|
53
59
|
)
|
|
54
60
|
|
|
55
61
|
# Conditional imports for nlu components requiring other dependencies than tensorflow
|
|
@@ -195,7 +195,7 @@ def fetch_remote_model_to_dir(
|
|
|
195
195
|
try:
|
|
196
196
|
return persistor.retrieve(model_name=model_name, target_path=target_path)
|
|
197
197
|
except FileNotFoundError as e:
|
|
198
|
-
raise ModelNotFound() from e
|
|
198
|
+
raise ModelNotFound("Model not found") from e
|
|
199
199
|
|
|
200
200
|
|
|
201
201
|
def fetch_size_of_remote_model(
|
|
@@ -9,9 +9,11 @@ from typing import Any, Dict, List, Optional, Text, Tuple, Type, TypeVar, Union
|
|
|
9
9
|
import numpy as np
|
|
10
10
|
import scipy.sparse
|
|
11
11
|
|
|
12
|
+
from rasa.utils.installation_utils import check_for_installation_issues
|
|
12
13
|
from rasa.utils.tensorflow import TENSORFLOW_AVAILABLE
|
|
13
14
|
|
|
14
15
|
if TENSORFLOW_AVAILABLE:
|
|
16
|
+
check_for_installation_issues()
|
|
15
17
|
import tensorflow as tf
|
|
16
18
|
else:
|
|
17
19
|
tf = None
|
rasa/privacy/privacy_config.py
CHANGED
|
@@ -211,7 +211,7 @@ def get_cron_trigger(cron_expression: str) -> CronTrigger:
|
|
|
211
211
|
"privacy_config.invalid_cron_expression",
|
|
212
212
|
cron=cron_expression,
|
|
213
213
|
)
|
|
214
|
-
raise RasaException from exc
|
|
214
|
+
raise RasaException("Invalid cron expression") from exc
|
|
215
215
|
|
|
216
216
|
return cron
|
|
217
217
|
|
|
@@ -139,20 +139,17 @@ class OAuth2AuthStrategy(AgentAuthStrategy):
|
|
|
139
139
|
resp.raise_for_status()
|
|
140
140
|
token_data = resp.json()
|
|
141
141
|
except httpx.HTTPStatusError as e:
|
|
142
|
-
raise
|
|
143
|
-
f"OAuth2 token request failed with status {e.response.status_code}: "
|
|
144
|
-
f"{e.response.text}"
|
|
145
|
-
) from e
|
|
142
|
+
raise e
|
|
146
143
|
except httpx.RequestError as e:
|
|
147
|
-
raise ValueError(f"OAuth2 token request failed
|
|
144
|
+
raise ValueError(f"OAuth2 token request failed - {e}") from e
|
|
148
145
|
except Exception as e:
|
|
149
146
|
raise ValueError(
|
|
150
|
-
f"Unexpected error during OAuth2 token request
|
|
147
|
+
f"Unexpected error during OAuth2 token request - {e}"
|
|
151
148
|
) from e
|
|
152
149
|
|
|
153
150
|
# Validate token data
|
|
154
151
|
if KEY_ACCESS_TOKEN not in token_data:
|
|
155
|
-
raise ValueError(f"No {KEY_ACCESS_TOKEN} in OAuth2 response")
|
|
152
|
+
raise ValueError(f"No `{KEY_ACCESS_TOKEN}` in OAuth2 response")
|
|
156
153
|
|
|
157
154
|
# Set access token and expires at
|
|
158
155
|
self._access_token = token_data[KEY_ACCESS_TOKEN]
|
rasa/shared/core/slots.py
CHANGED
|
@@ -355,8 +355,8 @@ class FloatSlot(Slot):
|
|
|
355
355
|
mappings: List[Dict[Text, Any]],
|
|
356
356
|
initial_value: Optional[float] = None,
|
|
357
357
|
value_reset_delay: Optional[int] = None,
|
|
358
|
-
max_value: float =
|
|
359
|
-
min_value: float =
|
|
358
|
+
max_value: Optional[float] = None,
|
|
359
|
+
min_value: Optional[float] = None,
|
|
360
360
|
influence_conversation: bool = True,
|
|
361
361
|
is_builtin: bool = False,
|
|
362
362
|
shared_for_coexistence: bool = False,
|
|
@@ -380,32 +380,24 @@ class FloatSlot(Slot):
|
|
|
380
380
|
filled_by=filled_by,
|
|
381
381
|
validation=validation,
|
|
382
382
|
)
|
|
383
|
+
self.validate_min_max_range(min_value, max_value)
|
|
384
|
+
|
|
383
385
|
self.max_value = max_value
|
|
384
386
|
self.min_value = min_value
|
|
385
387
|
|
|
386
|
-
if min_value >= max_value:
|
|
387
|
-
raise InvalidSlotConfigError(
|
|
388
|
-
"Float slot ('{}') created with an invalid range "
|
|
389
|
-
"using min ({}) and max ({}) values. Make sure "
|
|
390
|
-
"min is smaller than max."
|
|
391
|
-
"".format(self.name, self.min_value, self.max_value)
|
|
392
|
-
)
|
|
393
|
-
|
|
394
|
-
if initial_value is not None and not (min_value <= initial_value <= max_value):
|
|
395
|
-
rasa.shared.utils.io.raise_warning(
|
|
396
|
-
f"Float slot ('{self.name}') created with an initial value "
|
|
397
|
-
f"{self.value}. This value is outside of the configured min "
|
|
398
|
-
f"({self.min_value}) and max ({self.max_value}) values."
|
|
399
|
-
)
|
|
400
|
-
|
|
401
388
|
def _as_feature(self) -> List[float]:
|
|
389
|
+
# set default min and max values used in prior releases
|
|
390
|
+
# to prevent regressions for existing models
|
|
391
|
+
min_value = self.min_value or 0.0
|
|
392
|
+
max_value = self.max_value or 1.0
|
|
393
|
+
|
|
402
394
|
try:
|
|
403
|
-
capped_value = max(
|
|
404
|
-
if abs(
|
|
405
|
-
covered_range = abs(
|
|
395
|
+
capped_value = max(min_value, min(max_value, float(self.value)))
|
|
396
|
+
if abs(max_value - min_value) > 0:
|
|
397
|
+
covered_range = abs(max_value - min_value)
|
|
406
398
|
else:
|
|
407
399
|
covered_range = 1
|
|
408
|
-
return [1.0, (capped_value -
|
|
400
|
+
return [1.0, (capped_value - min_value) / covered_range]
|
|
409
401
|
except (TypeError, ValueError):
|
|
410
402
|
return [0.0, 0.0]
|
|
411
403
|
|
|
@@ -424,13 +416,52 @@ class FloatSlot(Slot):
|
|
|
424
416
|
return value
|
|
425
417
|
|
|
426
418
|
def is_valid_value(self, value: Any) -> bool:
|
|
427
|
-
"""Checks if the slot
|
|
428
|
-
|
|
429
|
-
|
|
419
|
+
"""Checks if the slot value is valid."""
|
|
420
|
+
if value is None:
|
|
421
|
+
return True
|
|
422
|
+
|
|
423
|
+
if not isinstance(self.coerce_value(value), float):
|
|
424
|
+
return False
|
|
425
|
+
|
|
426
|
+
if (
|
|
427
|
+
self.min_value is not None
|
|
428
|
+
and self.max_value is not None
|
|
429
|
+
and not (self.min_value <= value <= self.max_value)
|
|
430
|
+
):
|
|
431
|
+
return False
|
|
432
|
+
|
|
433
|
+
return True
|
|
430
434
|
|
|
431
435
|
def _feature_dimensionality(self) -> int:
|
|
432
436
|
return len(self.as_feature())
|
|
433
437
|
|
|
438
|
+
def validate_min_max_range(
|
|
439
|
+
self, min_value: Optional[float], max_value: Optional[float]
|
|
440
|
+
) -> None:
|
|
441
|
+
"""Validates the min-max range for the slot.
|
|
442
|
+
|
|
443
|
+
Raises:
|
|
444
|
+
InvalidSlotConfigError, if the min-max range is invalid.
|
|
445
|
+
"""
|
|
446
|
+
if min_value is not None and max_value is not None and min_value >= max_value:
|
|
447
|
+
raise InvalidSlotConfigError(
|
|
448
|
+
f"Float slot ('{self.name}') created with an invalid range "
|
|
449
|
+
f"using min ({min_value}) and max ({max_value}) values. Make sure "
|
|
450
|
+
f"min is smaller than max."
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
if (
|
|
454
|
+
self.initial_value is not None
|
|
455
|
+
and min_value is not None
|
|
456
|
+
and max_value is not None
|
|
457
|
+
and not (min_value <= self.initial_value <= max_value)
|
|
458
|
+
):
|
|
459
|
+
raise InvalidSlotConfigError(
|
|
460
|
+
f"Float slot ('{self.name}') created with an initial value "
|
|
461
|
+
f"{self.initial_value}. This value is outside of the configured min "
|
|
462
|
+
f"({min_value}) and max ({max_value}) values."
|
|
463
|
+
)
|
|
464
|
+
|
|
434
465
|
|
|
435
466
|
class BooleanSlot(Slot):
|
|
436
467
|
"""A slot storing a truth value."""
|
rasa/shared/exceptions.py
CHANGED
|
@@ -16,6 +16,17 @@ class RasaException(Exception):
|
|
|
16
16
|
to the users, but will be ignored in telemetry.
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
|
+
def __init__(self, message: str, suppress_stack_trace: bool = False, **kwargs: Any):
|
|
20
|
+
"""Initialize the exception.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
message: The error message.
|
|
24
|
+
suppress_stack_trace: If True, the stack trace will be suppressed in logs.
|
|
25
|
+
**kwargs: Additional keyword arguments (e.g., cause for exception chaining).
|
|
26
|
+
"""
|
|
27
|
+
Exception.__init__(self, message)
|
|
28
|
+
self.suppress_stack_trace = suppress_stack_trace
|
|
29
|
+
|
|
19
30
|
|
|
20
31
|
class RasaCoreException(RasaException):
|
|
21
32
|
"""Basic exception for errors raised by Rasa Core."""
|
|
@@ -113,6 +124,17 @@ class SchemaValidationError(RasaException, jsonschema.ValidationError):
|
|
|
113
124
|
class InvalidEntityFormatException(RasaException, json.JSONDecodeError):
|
|
114
125
|
"""Raised if the format of an entity is invalid."""
|
|
115
126
|
|
|
127
|
+
def __init__(self, msg: str, doc: str = "", pos: int = 0):
|
|
128
|
+
"""Initialize the exception.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
msg: The error message.
|
|
132
|
+
doc: The document that caused the error.
|
|
133
|
+
pos: The position in the document where the error occurred.
|
|
134
|
+
"""
|
|
135
|
+
RasaException.__init__(self, msg)
|
|
136
|
+
json.JSONDecodeError.__init__(self, msg, doc, pos)
|
|
137
|
+
|
|
116
138
|
@classmethod
|
|
117
139
|
def create_from(
|
|
118
140
|
cls, other: json.JSONDecodeError, msg: Text
|
|
@@ -130,8 +152,7 @@ class ConnectionException(RasaException):
|
|
|
130
152
|
|
|
131
153
|
|
|
132
154
|
class ProviderClientAPIException(RasaException):
|
|
133
|
-
"""
|
|
134
|
-
with LLM / embedding providers.
|
|
155
|
+
"""For errors during API interactions with LLM / embedding providers.
|
|
135
156
|
|
|
136
157
|
Attributes:
|
|
137
158
|
original_exception (Exception): The original exception that was
|
|
@@ -151,7 +151,7 @@ class LiteLLMRouterLLMClient(_BaseLiteLLMRouterClient, _BaseLiteLLMClient):
|
|
|
151
151
|
if not self._use_chat_completions_endpoint:
|
|
152
152
|
return self._text_completion(messages)
|
|
153
153
|
try:
|
|
154
|
-
formatted_messages = self.
|
|
154
|
+
formatted_messages = self._get_formatted_messages(messages)
|
|
155
155
|
response = self.router_client.completion(
|
|
156
156
|
messages=formatted_messages, **{**self._completion_fn_args, **kwargs}
|
|
157
157
|
)
|
|
@@ -189,7 +189,7 @@ class LiteLLMRouterLLMClient(_BaseLiteLLMRouterClient, _BaseLiteLLMClient):
|
|
|
189
189
|
if not self._use_chat_completions_endpoint:
|
|
190
190
|
return await self._atext_completion(messages)
|
|
191
191
|
try:
|
|
192
|
-
formatted_messages = self.
|
|
192
|
+
formatted_messages = self._get_formatted_messages(messages)
|
|
193
193
|
response = await self.router_client.acompletion(
|
|
194
194
|
messages=formatted_messages, **{**self._completion_fn_args, **kwargs}
|
|
195
195
|
)
|
rasa/shared/utils/common.py
CHANGED
|
@@ -26,6 +26,7 @@ from rasa.exceptions import MissingDependencyException
|
|
|
26
26
|
from rasa.shared.constants import DOCS_URL_MIGRATION_GUIDE
|
|
27
27
|
from rasa.shared.exceptions import ProviderClientValidationError, RasaException
|
|
28
28
|
from rasa.shared.utils.cli import print_success
|
|
29
|
+
from rasa.utils.installation_utils import check_for_installation_issues
|
|
29
30
|
|
|
30
31
|
logger = logging.getLogger(__name__)
|
|
31
32
|
|
|
@@ -396,7 +397,11 @@ Sign up at: https://feedback.rasa.com
|
|
|
396
397
|
print_success(message)
|
|
397
398
|
|
|
398
399
|
|
|
399
|
-
def conditional_import(
|
|
400
|
+
def conditional_import(
|
|
401
|
+
module_name: str,
|
|
402
|
+
class_name: str,
|
|
403
|
+
check_installation_setup: bool = False,
|
|
404
|
+
) -> Tuple[Any, bool]:
|
|
400
405
|
"""Conditionally import a class, returning (class, is_available) tuple.
|
|
401
406
|
|
|
402
407
|
Args:
|
|
@@ -408,6 +413,9 @@ def conditional_import(module_name: str, class_name: str) -> Tuple[Any, bool]:
|
|
|
408
413
|
or None if import failed, and is_available is a boolean indicating
|
|
409
414
|
whether the import was successful.
|
|
410
415
|
"""
|
|
416
|
+
if check_installation_setup:
|
|
417
|
+
check_for_installation_issues()
|
|
418
|
+
|
|
411
419
|
try:
|
|
412
420
|
module = __import__(module_name, fromlist=[class_name])
|
|
413
421
|
return getattr(module, class_name), True
|
rasa/shared/utils/llm.py
CHANGED
|
@@ -1074,7 +1074,7 @@ def _get_llm_command_generator_config(
|
|
|
1074
1074
|
|
|
1075
1075
|
|
|
1076
1076
|
def _get_compact_llm_command_generator_prompt(
|
|
1077
|
-
config: Dict[Text, Any],
|
|
1077
|
+
config: Dict[Text, Any], model_groups: Dict[Text, Any]
|
|
1078
1078
|
) -> Text:
|
|
1079
1079
|
"""Get the command generator prompt based on the config."""
|
|
1080
1080
|
from rasa.dialogue_understanding.generator.single_step.compact_llm_command_generator import ( # noqa: E501
|
|
@@ -1084,7 +1084,7 @@ def _get_compact_llm_command_generator_prompt(
|
|
|
1084
1084
|
model_config = _get_llm_command_generator_config(config)
|
|
1085
1085
|
llm_config = resolve_model_client_config(
|
|
1086
1086
|
model_config=model_config,
|
|
1087
|
-
model_groups=
|
|
1087
|
+
model_groups=model_groups,
|
|
1088
1088
|
)
|
|
1089
1089
|
return get_default_prompt_template_based_on_model(
|
|
1090
1090
|
llm_config=llm_config or {},
|
|
@@ -1119,7 +1119,7 @@ def get_system_default_prompts(
|
|
|
1119
1119
|
|
|
1120
1120
|
Args:
|
|
1121
1121
|
config: The config.yml file data.
|
|
1122
|
-
endpoints: The endpoints
|
|
1122
|
+
endpoints: The endpoints configuration dictionary.
|
|
1123
1123
|
|
|
1124
1124
|
Returns:
|
|
1125
1125
|
SystemPrompts: A Pydantic model containing all default prompts.
|
|
@@ -1128,8 +1128,25 @@ def get_system_default_prompts(
|
|
|
1128
1128
|
DEFAULT_RESPONSE_VARIATION_PROMPT_TEMPLATE,
|
|
1129
1129
|
)
|
|
1130
1130
|
|
|
1131
|
+
# The Model Manager / Model API service receives the endpoints configuration
|
|
1132
|
+
# as raw YAML text rather than as a file path. However, both
|
|
1133
|
+
# Configuration.initialise_endpoints() and AvailableEndpoints.read_endpoints()
|
|
1134
|
+
# currently only accept a Path input and do not support loading from in-memory
|
|
1135
|
+
# YAML content.
|
|
1136
|
+
|
|
1137
|
+
# Since these classes only support file-based initialization today, we need
|
|
1138
|
+
# to bootstrap the Configuration with an empty AvailableEndpoints instance for now.
|
|
1139
|
+
|
|
1140
|
+
# IMPORTANT: This configuration must be properly initialized with valid endpoints
|
|
1141
|
+
# and available agents once Studio introduces full support for Agent configurations
|
|
1142
|
+
# (A2A and MCP).
|
|
1143
|
+
Configuration.initialise_empty()
|
|
1144
|
+
|
|
1145
|
+
model_groups = endpoints.get(MODEL_GROUPS_CONFIG_KEY)
|
|
1131
1146
|
return SystemPrompts(
|
|
1132
|
-
command_generator=_get_compact_llm_command_generator_prompt(
|
|
1147
|
+
command_generator=_get_compact_llm_command_generator_prompt(
|
|
1148
|
+
config, model_groups
|
|
1149
|
+
),
|
|
1133
1150
|
enterprise_search=_get_enterprise_search_prompt(config),
|
|
1134
1151
|
contextual_response_rephraser=DEFAULT_RESPONSE_VARIATION_PROMPT_TEMPLATE,
|
|
1135
1152
|
)
|
|
@@ -121,7 +121,8 @@ class MCPServerConnection:
|
|
|
121
121
|
except Exception as eg:
|
|
122
122
|
for exc in getattr(eg, "exceptions", [eg]):
|
|
123
123
|
event_info = (
|
|
124
|
-
f"Failed to connect to MCP server `{self.server_name}
|
|
124
|
+
f"Failed to connect to MCP server `{self.server_name}`. \nOriginal "
|
|
125
|
+
f"error: {exc!s}"
|
|
125
126
|
)
|
|
126
127
|
if isinstance(exc, HTTPStatusError):
|
|
127
128
|
status_code = exc.response.status_code
|
|
@@ -135,9 +136,11 @@ class MCPServerConnection:
|
|
|
135
136
|
)
|
|
136
137
|
await self._cleanup()
|
|
137
138
|
if status_code in [400, 401, 403]:
|
|
138
|
-
raise AuthenticationError(
|
|
139
|
+
raise AuthenticationError(str(exc)) from eg
|
|
140
|
+
elif status_code == 404:
|
|
141
|
+
raise Exception(str(exc)) from eg
|
|
139
142
|
else:
|
|
140
|
-
raise ConnectionError(
|
|
143
|
+
raise ConnectionError(str(exc)) from eg
|
|
141
144
|
else:
|
|
142
145
|
structlogger.error(
|
|
143
146
|
"mcp_server_connection.connect.other_exception",
|
|
@@ -147,7 +150,7 @@ class MCPServerConnection:
|
|
|
147
150
|
error=str(exc),
|
|
148
151
|
)
|
|
149
152
|
await self._cleanup()
|
|
150
|
-
raise ConnectionError(
|
|
153
|
+
raise ConnectionError(str(exc)) from eg
|
|
151
154
|
|
|
152
155
|
except asyncio.CancelledError as e:
|
|
153
156
|
event_info = f"Connection to MCP server `{self.server_name}` was cancelled."
|
rasa/studio/download.py
CHANGED
|
@@ -10,6 +10,7 @@ from ruamel.yaml.scalarstring import LiteralScalarString
|
|
|
10
10
|
|
|
11
11
|
import rasa.cli.utils
|
|
12
12
|
import rasa.shared.utils.cli
|
|
13
|
+
from rasa.core.config.configuration import Configuration
|
|
13
14
|
from rasa.shared.constants import (
|
|
14
15
|
DEFAULT_CONFIG_PATH,
|
|
15
16
|
DEFAULT_DATA_PATH,
|
|
@@ -116,6 +117,8 @@ def _handle_endpoints(handler: StudioDataHandler, root: Path) -> None:
|
|
|
116
117
|
endpoints_path = root / DEFAULT_ENDPOINTS_PATH
|
|
117
118
|
endpoints_path.write_text(endpoints_data, encoding="utf-8")
|
|
118
119
|
|
|
120
|
+
Configuration.initialise_endpoints(endpoints_path=endpoints_path)
|
|
121
|
+
|
|
119
122
|
|
|
120
123
|
def _handle_domain(handler: StudioDataHandler, root: Path) -> None:
|
|
121
124
|
"""Persist the assistant’s domain file.
|
rasa/studio/prompts.py
CHANGED
|
@@ -43,6 +43,7 @@ def handle_prompts(prompts: Dict[Text, Text], root: Path) -> None:
|
|
|
43
43
|
config: Dict = read_yaml(config_path)
|
|
44
44
|
endpoints: Dict = read_yaml(endpoints_path)
|
|
45
45
|
|
|
46
|
+
# System default prompts are dependent on the endpoints
|
|
46
47
|
system_prompts = get_system_default_prompts(config, endpoints)
|
|
47
48
|
|
|
48
49
|
_handle_contextual_response_rephraser(
|
rasa/studio/upload.py
CHANGED
|
@@ -154,6 +154,10 @@ def handle_upload(args: argparse.Namespace) -> None:
|
|
|
154
154
|
"Authentication is invalid or expired. Please run `rasa studio login`."
|
|
155
155
|
)
|
|
156
156
|
|
|
157
|
+
from rasa.core.config.configuration import Configuration
|
|
158
|
+
|
|
159
|
+
Configuration.initialise_empty()
|
|
160
|
+
|
|
157
161
|
structlogger.info("rasa.studio.upload.loading_data", event_info="Loading data...")
|
|
158
162
|
|
|
159
163
|
args.domain = get_validated_path(args.domain, "domain", DEFAULT_DOMAIN_PATHS)
|
rasa/utils/common.py
CHANGED
|
@@ -36,6 +36,7 @@ from rasa.constants import (
|
|
|
36
36
|
ENV_LOG_LEVEL_LIBRARIES,
|
|
37
37
|
ENV_LOG_LEVEL_MATPLOTLIB,
|
|
38
38
|
ENV_LOG_LEVEL_MCP,
|
|
39
|
+
ENV_LOG_LEVEL_PYMONGO,
|
|
39
40
|
ENV_LOG_LEVEL_RABBITMQ,
|
|
40
41
|
ENV_MCP_LOGGING_ENABLED,
|
|
41
42
|
)
|
|
@@ -297,6 +298,7 @@ def configure_library_logging() -> None:
|
|
|
297
298
|
update_rabbitmq_log_level(library_log_level)
|
|
298
299
|
update_websockets_log_level(library_log_level)
|
|
299
300
|
update_mcp_log_level()
|
|
301
|
+
update_pymongo_log_level(library_log_level)
|
|
300
302
|
|
|
301
303
|
|
|
302
304
|
def update_apscheduler_log_level() -> None:
|
|
@@ -481,6 +483,13 @@ def update_mcp_log_level() -> None:
|
|
|
481
483
|
logging.getLogger(logger_name).propagate = False
|
|
482
484
|
|
|
483
485
|
|
|
486
|
+
def update_pymongo_log_level(library_log_level: str) -> None:
|
|
487
|
+
"""Set the log level of pymongo."""
|
|
488
|
+
log_level = os.environ.get(ENV_LOG_LEVEL_PYMONGO, library_log_level)
|
|
489
|
+
logging.getLogger("pymongo").setLevel(log_level)
|
|
490
|
+
logging.getLogger("pymongo").propagate = False
|
|
491
|
+
|
|
492
|
+
|
|
484
493
|
def sort_list_of_dicts_by_first_key(dicts: List[Dict]) -> List[Dict]:
|
|
485
494
|
"""Sorts a list of dictionaries by their first key."""
|
|
486
495
|
return sorted(dicts, key=lambda d: next(iter(d.keys())))
|
rasa/utils/endpoints.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import ssl
|
|
3
|
+
from functools import lru_cache
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from types import ModuleType
|
|
5
6
|
from typing import Any, Dict, List, Optional, Text, Union
|
|
@@ -16,6 +17,7 @@ from rasa.shared.utils.yaml import read_config_file
|
|
|
16
17
|
structlogger = structlog.get_logger()
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
@lru_cache(maxsize=10)
|
|
19
21
|
def read_endpoint_config(
|
|
20
22
|
filename: Union[str, Path], endpoint_type: Text
|
|
21
23
|
) -> Optional["EndpointConfig"]:
|