Skip to content

Fix Tool Call Success scoring, binary_aggregate and Decision input order, and name Decision failure reasons - #49804

Merged
bonanza89 merged 19 commits into
Azure:mainfrom
bonanza89:users/aqardahji/token-reuse-and-tool-call-success
Nov 4, 2026
Merged

bonanza89 merged 19 commits into
Azure:mainfrom
bonanza89:users/aqardahji/token-reuse-and-tool-call-success

Conversation

@bonanza89

@bonanza89 bonanza89 commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Fixes for azure-ai-evaluation 1.18.11 (unreleased). The Tool Call Success scoring bug also affects chat evaluators in 1.18.10, the binary_aggregate behavior is older, and the Decision input order bug has affected Decision evaluations since 1.18.10, the first release with Decision models.

Token reuse moved to its own PR, #49815. The OpenAI Decisions protocol stays in #49695.

1. Tool Call Success scoring

Problem: all 14 example outputs in tool_call_success.prompty had a trailing comma after "failed_tools": "", which is invalid JSON. gpt-4.1 and gpt-4o then put score and status inside properties, and float(llm_output.get("score", 0)) scored successful tool calls 0. On main, gpt-4o scored all 3 successful tool-call message rows 0, and gpt-4.1 and gpt-4o scored all 3 successful tool traces 0.

Fix:

  • The examples are valid JSON. With this, every chat judge in the live runs below returned a top-level score.
  • score, status and reason fall back to properties when the top-level value is missing.
  • A score other than 0 or 1 raises a system-classified EvaluationException, as _ToolInputAccuracyEvaluator does, instead of becoming 0. Values that already read as 0 or 1, such as True, "1" and 1.0, still do.

2. Tool Call Success applicability

Problem: on main, a response with no tool calls went to the judge, and gpt-4o-mini, gpt-5-mini and o3-mini scored text-only responses as passed.

Fix: a list of messages that holds only text or MCP approvals, with no tool calls, results or [TOOL_CALL]/[TOOL_RESULT] traces, is now not applicable without a judge call, for chat and Decision models. A tool_calls, function_call or tool_call_id field counts only when it holds something: None, "", [] and {}, as in OpenAI's model_dump() of a reply without tool calls, are treated as absent, and any other value, such as [None] or False, still counts. A role or content type that is not a string, such as a list or a dict, is left to the judge or to the input validator, as before. The decision is made before preprocessing. Strings, text that carries tool traces, and any other content still go to the judge.

3. Decision failure reasons

Problem: a Decision request the provider refused with HTTP 422 failed as "The decision model returned HTTP 422." The provider states the reason only in a detail sentence, so callers could not tell a context window overflow from an invalid question.

Fix:

  • decision_model.error_kind in the error's properties names a recognized reason, _decision_error_metadata keeps it on failed result rows, and the message adds it, for example The decision model returned HTTP 422 (context_window_exceeded).
    • context_window_exceeded: the evidence and one question exceed the model's context window.
    • request_token_limit_exceeded: the evidence and all questions exceed the model's request limit.
    • question_limit_exceeded and invalid_question.
    • request_bytes_exceeded: a request over max_request_bytes or max_state_question_bytes, which is not sent.
  • A reason is read from the fixed opening of the provider's sentence, within its first 1,024 characters, so a quoted question identifier cannot change it. The sentence itself is never surfaced.
  • The Decision retry policy no longer passes retry_on_status_codes. azure-core adds its defaults to that list, and they already include every status it named, so retries are unchanged.

4. binary_aggregate

Problem: evaluate() reported <evaluator>.binary_aggregate as the passes divided by all rows, so not applicable and errored rows counted as failures, while the mean score leaves them out. Rows that passed, failed and were not applicable gave a mean of 0.5 and a binary_aggregate of 0.33.

Fix: binary_aggregate is the passes divided by the rows whose result is pass or fail. With several _result columns, the last one decides, as before. When that column has no pass or fail rows, for example a custom evaluator whose results use other values, the evaluator has no binary_aggregate instead of 0.0, as one without scores has no mean. Azure OpenAI graders' <grader>.pass_rate uses the same denominator, although it reports 0.0 when nothing was judged. The evaluation service stores this metric as reported and does not read it. The metric on a run's result asset now agrees with the Foundry portal's pass-rate card, which counts only passed and failed rows: a Tool Call Success run with one passed, one failed and one not applicable row reads 0.5, where it read 0.33.

