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.
- deepeval/_version.py +1 -1
- deepeval/config/settings.py +35 -1
- deepeval/dataset/api.py +23 -1
- deepeval/dataset/golden.py +139 -2
- deepeval/evaluate/evaluate.py +16 -11
- deepeval/evaluate/execute.py +13 -181
- deepeval/evaluate/utils.py +6 -26
- deepeval/integrations/pydantic_ai/agent.py +19 -2
- deepeval/integrations/pydantic_ai/instrumentator.py +62 -23
- deepeval/key_handler.py +3 -0
- deepeval/metrics/__init__.py +14 -16
- deepeval/metrics/answer_relevancy/answer_relevancy.py +118 -116
- deepeval/metrics/answer_relevancy/template.py +22 -3
- deepeval/metrics/arena_g_eval/arena_g_eval.py +98 -96
- deepeval/metrics/arena_g_eval/template.py +17 -1
- deepeval/metrics/argument_correctness/argument_correctness.py +81 -87
- deepeval/metrics/argument_correctness/template.py +19 -2
- deepeval/metrics/base_metric.py +13 -44
- deepeval/metrics/bias/bias.py +102 -108
- deepeval/metrics/bias/template.py +14 -2
- deepeval/metrics/contextual_precision/contextual_precision.py +96 -94
- deepeval/metrics/contextual_precision/template.py +115 -66
- deepeval/metrics/contextual_recall/contextual_recall.py +94 -84
- deepeval/metrics/contextual_recall/template.py +106 -55
- deepeval/metrics/contextual_relevancy/contextual_relevancy.py +86 -84
- deepeval/metrics/contextual_relevancy/template.py +87 -58
- deepeval/metrics/conversation_completeness/conversation_completeness.py +101 -119
- deepeval/metrics/conversation_completeness/template.py +23 -3
- deepeval/metrics/conversational_dag/conversational_dag.py +12 -8
- deepeval/metrics/conversational_dag/nodes.py +66 -123
- deepeval/metrics/conversational_dag/templates.py +16 -0
- deepeval/metrics/conversational_g_eval/conversational_g_eval.py +47 -66
- deepeval/metrics/dag/dag.py +10 -0
- deepeval/metrics/dag/nodes.py +63 -126
- deepeval/metrics/dag/templates.py +16 -2
- deepeval/metrics/exact_match/exact_match.py +9 -1
- deepeval/metrics/faithfulness/faithfulness.py +138 -149
- deepeval/metrics/faithfulness/schema.py +1 -1
- deepeval/metrics/faithfulness/template.py +200 -115
- deepeval/metrics/g_eval/g_eval.py +87 -78
- deepeval/metrics/g_eval/template.py +18 -1
- deepeval/metrics/g_eval/utils.py +7 -6
- deepeval/metrics/goal_accuracy/goal_accuracy.py +91 -76
- deepeval/metrics/goal_accuracy/template.py +21 -3
- deepeval/metrics/hallucination/hallucination.py +60 -75
- deepeval/metrics/hallucination/template.py +13 -0
- deepeval/metrics/indicator.py +7 -10
- deepeval/metrics/json_correctness/json_correctness.py +40 -38
- deepeval/metrics/json_correctness/template.py +10 -0
- deepeval/metrics/knowledge_retention/knowledge_retention.py +60 -97
- deepeval/metrics/knowledge_retention/schema.py +9 -3
- deepeval/metrics/knowledge_retention/template.py +12 -0
- deepeval/metrics/mcp/mcp_task_completion.py +68 -38
- deepeval/metrics/mcp/multi_turn_mcp_use_metric.py +92 -74
- deepeval/metrics/mcp/template.py +52 -0
- deepeval/metrics/mcp_use_metric/mcp_use_metric.py +58 -64
- deepeval/metrics/mcp_use_metric/template.py +12 -0
- deepeval/metrics/misuse/misuse.py +77 -97
- deepeval/metrics/misuse/template.py +15 -0
- deepeval/metrics/multimodal_metrics/__init__.py +0 -19
- deepeval/metrics/multimodal_metrics/image_coherence/image_coherence.py +59 -53
- deepeval/metrics/multimodal_metrics/image_editing/image_editing.py +79 -95
- deepeval/metrics/multimodal_metrics/image_helpfulness/image_helpfulness.py +59 -53
- deepeval/metrics/multimodal_metrics/image_reference/image_reference.py +59 -53
- deepeval/metrics/multimodal_metrics/text_to_image/text_to_image.py +111 -109
- deepeval/metrics/non_advice/non_advice.py +79 -105
- deepeval/metrics/non_advice/template.py +12 -0
- deepeval/metrics/pattern_match/pattern_match.py +12 -4
- deepeval/metrics/pii_leakage/pii_leakage.py +75 -106
- deepeval/metrics/pii_leakage/template.py +14 -0
- deepeval/metrics/plan_adherence/plan_adherence.py +63 -89
- deepeval/metrics/plan_adherence/template.py +11 -0
- deepeval/metrics/plan_quality/plan_quality.py +63 -87
- deepeval/metrics/plan_quality/template.py +9 -0
- deepeval/metrics/prompt_alignment/prompt_alignment.py +72 -83
- deepeval/metrics/prompt_alignment/template.py +12 -0
- deepeval/metrics/ragas.py +3 -3
- deepeval/metrics/role_adherence/role_adherence.py +48 -71
- deepeval/metrics/role_adherence/template.py +14 -0
- deepeval/metrics/role_violation/role_violation.py +75 -108
- deepeval/metrics/role_violation/template.py +12 -0
- deepeval/metrics/step_efficiency/step_efficiency.py +55 -65
- deepeval/metrics/step_efficiency/template.py +11 -0
- deepeval/metrics/summarization/summarization.py +115 -183
- deepeval/metrics/summarization/template.py +19 -0
- deepeval/metrics/task_completion/task_completion.py +67 -73
- deepeval/metrics/tool_correctness/tool_correctness.py +45 -44
- deepeval/metrics/tool_use/tool_use.py +42 -66
- deepeval/metrics/topic_adherence/template.py +13 -0
- deepeval/metrics/topic_adherence/topic_adherence.py +53 -67
- deepeval/metrics/toxicity/template.py +13 -0
- deepeval/metrics/toxicity/toxicity.py +80 -99
- deepeval/metrics/turn_contextual_precision/schema.py +21 -0
- deepeval/metrics/turn_contextual_precision/template.py +187 -0
- deepeval/metrics/turn_contextual_precision/turn_contextual_precision.py +592 -0
- deepeval/metrics/turn_contextual_recall/schema.py +21 -0
- deepeval/metrics/turn_contextual_recall/template.py +178 -0
- deepeval/metrics/turn_contextual_recall/turn_contextual_recall.py +563 -0
- deepeval/metrics/{multimodal_metrics/multimodal_contextual_relevancy → turn_contextual_relevancy}/schema.py +7 -1
- deepeval/metrics/turn_contextual_relevancy/template.py +161 -0
- deepeval/metrics/turn_contextual_relevancy/turn_contextual_relevancy.py +576 -0
- deepeval/metrics/{multimodal_metrics/multimodal_faithfulness → turn_faithfulness}/schema.py +11 -3
- deepeval/metrics/turn_faithfulness/template.py +218 -0
- deepeval/metrics/turn_faithfulness/turn_faithfulness.py +627 -0
- deepeval/metrics/turn_relevancy/template.py +14 -0
- deepeval/metrics/turn_relevancy/turn_relevancy.py +56 -69
- deepeval/metrics/utils.py +158 -122
- deepeval/models/__init__.py +0 -12
- deepeval/models/base_model.py +49 -33
- deepeval/models/embedding_models/__init__.py +7 -0
- deepeval/models/embedding_models/azure_embedding_model.py +79 -33
- deepeval/models/embedding_models/local_embedding_model.py +39 -20
- deepeval/models/embedding_models/ollama_embedding_model.py +52 -19
- deepeval/models/embedding_models/openai_embedding_model.py +42 -22
- deepeval/models/llms/amazon_bedrock_model.py +226 -72
- deepeval/models/llms/anthropic_model.py +178 -63
- deepeval/models/llms/azure_model.py +218 -60
- deepeval/models/llms/constants.py +2032 -0
- deepeval/models/llms/deepseek_model.py +95 -40
- deepeval/models/llms/gemini_model.py +209 -64
- deepeval/models/llms/grok_model.py +139 -68
- deepeval/models/llms/kimi_model.py +140 -90
- deepeval/models/llms/litellm_model.py +131 -37
- deepeval/models/llms/local_model.py +125 -21
- deepeval/models/llms/ollama_model.py +147 -24
- deepeval/models/llms/openai_model.py +222 -269
- deepeval/models/llms/portkey_model.py +81 -22
- deepeval/models/llms/utils.py +8 -3
- deepeval/models/retry_policy.py +17 -14
- deepeval/models/utils.py +106 -5
- deepeval/optimizer/__init__.py +5 -0
- deepeval/optimizer/algorithms/__init__.py +6 -0
- deepeval/optimizer/algorithms/base.py +29 -0
- deepeval/optimizer/algorithms/configs.py +18 -0
- deepeval/optimizer/algorithms/copro/__init__.py +5 -0
- deepeval/{optimization/copro/loop.py → optimizer/algorithms/copro/copro.py} +112 -113
- deepeval/optimizer/algorithms/gepa/__init__.py +5 -0
- deepeval/{optimization/gepa/loop.py → optimizer/algorithms/gepa/gepa.py} +175 -115
- deepeval/optimizer/algorithms/miprov2/__init__.py +17 -0
- deepeval/optimizer/algorithms/miprov2/bootstrapper.py +435 -0
- deepeval/optimizer/algorithms/miprov2/miprov2.py +752 -0
- deepeval/optimizer/algorithms/miprov2/proposer.py +301 -0
- deepeval/optimizer/algorithms/simba/__init__.py +5 -0
- deepeval/{optimization/simba/loop.py → optimizer/algorithms/simba/simba.py} +128 -112
- deepeval/{optimization → optimizer}/configs.py +5 -8
- deepeval/{optimization/policies/selection.py → optimizer/policies.py} +63 -2
- deepeval/optimizer/prompt_optimizer.py +263 -0
- deepeval/optimizer/rewriter/__init__.py +5 -0
- deepeval/optimizer/rewriter/rewriter.py +124 -0
- deepeval/optimizer/rewriter/utils.py +214 -0
- deepeval/optimizer/scorer/__init__.py +5 -0
- deepeval/optimizer/scorer/base.py +86 -0
- deepeval/optimizer/scorer/scorer.py +316 -0
- deepeval/optimizer/scorer/utils.py +30 -0
- deepeval/optimizer/types.py +148 -0
- deepeval/{optimization → optimizer}/utils.py +47 -165
- deepeval/prompt/prompt.py +5 -9
- deepeval/simulator/conversation_simulator.py +43 -0
- deepeval/simulator/template.py +13 -0
- deepeval/test_case/__init__.py +1 -3
- deepeval/test_case/api.py +26 -45
- deepeval/test_case/arena_test_case.py +7 -2
- deepeval/test_case/conversational_test_case.py +68 -1
- deepeval/test_case/llm_test_case.py +206 -1
- deepeval/test_case/utils.py +4 -8
- deepeval/test_run/api.py +18 -14
- deepeval/test_run/test_run.py +3 -3
- deepeval/tracing/patchers.py +9 -4
- deepeval/tracing/tracing.py +2 -2
- deepeval/utils.py +65 -0
- {deepeval-3.7.4.dist-info → deepeval-3.7.6.dist-info}/METADATA +1 -4
- {deepeval-3.7.4.dist-info → deepeval-3.7.6.dist-info}/RECORD +180 -193
- deepeval/metrics/multimodal_metrics/multimodal_answer_relevancy/multimodal_answer_relevancy.py +0 -343
- deepeval/metrics/multimodal_metrics/multimodal_answer_relevancy/schema.py +0 -19
- deepeval/metrics/multimodal_metrics/multimodal_answer_relevancy/template.py +0 -122
- deepeval/metrics/multimodal_metrics/multimodal_contextual_precision/multimodal_contextual_precision.py +0 -301
- deepeval/metrics/multimodal_metrics/multimodal_contextual_precision/schema.py +0 -15
- deepeval/metrics/multimodal_metrics/multimodal_contextual_precision/template.py +0 -132
- deepeval/metrics/multimodal_metrics/multimodal_contextual_recall/multimodal_contextual_recall.py +0 -285
- deepeval/metrics/multimodal_metrics/multimodal_contextual_recall/schema.py +0 -15
- deepeval/metrics/multimodal_metrics/multimodal_contextual_recall/template.py +0 -112
- deepeval/metrics/multimodal_metrics/multimodal_contextual_relevancy/multimodal_contextual_relevancy.py +0 -282
- deepeval/metrics/multimodal_metrics/multimodal_contextual_relevancy/template.py +0 -102
- deepeval/metrics/multimodal_metrics/multimodal_faithfulness/__init__.py +0 -0
- deepeval/metrics/multimodal_metrics/multimodal_faithfulness/multimodal_faithfulness.py +0 -356
- deepeval/metrics/multimodal_metrics/multimodal_faithfulness/template.py +0 -175
- deepeval/metrics/multimodal_metrics/multimodal_g_eval/__init__.py +0 -0
- deepeval/metrics/multimodal_metrics/multimodal_g_eval/multimodal_g_eval.py +0 -386
- deepeval/metrics/multimodal_metrics/multimodal_g_eval/schema.py +0 -11
- deepeval/metrics/multimodal_metrics/multimodal_g_eval/template.py +0 -148
- deepeval/metrics/multimodal_metrics/multimodal_g_eval/utils.py +0 -68
- deepeval/metrics/multimodal_metrics/multimodal_tool_correctness/__init__.py +0 -0
- deepeval/metrics/multimodal_metrics/multimodal_tool_correctness/multimodal_tool_correctness.py +0 -290
- deepeval/models/mlllms/__init__.py +0 -4
- deepeval/models/mlllms/azure_model.py +0 -343
- deepeval/models/mlllms/gemini_model.py +0 -313
- deepeval/models/mlllms/ollama_model.py +0 -175
- deepeval/models/mlllms/openai_model.py +0 -309
- deepeval/optimization/__init__.py +0 -13
- deepeval/optimization/adapters/__init__.py +0 -2
- deepeval/optimization/adapters/deepeval_scoring_adapter.py +0 -588
- deepeval/optimization/aggregates.py +0 -14
- deepeval/optimization/copro/configs.py +0 -31
- deepeval/optimization/gepa/__init__.py +0 -7
- deepeval/optimization/gepa/configs.py +0 -115
- deepeval/optimization/miprov2/configs.py +0 -134
- deepeval/optimization/miprov2/loop.py +0 -785
- deepeval/optimization/mutations/__init__.py +0 -0
- deepeval/optimization/mutations/prompt_rewriter.py +0 -458
- deepeval/optimization/policies/__init__.py +0 -16
- deepeval/optimization/policies/tie_breaker.py +0 -67
- deepeval/optimization/prompt_optimizer.py +0 -462
- deepeval/optimization/simba/__init__.py +0 -0
- deepeval/optimization/simba/configs.py +0 -33
- deepeval/optimization/types.py +0 -361
- deepeval/test_case/mllm_test_case.py +0 -170
- /deepeval/metrics/{multimodal_metrics/multimodal_answer_relevancy → turn_contextual_precision}/__init__.py +0 -0
- /deepeval/metrics/{multimodal_metrics/multimodal_contextual_precision → turn_contextual_recall}/__init__.py +0 -0
- /deepeval/metrics/{multimodal_metrics/multimodal_contextual_recall → turn_contextual_relevancy}/__init__.py +0 -0
- /deepeval/metrics/{multimodal_metrics/multimodal_contextual_relevancy → turn_faithfulness}/__init__.py +0 -0
- /deepeval/{optimization → optimizer/algorithms}/simba/types.py +0 -0
- {deepeval-3.7.4.dist-info → deepeval-3.7.6.dist-info}/LICENSE.md +0 -0
- {deepeval-3.7.4.dist-info → deepeval-3.7.6.dist-info}/WHEEL +0 -0
- {deepeval-3.7.4.dist-info → deepeval-3.7.6.dist-info}/entry_points.txt +0 -0
deepeval/models/base_model.py
CHANGED
|
@@ -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,
|
|
35
|
-
self.
|
|
36
|
-
self.model = self.load_model(
|
|
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
|
-
|
|
76
|
-
def
|
|
77
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
self.model_name = parse_model_name(model_name)
|
|
98
|
+
def supports_multimodal(self) -> Union[bool, None]:
|
|
99
|
+
return None
|
|
83
100
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"""Runs the model to output MLLM response.
|
|
101
|
+
def supports_structured_outputs(self) -> Union[bool, None]:
|
|
102
|
+
return None
|
|
87
103
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
"""
|
|
91
|
-
pass
|
|
104
|
+
def supports_json_mode(self) -> Union[bool, None]:
|
|
105
|
+
return None
|
|
92
106
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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,
|
|
109
|
-
self.
|
|
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
|
-
|
|
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
|
|
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
|
-
**
|
|
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
|
|
55
|
+
if api_key is not None:
|
|
32
56
|
# keep it secret, keep it safe from serializings, logging and alike
|
|
33
|
-
self.
|
|
57
|
+
self.api_key: Optional[SecretStr] = SecretStr(api_key)
|
|
34
58
|
else:
|
|
35
|
-
self.
|
|
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
|
-
|
|
38
|
-
|
|
67
|
+
deployment_name = (
|
|
68
|
+
deployment_name or settings.AZURE_EMBEDDING_DEPLOYMENT_NAME
|
|
39
69
|
)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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.
|
|
47
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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__(
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
139
|
+
self.api_key,
|
|
97
140
|
provider_label="AzureOpenAI",
|
|
98
141
|
env_var_name="AZURE_OPENAI_API_KEY",
|
|
99
|
-
param_hint="`
|
|
142
|
+
param_hint="`api_key` to AzureOpenAIEmbeddingModel(...)",
|
|
100
143
|
)
|
|
101
144
|
|
|
102
|
-
client_kwargs = self.
|
|
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.
|
|
109
|
-
azure_endpoint=self.
|
|
110
|
-
azure_deployment=self.
|
|
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
|
|
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
|
-
**
|
|
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
|
|
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
|
-
|
|
36
|
-
base_url
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
41
|
-
|
|
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__(
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
24
|
+
base_url: Optional[str] = None,
|
|
20
25
|
generation_kwargs: Optional[Dict] = None,
|
|
21
|
-
**
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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__(
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
110
|
+
return cls(host=self.base_url, **self.kwargs)
|
|
78
111
|
|
|
79
112
|
def get_model_name(self):
|
|
80
|
-
return f"{self.
|
|
113
|
+
return f"{self.name} (Ollama)"
|