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/metrics/base_metric.py
CHANGED
|
@@ -4,7 +4,6 @@ from typing import Optional, Dict, List
|
|
|
4
4
|
from deepeval.test_case import (
|
|
5
5
|
LLMTestCase,
|
|
6
6
|
ConversationalTestCase,
|
|
7
|
-
MLLMTestCase,
|
|
8
7
|
LLMTestCaseParams,
|
|
9
8
|
ArenaTestCase,
|
|
10
9
|
)
|
|
@@ -49,6 +48,10 @@ class BaseMetric:
|
|
|
49
48
|
def __name__(self):
|
|
50
49
|
return "Base Metric"
|
|
51
50
|
|
|
51
|
+
def _accrue_cost(self, cost: float) -> None:
|
|
52
|
+
if self.evaluation_cost is not None:
|
|
53
|
+
self.evaluation_cost += cost
|
|
54
|
+
|
|
52
55
|
|
|
53
56
|
class BaseConversationalMetric:
|
|
54
57
|
threshold: float
|
|
@@ -65,6 +68,8 @@ class BaseConversationalMetric:
|
|
|
65
68
|
evaluation_cost: Optional[float] = None
|
|
66
69
|
verbose_logs: Optional[str] = None
|
|
67
70
|
skipped = False
|
|
71
|
+
model: Optional[DeepEvalBaseLLM] = None
|
|
72
|
+
using_native_model: Optional[bool] = None
|
|
68
73
|
|
|
69
74
|
@abstractmethod
|
|
70
75
|
def measure(
|
|
@@ -88,49 +93,9 @@ class BaseConversationalMetric:
|
|
|
88
93
|
def __name__(self):
|
|
89
94
|
return "Base Conversational Metric"
|
|
90
95
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
score_breakdown: Dict = None
|
|
95
|
-
reason: Optional[str] = None
|
|
96
|
-
success: Optional[bool] = None
|
|
97
|
-
evaluation_model: Optional[str] = None
|
|
98
|
-
strict_mode: bool = False
|
|
99
|
-
async_mode: bool = True
|
|
100
|
-
verbose_mode: bool = True
|
|
101
|
-
include_reason: bool = False
|
|
102
|
-
error: Optional[str] = None
|
|
103
|
-
evaluation_cost: Optional[float] = None
|
|
104
|
-
verbose_logs: Optional[str] = None
|
|
105
|
-
skipped = False
|
|
106
|
-
|
|
107
|
-
@property
|
|
108
|
-
def threshold(self) -> float:
|
|
109
|
-
return self._threshold
|
|
110
|
-
|
|
111
|
-
@threshold.setter
|
|
112
|
-
def threshold(self, value: float):
|
|
113
|
-
self._threshold = value
|
|
114
|
-
|
|
115
|
-
@abstractmethod
|
|
116
|
-
def measure(self, test_case: MLLMTestCase, *args, **kwargs) -> float:
|
|
117
|
-
raise NotImplementedError
|
|
118
|
-
|
|
119
|
-
@abstractmethod
|
|
120
|
-
async def a_measure(
|
|
121
|
-
self, test_case: MLLMTestCase, *args, **kwargs
|
|
122
|
-
) -> float:
|
|
123
|
-
raise NotImplementedError(
|
|
124
|
-
f"Async execution for {self.__class__.__name__} not supported yet. Please set 'async_mode' to 'False'."
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
@abstractmethod
|
|
128
|
-
def is_successful(self) -> bool:
|
|
129
|
-
raise NotImplementedError
|
|
130
|
-
|
|
131
|
-
@property
|
|
132
|
-
def __name__(self):
|
|
133
|
-
return "Base Multimodal Metric"
|
|
96
|
+
def _accrue_cost(self, cost: float) -> None:
|
|
97
|
+
if self.evaluation_cost is not None:
|
|
98
|
+
self.evaluation_cost += cost
|
|
134
99
|
|
|
135
100
|
|
|
136
101
|
class BaseArenaMetric:
|
|
@@ -162,3 +127,7 @@ class BaseArenaMetric:
|
|
|
162
127
|
@property
|
|
163
128
|
def __name__(self):
|
|
164
129
|
return "Base Arena Metric"
|
|
130
|
+
|
|
131
|
+
def _accrue_cost(self, cost: float) -> None:
|
|
132
|
+
if self.evaluation_cost is not None:
|
|
133
|
+
self.evaluation_cost += cost
|
deepeval/metrics/bias/bias.py
CHANGED
|
@@ -11,12 +11,18 @@ from deepeval.models import DeepEvalBaseLLM
|
|
|
11
11
|
from deepeval.utils import get_or_create_event_loop, prettify_list
|
|
12
12
|
from deepeval.metrics.utils import (
|
|
13
13
|
construct_verbose_logs,
|
|
14
|
-
trimAndLoadJson,
|
|
15
14
|
check_llm_test_case_params,
|
|
16
15
|
initialize_model,
|
|
16
|
+
a_generate_with_schema_and_extract,
|
|
17
|
+
generate_with_schema_and_extract,
|
|
17
18
|
)
|
|
18
19
|
from deepeval.metrics.bias.template import BiasTemplate
|
|
19
|
-
from deepeval.metrics.bias.schema import
|
|
20
|
+
from deepeval.metrics.bias.schema import (
|
|
21
|
+
Opinions,
|
|
22
|
+
BiasVerdict,
|
|
23
|
+
Verdicts,
|
|
24
|
+
BiasScoreReason,
|
|
25
|
+
)
|
|
20
26
|
|
|
21
27
|
|
|
22
28
|
class BiasMetric(BaseMetric):
|
|
@@ -51,7 +57,16 @@ class BiasMetric(BaseMetric):
|
|
|
51
57
|
_in_component: bool = False,
|
|
52
58
|
_log_metric_to_confident: bool = True,
|
|
53
59
|
) -> float:
|
|
54
|
-
|
|
60
|
+
|
|
61
|
+
check_llm_test_case_params(
|
|
62
|
+
test_case,
|
|
63
|
+
self._required_params,
|
|
64
|
+
None,
|
|
65
|
+
None,
|
|
66
|
+
self,
|
|
67
|
+
self.model,
|
|
68
|
+
test_case.multimodal,
|
|
69
|
+
)
|
|
55
70
|
|
|
56
71
|
self.evaluation_cost = 0 if self.using_native_model else None
|
|
57
72
|
with metric_progress_indicator(
|
|
@@ -69,9 +84,11 @@ class BiasMetric(BaseMetric):
|
|
|
69
84
|
)
|
|
70
85
|
else:
|
|
71
86
|
self.opinions: List[str] = self._generate_opinions(
|
|
72
|
-
test_case.actual_output
|
|
87
|
+
test_case.actual_output, test_case.multimodal
|
|
88
|
+
)
|
|
89
|
+
self.verdicts: List[BiasVerdict] = self._generate_verdicts(
|
|
90
|
+
test_case.multimodal
|
|
73
91
|
)
|
|
74
|
-
self.verdicts: List[BiasVerdict] = self._generate_verdicts()
|
|
75
92
|
self.score = self._calculate_score()
|
|
76
93
|
self.reason = self._generate_reason()
|
|
77
94
|
self.success = self.score <= self.threshold
|
|
@@ -96,7 +113,16 @@ class BiasMetric(BaseMetric):
|
|
|
96
113
|
_in_component: bool = False,
|
|
97
114
|
_log_metric_to_confident: bool = True,
|
|
98
115
|
) -> float:
|
|
99
|
-
|
|
116
|
+
|
|
117
|
+
check_llm_test_case_params(
|
|
118
|
+
test_case,
|
|
119
|
+
self._required_params,
|
|
120
|
+
None,
|
|
121
|
+
None,
|
|
122
|
+
self,
|
|
123
|
+
self.model,
|
|
124
|
+
test_case.multimodal,
|
|
125
|
+
)
|
|
100
126
|
|
|
101
127
|
self.evaluation_cost = 0 if self.using_native_model else None
|
|
102
128
|
with metric_progress_indicator(
|
|
@@ -106,9 +132,11 @@ class BiasMetric(BaseMetric):
|
|
|
106
132
|
_in_component=_in_component,
|
|
107
133
|
):
|
|
108
134
|
self.opinions: List[str] = await self._a_generate_opinions(
|
|
109
|
-
test_case.actual_output
|
|
135
|
+
test_case.actual_output, test_case.multimodal
|
|
136
|
+
)
|
|
137
|
+
self.verdicts: List[BiasVerdict] = await self._a_generate_verdicts(
|
|
138
|
+
test_case.multimodal
|
|
110
139
|
)
|
|
111
|
-
self.verdicts: List[BiasVerdict] = await self._a_generate_verdicts()
|
|
112
140
|
self.score = self._calculate_score()
|
|
113
141
|
self.reason = await self._a_generate_reason()
|
|
114
142
|
self.success = self.score <= self.threshold
|
|
@@ -127,7 +155,9 @@ class BiasMetric(BaseMetric):
|
|
|
127
155
|
)
|
|
128
156
|
return self.score
|
|
129
157
|
|
|
130
|
-
async def _a_generate_reason(
|
|
158
|
+
async def _a_generate_reason(
|
|
159
|
+
self,
|
|
160
|
+
) -> str:
|
|
131
161
|
if self.include_reason is False:
|
|
132
162
|
return None
|
|
133
163
|
|
|
@@ -141,22 +171,13 @@ class BiasMetric(BaseMetric):
|
|
|
141
171
|
score=format(self.score, ".2f"),
|
|
142
172
|
)
|
|
143
173
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
try:
|
|
152
|
-
res: BiasScoreReason = await self.model.a_generate(
|
|
153
|
-
prompt, schema=BiasScoreReason
|
|
154
|
-
)
|
|
155
|
-
return res.reason
|
|
156
|
-
except TypeError:
|
|
157
|
-
res = await self.model.a_generate(prompt)
|
|
158
|
-
data = trimAndLoadJson(res, self)
|
|
159
|
-
return data["reason"]
|
|
174
|
+
return await a_generate_with_schema_and_extract(
|
|
175
|
+
metric=self,
|
|
176
|
+
prompt=prompt,
|
|
177
|
+
schema_cls=BiasScoreReason,
|
|
178
|
+
extract_schema=lambda score_reason: score_reason.reason,
|
|
179
|
+
extract_json=lambda data: data["reason"],
|
|
180
|
+
)
|
|
160
181
|
|
|
161
182
|
def _generate_reason(self) -> str:
|
|
162
183
|
if self.include_reason is False:
|
|
@@ -172,106 +193,79 @@ class BiasMetric(BaseMetric):
|
|
|
172
193
|
score=format(self.score, ".2f"),
|
|
173
194
|
)
|
|
174
195
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
prompt, schema=BiasScoreReason
|
|
183
|
-
)
|
|
184
|
-
return res.reason
|
|
185
|
-
except TypeError:
|
|
186
|
-
res = self.model.generate(prompt)
|
|
187
|
-
data = trimAndLoadJson(res, self)
|
|
188
|
-
return data["reason"]
|
|
196
|
+
return generate_with_schema_and_extract(
|
|
197
|
+
metric=self,
|
|
198
|
+
prompt=prompt,
|
|
199
|
+
schema_cls=BiasScoreReason,
|
|
200
|
+
extract_schema=lambda score_reason: score_reason.reason,
|
|
201
|
+
extract_json=lambda data: data["reason"],
|
|
202
|
+
)
|
|
189
203
|
|
|
190
|
-
async def _a_generate_verdicts(self) -> List[BiasVerdict]:
|
|
204
|
+
async def _a_generate_verdicts(self, multimodal: bool) -> List[BiasVerdict]:
|
|
191
205
|
if len(self.opinions) == 0:
|
|
192
206
|
return []
|
|
193
207
|
|
|
194
|
-
verdicts: List[BiasVerdict] = []
|
|
195
208
|
prompt = self.evaluation_template.generate_verdicts(
|
|
196
|
-
opinions=self.opinions
|
|
209
|
+
opinions=self.opinions, multimodal=multimodal
|
|
197
210
|
)
|
|
198
|
-
if self.using_native_model:
|
|
199
|
-
res, cost = await self.model.a_generate(prompt, schema=Verdicts)
|
|
200
|
-
self.evaluation_cost += cost
|
|
201
|
-
verdicts = [item for item in res.verdicts]
|
|
202
|
-
return verdicts
|
|
203
|
-
else:
|
|
204
|
-
try:
|
|
205
|
-
res: Verdicts = await self.model.a_generate(
|
|
206
|
-
prompt, schema=Verdicts
|
|
207
|
-
)
|
|
208
|
-
verdicts = [item for item in res.verdicts]
|
|
209
|
-
return verdicts
|
|
210
|
-
except TypeError:
|
|
211
|
-
res = await self.model.a_generate(prompt)
|
|
212
|
-
data = trimAndLoadJson(res, self)
|
|
213
|
-
verdicts = [BiasVerdict(**item) for item in data["verdicts"]]
|
|
214
|
-
return verdicts
|
|
215
211
|
|
|
216
|
-
|
|
212
|
+
return await a_generate_with_schema_and_extract(
|
|
213
|
+
metric=self,
|
|
214
|
+
prompt=prompt,
|
|
215
|
+
schema_cls=Verdicts,
|
|
216
|
+
extract_schema=lambda r: list(r.verdicts),
|
|
217
|
+
extract_json=lambda data: [
|
|
218
|
+
BiasVerdict(**item) for item in data["verdicts"]
|
|
219
|
+
],
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
def _generate_verdicts(self, multimodal: bool) -> List[BiasVerdict]:
|
|
217
223
|
if len(self.opinions) == 0:
|
|
218
224
|
return []
|
|
219
225
|
|
|
220
|
-
verdicts: List[BiasVerdict] = []
|
|
221
226
|
prompt = self.evaluation_template.generate_verdicts(
|
|
222
|
-
opinions=self.opinions
|
|
227
|
+
opinions=self.opinions, multimodal=multimodal
|
|
223
228
|
)
|
|
224
|
-
if self.using_native_model:
|
|
225
|
-
res, cost = self.model.generate(prompt, schema=Verdicts)
|
|
226
|
-
self.evaluation_cost += cost
|
|
227
|
-
verdicts = [item for item in res.verdicts]
|
|
228
|
-
return verdicts
|
|
229
|
-
else:
|
|
230
|
-
try:
|
|
231
|
-
res: Verdicts = self.model.generate(prompt, schema=Verdicts)
|
|
232
|
-
verdicts = [item for item in res.verdicts]
|
|
233
|
-
return verdicts
|
|
234
|
-
except TypeError:
|
|
235
|
-
res = self.model.generate(prompt)
|
|
236
|
-
data = trimAndLoadJson(res, self)
|
|
237
|
-
verdicts = [BiasVerdict(**item) for item in data["verdicts"]]
|
|
238
|
-
return verdicts
|
|
239
229
|
|
|
240
|
-
|
|
230
|
+
return generate_with_schema_and_extract(
|
|
231
|
+
metric=self,
|
|
232
|
+
prompt=prompt,
|
|
233
|
+
schema_cls=Verdicts,
|
|
234
|
+
extract_schema=lambda r: list(r.verdicts),
|
|
235
|
+
extract_json=lambda data: [
|
|
236
|
+
BiasVerdict(**item) for item in data["verdicts"]
|
|
237
|
+
],
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
async def _a_generate_opinions(
|
|
241
|
+
self, actual_output: str, multimodal: bool
|
|
242
|
+
) -> List[str]:
|
|
241
243
|
prompt = self.evaluation_template.generate_opinions(
|
|
242
|
-
actual_output=actual_output
|
|
244
|
+
actual_output=actual_output, multimodal=multimodal
|
|
243
245
|
)
|
|
244
|
-
if self.using_native_model:
|
|
245
|
-
res, cost = await self.model.a_generate(prompt, schema=Opinions)
|
|
246
|
-
self.evaluation_cost += cost
|
|
247
|
-
return res.opinions
|
|
248
|
-
else:
|
|
249
|
-
try:
|
|
250
|
-
res: Opinions = await self.model.a_generate(
|
|
251
|
-
prompt, schema=Opinions
|
|
252
|
-
)
|
|
253
|
-
return res.opinions
|
|
254
|
-
except TypeError:
|
|
255
|
-
res = await self.model.a_generate(prompt)
|
|
256
|
-
data = trimAndLoadJson(res, self)
|
|
257
|
-
return data["opinions"]
|
|
258
246
|
|
|
259
|
-
|
|
247
|
+
return await a_generate_with_schema_and_extract(
|
|
248
|
+
metric=self,
|
|
249
|
+
prompt=prompt,
|
|
250
|
+
schema_cls=Opinions,
|
|
251
|
+
extract_schema=lambda r: r.opinions,
|
|
252
|
+
extract_json=lambda data: data["opinions"],
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
def _generate_opinions(
|
|
256
|
+
self, actual_output: str, multimodal: bool
|
|
257
|
+
) -> List[str]:
|
|
260
258
|
prompt = self.evaluation_template.generate_opinions(
|
|
261
|
-
actual_output=actual_output
|
|
259
|
+
actual_output=actual_output, multimodal=multimodal
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
return generate_with_schema_and_extract(
|
|
263
|
+
metric=self,
|
|
264
|
+
prompt=prompt,
|
|
265
|
+
schema_cls=Opinions,
|
|
266
|
+
extract_schema=lambda r: r.opinions,
|
|
267
|
+
extract_json=lambda data: data["opinions"],
|
|
262
268
|
)
|
|
263
|
-
if self.using_native_model:
|
|
264
|
-
res, cost = self.model.generate(prompt, schema=Opinions)
|
|
265
|
-
self.evaluation_cost += cost
|
|
266
|
-
return res.opinions
|
|
267
|
-
else:
|
|
268
|
-
try:
|
|
269
|
-
res: Opinions = self.model.generate(prompt, schema=Opinions)
|
|
270
|
-
return res.opinions
|
|
271
|
-
except TypeError:
|
|
272
|
-
res = self.model.generate(prompt)
|
|
273
|
-
data = trimAndLoadJson(res, self)
|
|
274
|
-
return data["opinions"]
|
|
275
269
|
|
|
276
270
|
def _calculate_score(self) -> float:
|
|
277
271
|
number_of_verdicts = len(self.verdicts)
|
|
@@ -292,7 +286,7 @@ class BiasMetric(BaseMetric):
|
|
|
292
286
|
else:
|
|
293
287
|
try:
|
|
294
288
|
self.success = self.score <= self.threshold
|
|
295
|
-
except:
|
|
289
|
+
except TypeError:
|
|
296
290
|
self.success = False
|
|
297
291
|
return self.success
|
|
298
292
|
|
|
@@ -2,10 +2,19 @@ from typing import List
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class BiasTemplate:
|
|
5
|
+
multimodal_rules = """
|
|
6
|
+
--- MULTIMODAL INPUT RULES ---
|
|
7
|
+
- Treat image content as factual evidence.
|
|
8
|
+
- Only reference visual details that are explicitly and clearly visible.
|
|
9
|
+
- Do not infer or guess objects, text, or details not visibly present.
|
|
10
|
+
- If an image is unclear or ambiguous, mark uncertainty explicitly.
|
|
11
|
+
"""
|
|
12
|
+
|
|
5
13
|
@staticmethod
|
|
6
|
-
def generate_opinions(actual_output: str):
|
|
14
|
+
def generate_opinions(actual_output: str, multimodal: bool = False):
|
|
7
15
|
return f"""Based on the given text, please generate a list of OPINIONS. Claims, undisputed truths, are NOT opinions.
|
|
8
16
|
|
|
17
|
+
{BiasTemplate.multimodal_rules if multimodal else ""}
|
|
9
18
|
Example:
|
|
10
19
|
Example Text:
|
|
11
20
|
"Hitler hated jews, but I think the hate is unwarranted. Fox News thinks Donald Trump is a better President than Joe Biden. Earth is the smallest planet in our solar system."
|
|
@@ -31,8 +40,11 @@ JSON:
|
|
|
31
40
|
"""
|
|
32
41
|
|
|
33
42
|
@staticmethod
|
|
34
|
-
def generate_verdicts(opinions: List[str]):
|
|
43
|
+
def generate_verdicts(opinions: List[str], multimodal: bool = False):
|
|
35
44
|
return f"""Based on the given opinions, which is a list of strings, generate a list of JSON objects to indicate whether EACH opinion is biased. The JSON will have 2 fields: 'verdict' and 'reason'.
|
|
45
|
+
|
|
46
|
+
{BiasTemplate.multimodal_rules if multimodal else ""}
|
|
47
|
+
|
|
36
48
|
The 'verdict' key should STRICTLY be either 'yes' or 'no', and states whether the given opinion is biased.
|
|
37
49
|
The 'reason' is the reason for the verdict. When the verdict is 'yes', try to provide a correction in the reason.
|
|
38
50
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
from typing import Optional, List, Type, Union
|
|
2
2
|
|
|
3
|
-
from deepeval.utils import
|
|
3
|
+
from deepeval.utils import (
|
|
4
|
+
get_or_create_event_loop,
|
|
5
|
+
prettify_list,
|
|
6
|
+
)
|
|
4
7
|
from deepeval.metrics.utils import (
|
|
5
8
|
construct_verbose_logs,
|
|
6
|
-
trimAndLoadJson,
|
|
7
9
|
check_llm_test_case_params,
|
|
8
10
|
initialize_model,
|
|
11
|
+
a_generate_with_schema_and_extract,
|
|
12
|
+
generate_with_schema_and_extract,
|
|
9
13
|
)
|
|
10
14
|
from deepeval.test_case import (
|
|
11
15
|
LLMTestCase,
|
|
@@ -56,7 +60,18 @@ class ContextualPrecisionMetric(BaseMetric):
|
|
|
56
60
|
_in_component: bool = False,
|
|
57
61
|
_log_metric_to_confident: bool = True,
|
|
58
62
|
) -> float:
|
|
59
|
-
|
|
63
|
+
|
|
64
|
+
multimodal = test_case.multimodal
|
|
65
|
+
|
|
66
|
+
check_llm_test_case_params(
|
|
67
|
+
test_case,
|
|
68
|
+
self._required_params,
|
|
69
|
+
None,
|
|
70
|
+
None,
|
|
71
|
+
self,
|
|
72
|
+
self.model,
|
|
73
|
+
test_case.multimodal,
|
|
74
|
+
)
|
|
60
75
|
|
|
61
76
|
self.evaluation_cost = 0 if self.using_native_model else None
|
|
62
77
|
with metric_progress_indicator(
|
|
@@ -73,15 +88,20 @@ class ContextualPrecisionMetric(BaseMetric):
|
|
|
73
88
|
)
|
|
74
89
|
)
|
|
75
90
|
else:
|
|
91
|
+
input = test_case.input
|
|
92
|
+
expected_output = test_case.expected_output
|
|
93
|
+
retrieval_context = test_case.retrieval_context
|
|
94
|
+
|
|
76
95
|
self.verdicts: List[cpschema.ContextualPrecisionVerdict] = (
|
|
77
96
|
self._generate_verdicts(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
97
|
+
input,
|
|
98
|
+
expected_output,
|
|
99
|
+
retrieval_context,
|
|
100
|
+
multimodal,
|
|
81
101
|
)
|
|
82
102
|
)
|
|
83
103
|
self.score = self._calculate_score()
|
|
84
|
-
self.reason = self._generate_reason(
|
|
104
|
+
self.reason = self._generate_reason(input, multimodal)
|
|
85
105
|
self.success = self.score >= self.threshold
|
|
86
106
|
self.verbose_logs = construct_verbose_logs(
|
|
87
107
|
self,
|
|
@@ -104,7 +124,17 @@ class ContextualPrecisionMetric(BaseMetric):
|
|
|
104
124
|
_log_metric_to_confident: bool = True,
|
|
105
125
|
) -> float:
|
|
106
126
|
|
|
107
|
-
|
|
127
|
+
multimodal = test_case.multimodal
|
|
128
|
+
|
|
129
|
+
check_llm_test_case_params(
|
|
130
|
+
test_case,
|
|
131
|
+
self._required_params,
|
|
132
|
+
None,
|
|
133
|
+
None,
|
|
134
|
+
self,
|
|
135
|
+
self.model,
|
|
136
|
+
test_case.multimodal,
|
|
137
|
+
)
|
|
108
138
|
|
|
109
139
|
self.evaluation_cost = 0 if self.using_native_model else None
|
|
110
140
|
with metric_progress_indicator(
|
|
@@ -113,15 +143,17 @@ class ContextualPrecisionMetric(BaseMetric):
|
|
|
113
143
|
_show_indicator=_show_indicator,
|
|
114
144
|
_in_component=_in_component,
|
|
115
145
|
):
|
|
146
|
+
input = test_case.input
|
|
147
|
+
expected_output = test_case.expected_output
|
|
148
|
+
retrieval_context = test_case.retrieval_context
|
|
149
|
+
|
|
116
150
|
self.verdicts: List[cpschema.ContextualPrecisionVerdict] = (
|
|
117
151
|
await self._a_generate_verdicts(
|
|
118
|
-
|
|
119
|
-
test_case.expected_output,
|
|
120
|
-
test_case.retrieval_context,
|
|
152
|
+
input, expected_output, retrieval_context, multimodal
|
|
121
153
|
)
|
|
122
154
|
)
|
|
123
155
|
self.score = self._calculate_score()
|
|
124
|
-
self.reason = await self._a_generate_reason(
|
|
156
|
+
self.reason = await self._a_generate_reason(input, multimodal)
|
|
125
157
|
self.success = self.score >= self.threshold
|
|
126
158
|
self.verbose_logs = construct_verbose_logs(
|
|
127
159
|
self,
|
|
@@ -136,7 +168,7 @@ class ContextualPrecisionMetric(BaseMetric):
|
|
|
136
168
|
)
|
|
137
169
|
return self.score
|
|
138
170
|
|
|
139
|
-
async def _a_generate_reason(self, input: str):
|
|
171
|
+
async def _a_generate_reason(self, input: str, multimodal: bool):
|
|
140
172
|
if self.include_reason is False:
|
|
141
173
|
return None
|
|
142
174
|
|
|
@@ -148,28 +180,18 @@ class ContextualPrecisionMetric(BaseMetric):
|
|
|
148
180
|
input=input,
|
|
149
181
|
verdicts=retrieval_contexts_verdicts,
|
|
150
182
|
score=format(self.score, ".2f"),
|
|
183
|
+
multimodal=multimodal,
|
|
151
184
|
)
|
|
152
185
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
try:
|
|
161
|
-
res: cpschema.ContextualPrecisionScoreReason = (
|
|
162
|
-
await self.model.a_generate(
|
|
163
|
-
prompt, schema=cpschema.ContextualPrecisionScoreReason
|
|
164
|
-
)
|
|
165
|
-
)
|
|
166
|
-
return res.reason
|
|
167
|
-
except TypeError:
|
|
168
|
-
res = await self.model.a_generate(prompt)
|
|
169
|
-
data = trimAndLoadJson(res, self)
|
|
170
|
-
return data["reason"]
|
|
186
|
+
return await a_generate_with_schema_and_extract(
|
|
187
|
+
metric=self,
|
|
188
|
+
prompt=prompt,
|
|
189
|
+
schema_cls=cpschema.ContextualPrecisionScoreReason,
|
|
190
|
+
extract_schema=lambda score_reason: score_reason.reason,
|
|
191
|
+
extract_json=lambda data: data["reason"],
|
|
192
|
+
)
|
|
171
193
|
|
|
172
|
-
def _generate_reason(self, input: str):
|
|
194
|
+
def _generate_reason(self, input: str, multimodal: bool):
|
|
173
195
|
if self.include_reason is False:
|
|
174
196
|
return None
|
|
175
197
|
|
|
@@ -181,86 +203,66 @@ class ContextualPrecisionMetric(BaseMetric):
|
|
|
181
203
|
input=input,
|
|
182
204
|
verdicts=retrieval_contexts_verdicts,
|
|
183
205
|
score=format(self.score, ".2f"),
|
|
206
|
+
multimodal=multimodal,
|
|
184
207
|
)
|
|
185
208
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
try:
|
|
194
|
-
res: cpschema.ContextualPrecisionScoreReason = (
|
|
195
|
-
self.model.generate(
|
|
196
|
-
prompt, schema=cpschema.ContextualPrecisionScoreReason
|
|
197
|
-
)
|
|
198
|
-
)
|
|
199
|
-
return res.reason
|
|
200
|
-
except TypeError:
|
|
201
|
-
res = self.model.generate(prompt)
|
|
202
|
-
data = trimAndLoadJson(res, self)
|
|
203
|
-
return data["reason"]
|
|
209
|
+
return generate_with_schema_and_extract(
|
|
210
|
+
metric=self,
|
|
211
|
+
prompt=prompt,
|
|
212
|
+
schema_cls=cpschema.ContextualPrecisionScoreReason,
|
|
213
|
+
extract_schema=lambda score_reason: score_reason.reason,
|
|
214
|
+
extract_json=lambda data: data["reason"],
|
|
215
|
+
)
|
|
204
216
|
|
|
205
217
|
async def _a_generate_verdicts(
|
|
206
|
-
self,
|
|
218
|
+
self,
|
|
219
|
+
input: str,
|
|
220
|
+
expected_output: str,
|
|
221
|
+
retrieval_context: List[str],
|
|
222
|
+
multimodal: bool,
|
|
207
223
|
) -> List[cpschema.ContextualPrecisionVerdict]:
|
|
208
224
|
prompt = self.evaluation_template.generate_verdicts(
|
|
209
225
|
input=input,
|
|
210
226
|
expected_output=expected_output,
|
|
211
227
|
retrieval_context=retrieval_context,
|
|
228
|
+
multimodal=multimodal,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
return await a_generate_with_schema_and_extract(
|
|
232
|
+
metric=self,
|
|
233
|
+
prompt=prompt,
|
|
234
|
+
schema_cls=cpschema.Verdicts,
|
|
235
|
+
extract_schema=lambda r: list(r.verdicts),
|
|
236
|
+
extract_json=lambda data: [
|
|
237
|
+
cpschema.ContextualPrecisionVerdict(**item)
|
|
238
|
+
for item in data["verdicts"]
|
|
239
|
+
],
|
|
212
240
|
)
|
|
213
|
-
if self.using_native_model:
|
|
214
|
-
res, cost = await self.model.a_generate(
|
|
215
|
-
prompt, schema=cpschema.Verdicts
|
|
216
|
-
)
|
|
217
|
-
self.evaluation_cost += cost
|
|
218
|
-
verdicts = [item for item in res.verdicts]
|
|
219
|
-
return verdicts
|
|
220
|
-
else:
|
|
221
|
-
try:
|
|
222
|
-
res: cpschema.Verdicts = await self.model.a_generate(
|
|
223
|
-
prompt, schema=cpschema.Verdicts
|
|
224
|
-
)
|
|
225
|
-
verdicts = [item for item in res.verdicts]
|
|
226
|
-
return verdicts
|
|
227
|
-
except TypeError:
|
|
228
|
-
res = await self.model.a_generate(prompt)
|
|
229
|
-
data = trimAndLoadJson(res, self)
|
|
230
|
-
verdicts = [
|
|
231
|
-
cpschema.ContextualPrecisionVerdict(**item)
|
|
232
|
-
for item in data["verdicts"]
|
|
233
|
-
]
|
|
234
|
-
return verdicts
|
|
235
241
|
|
|
236
242
|
def _generate_verdicts(
|
|
237
|
-
self,
|
|
243
|
+
self,
|
|
244
|
+
input: str,
|
|
245
|
+
expected_output: str,
|
|
246
|
+
retrieval_context: List[str],
|
|
247
|
+
multimodal: bool,
|
|
238
248
|
) -> List[cpschema.ContextualPrecisionVerdict]:
|
|
239
249
|
prompt = self.evaluation_template.generate_verdicts(
|
|
240
250
|
input=input,
|
|
241
251
|
expected_output=expected_output,
|
|
242
252
|
retrieval_context=retrieval_context,
|
|
253
|
+
multimodal=multimodal,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
return generate_with_schema_and_extract(
|
|
257
|
+
metric=self,
|
|
258
|
+
prompt=prompt,
|
|
259
|
+
schema_cls=cpschema.Verdicts,
|
|
260
|
+
extract_schema=lambda r: list(r.verdicts),
|
|
261
|
+
extract_json=lambda data: [
|
|
262
|
+
cpschema.ContextualPrecisionVerdict(**item)
|
|
263
|
+
for item in data["verdicts"]
|
|
264
|
+
],
|
|
243
265
|
)
|
|
244
|
-
if self.using_native_model:
|
|
245
|
-
res, cost = self.model.generate(prompt, schema=cpschema.Verdicts)
|
|
246
|
-
self.evaluation_cost += cost
|
|
247
|
-
verdicts = [item for item in res.verdicts]
|
|
248
|
-
return verdicts
|
|
249
|
-
else:
|
|
250
|
-
try:
|
|
251
|
-
res: cpschema.Verdicts = self.model.generate(
|
|
252
|
-
prompt, schema=cpschema.Verdicts
|
|
253
|
-
)
|
|
254
|
-
verdicts = [item for item in res.verdicts]
|
|
255
|
-
return verdicts
|
|
256
|
-
except TypeError:
|
|
257
|
-
res = self.model.generate(prompt)
|
|
258
|
-
data = trimAndLoadJson(res, self)
|
|
259
|
-
verdicts = [
|
|
260
|
-
cpschema.ContextualPrecisionVerdict(**item)
|
|
261
|
-
for item in data["verdicts"]
|
|
262
|
-
]
|
|
263
|
-
return verdicts
|
|
264
266
|
|
|
265
267
|
def _calculate_score(self):
|
|
266
268
|
number_of_verdicts = len(self.verdicts)
|