5. Decision input order

Problem: EvaluatorBase._get_all_singleton_inputs flattened the overloads' input names through a set, so _convert_kwargs_to_eval_input built each evaluator's inputs in the set's order, which varies with the process's hash seed. An evaluator that sends these inputs to a Decision model unchanged, as the Microsoft Foundry catalog's task completion, tool output utilization, output quality and tool use quality evaluators do, listed the same fields in a different order in each process, and the Decision model could answer the same row differently: one task completion row scored P(true) 0.18 in one process and 0.84 in another.

Fix: the names keep the order in which the overloads declare them, so the inputs, and the state of a Decision request that sends them unchanged, have the same order in every process. For those four catalog evaluators the order is query, response, tool_definitions, the order this package's own task completion and tool output utilization evaluators already send. The built-in evaluators in this package build their Decision state in a fixed order and are unchanged, and chat prompts place inputs by name. Catalog evaluators pick this up from the SDK installed in the evaluation worker.

Validation

Rows marked as measured before the split ran while this PR also carried token reuse, which changes token counts, not results.

Check main This PR
tests/unittests 2338 passed, 15 skipped 2481 passed, 15 skipped
Decision input order, offline with the catalog assets: state order sent across 8 hash seeds by task completion, tool output utilization, output quality and tool use quality 4 distinct orders for each One order for each: query, response, tool_definitions
Decision input order, live on Decision-1 through Vienna master's evaluation worker with registry assets, 4 rows per affected asset, hash seeds 0 to 3 3 orders per asset, and borderline rows moved with the process: task completion 0.08 to 0.62, output quality 0.06 to 0.49, tool use quality 0.56 to 0.95 Byte-identical requests in every process; probabilities within 0.03
Decision input order, labeled smoke on Decision-1, 12 rows per asset (6 pass, 6 fail), one run each Task completion 11 of 12 with each of the 4 orders old processes produced; output quality 12 of 12 Task completion 10 of 12: one row labeled fail passes only with the declared order (0.85, against 0.20 to 0.68 with the old orders); output quality 12 of 12. The sample is too small to rank orders; see the larger study below
Decision input order, larger labeled study on Decision-1, run after the push: task completion 60 rows (30 pass, 30 fail, 36 of them borderline) and tool use quality 32 rows, each scored with every order old processes produced and twice with the declared order Task completion 58 of 60 with each of the 4 old orders, mean probability of the expected label 0.937 to 0.950; tool use quality 29 to 32 of 32 Task completion 58 of 60, mean probability 0.942; paired against the average old order, -0.003 (95% interval -0.026 to 0.014) and no accuracy difference. Tool use quality 30 of 32; its two misses are rows whose tool arguments resolve "tomorrow" without a stated date, which every order passes once the date is given. The order flips 3 of 60 borderline task completion rows, and the declared order is right on 2 of them. No sign that the declared order is worse
Live Tool Call Success applicability and binary_aggregate on gpt-4o-mini and Decision-1, 16 checks: the review's three null or empty-field messages, an OpenAI model_dump() text reply, OpenAI tool calls that succeed and fail, and evaluate() over pass, fail and text rows and over text-only rows 4 of 16, measured on 4d7a193, whose _evaluate.py matches main and whose earlier shortcut these inputs bypassed: every empty-field and text reply reached the judge, gpt-4o-mini passed the text reply in evaluate(), binary_aggregate was 0.67 on gpt-4o-mini and 0.33 on Decision-1 (means 0.67 and 0.5), and 0.0 for text-only rows 16 of 16: empty-field and text replies are not applicable with no judge call, OpenAI tool calls still reach the judge and pass or fail as expected, binary_aggregate is 0.5 and equal to the mean on both judges, and text-only rows produce neither metric and no judge call
Malformed messages and normal inputs for Tool Call Success, live on gpt-4o-mini and Decision-1, 28 checks: a list or dict role, a dict or list content type, MCP approvals, empty tool-call fields, OpenAI text and tool calls, and evaluate() with one malformed row among good rows Before the guard in 0028aa3, 22 of 28: a list or dict role raised TypeError 28 of 28: a list or dict role reaches the judge, a dict or list content type gets the input validator's user error with no judge call, the other inputs are unchanged, and the other rows of evaluate() are unaffected
Every helper this PR adds, and the Tool Call Success call path, with 20,000 random JSON-like inputs each (lists, dicts, None, NaN, integers up to 400 digits, nested values) Not run No exception other than EvaluationException. Checked after the merge: a score of up to 4,300 digits gets EvaluationException, and a longer one raises ValueError while the error message is built (before this PR, OverflowError). A judge's JSON output can't hold such a number, since json.loads rejects integers over 4,300 digits
Live Decision-1 refusals: a window overflow, a request over the token limit, 256 questions, empty instructions, a question id that quotes the window sentence, a request over max_request_bytes, and CoherenceEvaluator with a window overflow Each failed as "HTTP 422" with no reason Each named with its kind; the quoted id stays invalid_question; the request over max_request_bytes made no attempt
Live failed later conversation turn on Decision-1, through _decision_error_metadata No reason error_kind context_window_exceeded kept with result_usage_scope reported_turns_only
Live Tool Call Success, measured before the split: 7 judges (gpt-4.1, gpt-4o, gpt-4o-mini, gpt-5-mini, o3-mini, gpt-6-luna, Microsoft-Decision-1), 5 cases, 3 runs each gpt-4o scored successful tool calls 0; gpt-4.1 and gpt-4o scored successful traces 0; three judges passed text-only responses Every run matched the expected result, except 2 of 105 Decision-1 runs that hit HTTP 429; the other runs of those cases matched
Live built-in evaluators, measured before the split: the 15 Decision-capable built-ins and QAEvaluator on Decision-1, gpt-4o-mini and gpt-6-luna Baseline Same result and status for every pair, except luna's QAEvaluator similarity, which main skipped and this run scored; two Decision-1 429s matched main when run alone
Evaluation worker at every level, in process: Vienna master's worker code with registry assets on Decision-1 and gpt-4o-mini; turn level with strings, context, ground truth and tool messages; conversation level; trace level from GenAI, hosted, OpenInference and OpenLLMetry spans at turn and conversation level; Decision failures Baseline Same outcomes, apart from judge variation confirmed by reruns with identical requests and one Decision task completion row whose verdict follows the input order (pass with the declared order, fail with the order that process used before); failed Decision rows add error_kind, and binary_aggregate changes as described in section 4
Vienna evaluation worker tests (13.7k) on master bade101a with this wheel 3 failures and 2 collection errors, unrelated to this PR Identical outcomes
azureml-assets catalog Decision and behavior tests (test_decision_models, test_evaluators_behavior), 4741 tests 10 regex_match failures and 4 audio module errors Identical outcomes
Vienna evaluation worker Decision qualification gate Not run 1155 passed on Vienna 2640626; the same 2 guard tests fail as on any build since #49781
Hosted smoke, local RAISvc stack with Vienna 2640626's worker and the on-behalf-of exchange not bypassed: Tool Call Success through the portal route on gpt-4o-mini and Decision-1 with pass, fail and text-only rows; a Decision-1 coherence run with one context-window overflow row; and the five catalog evaluators whose Decision input order changed (task_completion, tool_output_utilization, output_quality, tool_use_quality, deflection_rate), registry and project package, on Decision-1 On 4d7a193, whose _evaluate.py matches main: the result asset's binary_aggregate was 0.33 for the Tool Call Success runs and 0.67 for the overflow run 13 of 13 runs as expected. Tool Call Success is pass, fail and not applicable on both judges, and the result asset's binary_aggregate is 0.5, equal to the mean and to the portal's 1 of 2. The overflow row fails with the 422 context-window message, and binary_aggregate is 1 for the two scored rows. All 10 order-affected catalog runs pass. One package run was repeated after the local on-behalf-of token service returned 403 while saving its status, as also happened once on 4d7a193
Hosted, measured before the split: local RAISvc stack, 96 registry (portal route), project package and rubric cases on Decision-1, gpt-4o-mini and gpt-6-luna Not run 95 of 96. The remaining failure is luna marking the good Similarity row not applicable, a judge behavior also seen with main's SDK and with the released 1.18.10 worker
Hosted, measured before the split: local RAISvc stack with the on-behalf-of exchange not bypassed; registry built-ins, project packages, rubrics, F1 and size cases Not run 26 of 26. A window overflow is the caller's 422 named context_window_exceeded; a request over the worker's 256,000-byte limit is never sent

