Skip to content

Commit 7712632

Browse files
authored
[evaluation] Name both SDK and asset aliases in TaskNavigationEfficiency validator errors (#48048)
* [evaluation] Name both SDK and asset aliases in TaskNavigationEfficiency validator errors TaskNavigationEfficiencyValidator accepts either the SDK input names (response/ground_truth) or the azureml-assets names (actions/expected_actions), but its user-facing validation messages referenced only response/ground_truth. A caller who correctly supplied actions/expected_actions could get an error naming ground_truth/response - parameters they never used and that the eval service does not accept as data-mapping keys - which was confusing. Update every validation message that names response/ground_truth to also cite the accepted alias, e.g. 'ground_truth' (also accepted as 'expected_actions') and 'response' (also accepted as 'actions'). Message text only; no changes to categories, logic, or control flow. * [evaluation] Lead TaskNavigationEfficiency validator errors with azureml-assets names Swap the alias order in every TaskNavigationEfficiencyValidator error message so the azureml-assets input names ('actions'/'expected_actions') are primary and the SDK names ('response'/'ground_truth') are the parenthetical alias. Update the CHANGELOG entry to match. Message text only; no logic, category, or control-flow changes. * Add test
1 parent 4b3c314 commit 7712632

2 files changed

Lines changed: 34 additions & 11 deletions

File tree

sdk/evaluation/azure-ai-evaluation/azure/ai/evaluation/_evaluators/_common/_validators/_task_navigation_efficiency_validator.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@ def _validate_response(self, response: Any) -> Optional[EvaluationException]:
5353
"""Validate the response parameter."""
5454
if response is None:
5555
return EvaluationException(
56-
message="'response' parameter is required and cannot be None.",
56+
message="'actions' (also accepted as 'response') parameter is required and cannot be None.",
5757
blame=ErrorBlame.USER_ERROR,
5858
category=ErrorCategory.MISSING_FIELD,
5959
target=self.error_target,
6060
)
6161

6262
if not isinstance(response, list):
6363
return EvaluationException(
64-
message="'response' must be a list of messages.",
64+
message="'actions' (also accepted as 'response') must be a list of messages.",
6565
blame=ErrorBlame.USER_ERROR,
6666
category=ErrorCategory.INVALID_VALUE,
6767
target=self.error_target,
@@ -141,7 +141,7 @@ def _validate_ground_truth(self, ground_truth: Any) -> Optional[EvaluationExcept
141141
"""Validate the ground_truth parameter."""
142142
if not ground_truth:
143143
return EvaluationException(
144-
message="'ground_truth' parameter is required and cannot be None or empty.",
144+
message="'expected_actions' (also accepted as 'ground_truth') parameter is required and cannot be None or empty.",
145145
blame=ErrorBlame.USER_ERROR,
146146
category=ErrorCategory.INVALID_VALUE,
147147
target=self.error_target,
@@ -155,7 +155,7 @@ def _validate_ground_truth(self, ground_truth: Any) -> Optional[EvaluationExcept
155155
# Validate tuple format: (list, dict)
156156
if len(ground_truth) != 2:
157157
return EvaluationException(
158-
message="When 'ground_truth' is a tuple, it must contain exactly 2 elements: (tool_names_list, parameters_dict).",
158+
message="When 'expected_actions' (also accepted as 'ground_truth') is a tuple, it must contain exactly 2 elements: (tool_names_list, parameters_dict).",
159159
blame=ErrorBlame.USER_ERROR,
160160
category=ErrorCategory.INVALID_VALUE,
161161
target=self.error_target,
@@ -166,15 +166,15 @@ def _validate_ground_truth(self, ground_truth: Any) -> Optional[EvaluationExcept
166166
# Validate tool names list
167167
if not isinstance(tool_names, list):
168168
return EvaluationException(
169-
message="First element of 'ground_truth' tuple must be a list of tool names.",
169+
message="First element of 'expected_actions' (also accepted as 'ground_truth') tuple must be a list of tool names.",
170170
blame=ErrorBlame.USER_ERROR,
171171
category=ErrorCategory.INVALID_VALUE,
172172
target=self.error_target,
173173
)
174174

175175
if len(tool_names) == 0:
176176
return EvaluationException(
177-
message="Tool names list in 'ground_truth' cannot be empty.",
177+
message="Tool names list in 'expected_actions' (also accepted as 'ground_truth') cannot be empty.",
178178
blame=ErrorBlame.USER_ERROR,
179179
category=ErrorCategory.INVALID_VALUE,
180180
target=self.error_target,
@@ -183,7 +183,7 @@ def _validate_ground_truth(self, ground_truth: Any) -> Optional[EvaluationExcept
183183
for idx, name in enumerate(tool_names):
184184
if not isinstance(name, str):
185185
return EvaluationException(
186-
message=f"Tool name at index {idx} in 'ground_truth' must be a string, got {type(name).__name__}.",
186+
message=f"Tool name at index {idx} in 'expected_actions' (also accepted as 'ground_truth') must be a string, got {type(name).__name__}.",
187187
blame=ErrorBlame.USER_ERROR,
188188
category=ErrorCategory.INVALID_VALUE,
189189
target=self.error_target,
@@ -192,7 +192,7 @@ def _validate_ground_truth(self, ground_truth: Any) -> Optional[EvaluationExcept
192192
# Validate parameters dict
193193
if not isinstance(parameters, dict):
194194
return EvaluationException(
195-
message="Second element of 'ground_truth' tuple must be a dictionary of parameters.",
195+
message="Second element of 'expected_actions' (also accepted as 'ground_truth') tuple must be a dictionary of parameters.",
196196
blame=ErrorBlame.USER_ERROR,
197197
category=ErrorCategory.INVALID_VALUE,
198198
target=self.error_target,
@@ -202,7 +202,7 @@ def _validate_ground_truth(self, ground_truth: Any) -> Optional[EvaluationExcept
202202
for tool_name, params in parameters.items():
203203
if not isinstance(params, dict):
204204
return EvaluationException(
205-
message=f"Parameters for tool '{tool_name}' in 'ground_truth' must be a dictionary, got {type(params).__name__}.",
205+
message=f"Parameters for tool '{tool_name}' in 'expected_actions' (also accepted as 'ground_truth') must be a dictionary, got {type(params).__name__}.",
206206
blame=ErrorBlame.USER_ERROR,
207207
category=ErrorCategory.INVALID_VALUE,
208208
target=self.error_target,
@@ -212,7 +212,7 @@ def _validate_ground_truth(self, ground_truth: Any) -> Optional[EvaluationExcept
212212
# Validate list of tool names
213213
if len(ground_truth) == 0:
214214
return EvaluationException(
215-
message="'ground_truth' list cannot be empty.",
215+
message="'expected_actions' (also accepted as 'ground_truth') list cannot be empty.",
216216
blame=ErrorBlame.USER_ERROR,
217217
category=ErrorCategory.INVALID_VALUE,
218218
target=self.error_target,
@@ -229,7 +229,7 @@ def _validate_ground_truth(self, ground_truth: Any) -> Optional[EvaluationExcept
229229

230230
else:
231231
return EvaluationException(
232-
message="'ground_truth' must be either a list of tool names or a tuple of (tool_names_list, parameters_dict).",
232+
message="'expected_actions' (also accepted as 'ground_truth') must be either a list of tool names or a tuple of (tool_names_list, parameters_dict).",
233233
blame=ErrorBlame.USER_ERROR,
234234
category=ErrorCategory.INVALID_VALUE,
235235
target=self.error_target,

sdk/evaluation/azure-ai-evaluation/tests/unittests/test_common_validators.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,29 @@ def test_ground_truth_tuple_params_not_dict_raises(self):
513513
with pytest.raises(EvaluationException):
514514
validator.validate_eval_input(eval_input)
515515

516+
@pytest.mark.parametrize(
517+
"eval_input, canonical_name, alias_name",
518+
[
519+
# 'response'/'actions' missing (None).
520+
({"response": None, "ground_truth": ["search"]}, "response", "actions"),
521+
# 'response'/'actions' wrong type.
522+
({"response": "not a list", "ground_truth": ["search"]}, "response", "actions"),
523+
# 'ground_truth'/'expected_actions' empty -- the reported UX case.
524+
({"response": [{"role": "user", "content": "x"}], "ground_truth": []}, "ground_truth", "expected_actions"),
525+
# 'ground_truth'/'expected_actions' wrong type.
526+
({"response": [{"role": "user", "content": "x"}], "ground_truth": 123}, "ground_truth", "expected_actions"),
527+
],
528+
)
529+
def test_error_messages_name_both_canonical_and_alias(self, eval_input, canonical_name, alias_name):
530+
# Regression guard: user-facing validation errors must cite both the SDK input name and
531+
# its azureml-assets alias, so a caller never sees a parameter name they did not supply.
532+
validator = TaskNavigationEfficiencyValidator(error_target=TARGET)
533+
with pytest.raises(EvaluationException) as exc_info:
534+
validator.validate_eval_input(eval_input)
535+
message = str(exc_info.value)
536+
assert f"'{canonical_name}'" in message
537+
assert f"'{alias_name}'" in message
538+
516539

517540
@pytest.mark.unittest
518541
class TestMessagesOrQueryResponseInputValidator:

0 commit comments

Comments
 (0)