deepeval 3.7.3__py3-none-any.whl → 3.7.4__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.
Files changed (84) hide show
  1. deepeval/_version.py +1 -1
  2. deepeval/cli/test.py +1 -1
  3. deepeval/config/settings.py +102 -13
  4. deepeval/evaluate/configs.py +1 -1
  5. deepeval/evaluate/execute.py +4 -1
  6. deepeval/metrics/answer_relevancy/template.py +4 -4
  7. deepeval/metrics/argument_correctness/template.py +2 -2
  8. deepeval/metrics/bias/template.py +3 -3
  9. deepeval/metrics/contextual_precision/template.py +6 -6
  10. deepeval/metrics/contextual_recall/template.py +2 -2
  11. deepeval/metrics/contextual_relevancy/template.py +3 -3
  12. deepeval/metrics/conversation_completeness/template.py +2 -2
  13. deepeval/metrics/conversational_dag/templates.py +4 -4
  14. deepeval/metrics/conversational_g_eval/template.py +4 -3
  15. deepeval/metrics/dag/templates.py +4 -4
  16. deepeval/metrics/faithfulness/template.py +4 -4
  17. deepeval/metrics/hallucination/template.py +4 -4
  18. deepeval/metrics/misuse/template.py +2 -2
  19. deepeval/metrics/multimodal_metrics/multimodal_answer_relevancy/template.py +7 -7
  20. deepeval/metrics/multimodal_metrics/multimodal_contextual_precision/template.py +6 -6
  21. deepeval/metrics/multimodal_metrics/multimodal_contextual_recall/template.py +2 -2
  22. deepeval/metrics/multimodal_metrics/multimodal_contextual_relevancy/template.py +3 -3
  23. deepeval/metrics/multimodal_metrics/multimodal_faithfulness/template.py +9 -9
  24. deepeval/metrics/multimodal_metrics/multimodal_g_eval/template.py +4 -4
  25. deepeval/metrics/non_advice/template.py +2 -2
  26. deepeval/metrics/pii_leakage/template.py +2 -2
  27. deepeval/metrics/prompt_alignment/template.py +4 -4
  28. deepeval/metrics/role_violation/template.py +2 -2
  29. deepeval/metrics/step_efficiency/step_efficiency.py +1 -1
  30. deepeval/metrics/toxicity/template.py +4 -4
  31. deepeval/metrics/turn_relevancy/template.py +2 -2
  32. deepeval/models/embedding_models/azure_embedding_model.py +28 -15
  33. deepeval/models/embedding_models/local_embedding_model.py +23 -10
  34. deepeval/models/embedding_models/ollama_embedding_model.py +8 -6
  35. deepeval/models/embedding_models/openai_embedding_model.py +18 -2
  36. deepeval/models/llms/anthropic_model.py +17 -5
  37. deepeval/models/llms/azure_model.py +30 -18
  38. deepeval/models/llms/deepseek_model.py +22 -12
  39. deepeval/models/llms/gemini_model.py +120 -87
  40. deepeval/models/llms/grok_model.py +23 -16
  41. deepeval/models/llms/kimi_model.py +23 -12
  42. deepeval/models/llms/litellm_model.py +63 -25
  43. deepeval/models/llms/local_model.py +26 -18
  44. deepeval/models/llms/ollama_model.py +17 -7
  45. deepeval/models/llms/openai_model.py +22 -17
  46. deepeval/models/llms/portkey_model.py +132 -0
  47. deepeval/models/mlllms/azure_model.py +28 -19
  48. deepeval/models/mlllms/gemini_model.py +102 -73
  49. deepeval/models/mlllms/ollama_model.py +40 -9
  50. deepeval/models/mlllms/openai_model.py +65 -14
  51. deepeval/models/utils.py +48 -3
  52. deepeval/optimization/__init__.py +13 -0
  53. deepeval/optimization/adapters/__init__.py +2 -0
  54. deepeval/optimization/adapters/deepeval_scoring_adapter.py +588 -0
  55. deepeval/optimization/aggregates.py +14 -0
  56. deepeval/optimization/configs.py +34 -0
  57. deepeval/optimization/copro/configs.py +31 -0
  58. deepeval/optimization/copro/loop.py +837 -0
  59. deepeval/optimization/gepa/__init__.py +7 -0
  60. deepeval/optimization/gepa/configs.py +115 -0
  61. deepeval/optimization/gepa/loop.py +677 -0
  62. deepeval/optimization/miprov2/configs.py +134 -0
  63. deepeval/optimization/miprov2/loop.py +785 -0
  64. deepeval/optimization/mutations/__init__.py +0 -0
  65. deepeval/optimization/mutations/prompt_rewriter.py +458 -0
  66. deepeval/optimization/policies/__init__.py +16 -0
  67. deepeval/optimization/policies/selection.py +166 -0
  68. deepeval/optimization/policies/tie_breaker.py +67 -0
  69. deepeval/optimization/prompt_optimizer.py +462 -0
  70. deepeval/optimization/simba/__init__.py +0 -0
  71. deepeval/optimization/simba/configs.py +33 -0
  72. deepeval/optimization/simba/loop.py +983 -0
  73. deepeval/optimization/simba/types.py +15 -0
  74. deepeval/optimization/types.py +361 -0
  75. deepeval/optimization/utils.py +598 -0
  76. deepeval/prompt/prompt.py +10 -5
  77. deepeval/test_run/cache.py +2 -0
  78. deepeval/test_run/test_run.py +6 -1
  79. deepeval/utils.py +24 -0
  80. {deepeval-3.7.3.dist-info → deepeval-3.7.4.dist-info}/METADATA +1 -1
  81. {deepeval-3.7.3.dist-info → deepeval-3.7.4.dist-info}/RECORD +84 -59
  82. {deepeval-3.7.3.dist-info → deepeval-3.7.4.dist-info}/LICENSE.md +0 -0
  83. {deepeval-3.7.3.dist-info → deepeval-3.7.4.dist-info}/WHEEL +0 -0
  84. {deepeval-3.7.3.dist-info → deepeval-3.7.4.dist-info}/entry_points.txt +0 -0