Not in this PR

  • Token reuse for Decision and chat requests (Reuse credential tokens for Decision and chat model requests #49815).
  • The evaluation worker's uptake of this release: its two guard tests change with it.
  • The catalog's builtin.tool_call_success asset, which the portal route runs, keeps its own copy of this evaluator; the Tool Call Success fixes, including the empty-field rule, reach the portal only with a matching azureml-assets change.
  • The OpenAI Decisions protocol (Add Azure OpenAI Decisions protocol and evaluation metrics to evaluation runners #49695).
  • _drop_mcp_approval_messages drops a whole message that mixes an MCP approval with a tool call. This is existing behavior shared by every evaluator; the judge still gets the remaining messages.
  • Other binary evaluators that default a missing score to 0. Their prompts are valid JSON, and no nested output has been seen from them.

…ss scoring

Token reuse (from Azure#49695, without the OpenAI Decisions protocol):
- Decision requests, and Azure OpenAI chat requests that use Microsoft Entra ID, reuse a credential's token until
  five minutes before it expires instead of requesting one per request.
- Concurrent requests that share a synchronous credential wait for one token request, off the event loop; requests
  queued behind a token that is not cached use it.
- A 401 drops the token, and an acquisition that began before the rejection is not stored.
- The default chat credential shares one credential and token across concurrent first requests.
- A Decision credential that returns no string token is a user error, not a timeout.

Tool Call Success:
- The prompt's 14 example outputs are now valid JSON, which stops gpt-4.1 and gpt-4o nesting score and status
  in properties and scoring successful tool calls 0.
- score, status and reason fall back to properties, and a score other than 0 or 1 raises a system error instead of
  becoming 0.
- A list of messages without tool calls (only text or MCP approvals) is not applicable without a judge call;
  strings, tool traces in text and any other content still go to the judge.
@bonanza89
bonanza89 requested a review from a team as a code owner November 3, 2026 05:15
Copilot AI balanced review requested due to automatic review settings November 3, 2026 05:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error: Your billing is not configured or you have Copilot licenses from multiple standalone organizations or enterprises. To use premium requests, select a billing entity via the GitHub site, under Settings > Copilot > Features.

@github-actions github-actions Bot added the Evaluation Issues related to the client library for Azure AI Evaluation label Nov 3, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
10 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@sakoll Sashank Kolli (sakoll) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the diff and relevant unchanged context at dc10282. No actionable, high-confidence findings.

This was a static review; I did not independently run the package unit or live tests.

…ant retry list

Decision requests now request tokens for https://ai.azure.com/.default, the scope
Azure OpenAI chat requests use, so a credential shared by chat and Decision
evaluators needs one token instead of one per scope. Decision endpoints accept
both audiences. The explicit retry status list is removed: azure-core retries
those statuses by default and unions an explicit list with its defaults, so
retry behavior is unchanged. _get_token documents its absolute monotonic
deadline, and the token cache module records why get_bearer_token_provider is
not used in its place.
Copilot AI balanced review requested due to automatic review settings November 3, 2026 17:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Shared cached token-acquisition failures are replayed with incorrect exception types across chat and Decision callers, misclassifying errors and preventing expected credential retries.

1 open issue

Review effort: Balanced

… authentication failures with waiting requests

A failed Decision request names a recognized reason in decision_model.error_kind of the error's properties, which is
kept on failed result rows, and in the error message, for example "The decision model returned HTTP 422
(context_window_exceeded).". The provider states these reasons for an HTTP 422 only in text, which is never exposed:
context_window_exceeded, request_token_limit_exceeded, question_limit_exceeded and invalid_question, recognized by the
sentence's fixed opening so a quoted question identifier cannot change the kind. A request over max_request_bytes or
max_state_question_bytes, which is not sent, has the kind request_bytes_exceeded.

Requests that waited on a token request now share its failure only when it failed authentication, as on the chat
path; after any other failure each waiting request makes its own attempt, so a transient failure fails only the
request that hit it.
Copilot AI balanced review requested due to automatic review settings November 3, 2026 19:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

A shared token-acquisition failure causes Decision waiters to reuse raw chat authentication diagnostics, exposing credential details and incorrectly classifying the error instead of applying Decision-specific sanitization.

1 open issue
1 previously missed issue

In code that hasn't changed since last review

Medium severity Sanitize shared token failures for each waiting caller

sdk/​evaluation/​azure-ai-evaluation/​azure/​ai/​evaluation/​_legacy/​_common/​_token_cache.py:211

When a chat token acquisition fails authentication while a Decision request waits on the same credential and scope, the Decision waiter reconstructs EvaluationException from chat's raw message rather than its own sanitized error metadata. This produces an InternalError with unknown blame/category and exposes credential diagnostics instead of the expected user-classified token error. Store a caller-independent failure description, apply the waiting caller's formatter when replaying it, and add a mixed chat/Decision regression test.

Review effort: Balanced

Ahmad Qardahji added 2 commits November 3, 2026 11:51
… within each kind of request

Decision endpoints accept both audiences, but the hosted evaluation service selects the identity for Decision calls by token audience: for https://ai.azure.com it forwards the caller's own token, and for the Cognitive Services audience it exchanges that token or uses the project managed identity. With the chat scope, single-tenant projects would call Decision as the end user instead of the project managed identity, and the service's worker tests fail. Decision requests go back to https://cognitiveservices.azure.com/.default and their own cached token, and the redundant retry status list stays removed.

The token cache records a failure per failure formatter and exception type, with the attempt that ended in it, so a request shares only a failure of its own kind that ended after it queued, and a successful token request clears them. A request could otherwise replay another kind's failure with its own exception type: a Decision description raised as a chat ClientAuthenticationError, or a chat authentication failure raised as an EvaluationException with an unknown blame.

A credential that implements AsyncTokenCredential but whose get_token is a plain function returning an awaitable was asked on a worker thread, where a get_token that needs the running event loop failed. A credential whose get_token is a coroutine function, or that implements AsyncTokenCredential, is now asked on the caller's event loop, as released chat requests did.
The package enforces black, and these lines were not formatted. No code changes.
Copilot AI balanced review requested due to automatic review settings November 3, 2026 20:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Null or empty tool-call fields bypass the no-tool-call check, allowing text-only messages to reach a judge and potentially pass instead of being marked not_applicable.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Treat null or empty tool-call fields as absent

sdk/​evaluation/​azure-ai-evaluation/​azure/​ai/​evaluation/​_evaluators/​_tool_call_success/​_tool_call_success.py:323

A text-only message with tool_calls: null, tool_calls: [], or function_call: null bypasses the no-tool-call check solely because the key exists. These inputs pass the conversation validator and still reach the chat or Decision judge, which can incorrectly mark them as passed instead of not_applicable. Treat null or empty call fields as absent while retaining the check for populated calls.

1 resolved since last review

Review effort: Balanced

Ahmad Qardahji added 2 commits November 3, 2026 12:45
The Analyze job's black check failed on this file. No code changes.
…rotocol on a worker thread

A synchronous credential whose class also implements the asynchronous context manager protocol matched the structural AsyncTokenCredential check and was asked on the event loop, which blocked the loop, lost the shared token request and broke a get_token that runs its own event loop. A credential that implements the synchronous context manager protocol is again asked on a worker thread. A plain get_token that needs the caller's running loop must belong to an AsyncTokenCredential, as the CHANGELOG and docstrings now state, and fetch documents that its failure formatter and exception type key the shared failures and must be module-level.
Copilot AI balanced review requested due to automatic review settings November 3, 2026 20:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Human assessment is needed because the new lock breaks default-backed evaluator serialization, while transient authentication outages can fail every queued evaluation.

No open issues 🎉

Review effort: Balanced

Token reuse for Decision and chat requests is not part of the Tool Call
Success and Decision failure reason fixes, so it moves to a separate pull
request. This restores the base _prompty.py and _async_token_provider.py,
removes _token_cache.py and its tests, and returns DecisionModelRunner to
requesting a token for each call, with the Cognitive Services scope as
before. The error_kind reasons, the retry policy cleanup and the Tool Call
Success fixes are unchanged.
Ahmad Qardahji added 6 commits November 3, 2026 15:45
A tool_calls, function_call or tool_call_id field that is None or an empty
string, list or dict no longer sends a text-only response to the judge.
OpenAI's model_dump() writes tool_calls: None and function_call: None on every
reply, so those replies reached the chat or Decision judge and could pass.
Populated fields and values of any other type still go to the judge.
evaluate() divided an evaluator's passes by every row, so not applicable and
errored rows counted as failures and binary_aggregate disagreed with the mean
score. It now divides by the rows that passed or failed, and an evaluator
without such rows gets no binary_aggregate, as one without scores gets no mean.
_get_all_singleton_inputs flattened the overloads' input names through a
set, so an evaluator's converted inputs followed the set's order, which
varies with the process's hash seed. An evaluator that sends these inputs
to a Decision model unchanged, as several Microsoft Foundry catalog
evaluators do, could therefore list the same fields in a different order
in each process, and the Decision model could answer the same input
differently. The names now keep the order in which the overloads declare
them. The built-in evaluators in this package build their Decision state
in a fixed order and are unchanged.
An evaluator with several _result columns reports its last one, as before.
A last column without pass or fail rows now leaves no binary_aggregate
instead of an earlier column's rate. The CHANGELOG says only pass and fail
count, which also affects custom evaluators that return other values.
Tests cover all-fail and boolean result columns, several result columns,
and OpenAI tool calls on the Decision judge.
python -c puts the working directory first on the path, so in CI jobs that
install the built wheel and run pytest from the package directory, the
child processes imported the checkout instead of the installed package.
They now start in an empty temporary directory, and three seeds keep the
test short.
Copilot AI balanced review requested due to automatic review settings November 4, 2026 00:59
@bonanza89 bonanza89 changed the title Fix Tool Call Success scoring and name Decision failure reasons Fix Tool Call Success scoring, binary_aggregate and Decision input order, and name Decision failure reasons Nov 4, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are identified for the scoring, aggregation, and Decision-model diagnostic changes.

No open issues 🎉

Review effort: Balanced

@sakoll Sashank Kolli (sakoll) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed d100061. The previously reported P2 applicability issue is fixed: null/empty tool-call fields no longer trigger a judge call, while populated calls remain eligible. No additional actionable, high-confidence findings in the current diff, including the pass-rate denominator and deterministic input-order changes.

Review included isolated source-level checks. Full package unit tests, pandas integration tests, and live tests were not independently run.

Copilot AI balanced review requested due to automatic review settings November 4, 2026 01:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new applicability check raises TypeError for unhashable message type or role values, bypassing the existing formatter fallback instead of leaving unsupported formats for the judge.

1 open issue

Review effort: Balanced

The no-tool-call check compared role and content type values with sets,
so an unhashable value, such as a list role or a dict type, raised
TypeError. A value that is not a string now fails the check, so the
message goes to the judge or the input validator, as it did before the
check existed.
Copilot AI balanced review requested due to automatic review settings November 4, 2026 02:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues block the scoring, aggregation, and Decision-model updates.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@bonanza89
bonanza89 enabled auto-merge (squash) November 4, 2026 02:43
@bonanza89
bonanza89 merged commit ce8639d into Azure:main Nov 4, 2026
26 checks passed
bonanza89 pushed a commit to bonanza89/azure-sdk-for-python that referenced this pull request Nov 4, 2026
… the judge

Since Azure#49804, a list of text messages was reported as not applicable without a judge call unless its text held this evaluator's [TOOL_CALL] or [TOOL_RESULT] markers. A transcript in another text format, such as ReAct Action and Observation lines or <tool_call> tags, was left out of the pass rate, together with its failed tool calls.

Text that contains tool_call, tool_result, tool_response, function_call or function_result in any letter case, Llama's <|python_tag|> or <function= tags, the gpt-oss <|call|> token, or a line that starts with a label such as Action:, Observation:, Tool call: or Tool result: now counts as a tool transcript. Such a list goes to the judge model as its text, with each message's role and every tool definition, as a string does, because Decision-1 answered not applicable for ReAct steps inside a list of messages but judged the same text correctly.

Also corrects the 1.18.11 CHANGELOG entries for error_kind, the score check and binary_aggregate.
bonanza89 pushed a commit to bonanza89/azure-sdk-for-python that referenced this pull request Nov 6, 2026
Keeps both sides' additions where Azure#49804 and this change touched the same lines of _model_runners.py
and test_decision_model_support.py, and moves the token reuse entry from the released 1.18.11 section
to a 1.18.12 section with the version bump.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Evaluation Issues related to the client library for Azure AI Evaluation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants