deepeval 3.7.4__py3-none-any.whl → 3.7.6__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 (224) hide show
  1. deepeval/_version.py +1 -1
  2. deepeval/config/settings.py +35 -1
  3. deepeval/dataset/api.py +23 -1
  4. deepeval/dataset/golden.py +139 -2
  5. deepeval/evaluate/evaluate.py +16 -11
  6. deepeval/evaluate/execute.py +13 -181
  7. deepeval/evaluate/utils.py +6 -26
  8. deepeval/integrations/pydantic_ai/agent.py +19 -2
  9. deepeval/integrations/pydantic_ai/instrumentator.py +62 -23
  10. deepeval/key_handler.py +3 -0
  11. deepeval/metrics/__init__.py +14 -16
  12. deepeval/metrics/answer_relevancy/answer_relevancy.py +118 -116
  13. deepeval/metrics/answer_relevancy/template.py +22 -3
  14. deepeval/metrics/arena_g_eval/arena_g_eval.py +98 -96
  15. deepeval/metrics/arena_g_eval/template.py +17 -1
  16. deepeval/metrics/argument_correctness/argument_correctness.py +81 -87
  17. deepeval/metrics/argument_correctness/template.py +19 -2
  18. deepeval/metrics/base_metric.py +13 -44
  19. deepeval/metrics/bias/bias.py +102 -108
  20. deepeval/metrics/bias/template.py +14 -2
  21. deepeval/metrics/contextual_precision/contextual_precision.py +96 -94
  22. deepeval/metrics/contextual_precision/template.py +115 -66
  23. deepeval/metrics/contextual_recall/contextual_recall.py +94 -84
  24. deepeval/metrics/contextual_recall/template.py +106 -55
  25. deepeval/metrics/contextual_relevancy/contextual_relevancy.py +86 -84
  26. deepeval/metrics/contextual_relevancy/template.py +87 -58
  27. deepeval/metrics/conversation_completeness/conversation_completeness.py +101 -119
  28. deepeval/metrics/conversation_completeness/template.py +23 -3
  29. deepeval/metrics/conversational_dag/conversational_dag.py +12 -8
  30. deepeval/metrics/conversational_dag/nodes.py +66 -123
  31. deepeval/metrics/conversational_dag/templates.py +16 -0
  32. deepeval/metrics/conversational_g_eval/conversational_g_eval.py +47 -66
  33. deepeval/metrics/dag/dag.py +10 -0
  34. deepeval/metrics/dag/nodes.py +63 -126
  35. deepeval/metrics/dag/templates.py +16 -2
  36. deepeval/metrics/exact_match/exact_match.py +9 -1
  37. deepeval/metrics/faithfulness/faithfulness.py +138 -149
  38. deepeval/metrics/faithfulness/schema.py +1 -1
  39. deepeval/metrics/faithfulness/template.py +200 -115
  40. deepeval/metrics/g_eval/g_eval.py +87 -78
  41. deepeval/metrics/g_eval/template.py +18 -1
  42. deepeval/metrics/g_eval/utils.py +7 -6
  43. deepeval/metrics/goal_accuracy/goal_accuracy.py +91 -76
  44. deepeval/metrics/goal_accuracy/template.py +21 -3
  45. deepeval/metrics/hallucination/hallucination.py +60 -75
  46. deepeval/metrics/hallucination/template.py +13 -0
  47. deepeval/metrics/indicator.py +7 -10
  48. deepeval/metrics/json_correctness/json_correctness.py +40 -38
  49. deepeval/metrics/json_correctness/template.py +10 -0
  50. deepeval/metrics/knowledge_retention/knowledge_retention.py +60 -97
  51. deepeval/metrics/knowledge_retention/schema.py +9 -3
  52. deepeval/metrics/knowledge_retention/template.py +12 -0
  53. deepeval/metrics/mcp/mcp_task_completion.py +68 -38
  54. deepeval/metrics/mcp/multi_turn_mcp_use_metric.py +92 -74
  55. deepeval/metrics/mcp/template.py +52 -0
  56. deepeval/metrics/mcp_use_metric/mcp_use_metric.py +58 -64
  57. deepeval/metrics/mcp_use_metric/template.py +12 -0
  58. deepeval/metrics/misuse/misuse.py +77 -97
  59. deepeval/metrics/misuse/template.py +15 -0
  60. deepeval/metrics/multimodal_metrics/__init__.py +0 -19
  61. deepeval/metrics/multimodal_metrics/image_coherence/image_coherence.py +59 -53
  62. deepeval/metrics/multimodal_metrics/image_editing/image_editing.py +79 -95
  63. deepeval/metrics/multimodal_metrics/image_helpfulness/image_helpfulness.py +59 -53
  64. deepeval/metrics/multimodal_metrics/image_reference/image_reference.py +59 -53
  65. deepeval/metrics/multimodal_metrics/text_to_image/text_to_image.py +111 -109
  66. deepeval/metrics/non_advice/non_advice.py +79 -105
  67. deepeval/metrics/non_advice/template.py +12 -0
  68. deepeval/metrics/pattern_match/pattern_match.py +12 -4
  69. deepeval/metrics/pii_leakage/pii_leakage.py +75 -106
  70. deepeval/metrics/pii_leakage/template.py +14 -0
  71. deepeval/metrics/plan_adherence/plan_adherence.py +63 -89
  72. deepeval/metrics/plan_adherence/template.py +11 -0
  73. deepeval/metrics/plan_quality/plan_quality.py +63 -87
  74. deepeval/metrics/plan_quality/template.py +9 -0
  75. deepeval/metrics/prompt_alignment/prompt_alignment.py +72 -83
  76. deepeval/metrics/prompt_alignment/template.py +12 -0
  77. deepeval/metrics/ragas.py +3 -3
  78. deepeval/metrics/role_adherence/role_adherence.py +48 -71
  79. deepeval/metrics/role_adherence/template.py +14 -0
  80. deepeval/metrics/role_violation/role_violation.py +75 -108
  81. deepeval/metrics/role_violation/template.py +12 -0
  82. deepeval/metrics/step_efficiency/step_efficiency.py +55 -65
  83. deepeval/metrics/step_efficiency/template.py +11 -0
  84. deepeval/metrics/summarization/summarization.py +115 -183
  85. deepeval/metrics/summarization/template.py +19 -0
  86. deepeval/metrics/task_completion/task_completion.py +67 -73
  87. deepeval/metrics/tool_correctness/tool_correctness.py +45 -44
  88. deepeval/metrics/tool_use/tool_use.py +42 -66
  89. deepeval/metrics/topic_adherence/template.py +13 -0
  90. deepeval/metrics/topic_adherence/topic_adherence.py +53 -67
  91. deepeval/metrics/toxicity/template.py +13 -0
  92. deepeval/metrics/toxicity/toxicity.py +80 -99
  93. deepeval/metrics/turn_contextual_precision/schema.py +21 -0
  94. deepeval/metrics/turn_contextual_precision/template.py +187 -0
  95. deepeval/metrics/turn_contextual_precision/turn_contextual_precision.py +592 -0
  96. deepeval/metrics/turn_contextual_recall/schema.py +21 -0
  97. deepeval/metrics/turn_contextual_recall/template.py +178 -0
  98. deepeval/metrics/turn_contextual_recall/turn_contextual_recall.py +563 -0
  99. deepeval/metrics/{multimodal_metrics/multimodal_contextual_relevancy → turn_contextual_relevancy}/schema.py +7 -1
  100. deepeval/metrics/turn_contextual_relevancy/template.py +161 -0
  101. deepeval/metrics/turn_contextual_relevancy/turn_contextual_relevancy.py +576 -0
  102. deepeval/metrics/{multimodal_metrics/multimodal_faithfulness → turn_faithfulness}/schema.py +11 -3
  103. deepeval/metrics/turn_faithfulness/template.py +218 -0
  104. deepeval/metrics/turn_faithfulness/turn_faithfulness.py +627 -0
  105. deepeval/metrics/turn_relevancy/template.py +14 -0
  106. deepeval/metrics/turn_relevancy/turn_relevancy.py +56 -69
  107. deepeval/metrics/utils.py +158 -122
  108. deepeval/models/__init__.py +0 -12
  109. deepeval/models/base_model.py +49 -33
  110. deepeval/models/embedding_models/__init__.py +7 -0
  111. deepeval/models/embedding_models/azure_embedding_model.py +79 -33
  112. deepeval/models/embedding_models/local_embedding_model.py +39 -20
  113. deepeval/models/embedding_models/ollama_embedding_model.py +52 -19
  114. deepeval/models/embedding_models/openai_embedding_model.py +42 -22
  115. deepeval/models/llms/amazon_bedrock_model.py +226 -72
  116. deepeval/models/llms/anthropic_model.py +178 -63
  117. deepeval/models/llms/azure_model.py +218 -60
  118. deepeval/models/llms/constants.py +2032 -0
  119. deepeval/models/llms/deepseek_model.py +95 -40
  120. deepeval/models/llms/gemini_model.py +209 -64
  121. deepeval/models/llms/grok_model.py +139 -68
  122. deepeval/models/llms/kimi_model.py +140 -90
  123. deepeval/models/llms/litellm_model.py +131 -37
  124. deepeval/models/llms/local_model.py +125 -21
  125. deepeval/models/llms/ollama_model.py +147 -24
  126. deepeval/models/llms/openai_model.py +222 -269
  127. deepeval/models/llms/portkey_model.py +81 -22
  128. deepeval/models/llms/utils.py +8 -3
  129. deepeval/models/retry_policy.py +17 -14
  130. deepeval/models/utils.py +106 -5
  131. deepeval/optimizer/__init__.py +5 -0
  132. deepeval/optimizer/algorithms/__init__.py +6 -0
  133. deepeval/optimizer/algorithms/base.py +29 -0
  134. deepeval/optimizer/algorithms/configs.py +18 -0
  135. deepeval/optimizer/algorithms/copro/__init__.py +5 -0
  136. deepeval/{optimization/copro/loop.py → optimizer/algorithms/copro/copro.py} +112 -113
  137. deepeval/optimizer/algorithms/gepa/__init__.py +5 -0
  138. deepeval/{optimization/gepa/loop.py → optimizer/algorithms/gepa/gepa.py} +175 -115
  139. deepeval/optimizer/algorithms/miprov2/__init__.py +17 -0
  140. deepeval/optimizer/algorithms/miprov2/bootstrapper.py +435 -0
  141. deepeval/optimizer/algorithms/miprov2/miprov2.py +752 -0
  142. deepeval/optimizer/algorithms/miprov2/proposer.py +301 -0
  143. deepeval/optimizer/algorithms/simba/__init__.py +5 -0
  144. deepeval/{optimization/simba/loop.py → optimizer/algorithms/simba/simba.py} +128 -112
  145. deepeval/{optimization → optimizer}/configs.py +5 -8
  146. deepeval/{optimization/policies/selection.py → optimizer/policies.py} +63 -2
  147. deepeval/optimizer/prompt_optimizer.py +263 -0
  148. deepeval/optimizer/rewriter/__init__.py +5 -0
  149. deepeval/optimizer/rewriter/rewriter.py +124 -0
  150. deepeval/optimizer/rewriter/utils.py +214 -0
  151. deepeval/optimizer/scorer/__init__.py +5 -0
  152. deepeval/optimizer/scorer/base.py +86 -0
  153. deepeval/optimizer/scorer/scorer.py +316 -0
  154. deepeval/optimizer/scorer/utils.py +30 -0
  155. deepeval/optimizer/types.py +148 -0
  156. deepeval/{optimization → optimizer}/utils.py +47 -165
  157. deepeval/prompt/prompt.py +5 -9
  158. deepeval/simulator/conversation_simulator.py +43 -0
  159. deepeval/simulator/template.py +13 -0
  160. deepeval/test_case/__init__.py +1 -3
  161. deepeval/test_case/api.py +26 -45
  162. deepeval/test_case/arena_test_case.py +7 -2
  163. deepeval/test_case/conversational_test_case.py +68 -1
  164. deepeval/test_case/llm_test_case.py +206 -1
  165. deepeval/test_case/utils.py +4 -8
  166. deepeval/test_run/api.py +18 -14
  167. deepeval/test_run/test_run.py +3 -3
  168. deepeval/tracing/patchers.py +9 -4
  169. deepeval/tracing/tracing.py +2 -2
  170. deepeval/utils.py +65 -0
  171. {deepeval-3.7.4.dist-info → deepeval-3.7.6.dist-info}/METADATA +1 -4
  172. {deepeval-3.7.4.dist-info → deepeval-3.7.6.dist-info}/RECORD +180 -193
  173. deepeval/metrics/multimodal_metrics/multimodal_answer_relevancy/multimodal_answer_relevancy.py +0 -343
  174. deepeval/metrics/multimodal_metrics/multimodal_answer_relevancy/schema.py +0 -19
  175. deepeval/metrics/multimodal_metrics/multimodal_answer_relevancy/template.py +0 -122
  176. deepeval/metrics/multimodal_metrics/multimodal_contextual_precision/multimodal_contextual_precision.py +0 -301
  177. deepeval/metrics/multimodal_metrics/multimodal_contextual_precision/schema.py +0 -15
  178. deepeval/metrics/multimodal_metrics/multimodal_contextual_precision/template.py +0 -132
  179. deepeval/metrics/multimodal_metrics/multimodal_contextual_recall/multimodal_contextual_recall.py +0 -285
  180. deepeval/metrics/multimodal_metrics/multimodal_contextual_recall/schema.py +0 -15
  181. deepeval/metrics/multimodal_metrics/multimodal_contextual_recall/template.py +0 -112
  182. deepeval/metrics/multimodal_metrics/multimodal_contextual_relevancy/multimodal_contextual_relevancy.py +0 -282
  183. deepeval/metrics/multimodal_metrics/multimodal_contextual_relevancy/template.py +0 -102
  184. deepeval/metrics/multimodal_metrics/multimodal_faithfulness/__init__.py +0 -0
  185. deepeval/metrics/multimodal_metrics/multimodal_faithfulness/multimodal_faithfulness.py +0 -356
  186. deepeval/metrics/multimodal_metrics/multimodal_faithfulness/template.py +0 -175
  187. deepeval/metrics/multimodal_metrics/multimodal_g_eval/__init__.py +0 -0
  188. deepeval/metrics/multimodal_metrics/multimodal_g_eval/multimodal_g_eval.py +0 -386
  189. deepeval/metrics/multimodal_metrics/multimodal_g_eval/schema.py +0 -11
  190. deepeval/metrics/multimodal_metrics/multimodal_g_eval/template.py +0 -148
  191. deepeval/metrics/multimodal_metrics/multimodal_g_eval/utils.py +0 -68
  192. deepeval/metrics/multimodal_metrics/multimodal_tool_correctness/__init__.py +0 -0
  193. deepeval/metrics/multimodal_metrics/multimodal_tool_correctness/multimodal_tool_correctness.py +0 -290
  194. deepeval/models/mlllms/__init__.py +0 -4
  195. deepeval/models/mlllms/azure_model.py +0 -343
  196. deepeval/models/mlllms/gemini_model.py +0 -313
  197. deepeval/models/mlllms/ollama_model.py +0 -175
  198. deepeval/models/mlllms/openai_model.py +0 -309
  199. deepeval/optimization/__init__.py +0 -13
  200. deepeval/optimization/adapters/__init__.py +0 -2
  201. deepeval/optimization/adapters/deepeval_scoring_adapter.py +0 -588
  202. deepeval/optimization/aggregates.py +0 -14
  203. deepeval/optimization/copro/configs.py +0 -31
  204. deepeval/optimization/gepa/__init__.py +0 -7
  205. deepeval/optimization/gepa/configs.py +0 -115
  206. deepeval/optimization/miprov2/configs.py +0 -134
  207. deepeval/optimization/miprov2/loop.py +0 -785
  208. deepeval/optimization/mutations/__init__.py +0 -0
  209. deepeval/optimization/mutations/prompt_rewriter.py +0 -458
  210. deepeval/optimization/policies/__init__.py +0 -16
  211. deepeval/optimization/policies/tie_breaker.py +0 -67
  212. deepeval/optimization/prompt_optimizer.py +0 -462
  213. deepeval/optimization/simba/__init__.py +0 -0
  214. deepeval/optimization/simba/configs.py +0 -33
  215. deepeval/optimization/types.py +0 -361
  216. deepeval/test_case/mllm_test_case.py +0 -170
  217. /deepeval/metrics/{multimodal_metrics/multimodal_answer_relevancy → turn_contextual_precision}/__init__.py +0 -0
  218. /deepeval/metrics/{multimodal_metrics/multimodal_contextual_precision → turn_contextual_recall}/__init__.py +0 -0
  219. /deepeval/metrics/{multimodal_metrics/multimodal_contextual_recall → turn_contextual_relevancy}/__init__.py +0 -0
  220. /deepeval/metrics/{multimodal_metrics/multimodal_contextual_relevancy → turn_faithfulness}/__init__.py +0 -0
  221. /deepeval/{optimization → optimizer/algorithms}/simba/types.py +0 -0
  222. {deepeval-3.7.4.dist-info → deepeval-3.7.6.dist-info}/LICENSE.md +0 -0
  223. {deepeval-3.7.4.dist-info → deepeval-3.7.6.dist-info}/WHEEL +0 -0
  224. {deepeval-3.7.4.dist-info → deepeval-3.7.6.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,18 @@
1
1
  from abc import ABC, abstractmethod
2
- from typing import Any, Optional, List
2
+ from typing import Any, Optional, List, Union
3
3
  from deepeval.models.utils import parse_model_name
4
+ from dataclasses import dataclass
5
+
6
+
7
+ @dataclass
8
+ class DeepEvalModelData:
9
+ supports_log_probs: Optional[bool] = None
10
+ supports_multimodal: Optional[bool] = None
11
+ supports_structured_outputs: Optional[bool] = None
12
+ supports_json: Optional[bool] = None
13
+ input_price: Optional[float] = None
14
+ output_price: Optional[float] = None
15
+ supports_temperature: Optional[bool] = True
4
16
 
5
17
 
6
18
  class DeepEvalBaseModel(ABC):
@@ -31,9 +43,9 @@ class DeepEvalBaseModel(ABC):
31
43
 
32
44
 
33
45
  class DeepEvalBaseLLM(ABC):
34
- def __init__(self, model_name: Optional[str] = None, *args, **kwargs):
35
- self.model_name = parse_model_name(model_name)
36
- self.model = self.load_model(*args, **kwargs)
46
+ def __init__(self, model: Optional[str] = None, *args, **kwargs):
47
+ self.name = parse_model_name(model)
48
+ self.model = self.load_model()
37
49
 
38
50
  @abstractmethod
39
51
  def load_model(self, *args, **kwargs) -> "DeepEvalBaseLLM":
@@ -62,6 +74,10 @@ class DeepEvalBaseLLM(ABC):
62
74
  """
63
75
  pass
64
76
 
77
+ @abstractmethod
78
+ def get_model_name(self, *args, **kwargs) -> str:
79
+ return self.name
80
+
65
81
  def batch_generate(self, *args, **kwargs) -> List[str]:
66
82
  """Runs the model to output LLM responses.
67
83
 
@@ -72,43 +88,43 @@ class DeepEvalBaseLLM(ABC):
72
88
  "batch_generate is not implemented for this model"
73
89
  )