@@ -1,15 +1,17 @@
1
- from pydantic import BaseModel
1
+ import json
2
+
3
+ from pydantic import BaseModel, SecretStr
2
4
  from google.genai import types, Client
3
5
  from typing import Optional, Dict
4
6
 
7
+ from deepeval.config.settings import get_settings
8
+ from deepeval.models.utils import require_secret_api_key
5
9
  from deepeval.models.retry_policy import (
6
10
  create_retry_decorator,
7
11
  )
8
- from deepeval.key_handler import ModelKeyValues, KEY_FILE_HANDLER
9
12
  from deepeval.models.base_model import DeepEvalBaseLLM
10
13
  from deepeval.constants import ProviderSlug as PS
11
14
  from google.oauth2 import service_account
12
- import json
13
15
 
14
16
  default_gemini_model = "gemini-1.5-pro"
15
17
 
@@ -57,31 +59,32 @@ class GeminiModel(DeepEvalBaseLLM):
57
59
  generation_kwargs: Optional[Dict] = None,
58
60
  **kwargs,
59
61
  ):
62
+
63
+ settings = get_settings()
64
+
60
65
  model_name = (
61
- model_name
62
- or KEY_FILE_HANDLER.fetch_data(ModelKeyValues.GEMINI_MODEL_NAME)
63
- or default_gemini_model
66
+ model_name or settings.GEMINI_MODEL_NAME or default_gemini_model
64
67
  )
65
68
 
