openevals 0.0.0__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.
- evaluators/__init__.py +9 -0
- evaluators/exact.py +56 -0
- evaluators/json/__init__.py +13 -0
- evaluators/json/match.py +437 -0
- evaluators/llm.py +566 -0
- evaluators/prompts/__init__.py +9 -0
- evaluators/prompts/conciseness.py +40 -0
- evaluators/prompts/correctness.py +44 -0
- evaluators/prompts/hallucination.py +44 -0
- evaluators/string/__init__.py +12 -0
- evaluators/string/embedding_similarity.py +130 -0
- evaluators/string/levenshtein.py +90 -0
- evaluators/trajectory/__init__.py +18 -0
- evaluators/trajectory/llm.py +233 -0
- evaluators/trajectory/strict.py +118 -0
- evaluators/trajectory/subset.py +90 -0
- evaluators/trajectory/superset.py +91 -0
- evaluators/trajectory/unordered.py +95 -0
- evaluators/trajectory/utils.py +58 -0
- evaluators/types.py +74 -0
- evaluators/utils.py +176 -0
- openevals-0.0.0.dist-info/LICENSE +19 -0
- openevals-0.0.0.dist-info/METADATA +943 -0
- openevals-0.0.0.dist-info/RECORD +26 -0
- openevals-0.0.0.dist-info/WHEEL +5 -0
- openevals-0.0.0.dist-info/top_level.txt +1 -0
evaluators/__init__.py
ADDED
evaluators/exact.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from evaluators.types import EvaluatorResult
|
|
2
|
+
from evaluators.utils import _run_evaluator, _arun_evaluator
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _scorer(inputs: Any, outputs: Any) -> bool:
|
|
9
|
+
if inputs is None or outputs is None:
|
|
10
|
+
raise ValueError("Exact match requires both inputs and outputs")
|
|
11
|
+
# Convert both to JSON strings for deep comparison
|
|
12
|
+
inputs_json = json.dumps(inputs, sort_keys=True)
|
|
13
|
+
outputs_json = json.dumps(outputs, sort_keys=True)
|
|
14
|
+
return inputs_json == outputs_json
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def exact_match(*, inputs: Any, outputs: Any, **kwargs: Any) -> EvaluatorResult:
|
|
18
|
+
"""
|
|
19
|
+
Performs exact matching between input and output values.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
inputs (Any): Inputs to compare
|
|
23
|
+
outputs (Any): Outputs to compare
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
EvaluatorResult: Contains match result
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def get_score():
|
|
30
|
+
return _scorer(inputs, outputs)
|
|
31
|
+
|
|
32
|
+
return _run_evaluator(
|
|
33
|
+
run_name="exact_match", scorer=get_score, feedback_key="exact_match"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def exact_match_async(
|
|
38
|
+
*, inputs: Any, outputs: Any, **kwargs: Any
|
|
39
|
+
) -> EvaluatorResult:
|
|
40
|
+
"""
|
|
41
|
+
Performs exact matching between input and output values.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
inputs (Any): Inputs to compare
|
|
45
|
+
outputs (Any): Outputs to compare
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
EvaluatorResult: Contains match result
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
async def get_score():
|
|
52
|
+
return _scorer(inputs, outputs)
|
|
53
|
+
|
|
54
|
+
return await _arun_evaluator(
|
|
55
|
+
run_name="exact_match", scorer=get_score, feedback_key="exact_match"
|
|
56
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .match import (
|
|
2
|
+
create_json_match_evaluator,
|
|
3
|
+
create_async_json_match_evaluator,
|
|
4
|
+
SYSTEM_PROMPT,
|
|
5
|
+
USER_PROMPT,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"create_json_match_evaluator",
|
|
10
|
+
"create_async_json_match_evaluator",
|
|
11
|
+
"SYSTEM_PROMPT",
|
|
12
|
+
"USER_PROMPT",
|
|
13
|
+
]
|
evaluators/json/match.py
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
from typing import Literal, Optional, Dict, Any, Union
|
|
2
|
+
from evaluators.types import EvaluatorResult, SimpleEvaluator, SimpleAsyncEvaluator
|
|
3
|
+
from evaluators.utils import _run_evaluator, _arun_evaluator
|
|
4
|
+
from evaluators.llm import (
|
|
5
|
+
_create_llm_as_judge_scorer,
|
|
6
|
+
_create_async_llm_as_judge_scorer,
|
|
7
|
+
ModelClient,
|
|
8
|
+
LangChainLikeModel,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
SYSTEM_PROMPT = """You are an LLM that evaluates the accuracy of structured outputs.
|
|
12
|
+
Make sure to evaluate each key the users ask you to evaluate separately. Assign the score
|
|
13
|
+
for each key based on its own criteria - DO NOT convolute the scores of different keys.
|
|
14
|
+
Also only evaluate the output vs. the reference output based on the criteria. DO NOT EVALUATE
|
|
15
|
+
BASED ON ANYTHING ELSE. If the output does not match the reference output in some way that
|
|
16
|
+
is not mentioned in the criteria that is not a problem and you should ignore those discrepancies.
|
|
17
|
+
Only focus on finding discrepancies based on the criteria.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
USER_PROMPT = """Please evaluate the accuracy of the following output keys according to these criteria:
|
|
21
|
+
{rubric}
|
|
22
|
+
<Outputs>
|
|
23
|
+
{outputs}
|
|
24
|
+
</Outputs>
|
|
25
|
+
<Expected Outputs>
|
|
26
|
+
{reference_outputs}
|
|
27
|
+
</Expected Outputs>"""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _prepare_parameters(
|
|
31
|
+
*,
|
|
32
|
+
outputs: Any,
|
|
33
|
+
reference_outputs: Any,
|
|
34
|
+
rubric: Dict[str, str],
|
|
35
|
+
exclude_keys: list[str],
|
|
36
|
+
use_reasoning: bool,
|
|
37
|
+
):
|
|
38
|
+
json_schema = {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"title": "structured_match_score",
|
|
41
|
+
"description": "Scores measuring the accuracy of structured outputs",
|
|
42
|
+
"properties": {},
|
|
43
|
+
"required": [],
|
|
44
|
+
"additionalProperties": False,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
scores = {}
|
|
48
|
+
formatted_rubric = ""
|
|
49
|
+
use_list_reducer = False
|
|
50
|
+
if isinstance(outputs, list):
|
|
51
|
+
use_list_reducer = True
|
|
52
|
+
if not isinstance(reference_outputs, list):
|
|
53
|
+
raise ValueError(
|
|
54
|
+
"If outputs is a list, reference_outputs must also be a list"
|
|
55
|
+
)
|
|
56
|
+
outputs_to_use = {}
|
|
57
|
+
for i in range(len(outputs)):
|
|
58
|
+
for key, value in outputs[i].items():
|
|
59
|
+
outputs_to_use[f"{key}_{i}"] = value
|
|
60
|
+
outputs = outputs_to_use
|
|
61
|
+
reference_outputs_to_use = {}
|
|
62
|
+
for i in range(len(reference_outputs)):
|
|
63
|
+
for key, value in reference_outputs[i].items():
|
|
64
|
+
reference_outputs_to_use[f"{key}_{i}"] = value
|
|
65
|
+
reference_outputs = reference_outputs_to_use
|
|
66
|
+
|
|
67
|
+
for raw_key, value in outputs.items():
|
|
68
|
+
if use_list_reducer:
|
|
69
|
+
key = raw_key[: raw_key.rfind("_")]
|
|
70
|
+
else:
|
|
71
|
+
key = raw_key
|
|
72
|
+
if key in exclude_keys:
|
|
73
|
+
continue
|
|
74
|
+
if raw_key not in reference_outputs:
|
|
75
|
+
scores[raw_key] = 0
|
|
76
|
+
continue
|
|
77
|
+
if key not in rubric and reference_outputs[raw_key] == value:
|
|
78
|
+
scores[raw_key] = 1
|
|
79
|
+
elif key not in rubric:
|
|
80
|
+
scores[raw_key] = 0
|
|
81
|
+
else:
|
|
82
|
+
key_criteria = rubric[key]
|
|
83
|
+
formatted_rubric += f"Key: {key}, Criteria: {key_criteria}\n"
|
|
84
|
+
if not use_reasoning:
|
|
85
|
+
json_schema["properties"][raw_key] = {
|
|
86
|
+
"type": "boolean",
|
|
87
|
+
"description": f"Does the output for key {key}, follow the criteria? {key_criteria}",
|
|
88
|
+
}
|
|
89
|
+
else:
|
|
90
|
+
json_schema["properties"][raw_key] = {
|
|
91
|
+
"type": "object",
|
|
92
|
+
"properties": {
|
|
93
|
+
"reasoning": {
|
|
94
|
+
"type": "string",
|
|
95
|
+
"description": f"Reasoning for the score you assigned to key {key}",
|
|
96
|
+
},
|
|
97
|
+
"score": {
|
|
98
|
+
"type": "boolean",
|
|
99
|
+
"description": f"Does the output for key {key}, follow the criteria? {key_criteria}",
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
"required": ["score", "reasoning"],
|
|
103
|
+
"additionalProperties": False,
|
|
104
|
+
}
|
|
105
|
+
for raw_key, value in reference_outputs.items():
|
|
106
|
+
if use_list_reducer:
|
|
107
|
+
key = raw_key[: raw_key.rfind("_")]
|
|
108
|
+
else:
|
|
109
|
+
key = raw_key
|
|
110
|
+
if key not in exclude_keys and raw_key not in outputs:
|
|
111
|
+
scores[raw_key] = 0
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
outputs,
|
|
115
|
+
reference_outputs,
|
|
116
|
+
json_schema,
|
|
117
|
+
scores,
|
|
118
|
+
formatted_rubric,
|
|
119
|
+
use_list_reducer,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _aggregate_results(
|
|
124
|
+
*,
|
|
125
|
+
scores: dict,
|
|
126
|
+
use_list_reducer: bool,
|
|
127
|
+
aggregator: Optional[Literal["average", "all"]],
|
|
128
|
+
list_aggregator: Literal["average", "all"],
|
|
129
|
+
) -> dict:
|
|
130
|
+
if use_list_reducer:
|
|
131
|
+
scores_aggregated_across_list = {}
|
|
132
|
+
keys = set([k[: k.rfind("_")] for k in scores.keys()])
|
|
133
|
+
if list_aggregator == "average":
|
|
134
|
+
for key in keys:
|
|
135
|
+
scores_aggregated_across_list[key] = sum(
|
|
136
|
+
[
|
|
137
|
+
(
|
|
138
|
+
float(scores[k]["score"])
|
|
139
|
+
if isinstance(scores[k], dict)
|
|
140
|
+
else scores[k]
|
|
141
|
+
)
|
|
142
|
+
for k in scores
|
|
143
|
+
if k[: k.rfind("_")] == key
|
|
144
|
+
]
|
|
145
|
+
) / len([scores[k] for k in scores if k[: k.rfind("_")] == key])
|
|
146
|
+
elif list_aggregator == "all":
|
|
147
|
+
for key in keys:
|
|
148
|
+
scores_aggregated_across_list[key] = (
|
|
149
|
+
0
|
|
150
|
+
if 0
|
|
151
|
+
in [
|
|
152
|
+
(
|
|
153
|
+
float(scores[k]["score"])
|
|
154
|
+
if isinstance(scores[k], dict)
|
|
155
|
+
else scores[k]
|
|
156
|
+
)
|
|
157
|
+
for k in scores
|
|
158
|
+
if k[: k.rfind("_")] == key
|
|
159
|
+
]
|
|
160
|
+
else 1
|
|
161
|
+
)
|
|
162
|
+
scores = scores_aggregated_across_list
|
|
163
|
+
|
|
164
|
+
score = None
|
|
165
|
+
if aggregator == "average":
|
|
166
|
+
score = sum(
|
|
167
|
+
[float(v["score"]) if isinstance(v, dict) else v for v in scores.values()]
|
|
168
|
+
) / len(scores)
|
|
169
|
+
elif aggregator == "all":
|
|
170
|
+
score = (
|
|
171
|
+
0
|
|
172
|
+
if any(
|
|
173
|
+
[
|
|
174
|
+
(float(v["score"]) if isinstance(v, dict) else v) != 1
|
|
175
|
+
for v in scores.values()
|
|
176
|
+
]
|
|
177
|
+
)
|
|
178
|
+
else 1
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# If there is an aggregator, return a single result
|
|
182
|
+
if score is not None:
|
|
183
|
+
return score
|
|
184
|
+
else:
|
|
185
|
+
results = {}
|
|
186
|
+
for key, value in scores.items():
|
|
187
|
+
results[key] = value
|
|
188
|
+
if len(results) == 1:
|
|
189
|
+
ans = list(results.values())[0]
|
|
190
|
+
if isinstance(ans, dict):
|
|
191
|
+
return (float(ans["score"]), ans["reasoning"])
|
|
192
|
+
return ans
|
|
193
|
+
return results
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def create_json_match_evaluator(
|
|
197
|
+
*,
|
|
198
|
+
aggregator: Optional[Literal["average", "all"]] = None,
|
|
199
|
+
list_aggregator: Literal["average", "all"] = "all",
|
|
200
|
+
rubric: Dict[str, str] = {},
|
|
201
|
+
exclude_keys: list[str] = [],
|
|
202
|
+
judge: Optional[
|
|
203
|
+
Union[
|
|
204
|
+
ModelClient,
|
|
205
|
+
LangChainLikeModel,
|
|
206
|
+
]
|
|
207
|
+
] = None,
|
|
208
|
+
model: str = "openai:o3-mini",
|
|
209
|
+
use_reasoning: bool = True,
|
|
210
|
+
) -> SimpleEvaluator:
|
|
211
|
+
"""
|
|
212
|
+
Create an evaluator to evaluate the accuracy of structured outputs.
|
|
213
|
+
|
|
214
|
+
Parameters:
|
|
215
|
+
aggregator (Optional[Literal["average", "all"]]): The aggregation method to use for combining the keys of each structured object.
|
|
216
|
+
Defaults to None. If None, will return a single EvaluatorResult for each key that appears in either
|
|
217
|
+
the outputs or the reference_outputs or both. If "average", will return a single EvaluatorResult that
|
|
218
|
+
is the average of the feedback for each key in the outputs/reference_outputs. If "all", will return
|
|
219
|
+
a single EvaluatorResult that is a combined and statement of the feedback for each key in the outputs/reference_outputs.
|
|
220
|
+
If "all"/"average" the feedback key returned will be called "structured_match_score
|
|
221
|
+
list_aggregator (Literal["average", "all"]): The aggregation method to use when evaluating a list of outputs.
|
|
222
|
+
Defaults to "all". If "all", the score for a single feedback key will be a combined and statement of the scores for
|
|
223
|
+
that key across all elements of the list. If "average", the score for a single feedback key will be the
|
|
224
|
+
average of the scores for that key across all elements of the list
|
|
225
|
+
rubric (Optional[Dict[str,str]]): The rubric to use for the judge. Each entry of the dict is a
|
|
226
|
+
key/value pair where the key is the structured output key and the value is the criteria for the LLM to
|
|
227
|
+
evaluate that key on against the reference output.
|
|
228
|
+
exclude_keys (Optional[list[str]]): The keys to exclude from the evaluation. Use this if there are
|
|
229
|
+
keys in your structured output you don't care about evaluating. Every key not in `exclude_keys` or in `rubric`
|
|
230
|
+
will be evaluated for exact match with the reference output.
|
|
231
|
+
judge (ModelClient or LangChainLikeModel): The judge to use for the evaluation.
|
|
232
|
+
model (str): The model to use for the evaluation.
|
|
233
|
+
use_reasoning (bool): Whether to use reasoning for the keys in `rubric`. Defaults to True.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
A function that takes in outputs and reference_outputs and returns an EvaluatorResult or list of EvaluatorResults.
|
|
237
|
+
"""
|
|
238
|
+
|
|
239
|
+
def wrapped_evaluator(
|
|
240
|
+
*,
|
|
241
|
+
outputs: Any,
|
|
242
|
+
reference_outputs: Any,
|
|
243
|
+
**kwargs,
|
|
244
|
+
) -> EvaluatorResult | list[EvaluatorResult]:
|
|
245
|
+
def _scorer(
|
|
246
|
+
*,
|
|
247
|
+
outputs: Any,
|
|
248
|
+
reference_outputs: Any,
|
|
249
|
+
rubric: Optional[str] = None,
|
|
250
|
+
exclude_keys: Optional[list[str]] = None,
|
|
251
|
+
use_reasoning: Optional[bool] = None,
|
|
252
|
+
) -> Union[float, bool, dict]:
|
|
253
|
+
(
|
|
254
|
+
outputs,
|
|
255
|
+
reference_outputs,
|
|
256
|
+
json_schema,
|
|
257
|
+
scores,
|
|
258
|
+
formatted_rubric,
|
|
259
|
+
use_list_reducer,
|
|
260
|
+
) = _prepare_parameters(
|
|
261
|
+
outputs=outputs,
|
|
262
|
+
reference_outputs=reference_outputs,
|
|
263
|
+
rubric=rubric,
|
|
264
|
+
exclude_keys=exclude_keys,
|
|
265
|
+
use_reasoning=use_reasoning,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
scorer = None
|
|
269
|
+
if len(formatted_rubric) > 0:
|
|
270
|
+
output_keys = "\n".join(
|
|
271
|
+
[f"{key}: {outputs[key]}" for key in json_schema["properties"]]
|
|
272
|
+
)
|
|
273
|
+
expected_output_keys = "\n".join(
|
|
274
|
+
[
|
|
275
|
+
f"{key}: {reference_outputs[key]}"
|
|
276
|
+
for key in json_schema["properties"]
|
|
277
|
+
]
|
|
278
|
+
)
|
|
279
|
+
scorer = _create_llm_as_judge_scorer(
|
|
280
|
+
system=SYSTEM_PROMPT,
|
|
281
|
+
prompt=USER_PROMPT,
|
|
282
|
+
schema=json_schema,
|
|
283
|
+
judge=judge,
|
|
284
|
+
model=model,
|
|
285
|
+
)
|
|
286
|
+
else:
|
|
287
|
+
formatted_rubric, output_keys, expected_output_keys = None, None, None
|
|
288
|
+
if scorer is not None:
|
|
289
|
+
llm_scores = scorer(
|
|
290
|
+
outputs=output_keys,
|
|
291
|
+
reference_outputs=expected_output_keys,
|
|
292
|
+
rubric=rubric,
|
|
293
|
+
)
|
|
294
|
+
scores.update(llm_scores)
|
|
295
|
+
|
|
296
|
+
return _aggregate_results(
|
|
297
|
+
scores=scores,
|
|
298
|
+
use_list_reducer=use_list_reducer,
|
|
299
|
+
aggregator=aggregator,
|
|
300
|
+
list_aggregator=list_aggregator,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
return _run_evaluator(
|
|
304
|
+
run_name="structured_match_evaluator",
|
|
305
|
+
scorer=_scorer,
|
|
306
|
+
feedback_key="structured_match_score",
|
|
307
|
+
rubric=rubric,
|
|
308
|
+
outputs=outputs,
|
|
309
|
+
reference_outputs=reference_outputs,
|
|
310
|
+
exclude_keys=exclude_keys,
|
|
311
|
+
use_reasoning=use_reasoning,
|
|
312
|
+
**kwargs,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
return wrapped_evaluator
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def create_async_json_match_evaluator(
|
|
319
|
+
*,
|
|
320
|
+
aggregator: Optional[Literal["average", "all"]] = None,
|
|
321
|
+
list_aggregator: Literal["average", "all"] = "all",
|
|
322
|
+
rubric: Dict[str, str] = {},
|
|
323
|
+
exclude_keys: list[str] = [],
|
|
324
|
+
judge: Optional[
|
|
325
|
+
Union[
|
|
326
|
+
ModelClient,
|
|
327
|
+
LangChainLikeModel,
|
|
328
|
+
]
|
|
329
|
+
] = None,
|
|
330
|
+
model: str = "openai:o3-mini",
|
|
331
|
+
use_reasoning: bool = True,
|
|
332
|
+
) -> SimpleAsyncEvaluator:
|
|
333
|
+
"""
|
|
334
|
+
Create an evaluator to evaluate the accuracy of structured outputs.
|
|
335
|
+
|
|
336
|
+
Parameters:
|
|
337
|
+
aggregator (Optional[Literal["average", "all"]]): The aggregation method to use for combining the keys of each structured object.
|
|
338
|
+
Defaults to None. If None, will return a single EvaluatorResult for each key that appears in either
|
|
339
|
+
the outputs or the reference_outputs or both. If "average", will return a single EvaluatorResult that
|
|
340
|
+
is the average of the feedback for each key in the outputs/reference_outputs. If "all", will return
|
|
341
|
+
a single EvaluatorResult that is a combined and statement of the feedback for each key in the outputs/reference_outputs.
|
|
342
|
+
If "all"/"average" the feedback key returned will be called "structured_match_score
|
|
343
|
+
list_aggregator (Literal["average", "all"]): The aggregation method to use when evaluating a list of outputs.
|
|
344
|
+
Defaults to "all". If "all", the score for a single feedback key will be a combined and statement of the scores for
|
|
345
|
+
that key across all elements of the list. If "average", the score for a single feedback key will be the
|
|
346
|
+
average of the scores for that key across all elements of the list
|
|
347
|
+
rubric (Optional[Dict[str,str]]): The rubric to use for the judge. Each entry of the dict is a
|
|
348
|
+
key/value pair where the key is the structured output key and the value is the criteria for the LLM to
|
|
349
|
+
evaluate that key on against the reference output.
|
|
350
|
+
exclude_keys (Optional[list[str]]): The keys to exclude from the evaluation. Use this if there are
|
|
351
|
+
keys in your structured output you don't care about evaluating. Every key not in `exclude_keys` or in `rubric`
|
|
352
|
+
will be evaluated for exact match with the reference output.
|
|
353
|
+
judge (ModelClient or LangChainLikeModel): The judge to use for the evaluation.
|
|
354
|
+
model (str): The model to use for the evaluation.
|
|
355
|
+
use_reasoning (bool): Whether to use reasoning for the keys in `rubric`. Defaults to True.
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
A function that takes in outputs and reference_outputs and returns an EvaluatorResult or list of EvaluatorResults.
|
|
359
|
+
"""
|
|
360
|
+
|
|
361
|
+
async def wrapped_evaluator(
|
|
362
|
+
*,
|
|
363
|
+
outputs: Any,
|
|
364
|
+
reference_outputs: Any,
|
|
365
|
+
**kwargs,
|
|
366
|
+
) -> EvaluatorResult | list[EvaluatorResult]:
|
|
367
|
+
async def _ascorer(
|
|
368
|
+
*,
|
|
369
|
+
outputs: Any,
|
|
370
|
+
reference_outputs: Any,
|
|
371
|
+
rubric: Optional[str] = None,
|
|
372
|
+
exclude_keys: Optional[list[str]] = None,
|
|
373
|
+
use_reasoning: Optional[bool] = None,
|
|
374
|
+
) -> Union[float, bool, dict]:
|
|
375
|
+
(
|
|
376
|
+
outputs,
|
|
377
|
+
reference_outputs,
|
|
378
|
+
json_schema,
|
|
379
|
+
scores,
|
|
380
|
+
formatted_rubric,
|
|
381
|
+
use_list_reducer,
|
|
382
|
+
) = _prepare_parameters(
|
|
383
|
+
outputs=outputs,
|
|
384
|
+
reference_outputs=reference_outputs,
|
|
385
|
+
rubric=rubric,
|
|
386
|
+
exclude_keys=exclude_keys,
|
|
387
|
+
use_reasoning=use_reasoning,
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
scorer = None
|
|
391
|
+
if len(formatted_rubric) > 0:
|
|
392
|
+
output_keys = "\n".join(
|
|
393
|
+
[f"{key}: {outputs[key]}" for key in json_schema["properties"]]
|
|
394
|
+
)
|
|
395
|
+
expected_output_keys = "\n".join(
|
|
396
|
+
[
|
|
397
|
+
f"{key}: {reference_outputs[key]}"
|
|
398
|
+
for key in json_schema["properties"]
|
|
399
|
+
]
|
|
400
|
+
)
|
|
401
|
+
scorer = _create_async_llm_as_judge_scorer(
|
|
402
|
+
system=SYSTEM_PROMPT,
|
|
403
|
+
prompt=USER_PROMPT,
|
|
404
|
+
schema=json_schema,
|
|
405
|
+
judge=judge,
|
|
406
|
+
model=model,
|
|
407
|
+
)
|
|
408
|
+
else:
|
|
409
|
+
formatted_rubric, output_keys, expected_output_keys = None, None, None
|
|
410
|
+
if scorer is not None:
|
|
411
|
+
llm_scores = await scorer(
|
|
412
|
+
outputs=output_keys,
|
|
413
|
+
reference_outputs=expected_output_keys,
|
|
414
|
+
rubric=rubric,
|
|
415
|
+
)
|
|
416
|
+
scores.update(llm_scores)
|
|
417
|
+
|
|
418
|
+
return _aggregate_results(
|
|
419
|
+
scores=scores,
|
|
420
|
+
use_list_reducer=use_list_reducer,
|
|
421
|
+
aggregator=aggregator,
|
|
422
|
+
list_aggregator=list_aggregator,
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
return await _arun_evaluator(
|
|
426
|
+
run_name="structured_match_evaluator",
|
|
427
|
+
scorer=_ascorer,
|
|
428
|
+
feedback_key="structured_match_score",
|
|
429
|
+
rubric=rubric,
|
|
430
|
+
outputs=outputs,
|
|
431
|
+
reference_outputs=reference_outputs,
|
|
432
|
+
exclude_keys=exclude_keys,
|
|
433
|
+
use_reasoning=use_reasoning,
|
|
434
|
+
**kwargs,
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
return wrapped_evaluator
|