74
90
 
75
- @abstractmethod
76
- def get_model_name(self, *args, **kwargs) -> str:
77
- pass
91
+ # Capabilities
92
+ def supports_log_probs(self) -> Union[bool, None]:
93
+ return None
78
94
 
95
+ def supports_temperature(self) -> Union[bool, None]:
96
+ return None
79
97
 
80
- class DeepEvalBaseMLLM(ABC):
81
- def __init__(self, model_name: Optional[str] = None, *args, **kwargs):
82
- self.model_name = parse_model_name(model_name)
98
+ def supports_multimodal(self) -> Union[bool, None]:
99
+ return None
83
100
 
84
- @abstractmethod
85
- def generate(self, *args, **kwargs) -> str:
86
- """Runs the model to output MLLM response.
101
+ def supports_structured_outputs(self) -> Union[bool, None]:
102
+ return None
87
103
 
88
- Returns:
89
- A string.
90
- """
91
- pass
104
+ def supports_json_mode(self) -> Union[bool, None]:
105
+ return None
92
106
 
93
- @abstractmethod
94
- async def a_generate(self, *args, **kwargs) -> str:
95
- """Runs the model to output MLLM response.
107
+ def generate_with_schema(self, *args, schema=None, **kwargs):
108
+ if schema is not None:
109
+ try:
110
+ return self.generate(*args, schema=schema, **kwargs)
111
+ except TypeError:
112
+ pass # this means provider doesn't accept schema kwarg
113
+ return self.generate(*args, **kwargs)
96
114
 
97
- Returns:
98
- A string.
99
- """
100
- pass
101
-
102
- @abstractmethod
103
- def get_model_name(self, *args, **kwargs) -> str:
104
- pass
115
+ async def a_generate_with_schema(self, *args, schema=None, **kwargs):
116
+ if schema is not None:
117
+ try:
118
+ return await self.a_generate(*args, schema=schema, **kwargs)
119
+ except TypeError:
120
+ pass
121
+ return await self.a_generate(*args, **kwargs)
105
122
 
106
123
 
107
124
  class DeepEvalBaseEmbeddingModel(ABC):
108
- def __init__(self, model_name: Optional[str] = None, *args, **kwargs):
109
- self.model_name = parse_model_name(model_name)
110
-
111
- self.model = self.load_model(*args, **kwargs)
125
+ def __init__(self, model: Optional[str] = None, *args, **kwargs):
126
+ self.name = parse_model_name(model)
127
+ self.model = self.load_model()
112
128
 
113
129
  @abstractmethod
114
130
  def load_model(self, *args, **kwargs) -> "DeepEvalBaseEmbeddingModel":
@@ -157,4 +173,4 @@ class DeepEvalBaseEmbeddingModel(ABC):
157
173
 
158
174
  @abstractmethod
159
175
  def get_model_name(self, *args, **kwargs) -> str:
160
- pass
176
+ return self.name
@@ -2,3 +2,10 @@ from .azure_embedding_model import AzureOpenAIEmbeddingModel
2
2
  from .openai_embedding_model import OpenAIEmbeddingModel
3
3
  from .local_embedding_model import LocalEmbeddingModel
4
4
  from .ollama_embedding_model import OllamaEmbeddingModel
5
+
6
+ __all__ = [
7
+ "AzureOpenAIEmbeddingModel",
8
+ "OpenAIEmbeddingModel",
9
+ "LocalEmbeddingModel",
10
+ "OllamaEmbeddingModel",
11
+ ]
@@ -9,53 +9,99 @@ from deepeval.models.retry_policy import (
9
9
  sdk_retries_for,
10
10
  )
11
11
  from deepeval.constants import ProviderSlug as PS
12
- from deepeval.models.utils import require_secret_api_key
12
+ from deepeval.models.utils import (
13
+ require_secret_api_key,
14
+ normalize_kwargs_and_extract_aliases,
15
+ )
16
+ from deepeval.utils import require_param
13
17
 
14
18
 
15
19
  retry_azure = create_retry_decorator(PS.AZURE)
16
20
 
21
+ _ALIAS_MAP = {
22
+ "api_key": ["openai_api_key"],
23
+ "base_url": ["azure_endpoint"],
24
+ "deployment_name": ["azure_deployment"],
25
+ }
26
+
17
27
 
18
28
  class AzureOpenAIEmbeddingModel(DeepEvalBaseEmbeddingModel):
19
29
  def __init__(
20
30
  self,
21
- openai_api_key: Optional[str] = None,
22
- openai_api_version: Optional[str] = None,
23
- azure_endpoint: Optional[str] = None,
24
- azure_deployment: Optional[str] = None,
25
31
  model: Optional[str] = None,
32
+ api_key: Optional[str] = None,
33
+ base_url: Optional[str] = None,
34
+ deployment_name: Optional[str] = None,
35
+ api_version: Optional[str] = None,
26
36
  generation_kwargs: Optional[Dict] = None,
27
- **client_kwargs,
37
+ **kwargs,
28
38
  ):