66
- # Get API key from key handler if not provided
67
- self.api_key = api_key or KEY_FILE_HANDLER.fetch_data(
68
- ModelKeyValues.GOOGLE_API_KEY
69
- )
70
- self.project = project or KEY_FILE_HANDLER.fetch_data(
71
- ModelKeyValues.GOOGLE_CLOUD_PROJECT
72
- )
73
- self.location = location or KEY_FILE_HANDLER.fetch_data(
74
- ModelKeyValues.GOOGLE_CLOUD_LOCATION
75
- )
76
- self.use_vertexai = KEY_FILE_HANDLER.fetch_data(
77
- ModelKeyValues.GOOGLE_GENAI_USE_VERTEXAI
69
+ # Get API key from settings if not provided
70
+ if api_key is not None:
71
+ # keep it secret, keep it safe from serializings, logging and aolike
72
+ self.api_key: SecretStr | None = SecretStr(api_key)
73
+ else:
74
+ self.api_key = settings.GOOGLE_API_KEY
75
+
76
+ self.project = project or settings.GOOGLE_CLOUD_PROJECT
77
+ self.location = (
78
+ location
79
+ or settings.GOOGLE_CLOUD_LOCATION is not None
80
+ and str(settings.GOOGLE_CLOUD_LOCATION)
78
81
  )
82
+ self.use_vertexai = settings.GOOGLE_GENAI_USE_VERTEXAI
83
+
79
84
  if service_account_key:
80
85
  self.service_account_key = service_account_key
81
86
  else:
82
- service_account_key_data = KEY_FILE_HANDLER.fetch_data(
83
- ModelKeyValues.GOOGLE_SERVICE_ACCOUNT_KEY
84
- )
87
+ service_account_key_data = settings.GOOGLE_SERVICE_ACCOUNT_KEY
85
88
  if service_account_key_data is None:
86
89
  self.service_account_key = None
87
90
  elif isinstance(service_account_key_data, str):
@@ -90,69 +93,10 @@ class GeminiModel(DeepEvalBaseLLM):
90
93
  if temperature < 0:
91
94
  raise ValueError("Temperature must be >= 0.")
92
95
  self.temperature = temperature
96
+
97
+ # Raw kwargs destined for the underlying Client
93
98
  self.kwargs = kwargs
94
99
  self.generation_kwargs = generation_kwargs or {}
95
- super().__init__(model_name, **kwargs)
96
-
97
- def should_use_vertexai(self):
98
- """Checks if the model should use Vertex AI for generation.
99
-
100
- This is determined first by the value of `GOOGLE_GENAI_USE_VERTEXAI`
101
- environment variable. If not set, it checks for the presence of the
102
- project and location.
103
-
104
- Returns:
105
- True if the model should use Vertex AI, False otherwise
106
- """
107
- if self.use_vertexai is not None:
108
- return self.use_vertexai.lower() == "yes"
109
- if self.project and self.location:
110
- return True
111
- else:
112
- return False
113
-
114
- def load_model(self, *args, **kwargs):
115
- """Creates a client.
116
- With Gen AI SDK, model is set at inference time, so there is no
117
- model to load and initialize.
118
- This method name is kept for compatibility with other LLMs.
119
-
120
- Returns:
121
- A GenerativeModel instance configured for evaluation.
122
- """
123
- if self.should_use_vertexai():
124
- if not self.project or not self.location:
125
- raise ValueError(
126
- "When using Vertex AI API, both project and location are required."
127
- "Either provide them as arguments or set GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_LOCATION environment variables, "
128
- "or set them in your DeepEval configuration."
129
- )
130
-
131
- # Create client for Vertex AI
132
- self.client = Client(
133
- vertexai=True,
134
- project=self.project,
135
- location=self.location,
136
- credentials=(
137
- service_account.Credentials.from_service_account_info(
138
- self.service_account_key,
139
- scopes=[
140
- "https://www.googleapis.com/auth/cloud-platform"
141
- ],
142
- )
143
- if self.service_account_key
144
- else None
145
- ),
146
- **self.kwargs,
147
- )
148
- else:
149
- if not self.api_key:
150
- raise ValueError(
151
- "Google API key is required. Either provide it directly, set GOOGLE_API_KEY environment variable, "
152
- "or set it in your DeepEval configuration."
153
- )
154
- # Create client for Gemini API
155
- self.client = Client(api_key=self.api_key, **self.kwargs)
156
100
 
157
101
  # Configure default model generation settings
158
102
  self.model_safety_settings = [
@@ -173,7 +117,29 @@ class GeminiModel(DeepEvalBaseLLM):
173
117
  threshold=types.HarmBlockThreshold.BLOCK_NONE,
174
118
  ),
175
119
  ]
176
- return self.client.models
120
+
121
+ super().__init__(model_name, **kwargs)
122
+
123
+ def should_use_vertexai(self) -> bool:
124
+ """Checks if the model should use Vertex AI for generation.
125
+
126
+ This is determined first by the value of `GOOGLE_GENAI_USE_VERTEXAI`
127
+ environment variable. If not set, it checks for the presence of the
128
+ project and location.
129
+
130
+ Returns:
131
+ True if the model should use Vertex AI, False otherwise
132
+ """
133
+ if self.use_vertexai is not None:
134
+ return self.use_vertexai.lower() == "yes"
135
+ if self.project and self.location:
136
+ return True
137
+ else:
138
+ return False
139
+
140
+ ###############################################
141
+ # Generate functions
142
+ ###############################################
177
143
 
178
144
  @retry_gemini
179
145
  def generate(self, prompt: str, schema: Optional[BaseModel] = None) -> str:
@@ -186,8 +152,10 @@ class GeminiModel(DeepEvalBaseLLM):
186
152
  Returns:
187
153
  Generated text response or structured output as Pydantic model
188
154
  """
155
+ client = self.load_model()
156
+
189
157
  if schema is not None:
190
- response = self.client.models.generate_content(
158
+ response = client.models.generate_content(
191
159
  model=self.model_name,
192
160
  contents=prompt,
193
161
  config=types.GenerateContentConfig(
@@ -200,7 +168,7 @@ class GeminiModel(DeepEvalBaseLLM):
200
168
  )
201
169
  return response.parsed, 0
202
170
  else:
203
- response = self.client.models.generate_content(
171
+ response = client.models.generate_content(
204
172
  model=self.model_name,
205
173
  contents=prompt,
206
174
  config=types.GenerateContentConfig(
@@ -224,8 +192,10 @@ class GeminiModel(DeepEvalBaseLLM):
224
192
  Returns:
225
193
  Generated text response or structured output as Pydantic model
226
194
  """
195
+ client = self.load_model()
196
+
227
197
  if schema is not None:
228
- response = await self.client.aio.models.generate_content(
198
+ response = await client.aio.models.generate_content(
229
199
  model=self.model_name,
230
200
  contents=prompt,
231
201
  config=types.GenerateContentConfig(
@@ -238,7 +208,7 @@ class GeminiModel(DeepEvalBaseLLM):
238
208
  )
239
209
  return response.parsed, 0
240
210
  else:
241
- response = await self.client.aio.models.generate_content(
211
+ response = await client.aio.models.generate_content(
242
212
  model=self.model_name,
243
213
  contents=prompt,
244
214
  config=types.GenerateContentConfig(
@@ -249,6 +219,69 @@ class GeminiModel(DeepEvalBaseLLM):
249
219
  )
250
220
  return response.text, 0
251
221
 
222
+ #########
223
+ # Model #
224
+ #########
225
+
252
226
  def get_model_name(self) -> str:
253
227
  """Returns the name of the Gemini model being used."""
254
228
  return self.model_name
229
+
230
+ def load_model(self, *args, **kwargs):
231
+ """Creates a client.
232
+ With Gen AI SDK, model is set at inference time, so there is no
233
+ model to load and initialize.
234
+ This method name is kept for compatibility with other LLMs.
235
+
236
+ Returns:
237
+ A GenerativeModel instance configured for evaluation.
238
+ """
239
+ return self._build_client(**kwargs)
240
+
241
+ def _client_kwargs(self, **override_kwargs) -> Dict:
242
+ """Merge ctor kwargs with any overrides passed at load_model time."""
243
+ client_kwargs = dict(self.kwargs or {})
244
+ if override_kwargs:
245
+ client_kwargs.update(override_kwargs)
246
+ return client_kwargs
247
+
248
+ def _build_client(self, **override_kwargs) -> Client:
249
+ client_kwargs = self._client_kwargs(**override_kwargs)
250
+
251
+ if self.should_use_vertexai():
252
+ if not self.project or not self.location:
253
+ raise ValueError(
254
+ "When using Vertex AI API, both project and location are required. "
255
+ "Either provide them as arguments or set GOOGLE_CLOUD_PROJECT and "
256
+ "GOOGLE_CLOUD_LOCATION in your DeepEval configuration."
257
+ )
258
+
259
+ credentials = (
260
+ service_account.Credentials.from_service_account_info(
261
+ self.service_account_key,
262
+ scopes=[
263
+ "https://www.googleapis.com/auth/cloud-platform",
264
+ ],
265
+ )
266
+ if self.service_account_key
267
+ else None
268
+ )
269
+
270
+ client = Client(
271
+ vertexai=True,
272
+ project=self.project,
273
+ location=self.location,
274
+ credentials=credentials,
275
+ **client_kwargs,
276
+ )
277
+ else:
278
+ api_key = require_secret_api_key(
279
+ self.api_key,
280
+ provider_label="Google Gemini",
281
+ env_var_name="GOOGLE_API_KEY",
282
+ param_hint="`api_key` to GeminiModel(...)",
283
+ )
284
+
285
+ client = Client(api_key=api_key, **client_kwargs)
286
+
287
+ return client
@@ -1,14 +1,13 @@
1
- import os
2
-
3
1
  from typing import Optional, Tuple, Union, Dict
4
- from pydantic import BaseModel
2
+ from pydantic import BaseModel, SecretStr
5
3
 
4
+ from deepeval.config.settings import get_settings
6
5
  from deepeval.models.retry_policy import (
7
6
  create_retry_decorator,
8
7
  sdk_retries_for,
9
8
  )
10
- from deepeval.key_handler import ModelKeyValues, KEY_FILE_HANDLER
11
9
  from deepeval.models.llms.utils import trim_and_load_json
10
+ from deepeval.models.utils import require_secret_api_key
12
11
  from deepeval.models import DeepEvalBaseLLM
13
12
  from deepeval.constants import ProviderSlug as PS
14
13
 
@@ -62,27 +61,28 @@ class GrokModel(DeepEvalBaseLLM):
62
61
  generation_kwargs: Optional[Dict] = None,
63
62
  **kwargs,
64
63
  ):
65
- model_name = model or KEY_FILE_HANDLER.fetch_data(
66
- ModelKeyValues.GROK_MODEL_NAME
67
- )
64
+ settings = get_settings()
65
+
66
+ model_name = model or settings.GROK_MODEL_NAME
67
+
68
68
  if model_name not in model_pricing:
69
69
  raise ValueError(
70
70
  f"Invalid model. Available Grok models: {', '.join(model_pricing.keys())}"
71
71
  )
72
- temperature_from_key = KEY_FILE_HANDLER.fetch_data(
73
- ModelKeyValues.TEMPERATURE
74
- )
72
+ temperature_from_key = settings.TEMPERATURE
75
73
  if temperature_from_key is None:
76
74
  self.temperature = temperature
77
75
  else:
78
76
  self.temperature = float(temperature_from_key)
79
77
  if self.temperature < 0:
80
78
  raise ValueError("Temperature must be >= 0.")
81
- self.api_key = (
82
- api_key
83
- or KEY_FILE_HANDLER.fetch_data(ModelKeyValues.GROK_API_KEY)
84
- or os.getenv("GROK_API_KEY")
85
- )
79
+
80
+ if api_key is not None:
81
+ # keep it secret, keep it safe from serializings, logging and alike
82
+ self.api_key: SecretStr | None = SecretStr(api_key)
83
+ else:
84
+ self.api_key = settings.GROK_API_KEY
85
+
86
86
  self.kwargs = kwargs
87
87
  self.generation_kwargs = generation_kwargs or {}
88
88
  super().__init__(model_name)
@@ -226,7 +226,14 @@ class GrokModel(DeepEvalBaseLLM):
226
226
  return kwargs
227
227
 
228
228
  def _build_client(self, cls):
229
- kw = dict(api_key=self.api_key, **self._client_kwargs())
229
+ api_key = require_secret_api_key(
230
+ self.api_key,
231
+ provider_label="Grok",
232
+ env_var_name="GROK_API_KEY",
233
+ param_hint="`api_key` to GrokModel(...)",
234
+ )
235
+
236
+ kw = dict(api_key=api_key, **self._client_kwargs())
230
237
  try:
231
238
  return cls(**kw)
232
239
  except TypeError as e:
@@ -1,13 +1,14 @@
1
1
  from typing import Optional, Tuple, Union, Dict
2
2
  from openai import OpenAI, AsyncOpenAI
3
- from pydantic import BaseModel
3
+ from pydantic import BaseModel, SecretStr
4
4
 
5
+ from deepeval.config.settings import get_settings
5
6
  from deepeval.models.retry_policy import (
6
7
  create_retry_decorator,
7
8
  sdk_retries_for,
8
9
  )
9
- from deepeval.key_handler import ModelKeyValues, KEY_FILE_HANDLER
10
10
  from deepeval.models.llms.utils import trim_and_load_json
11
+ from deepeval.models.utils import require_secret_api_key
11
12
  from deepeval.models import DeepEvalBaseLLM
12
13
  from deepeval.constants import ProviderSlug as PS
13
14
 
@@ -79,25 +80,28 @@ class KimiModel(DeepEvalBaseLLM):
79
80
  generation_kwargs: Optional[Dict] = None,
80
81
  **kwargs,
81
82
  ):
82
- model_name = model or KEY_FILE_HANDLER.fetch_data(
83
- ModelKeyValues.MOONSHOT_MODEL_NAME
84
- )
83
+ settings = get_settings()
84
+
85
+ model_name = model or settings.MOONSHOT_MODEL_NAME
85
86
  if model_name not in model_pricing:
86
87
  raise ValueError(
87
88
  f"Invalid model. Available Moonshot models: {', '.join(model_pricing.keys())}"
88
89
  )
89
- temperature_from_key = KEY_FILE_HANDLER.fetch_data(
90
- ModelKeyValues.TEMPERATURE
91
- )
90
+
91
+ temperature_from_key = settings.TEMPERATURE
92
92
  if temperature_from_key is None:
93
93
  self.temperature = temperature
94
94
  else:
95
95
  self.temperature = float(temperature_from_key)
96
96
  if self.temperature < 0:
97
97
  raise ValueError("Temperature must be >= 0.")
98
- self.api_key = api_key or KEY_FILE_HANDLER.fetch_data(
99
- ModelKeyValues.MOONSHOT_API_KEY
100
- )
98
+
99
+ if api_key is not None:
100
+ # keep it secret, keep it safe from serializings, logging and alike
101
+ self.api_key: SecretStr | None = SecretStr(api_key)
102
+ else:
103
+ self.api_key = settings.MOONSHOT_API_KEY
104
+
101
105
  self.base_url = "https://api.moonshot.cn/v1"
102
106
  self.kwargs = kwargs
103
107
  self.generation_kwargs = generation_kwargs or {}
@@ -218,8 +222,15 @@ class KimiModel(DeepEvalBaseLLM):
218
222
  return kwargs
219
223
 
220
224
  def _build_client(self, cls):
225
+ api_key = require_secret_api_key(
226
+ self.api_key,
227
+ provider_label="Kimi",
228
+ env_var_name="MOONSHOT_API_KEY",
229
+ param_hint="`api_key` to KimiModel(...)",
230
+ )
231
+
221
232
  kw = dict(
222
- api_key=self.api_key,
233
+ api_key=api_key,
223
234
  base_url=self.base_url,
224
235
  **self._client_kwargs(),
225
236
  )
@@ -1,6 +1,6 @@
1
- from typing import Optional, Tuple, Union, Dict, List, Any
2
- from pydantic import BaseModel
3
1
  import logging
2
+ from typing import Optional, Tuple, Union, Dict, List, Any
3
+ from pydantic import BaseModel, SecretStr
4
4
  from tenacity import (
5
5
  retry,
6
6
  stop_after_attempt,
@@ -8,11 +8,11 @@ from tenacity import (
8
8
  wait_exponential_jitter,
9
9
  RetryCallState,
10
10
  )
11
- import os
12
11
 
12
+ from deepeval.config.settings import get_settings
13
+ from deepeval.models.utils import require_secret_api_key
13
14
  from deepeval.models import DeepEvalBaseLLM
14
15
  from deepeval.models.llms.utils import trim_and_load_json
15
- from deepeval.key_handler import ModelKeyValues, KEY_FILE_HANDLER
16
16
 
17
17
 
18
18
  def log_retry_error(retry_state: RetryCallState):
@@ -44,33 +44,41 @@ class LiteLLMModel(DeepEvalBaseLLM):
44
44
  generation_kwargs: Optional[Dict] = None,
45
45
  **kwargs,
46
46
  ):
47
- from litellm import completion, acompletion, get_llm_provider
48
47
 
48
+ settings = get_settings()
49
49
  # Get model name from parameter or key file
50
- model_name = model or KEY_FILE_HANDLER.fetch_data(
51
- ModelKeyValues.LITELLM_MODEL_NAME
52
- )
50
+ model_name = model or settings.LITELLM_MODEL_NAME
53
51
  if not model_name:
54
52
  raise ValueError(
55
53
  "Model name must be provided either through parameter or set-litellm command"
56
54
  )
57
55
 
58
- # Get API key from parameter, key file, or environment variable
59
- self.api_key = (
60
- api_key
61
- or KEY_FILE_HANDLER.fetch_data(ModelKeyValues.LITELLM_API_KEY)
62
- or os.getenv("LITELLM_PROXY_API_KEY")
63
- or os.getenv("OPENAI_API_KEY")
64
- or os.getenv("ANTHROPIC_API_KEY")
65
- or os.getenv("GOOGLE_API_KEY")
66
- )
56
+ # Get API key from parameter, or settings
57
+ if api_key is not None:
58
+ # keep it secret, keep it safe from serializings, logging and aolike
59
+ self.api_key: SecretStr | None = SecretStr(api_key)
60
+ else:
61
+ self.api_key = (
62
+ settings.LITELLM_API_KEY
63
+ or settings.LITELLM_PROXY_API_KEY
64
+ or settings.OPENAI_API_KEY
65
+ or settings.ANTHROPIC_API_KEY
66
+ or settings.GOOGLE_API_KEY
67
+ )
67
68
 
68
69
  # Get API base from parameter, key file, or environment variable
69
70
  self.api_base = (
70
71
  api_base
71
- or KEY_FILE_HANDLER.fetch_data(ModelKeyValues.LITELLM_API_BASE)
72
- or os.getenv("LITELLM_API_BASE")
73
- or os.getenv("LITELLM_PROXY_API_BASE")
72
+ or (
73
+ str(settings.LITELLM_API_BASE)
74
+ if settings.LITELLM_API_BASE is not None
75
+ else None
76
+ )
77
+ or (
78
+ str(settings.LITELLM_PROXY_API_BASE)
79
+ if settings.LITELLM_PROXY_API_BASE is not None
80
+ else None
81
+ )
74
82
  )
75
83
 
76
84
  if temperature < 0:
@@ -101,7 +109,13 @@ class LiteLLMModel(DeepEvalBaseLLM):
101
109
  }
102
110
 
103
111
  if self.api_key:
104
- completion_params["api_key"] = self.api_key
112
+ api_key = require_secret_api_key(
113
+ self.api_key,
114
+ provider_label="LiteLLM",
115
+ env_var_name="LITELLM_API_KEY|OPENAI_API_KEY|ANTHROPIC_API_KEY|GOOGLE_API_KEY",
116
+ param_hint="`api_key` to LiteLLMModel(...)",
117
+ )
118
+ completion_params["api_key"] = api_key
105
119
  if self.api_base:
106
120
  completion_params["api_base"] = self.api_base
107
121
 
@@ -150,7 +164,13 @@ class LiteLLMModel(DeepEvalBaseLLM):
150
164
  }
