rasa-pro 3.9.18__py3-none-any.whl → 3.10.16__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.
- README.md +0 -374
- rasa/__init__.py +1 -2
- rasa/__main__.py +5 -0
- rasa/anonymization/anonymization_rule_executor.py +2 -2
- rasa/api.py +27 -23
- rasa/cli/arguments/data.py +27 -2
- rasa/cli/arguments/default_arguments.py +25 -3
- rasa/cli/arguments/run.py +9 -9
- rasa/cli/arguments/train.py +11 -3
- rasa/cli/data.py +70 -8
- rasa/cli/e2e_test.py +104 -431
- rasa/cli/evaluate.py +1 -1
- rasa/cli/interactive.py +1 -0
- rasa/cli/llm_fine_tuning.py +398 -0
- rasa/cli/project_templates/calm/endpoints.yml +1 -1
- rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
- rasa/cli/run.py +15 -14
- rasa/cli/scaffold.py +10 -8
- rasa/cli/studio/studio.py +35 -5
- rasa/cli/train.py +56 -8
- rasa/cli/utils.py +22 -5
- rasa/cli/x.py +1 -1
- rasa/constants.py +7 -1
- rasa/core/actions/action.py +98 -49
- rasa/core/actions/action_run_slot_rejections.py +4 -1
- rasa/core/actions/custom_action_executor.py +9 -6
- rasa/core/actions/direct_custom_actions_executor.py +80 -0
- rasa/core/actions/e2e_stub_custom_action_executor.py +68 -0
- rasa/core/actions/grpc_custom_action_executor.py +2 -2
- rasa/core/actions/http_custom_action_executor.py +6 -5
- rasa/core/agent.py +21 -17
- rasa/core/channels/__init__.py +2 -0
- rasa/core/channels/audiocodes.py +1 -16
- rasa/core/channels/voice_aware/__init__.py +0 -0
- rasa/core/channels/voice_aware/jambonz.py +103 -0
- rasa/core/channels/voice_aware/jambonz_protocol.py +344 -0
- rasa/core/channels/voice_aware/utils.py +20 -0
- rasa/core/channels/voice_native/__init__.py +0 -0
- rasa/core/constants.py +6 -1
- rasa/core/information_retrieval/faiss.py +7 -4
- rasa/core/information_retrieval/information_retrieval.py +8 -0
- rasa/core/information_retrieval/milvus.py +9 -2
- rasa/core/information_retrieval/qdrant.py +1 -1
- rasa/core/nlg/contextual_response_rephraser.py +32 -10
- rasa/core/nlg/summarize.py +4 -3
- rasa/core/policies/enterprise_search_policy.py +113 -45
- rasa/core/policies/flows/flow_executor.py +122 -76
- rasa/core/policies/intentless_policy.py +83 -29
- rasa/core/processor.py +72 -54
- rasa/core/run.py +5 -4
- rasa/core/tracker_store.py +8 -4
- rasa/core/training/interactive.py +1 -1
- rasa/core/utils.py +56 -57
- rasa/dialogue_understanding/coexistence/llm_based_router.py +53 -13
- rasa/dialogue_understanding/commands/__init__.py +6 -0
- rasa/dialogue_understanding/commands/restart_command.py +58 -0
- rasa/dialogue_understanding/commands/session_start_command.py +59 -0
- rasa/dialogue_understanding/commands/utils.py +40 -0
- rasa/dialogue_understanding/generator/constants.py +10 -3
- rasa/dialogue_understanding/generator/flow_retrieval.py +21 -5
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +13 -3
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +134 -90
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +47 -7
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +127 -41
- rasa/dialogue_understanding/patterns/restart.py +37 -0
- rasa/dialogue_understanding/patterns/session_start.py +37 -0
- rasa/dialogue_understanding/processor/command_processor.py +16 -3
- rasa/dialogue_understanding/processor/command_processor_component.py +6 -2
- rasa/e2e_test/aggregate_test_stats_calculator.py +134 -0
- rasa/e2e_test/assertions.py +1223 -0
- rasa/e2e_test/assertions_schema.yml +106 -0
- rasa/e2e_test/constants.py +20 -0
- rasa/e2e_test/e2e_config.py +220 -0
- rasa/e2e_test/e2e_config_schema.yml +26 -0
- rasa/e2e_test/e2e_test_case.py +131 -8
- rasa/e2e_test/e2e_test_converter.py +363 -0
- rasa/e2e_test/e2e_test_converter_prompt.jinja2 +70 -0
- rasa/e2e_test/e2e_test_coverage_report.py +364 -0
- rasa/e2e_test/e2e_test_result.py +26 -6
- rasa/e2e_test/e2e_test_runner.py +493 -71
- rasa/e2e_test/e2e_test_schema.yml +96 -0
- rasa/e2e_test/pykwalify_extensions.py +39 -0
- rasa/e2e_test/stub_custom_action.py +70 -0
- rasa/e2e_test/utils/__init__.py +0 -0
- rasa/e2e_test/utils/e2e_yaml_utils.py +55 -0
- rasa/e2e_test/utils/io.py +598 -0
- rasa/e2e_test/utils/validation.py +80 -0
- rasa/engine/graph.py +9 -3
- rasa/engine/recipes/default_components.py +0 -2
- rasa/engine/recipes/default_recipe.py +10 -2
- rasa/engine/storage/local_model_storage.py +40 -12
- rasa/engine/validation.py +78 -1
- rasa/env.py +9 -0
- rasa/graph_components/providers/story_graph_provider.py +59 -6
- rasa/llm_fine_tuning/__init__.py +0 -0
- rasa/llm_fine_tuning/annotation_module.py +241 -0
- rasa/llm_fine_tuning/conversations.py +144 -0
- rasa/llm_fine_tuning/llm_data_preparation_module.py +178 -0
- rasa/llm_fine_tuning/notebooks/unsloth_finetuning.ipynb +407 -0
- rasa/llm_fine_tuning/paraphrasing/__init__.py +0 -0
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +281 -0
- rasa/llm_fine_tuning/paraphrasing/default_rephrase_prompt_template.jina2 +44 -0
- rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +121 -0
- rasa/llm_fine_tuning/paraphrasing/rephrased_user_message.py +10 -0
- rasa/llm_fine_tuning/paraphrasing_module.py +128 -0
- rasa/llm_fine_tuning/storage.py +174 -0
- rasa/llm_fine_tuning/train_test_split_module.py +441 -0
- rasa/model_training.py +56 -16
- rasa/nlu/persistor.py +157 -36
- rasa/server.py +45 -10
- rasa/shared/constants.py +76 -16
- rasa/shared/core/domain.py +27 -19
- rasa/shared/core/events.py +28 -2
- rasa/shared/core/flows/flow.py +208 -13
- rasa/shared/core/flows/flow_path.py +84 -0
- rasa/shared/core/flows/flows_list.py +33 -11
- rasa/shared/core/flows/flows_yaml_schema.json +269 -193
- rasa/shared/core/flows/validation.py +112 -25
- rasa/shared/core/flows/yaml_flows_io.py +149 -10
- rasa/shared/core/trackers.py +6 -0
- rasa/shared/core/training_data/structures.py +20 -0
- rasa/shared/core/training_data/visualization.html +2 -2
- rasa/shared/exceptions.py +4 -0
- rasa/shared/importers/importer.py +64 -16
- rasa/shared/nlu/constants.py +2 -0
- rasa/shared/providers/_configs/__init__.py +0 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +183 -0
- rasa/shared/providers/_configs/client_config.py +57 -0
- rasa/shared/providers/_configs/default_litellm_client_config.py +130 -0
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +234 -0
- rasa/shared/providers/_configs/openai_client_config.py +175 -0
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +176 -0
- rasa/shared/providers/_configs/utils.py +101 -0
- rasa/shared/providers/_ssl_verification_utils.py +124 -0
- rasa/shared/providers/embedding/__init__.py +0 -0
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +259 -0
- rasa/shared/providers/embedding/_langchain_embedding_client_adapter.py +74 -0
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +277 -0
- rasa/shared/providers/embedding/default_litellm_embedding_client.py +102 -0
- rasa/shared/providers/embedding/embedding_client.py +90 -0
- rasa/shared/providers/embedding/embedding_response.py +41 -0
- rasa/shared/providers/embedding/huggingface_local_embedding_client.py +191 -0
- rasa/shared/providers/embedding/openai_embedding_client.py +172 -0
- rasa/shared/providers/llm/__init__.py +0 -0
- rasa/shared/providers/llm/_base_litellm_client.py +251 -0
- rasa/shared/providers/llm/azure_openai_llm_client.py +338 -0
- rasa/shared/providers/llm/default_litellm_llm_client.py +84 -0
- rasa/shared/providers/llm/llm_client.py +76 -0
- rasa/shared/providers/llm/llm_response.py +50 -0
- rasa/shared/providers/llm/openai_llm_client.py +155 -0
- rasa/shared/providers/llm/self_hosted_llm_client.py +293 -0
- rasa/shared/providers/mappings.py +75 -0
- rasa/shared/utils/cli.py +30 -0
- rasa/shared/utils/io.py +65 -2
- rasa/shared/utils/llm.py +246 -200
- rasa/shared/utils/yaml.py +121 -15
- rasa/studio/auth.py +6 -4
- rasa/studio/config.py +13 -4
- rasa/studio/constants.py +1 -0
- rasa/studio/data_handler.py +10 -3
- rasa/studio/download.py +19 -13
- rasa/studio/train.py +2 -3
- rasa/studio/upload.py +19 -11
- rasa/telemetry.py +113 -58
- rasa/tracing/instrumentation/attribute_extractors.py +32 -17
- rasa/utils/common.py +18 -19
- rasa/utils/endpoints.py +7 -4
- rasa/utils/json_utils.py +60 -0
- rasa/utils/licensing.py +9 -1
- rasa/utils/ml_utils.py +4 -2
- rasa/validator.py +213 -3
- rasa/version.py +1 -1
- rasa_pro-3.10.16.dist-info/METADATA +196 -0
- {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/RECORD +179 -113
- rasa/nlu/classifiers/llm_intent_classifier.py +0 -519
- rasa/shared/providers/openai/clients.py +0 -43
- rasa/shared/providers/openai/session_handler.py +0 -110
- rasa_pro-3.9.18.dist-info/METADATA +0 -563
- /rasa/{shared/providers/openai → cli/project_templates/tutorial/actions}/__init__.py +0 -0
- /rasa/cli/project_templates/tutorial/{actions.py → actions/actions.py} +0 -0
- {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/NOTICE +0 -0
- {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/WHEEL +0 -0
- {rasa_pro-3.9.18.dist-info → rasa_pro-3.10.16.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any, Dict, Optional
|
|
3
|
+
|
|
4
|
+
import structlog
|
|
5
|
+
|
|
6
|
+
from rasa.shared.constants import (
|
|
7
|
+
OPENAI_API_BASE_ENV_VAR,
|
|
8
|
+
OPENAI_API_TYPE_ENV_VAR,
|
|
9
|
+
OPENAI_API_VERSION_ENV_VAR,
|
|
10
|
+
OPENAI_PROVIDER,
|
|
11
|
+
)
|
|
12
|
+
from rasa.shared.providers._configs.openai_client_config import OpenAIClientConfig
|
|
13
|
+
from rasa.shared.providers.embedding._base_litellm_embedding_client import (
|
|
14
|
+
_BaseLiteLLMEmbeddingClient,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
structlogger = structlog.get_logger()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class OpenAIEmbeddingClient(_BaseLiteLLMEmbeddingClient):
|
|
21
|
+
"""A client for interfacing with OpenAI Embeddings.
|
|
22
|
+
|
|
23
|
+
Parameters:
|
|
24
|
+
model (str): The OpenAI model name.
|
|
25
|
+
api_base (Optional[str]): Optional, the base URL for the API endpoints.
|
|
26
|
+
If not provided, it will be set via environment variables.
|
|
27
|
+
api_type (Optional[str]): Optional, the type of the API to use.
|
|
28
|
+
If not provided, it will be set via environment variable.
|
|
29
|
+
api_version (Optional[str]): Optional, the version of the API to use.
|
|
30
|
+
If not provided, it will be set via environment variable.
|
|
31
|
+
kwargs (Optional[Dict[str, Any]]): Optional configuration parameters specific
|
|
32
|
+
to the embedding model deployment.
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
ProviderClientValidationError: If validation of the client setup fails.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
model: str,
|
|
41
|
+
api_base: Optional[str] = None,
|
|
42
|
+
api_type: Optional[str] = None,
|
|
43
|
+
api_version: Optional[str] = None,
|
|
44
|
+
**kwargs: Any,
|
|
45
|
+
):
|
|
46
|
+
super().__init__() # type: ignore
|
|
47
|
+
self._model = model
|
|
48
|
+
self._api_base = api_base or os.environ.get(OPENAI_API_BASE_ENV_VAR)
|
|
49
|
+
self._api_type = api_type or os.environ.get(OPENAI_API_TYPE_ENV_VAR)
|
|
50
|
+
self._api_version = api_version or os.environ.get(OPENAI_API_VERSION_ENV_VAR)
|
|
51
|
+
self._extra_parameters = kwargs or {}
|
|
52
|
+
self.validate_client_setup()
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def from_config(cls, config: Dict[str, Any]) -> "OpenAIEmbeddingClient":
|
|
56
|
+
"""
|
|
57
|
+
Initializes the client from given configuration.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
config (Dict[str, Any]): Configuration.
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
ValueError: Raised in cases of invalid configuration:
|
|
64
|
+
- If any of the required configuration keys are missing.
|
|
65
|
+
- If `api_type` has a value different from `azure`.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
OpenAIEmbeddingClient: Initialized client.
|
|
69
|
+
"""
|
|
70
|
+
try:
|
|
71
|
+
openai_config = OpenAIClientConfig.from_dict(config)
|
|
72
|
+
except ValueError as e:
|
|
73
|
+
message = "Cannot instantiate a client from the passed configuration."
|
|
74
|
+
structlogger.error(
|
|
75
|
+
"openai_embedding_client.from_config.error",
|
|
76
|
+
message=message,
|
|
77
|
+
config=config,
|
|
78
|
+
original_error=e,
|
|
79
|
+
)
|
|
80
|
+
raise
|
|
81
|
+
|
|
82
|
+
return cls(
|
|
83
|
+
model=openai_config.model,
|
|
84
|
+
api_base=openai_config.api_base,
|
|
85
|
+
api_type=openai_config.api_type,
|
|
86
|
+
api_version=openai_config.api_version,
|
|
87
|
+
**openai_config.extra_parameters,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def config(self) -> Dict:
|
|
92
|
+
"""
|
|
93
|
+
Returns the configuration for the openai embedding client.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Dictionary containing the configuration.
|
|
97
|
+
"""
|
|
98
|
+
config = OpenAIClientConfig(
|
|
99
|
+
model=self.model,
|
|
100
|
+
api_base=self.api_base,
|
|
101
|
+
api_type=self.api_type,
|
|
102
|
+
api_version=self.api_version,
|
|
103
|
+
extra_parameters=self._extra_parameters,
|
|
104
|
+
)
|
|
105
|
+
return config.to_dict()
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def model(self) -> str:
|
|
109
|
+
"""
|
|
110
|
+
Returns the model name for the openai embedding client.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
String representing the model name.
|
|
114
|
+
"""
|
|
115
|
+
return self._model
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def api_base(self) -> Optional[str]:
|
|
119
|
+
"""
|
|
120
|
+
Returns the base API URL for the openai embedding client.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
String representing the base API URL.
|
|
124
|
+
"""
|
|
125
|
+
return self._api_base
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def api_type(self) -> Optional[str]:
|
|
129
|
+
"""
|
|
130
|
+
Returns the API type for the openai embedding client.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
String representing the API version.
|
|
134
|
+
"""
|
|
135
|
+
return self._api_type
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def api_version(self) -> Optional[str]:
|
|
139
|
+
"""
|
|
140
|
+
Returns the API version for the openai embedding client.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
String representing the API version.
|
|
144
|
+
"""
|
|
145
|
+
return self._api_version
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def _litellm_extra_parameters(self) -> Dict[str, Any]:
|
|
149
|
+
"""
|
|
150
|
+
Returns the model parameters for the openai embedding client.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Dictionary containing the model parameters.
|
|
154
|
+
"""
|
|
155
|
+
return self._extra_parameters
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def _embedding_fn_args(self) -> Dict[str, Any]:
|
|
159
|
+
return {
|
|
160
|
+
"model": self._litellm_model_name,
|
|
161
|
+
"api_base": self.api_base,
|
|
162
|
+
"api_type": self.api_type,
|
|
163
|
+
"api_version": self.api_version,
|
|
164
|
+
**self._litellm_extra_parameters,
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def _litellm_model_name(self) -> str:
|
|
169
|
+
"""Returns the LiteLLM model name for the openai embedding client."""
|
|
170
|
+
if self._model and f"{OPENAI_PROVIDER}/" not in self._model:
|
|
171
|
+
return f"{OPENAI_PROVIDER}/{self._model}"
|
|
172
|
+
return self._model
|
|
File without changes
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from typing import Dict, List, Any, Union
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import structlog
|
|
6
|
+
from litellm import (
|
|
7
|
+
completion,
|
|
8
|
+
acompletion,
|
|
9
|
+
validate_environment,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from rasa.shared.constants import API_BASE_CONFIG_KEY
|
|
13
|
+
from rasa.shared.exceptions import (
|
|
14
|
+
ProviderClientAPIException,
|
|
15
|
+
ProviderClientValidationError,
|
|
16
|
+
)
|
|
17
|
+
from rasa.shared.providers._ssl_verification_utils import (
|
|
18
|
+
ensure_ssl_certificates_for_litellm_non_openai_based_clients,
|
|
19
|
+
ensure_ssl_certificates_for_litellm_openai_based_clients,
|
|
20
|
+
)
|
|
21
|
+
from rasa.shared.providers.llm.llm_response import LLMResponse, LLMUsage
|
|
22
|
+
from rasa.shared.utils.io import suppress_logs
|
|
23
|
+
|
|
24
|
+
structlogger = structlog.get_logger()
|
|
25
|
+
|
|
26
|
+
_VALIDATE_ENVIRONMENT_MISSING_KEYS_KEY = "missing_keys"
|
|
27
|
+
|
|
28
|
+
# Suppress LiteLLM info and debug logs - Global level.
|
|
29
|
+
logging.getLogger("LiteLLM").setLevel(logging.WARNING)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _BaseLiteLLMClient:
|
|
33
|
+
"""An abstract base class for LiteLLM clients.
|
|
34
|
+
|
|
35
|
+
This class defines the interface and common functionality for all clients
|
|
36
|
+
based on LiteLLM.
|
|
37
|
+
|
|
38
|
+
The class is made private to prevent it from being part of the
|
|
39
|
+
public-facing interface, as it serves as an internal base class
|
|
40
|
+
for specific implementations of clients that are currently based on
|
|
41
|
+
LiteLLM.
|
|
42
|
+
|
|
43
|
+
By keeping it private, we ensure that only the derived, concrete
|
|
44
|
+
implementations are exposed to users, maintaining a cleaner and
|
|
45
|
+
more controlled API surface.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self): # type: ignore
|
|
49
|
+
self._ensure_certificates()
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def from_config(cls, config: Dict[str, Any]) -> "_BaseLiteLLMClient":
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def config(self) -> dict:
|
|
59
|
+
"""Returns the configuration for that the llm client
|
|
60
|
+
in dictionary form.
|
|
61
|
+
"""
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def _litellm_model_name(self) -> str:
|
|
67
|
+
"""Returns the value of LiteLLM's model parameter to be used in
|
|
68
|
+
completion/acompletion in LiteLLM format:
|
|
69
|
+
|
|
70
|
+
<provider>/<model or deployment name>
|
|
71
|
+
"""
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def _litellm_extra_parameters(self) -> Dict[str, Any]:
|
|
76
|
+
"""Returns a dictionary of extra parameters which include model
|
|
77
|
+
parameters as well as LiteLLM specific input parameters.
|
|
78
|
+
|
|
79
|
+
By default, this returns an empty dictionary (no extra parameters).
|
|
80
|
+
"""
|
|
81
|
+
return {}
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def _completion_fn_args(self) -> dict:
|
|
85
|
+
return {
|
|
86
|
+
**self._litellm_extra_parameters,
|
|
87
|
+
"model": self._litellm_model_name,
|
|
88
|
+
# Since all providers covered by LiteLLM use the OpenAI format, but
|
|
89
|
+
# not all support every OpenAI parameter, raise an exception if
|
|
90
|
+
# provider/model uses unsupported parameter
|
|
91
|
+
"drop_params": False,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
def validate_client_setup(self) -> None:
|
|
95
|
+
"""Perform client validation. By default only environment variables
|
|
96
|
+
are validated.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
ProviderClientValidationError if validation fails.
|
|
100
|
+
"""
|
|
101
|
+
self._validate_environment_variables()
|
|
102
|
+
self._validate_api_key_not_in_config()
|
|
103
|
+
|
|
104
|
+
def _validate_environment_variables(self) -> None:
|
|
105
|
+
"""Validate that the required environment variables are set."""
|
|
106
|
+
validation_info = validate_environment(
|
|
107
|
+
self._litellm_model_name,
|
|
108
|
+
api_base=self._litellm_extra_parameters.get(API_BASE_CONFIG_KEY),
|
|
109
|
+
)
|
|
110
|
+
if missing_environment_variables := validation_info.get(
|
|
111
|
+
_VALIDATE_ENVIRONMENT_MISSING_KEYS_KEY
|
|
112
|
+
):
|
|
113
|
+
event_info = (
|
|
114
|
+
f"Environment variables: {missing_environment_variables} "
|
|
115
|
+
f"not set. Required for API calls."
|
|
116
|
+
)
|
|
117
|
+
structlogger.error(
|
|
118
|
+
"base_litellm_client.validate_environment_variables",
|
|
119
|
+
event_info=event_info,
|
|
120
|
+
missing_environment_variables=missing_environment_variables,
|
|
121
|
+
)
|
|
122
|
+
raise ProviderClientValidationError(event_info)
|
|
123
|
+
|
|
124
|
+
def _validate_api_key_not_in_config(self) -> None:
|
|
125
|
+
if "api_key" in self._litellm_extra_parameters:
|
|
126
|
+
event_info = (
|
|
127
|
+
"API Key is set through `api_key` extra parameter."
|
|
128
|
+
"Set API keys through environment variables."
|
|
129
|
+
)
|
|
130
|
+
structlogger.error(
|
|
131
|
+
"base_litellm_client.validate_api_key_not_in_config",
|
|
132
|
+
event_info=event_info,
|
|
133
|
+
)
|
|
134
|
+
raise ProviderClientValidationError(event_info)
|
|
135
|
+
|
|
136
|
+
@suppress_logs(log_level=logging.WARNING)
|
|
137
|
+
def completion(self, messages: Union[List[str], str]) -> LLMResponse:
|
|
138
|
+
"""Synchronously generate completions for given list of messages.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
messages: List of messages or a single message to generate the
|
|
142
|
+
completion for.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
List of message completions.
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
ProviderClientAPIException: If the API request fails.
|
|
149
|
+
"""
|
|
150
|
+
try:
|
|
151
|
+
formatted_messages = self._format_messages(messages)
|
|
152
|
+
response = completion(
|
|
153
|
+
messages=formatted_messages, **self._completion_fn_args
|
|
154
|
+
)
|
|
155
|
+
return self._format_response(response)
|
|
156
|
+
except Exception as e:
|
|
157
|
+
raise ProviderClientAPIException(e)
|
|
158
|
+
|
|
159
|
+
@suppress_logs(log_level=logging.WARNING)
|
|
160
|
+
async def acompletion(self, messages: Union[List[str], str]) -> LLMResponse:
|
|
161
|
+
"""Asynchronously generate completions for given list of messages.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
messages: List of messages or a single message to generate the
|
|
165
|
+
completion for.
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
List of message completions.
|
|
169
|
+
|
|
170
|
+
Raises:
|
|
171
|
+
ProviderClientAPIException: If the API request fails.
|
|
172
|
+
"""
|
|
173
|
+
try:
|
|
174
|
+
formatted_messages = self._format_messages(messages)
|
|
175
|
+
response = await acompletion(
|
|
176
|
+
messages=formatted_messages, **self._completion_fn_args
|
|
177
|
+
)
|
|
178
|
+
return self._format_response(response)
|
|
179
|
+
except Exception as e:
|
|
180
|
+
message = ""
|
|
181
|
+
from rasa.shared.providers.llm.self_hosted_llm_client import (
|
|
182
|
+
SelfHostedLLMClient,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
if isinstance(self, SelfHostedLLMClient):
|
|
186
|
+
message = (
|
|
187
|
+
"If you are using 'provider=self-hosted' to call a hosted vllm "
|
|
188
|
+
"server make sure your config is correctly setup. You should have "
|
|
189
|
+
"the following mandatory keys in your config: "
|
|
190
|
+
"provider=self-hosted; "
|
|
191
|
+
"model='<your-vllm-model-name>'; "
|
|
192
|
+
"api_base='your-hosted-vllm-serv'."
|
|
193
|
+
"In case you are getting OpenAI connection errors, such as missing "
|
|
194
|
+
"API key, your configuration is incorrect."
|
|
195
|
+
)
|
|
196
|
+
raise ProviderClientAPIException(e, message)
|
|
197
|
+
|
|
198
|
+
def _format_messages(self, messages: Union[List[str], str]) -> List[Dict[str, str]]:
|
|
199
|
+
"""Formats messages (or a single message) to OpenAI format."""
|
|
200
|
+
if isinstance(messages, str):
|
|
201
|
+
messages = [messages]
|
|
202
|
+
return [{"content": message, "role": "user"} for message in messages]
|
|
203
|
+
|
|
204
|
+
def _format_response(self, response: Any) -> LLMResponse:
|
|
205
|
+
"""Parses the LiteLLM response to Rasa format."""
|
|
206
|
+
formatted_response = LLMResponse(
|
|
207
|
+
id=response.id,
|
|
208
|
+
created=response.created,
|
|
209
|
+
choices=[choice.message.content for choice in response.choices],
|
|
210
|
+
model=response.model,
|
|
211
|
+
)
|
|
212
|
+
if (
|
|
213
|
+
response.model_extra
|
|
214
|
+
and (usage := response.model_extra.get("usage")) is not None
|
|
215
|
+
):
|
|
216
|
+
# We use `.get()` for accessing litellm.utils.Usage attributes.
|
|
217
|
+
# litellm.utils.Usage does not set the attributes if
|
|
218
|
+
# `prompt_tokens` or `completion_tokens` are absent (None).
|
|
219
|
+
prompt_tokens = (
|
|
220
|
+
num_tokens
|
|
221
|
+
if isinstance(num_tokens := usage.get("prompt_tokens", 0), (int, float))
|
|
222
|
+
else 0
|
|
223
|
+
)
|
|
224
|
+
completion_tokens = (
|
|
225
|
+
num_tokens
|
|
226
|
+
if isinstance(
|
|
227
|
+
num_tokens := usage.get("completion_tokens", 0), (int, float)
|
|
228
|
+
)
|
|
229
|
+
else 0
|
|
230
|
+
)
|
|
231
|
+
formatted_response.usage = LLMUsage(prompt_tokens, completion_tokens)
|
|
232
|
+
structlogger.debug(
|
|
233
|
+
"base_litellm_client.formatted_response",
|
|
234
|
+
formatted_response=formatted_response.to_dict(),
|
|
235
|
+
)
|
|
236
|
+
return formatted_response
|
|
237
|
+
|
|
238
|
+
@staticmethod
|
|
239
|
+
def _ensure_certificates() -> None:
|
|
240
|
+
"""Configures SSL certificates for LiteLLM. This method is invoked during
|
|
241
|
+
client initialization.
|
|
242
|
+
|
|
243
|
+
LiteLLM may utilize `openai` clients or other providers that require
|
|
244
|
+
SSL verification settings through the `SSL_VERIFY` / `SSL_CERTIFICATE`
|
|
245
|
+
environment variables or the `litellm.ssl_verify` /
|
|
246
|
+
`litellm.ssl_certificate` global settings.
|
|
247
|
+
|
|
248
|
+
This method ensures proper SSL configuration for both cases.
|
|
249
|
+
"""
|
|
250
|
+
ensure_ssl_certificates_for_litellm_non_openai_based_clients()
|
|
251
|
+
ensure_ssl_certificates_for_litellm_openai_based_clients()
|