39
+ normalized_kwargs, alias_values = normalize_kwargs_and_extract_aliases(
40
+ "AzureOpenAIEmbeddingModel",
41
+ kwargs,
42
+ _ALIAS_MAP,
43
+ )
44
+
45
+ # re-map depricated keywords to re-named positional args
46
+ if api_key is None and "api_key" in alias_values:
47
+ api_key = alias_values["api_key"]
48
+ if base_url is None and "base_url" in alias_values:
49
+ base_url = alias_values["base_url"]
50
+ if deployment_name is None and "deployment_name" in alias_values:
51
+ deployment_name = alias_values["deployment_name"]
52
+
29
53
  settings = get_settings()
30
54
 
31
- if openai_api_key is not None:
55
+ if api_key is not None:
32
56
  # keep it secret, keep it safe from serializings, logging and alike
33
- self.openai_api_key: SecretStr | None = SecretStr(openai_api_key)
57
+ self.api_key: Optional[SecretStr] = SecretStr(api_key)
34
58
  else:
35
- self.openai_api_key = settings.AZURE_OPENAI_API_KEY
59
+ self.api_key = settings.AZURE_OPENAI_API_KEY
60
+
61
+ api_version = api_version or settings.OPENAI_API_VERSION
62
+ if base_url is not None:
63
+ base_url = str(base_url).rstrip("/")
64
+ elif settings.AZURE_OPENAI_ENDPOINT is not None:
65
+ base_url = str(settings.AZURE_OPENAI_ENDPOINT).rstrip("/")
36
66
 
37
- self.openai_api_version = (
38
- openai_api_version or settings.OPENAI_API_VERSION
67
+ deployment_name = (
68
+ deployment_name or settings.AZURE_EMBEDDING_DEPLOYMENT_NAME
39
69
  )
40
- self.azure_endpoint = (
41
- azure_endpoint
42
- or settings.AZURE_OPENAI_ENDPOINT
43
- and str(settings.AZURE_OPENAI_ENDPOINT)
70
+
71
+ model = model or settings.AZURE_EMBEDDING_MODEL_NAME or deployment_name
72
+
73
+ # validation
74
+ self.deployment_name = require_param(
75
+ deployment_name,
76
+ provider_label="AzureOpenAIEmbeddingModel",
77
+ env_var_name="AZURE_EMBEDDING_DEPLOYMENT_NAME",
78
+ param_hint="deployment_name",
79
+ )
80
+
81
+ self.base_url = require_param(
82
+ base_url,
83
+ provider_label="AzureOpenAIEmbeddingModel",
84
+ env_var_name="AZURE_OPENAI_ENDPOINT",
85
+ param_hint="base_url",
44
86
  )
45
87
 
46
- self.azure_deployment = (
47
- azure_deployment or settings.AZURE_EMBEDDING_DEPLOYMENT_NAME
88
+ self.api_version = require_param(
89
+ api_version,
90
+ provider_label="AzureOpenAIEmbeddingModel",
91
+ env_var_name="OPENAI_API_VERSION",
92
+ param_hint="api_version",
48
93
  )
49
- self.client_kwargs = client_kwargs or {}
50
- self.model_name = model or self.azure_deployment
94
+
95
+ # Keep sanitized kwargs for client call to strip legacy keys
96
+ self.kwargs = normalized_kwargs
51
97
  self.generation_kwargs = generation_kwargs or {}
52
- super().__init__(self.model_name)
98
+ super().__init__(model)
53
99
 
54
100
  @retry_azure
55
101
  def embed_text(self, text: str) -> List[float]:
56
102
  client = self.load_model(async_mode=False)
57
103
  response = client.embeddings.create(
58
- input=text, model=self.model_name, **self.generation_kwargs
104
+ input=text, model=self.name, **self.generation_kwargs
59
105
  )
60
106
  return response.data[0].embedding
61
107
 
@@ -63,7 +109,7 @@ class AzureOpenAIEmbeddingModel(DeepEvalBaseEmbeddingModel):
63
109
  def embed_texts(self, texts: List[str]) -> List[List[float]]:
64
110
  client = self.load_model(async_mode=False)
65
111
  response = client.embeddings.create(
66
- input=texts, model=self.model_name, **self.generation_kwargs
112
+ input=texts, model=self.name, **self.generation_kwargs
67
113
  )
68
114
  return [item.embedding for item in response.data]
69
115
 
@@ -71,7 +117,7 @@ class AzureOpenAIEmbeddingModel(DeepEvalBaseEmbeddingModel):
71
117
  async def a_embed_text(self, text: str) -> List[float]:
72
118
  client = self.load_model(async_mode=True)
73
119
  response = await client.embeddings.create(
74
- input=text, model=self.model_name, **self.generation_kwargs
120
+ input=text, model=self.name, **self.generation_kwargs
75
121
  )
76
122
  return response.data[0].embedding
77
123
 
@@ -79,13 +125,10 @@ class AzureOpenAIEmbeddingModel(DeepEvalBaseEmbeddingModel):
79
125
  async def a_embed_texts(self, texts: List[str]) -> List[List[float]]:
80
126
  client = self.load_model(async_mode=True)
81
127
  response = await client.embeddings.create(
82
- input=texts, model=self.model_name, **self.generation_kwargs
128
+ input=texts, model=self.name, **self.generation_kwargs
83
129
  )
84
130
  return [item.embedding for item in response.data]
85
131
 
86
- def get_model_name(self) -> str:
87
- return self.model_name
88
-
89
132
  def load_model(self, async_mode: bool = False):
90
133
  if not async_mode:
91
134
  return self._build_client(AzureOpenAI)
@@ -93,21 +136,21 @@ class AzureOpenAIEmbeddingModel(DeepEvalBaseEmbeddingModel):
93
136
 
94
137
  def _build_client(self, cls):
95
138
  api_key = require_secret_api_key(
96
- self.openai_api_key,
139
+ self.api_key,
97
140
  provider_label="AzureOpenAI",
98
141
  env_var_name="AZURE_OPENAI_API_KEY",
99
- param_hint="`openai_api_key` to AzureOpenAIEmbeddingModel(...)",
142
+ param_hint="`api_key` to AzureOpenAIEmbeddingModel(...)",
100
143
  )
101
144
 
102
- client_kwargs = self.client_kwargs.copy()
145
+ client_kwargs = self.kwargs.copy()
103
146
  if not sdk_retries_for(PS.AZURE):
104
147
  client_kwargs["max_retries"] = 0
105
148
 
106
149
  client_init_kwargs = dict(
107
150
  api_key=api_key,
108
- api_version=self.openai_api_version,
109
- azure_endpoint=self.azure_endpoint,
110
- azure_deployment=self.azure_deployment,
151
+ api_version=self.api_version,
152
+ azure_endpoint=self.base_url,
153
+ azure_deployment=self.deployment_name,
111
154
  **client_kwargs,
112
155
  )
113
156
  try:
@@ -118,3 +161,6 @@ class AzureOpenAIEmbeddingModel(DeepEvalBaseEmbeddingModel):
118
161
  client_init_kwargs.pop("max_retries", None)
119
162
  return cls(**client_init_kwargs)
120
163
  raise
164
+
165
+ def get_model_name(self):
166
+ return f"{self.name} (Azure)"
@@ -3,14 +3,16 @@ from typing import Dict, List, Optional
3
3
  from pydantic import SecretStr
4
4
 
5
5
  from deepeval.config.settings import get_settings
6
- from deepeval.models.utils import require_secret_api_key
6
+ from deepeval.models.utils import (
7
+ require_secret_api_key,
8
+ )
7
9
  from deepeval.models import DeepEvalBaseEmbeddingModel
8
10
  from deepeval.models.retry_policy import (
9
11
  create_retry_decorator,
10
12
  sdk_retries_for,
11
13
  )
12
14
  from deepeval.constants import ProviderSlug as PS
13
-
15
+ from deepeval.utils import require_param
14
16
 
15
17
  # consistent retry rules
16
18
  retry_local = create_retry_decorator(PS.LOCAL)
@@ -19,34 +21,51 @@ retry_local = create_retry_decorator(PS.LOCAL)
19
21
  class LocalEmbeddingModel(DeepEvalBaseEmbeddingModel):
20
22
  def __init__(
21
23
  self,
24
+ model: Optional[str] = None,
22
25
  api_key: Optional[str] = None,
23
26
  base_url: Optional[str] = None,
24
- model: Optional[str] = None,
25
27
  generation_kwargs: Optional[Dict] = None,
26
- **client_kwargs,
28
+ **kwargs,
27
29
  ):
30
+
28
31
  settings = get_settings()
29
32
  if api_key is not None:
30
33
  # keep it secret, keep it safe from serializings, logging and alike
31
- self.api_key: SecretStr | None = SecretStr(api_key)
34
+ self.api_key: Optional[SecretStr] = SecretStr(api_key)
32
35
  else:
33
36
  self.api_key = get_settings().LOCAL_EMBEDDING_API_KEY
34
37
 
35
- self.base_url = (
36
- base_url
37
- or settings.LOCAL_EMBEDDING_BASE_URL
38
- and str(settings.LOCAL_EMBEDDING_BASE_URL)
38
+ if base_url is not None:
39
+ base_url = str(base_url).rstrip("/")
40
+ elif settings.LOCAL_EMBEDDING_BASE_URL is not None:
41
+ base_url = str(settings.LOCAL_EMBEDDING_BASE_URL).rstrip("/")
42
+
43
+ model = model or settings.LOCAL_EMBEDDING_MODEL_NAME
44
+ # validation
45
+ model = require_param(
46
+ model,
47
+ provider_label="LocalEmbeddingModel",
48
+ env_var_name="LOCAL_EMBEDDING_MODEL_NAME",
49
+ param_hint="model",
50
+ )
51
+
52
+ self.base_url = require_param(
53
+ base_url,
54
+ provider_label="LocalEmbeddingModel",
55
+ env_var_name="LOCAL_EMBEDDING_BASE_URL",
56
+ param_hint="base_url",
39
57
  )
40
- self.model_name = model or settings.LOCAL_EMBEDDING_MODEL_NAME
41
- self.client_kwargs = client_kwargs or {}
58
+
59
+ # Keep sanitized kwargs for client call to strip legacy keys
60
+ self.kwargs = kwargs
42
61
  self.generation_kwargs = generation_kwargs or {}
43
- super().__init__(self.model_name)
62
+ super().__init__(model)
44
63
 
45
64
  @retry_local
46
65
  def embed_text(self, text: str) -> List[float]:
47
66
  embedding_model = self.load_model()
48
67
  response = embedding_model.embeddings.create(
49
- model=self.model_name, input=[text], **self.generation_kwargs
68
+ model=self.name, input=[text], **self.generation_kwargs
50
69
  )
51
70
  return response.data[0].embedding
52
71
 
@@ -54,7 +73,7 @@ class LocalEmbeddingModel(DeepEvalBaseEmbeddingModel):
54
73
  def embed_texts(self, texts: List[str]) -> List[List[float]]:
55
74
  embedding_model = self.load_model()
56
75
  response = embedding_model.embeddings.create(
57
- model=self.model_name, input=texts, **self.generation_kwargs
76
+ model=self.name, input=texts, **self.generation_kwargs
58
77
  )
59
78
  return [data.embedding for data in response.data]
60
79
 
@@ -62,7 +81,7 @@ class LocalEmbeddingModel(DeepEvalBaseEmbeddingModel):
62
81
  async def a_embed_text(self, text: str) -> List[float]:
63
82
  embedding_model = self.load_model(async_mode=True)
64
83
  response = await embedding_model.embeddings.create(
65
- model=self.model_name, input=[text], **self.generation_kwargs
84
+ model=self.name, input=[text], **self.generation_kwargs
66
85
  )
67
86
  return response.data[0].embedding
68
87
 
@@ -70,7 +89,7 @@ class LocalEmbeddingModel(DeepEvalBaseEmbeddingModel):
70
89
  async def a_embed_texts(self, texts: List[str]) -> List[List[float]]:
71
90
  embedding_model = self.load_model(async_mode=True)
72
91
  response = await embedding_model.embeddings.create(
73
- model=self.model_name, input=texts, **self.generation_kwargs
92
+ model=self.name, input=texts, **self.generation_kwargs
74
93
  )
75
94
  return [data.embedding for data in response.data]
76
95
 
@@ -78,9 +97,6 @@ class LocalEmbeddingModel(DeepEvalBaseEmbeddingModel):
78
97
  # Model
79
98
  ###############################################
80
99
 
81
- def get_model_name(self):
82
- return self.model_name
83
-
84
100
  def load_model(self, async_mode: bool = False):
85
101
  if not async_mode:
86
102
  return self._build_client(OpenAI)
@@ -94,7 +110,7 @@ class LocalEmbeddingModel(DeepEvalBaseEmbeddingModel):
94
110
  param_hint="`api_key` to LocalEmbeddingModel(...)",
95
111
  )
96
112
 
97
- client_kwargs = self.client_kwargs.copy()
113
+ client_kwargs = self.kwargs.copy()
98
114
  if not sdk_retries_for(PS.LOCAL):
99
115
  client_kwargs["max_retries"] = 0
100
116
 
@@ -111,3 +127,6 @@ class LocalEmbeddingModel(DeepEvalBaseEmbeddingModel):
111
127
  client_init_kwargs.pop("max_retries", None)
112
128
  return cls(**client_init_kwargs)
113
129
  raise
130
+
131
+ def get_model_name(self):
132
+ return f"{self.name} (Local Model)"
@@ -1,42 +1,69 @@
1
- from ollama import Client, AsyncClient
2
1
  from typing import List, Optional, Dict
3
2
 
4
3
  from deepeval.config.settings import get_settings
4
+ from deepeval.utils import require_dependency
5
5
  from deepeval.models import DeepEvalBaseEmbeddingModel
6
+ from deepeval.models.utils import (
7
+ normalize_kwargs_and_extract_aliases,
8
+ )
6
9
  from deepeval.models.retry_policy import (
7
10
  create_retry_decorator,
8
11
  )
9
12
  from deepeval.constants import ProviderSlug as PS
10
-
13
+ from deepeval.utils import require_param
11
14
 
12
15
  retry_ollama = create_retry_decorator(PS.OLLAMA)
13
16
 
17
+ _ALIAS_MAP = {"base_url": ["host"]}
18
+
14
19
 
15
20
  class OllamaEmbeddingModel(DeepEvalBaseEmbeddingModel):
16
21
  def __init__(
17
22
  self,
18
23
  model: Optional[str] = None,
19
- host: Optional[str] = None,
24
+ base_url: Optional[str] = None,
20
25
  generation_kwargs: Optional[Dict] = None,
21
- **client_kwargs,
26
+ **kwargs,
22
27
  ):
28
+ normalized_kwargs, alias_values = normalize_kwargs_and_extract_aliases(
29
+ "OllamaEmbeddingModel",
30
+ kwargs,
31
+ _ALIAS_MAP,
32
+ )
33
+
34
+ # re-map depricated keywords to re-named positional args
35
+ if base_url is None and "base_url" in alias_values:
36
+ base_url = alias_values["base_url"]
37
+
23
38
  settings = get_settings()
24
39
 
25
- self.host = (
26
- host
27
- or settings.LOCAL_EMBEDDING_BASE_URL
28
- and str(settings.LOCAL_EMBEDDING_BASE_URL)
40
+ if base_url is not None:
41
+ self.base_url = str(base_url).rstrip("/")
42
+ elif settings.LOCAL_EMBEDDING_BASE_URL is not None:
43
+ self.base_url = str(settings.LOCAL_EMBEDDING_BASE_URL).rstrip("/")
44
+ else:
45
+ self.base_url = "http://localhost:11434"
46
+
47
+ model = model or settings.LOCAL_EMBEDDING_MODEL_NAME
48
+
49
+ # validation
50
+ model = require_param(
51
+ model,
52
+ provider_label="OllamaEmbeddingModel",
53
+ env_var_name="LOCAL_EMBEDDING_MODEL_NAME",
54
+ param_hint="model",
29
55
  )
30
- self.model_name = model or settings.LOCAL_EMBEDDING_MODEL_NAME
31
- self.client_kwargs = client_kwargs or {}
56
+
57
+ # Keep sanitized kwargs for client call to strip legacy keys
58
+ self.kwargs = normalized_kwargs
32
59
  self.generation_kwargs = generation_kwargs or {}
33
- super().__init__(self.model_name)
60
+ super().__init__(model)
34
61
 
35
62
  @retry_ollama
36
63
  def embed_text(self, text: str) -> List[float]:
37
64
  embedding_model = self.load_model()
38
65
  response = embedding_model.embed(
39
- model=self.model_name, input=text, **self.generation_kwargs
66
+ model=self.name, input=text, **self.generation_kwargs
40
67
  )
41
68
  return response["embeddings"][0]
42
69
 
@@ -44,7 +71,7 @@ class OllamaEmbeddingModel(DeepEvalBaseEmbeddingModel):
44
71
  def embed_texts(self, texts: List[str]) -> List[List[float]]:
45
72
  embedding_model = self.load_model()
46
73
  response = embedding_model.embed(
47
- model=self.model_name, input=texts, **self.generation_kwargs
74
+ model=self.name, input=texts, **self.generation_kwargs
48
75
  )
49
76
  return response["embeddings"]
50
77
 
@@ -52,7 +79,7 @@ class OllamaEmbeddingModel(DeepEvalBaseEmbeddingModel):
52
79
  async def a_embed_text(self, text: str) -> List[float]:
53
80
  embedding_model = self.load_model(async_mode=True)
54
81
  response = await embedding_model.embed(
55
- model=self.model_name, input=text, **self.generation_kwargs
82
+ model=self.name, input=text, **self.generation_kwargs
56
83
  )
57
84
  return response["embeddings"][0]
58
85
 
@@ -60,7 +87,7 @@ class OllamaEmbeddingModel(DeepEvalBaseEmbeddingModel):
60
87
  async def a_embed_texts(self, texts: List[str]) -> List[List[float]]:
61
88
  embedding_model = self.load_model(async_mode=True)
62
89
  response = await embedding_model.embed(
63
- model=self.model_name, input=texts, **self.generation_kwargs
90
+ model=self.name, input=texts, **self.generation_kwargs
64
91
  )
65
92
  return response["embeddings"]
66
93
 
@@ -69,12 +96,18 @@ class OllamaEmbeddingModel(DeepEvalBaseEmbeddingModel):
69
96
  ###############################################
70
97
 
71
98
  def load_model(self, async_mode: bool = False):
99
+ ollama = require_dependency(
100
+ "ollama",
101
+ provider_label="OllamaEmbeddingModel",
102
+ install_hint="Install it with `pip install ollama`.",
103
+ )
104
+
72
105
  if not async_mode:
73
- return self._build_client(Client)
74
- return self._build_client(AsyncClient)
106
+ return self._build_client(ollama.Client)
107
+ return self._build_client(ollama.AsyncClient)
75
108
 
76
109
  def _build_client(self, cls):
77
- return cls(host=self.host, **self.client_kwargs)
110
+ return cls(host=self.base_url, **self.kwargs)
78
111
 
79
112
  def get_model_name(self):
80
- return f"{self.model_name} (Ollama)"
113
+ return f"{self.name} (Ollama)"