151
165
 
152
166
  if self.api_key:
153
- completion_params["api_key"] = self.api_key
167
+ api_key = require_secret_api_key(
168
+ self.api_key,
169
+ provider_label="LiteLLM",
170
+ env_var_name="LITELLM_API_KEY|OPENAI_API_KEY|ANTHROPIC_API_KEY|GOOGLE_API_KEY",
171
+ param_hint="`api_key` to LiteLLMModel(...)",
172
+ )
173
+ completion_params["api_key"] = api_key
154
174
  if self.api_base:
155
175
  completion_params["api_base"] = self.api_base
156
176
 
@@ -195,11 +215,17 @@ class LiteLLMModel(DeepEvalBaseLLM):
195
215
  from litellm import completion
196
216
 
197
217
  try:
218
+ api_key = require_secret_api_key(
219
+ self.api_key,
220
+ provider_label="LiteLLM",
221
+ env_var_name="LITELLM_API_KEY|OPENAI_API_KEY|ANTHROPIC_API_KEY|GOOGLE_API_KEY",
222
+ param_hint="`api_key` to LiteLLMModel(...)",
223
+ )
198
224
  completion_params = {
199
225
  "model": self.model_name,
200
226
  "messages": [{"role": "user", "content": prompt}],
201
227
  "temperature": self.temperature,
202
- "api_key": self.api_key,
228
+ "api_key": api_key,
203
229
  "api_base": self.api_base,
204
230
  "logprobs": True,
205
231
  "top_logprobs": top_logprobs,
@@ -230,11 +256,17 @@ class LiteLLMModel(DeepEvalBaseLLM):
230
256
  from litellm import acompletion
231
257
 
232
258
  try:
259
+ api_key = require_secret_api_key(
260
+ self.api_key,
261
+ provider_label="LiteLLM",
262
+ env_var_name="LITELLM_API_KEY|OPENAI_API_KEY|ANTHROPIC_API_KEY|GOOGLE_API_KEY",
263
+ param_hint="`api_key` to LiteLLMModel(...)",
264
+ )
233
265
  completion_params = {
234
266
  "model": self.model_name,
235
267
  "messages": [{"role": "user", "content": prompt}],
236
268
  "temperature": self.temperature,
237
- "api_key": self.api_key,
269
+ "api_key": api_key,
238
270
  "api_base": self.api_base,
239
271
  "logprobs": True,
240
272
  "top_logprobs": top_logprobs,
@@ -263,12 +295,18 @@ class LiteLLMModel(DeepEvalBaseLLM):
263
295
  from litellm import completion
264
296
 
265
297
  try:
298
+ api_key = require_secret_api_key(
299
+ self.api_key,
300
+ provider_label="LiteLLM",
301
+ env_var_name="LITELLM_API_KEY|OPENAI_API_KEY|ANTHROPIC_API_KEY|GOOGLE_API_KEY",
302
+ param_hint="`api_key` to LiteLLMModel(...)",
303
+ )
266
304
  completion_params = {
267
305
  "model": self.model_name,
268
306
  "messages": [{"role": "user", "content": prompt}],
269
307
  "temperature": temperature,
270
308
  "n": n,
271
- "api_key": self.api_key,
309
+ "api_key": api_key,
272
310
  "api_base": self.api_base,
273
311
  }
274
312
  completion_params.update(self.kwargs)
@@ -1,15 +1,16 @@
1
1
  from typing import Optional, Tuple, Union, Dict
2
- from pydantic import BaseModel
2
+ from pydantic import BaseModel, SecretStr
3
3
  from openai import OpenAI, AsyncOpenAI
4
4
  from openai.types.chat import ChatCompletion
5
5
 
6
+ from deepeval.config.settings import get_settings
6
7
  from deepeval.models.retry_policy import (
7
8
  create_retry_decorator,
8
9
  sdk_retries_for,
9
10
  )
10
11
  from deepeval.models.llms.utils import trim_and_load_json
12
+ from deepeval.models.utils import require_secret_api_key
11
13
  from deepeval.models import DeepEvalBaseLLM
12
- from deepeval.key_handler import ModelKeyValues, KEY_FILE_HANDLER
13
14
  from deepeval.constants import ProviderSlug as PS
14
15
 
15
16
 
@@ -28,18 +29,21 @@ class LocalModel(DeepEvalBaseLLM):
28
29
  generation_kwargs: Optional[Dict] = None,
29
30
  **kwargs,
30
31
  ):
31
- model_name = model or KEY_FILE_HANDLER.fetch_data(
32
- ModelKeyValues.LOCAL_MODEL_NAME
33
- )
34
- self.local_model_api_key = api_key or KEY_FILE_HANDLER.fetch_data(
35
- ModelKeyValues.LOCAL_MODEL_API_KEY
36
- )
37
- self.base_url = base_url or KEY_FILE_HANDLER.fetch_data(
38
- ModelKeyValues.LOCAL_MODEL_BASE_URL
39
- )
40
- self.format = format or KEY_FILE_HANDLER.fetch_data(
41
- ModelKeyValues.LOCAL_MODEL_FORMAT
32
+ settings = get_settings()
33
+
34
+ model_name = model or settings.LOCAL_MODEL_NAME
35
+ if api_key is not None:
36
+ # keep it secret, keep it safe from serializings, logging and alike
37
+ self.local_model_api_key: SecretStr | None = SecretStr(api_key)
38
+ else:
39
+ self.local_model_api_key = settings.LOCAL_MODEL_API_KEY
40
+
41
+ self.base_url = (
42
+ base_url
43
+ or settings.LOCAL_MODEL_BASE_URL
44
+ and str(settings.LOCAL_MODEL_BASE_URL)
42
45
  )
46
+ self.format = format or settings.LOCAL_MODEL_FORMAT
43
47
  if temperature < 0:
44
48
  raise ValueError("Temperature must be >= 0.")
45
49
  self.temperature = temperature
@@ -94,10 +98,7 @@ class LocalModel(DeepEvalBaseLLM):
94
98
  ###############################################
95
99
 
96
100
  def get_model_name(self):
97
- model_name = KEY_FILE_HANDLER.fetch_data(
98
- ModelKeyValues.LOCAL_MODEL_NAME
99
- )
100
- return f"{model_name} (Local Model)"
101
+ return f"{self.model_name} (Local Model)"
101
102
 
102
103
  def load_model(self, async_mode: bool = False):
103
104
  if not async_mode:
@@ -115,8 +116,15 @@ class LocalModel(DeepEvalBaseLLM):
115
116
  return kwargs
116
117
 
117
118
  def _build_client(self, cls):
119
+ local_model_api_key = require_secret_api_key(
120
+ self.local_model_api_key,
121
+ provider_label="Local",
122
+ env_var_name="LOCAL_MODEL_API_KEY",
123
+ param_hint="`api_key` to LocalModel(...)",
124
+ )
125
+
118
126
  kw = dict(
119
- api_key=self.local_model_api_key,
127
+ api_key=local_model_api_key,
120
128
  base_url=self.base_url,
121
129
  **self._client_kwargs(),
122
130
  )