Commit b4edba7
Route BYO admin-connected judge models through the project Responses API (prompty judge path) (#48002)
* Prototype: BYO admin-connected judge models via project Responses API shim
AzureOpenAIGrader routes admin-connected judge models ('connection/deployment') through the Foundry project Responses API using a chat.completions-compatible shim (_byo_judge.py), so the platform resolves the connection. Judges cannot use chat.completions for BYO (404 DeploymentNotFound).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Route admin-connected (BYO) judge models through the project Responses API (prompty judges)
Enables the 18 prompty-based LLM-as-a-judge builtin evaluators (coherence, relevance,
fluency, groundedness, intent_resolution, similarity, retrieval, task_adherence,
task_completion, tool_call_accuracy/success, tool_input_accuracy, tool_output_utilization,
tool_selection, response_completeness, customer_satisfaction, deflection_rate, quality_grader)
to use admin-connected models referenced as "connection/deployment".
- _byo_judge.py: AsyncByoProjectResponsesClient shims chat.completions -> project Responses
API; _Usage adapts Responses token usage to chat shape.
- _common/utils.py: validate_model_config passes BYO configs through (they omit
azure_endpoint/azure_deployment).
- _legacy/prompty/_prompty.py: __call__ routes BYO configs to the async shim.
- Scope: score_model/label_model (AOAI graders) are NOT supported - reverted the grader
changes and removed the sync client.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Forward response_format as text.format in BYO judge Responses shim
The BYO prompty-judge shim dropped the chat.completions `response_format`
param, so admin-connected judge models were not held to JSON output mode
(coherence/relevance/etc. prompts request json_object). The Responses API
exposes the same capability under `text.format`, so translate it:
response_format={'type':'json_object'} -> text={'format':{'type':'json_object'}}
response_format json_schema (nested) -> text.format json_schema (flattened)
This restores enforced-JSON parity with the AzureOpenAI path and prevents
json.loads failures when a BYO model would otherwise drift to prose output.
Added 6 unit tests for _map_response_format and the _map_params wiring.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Fix black formatting in test_byo_judge.py
Collapse a multi-line AsyncByoProjectResponsesClient constructor call onto one
line (fits within the 120-char limit) to satisfy the azpysdk black check.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Fix: require both BYO markers (byo_model + project_endpoint)
is_byo_model_config returned True on byo_model alone, but the prompty branch
reads configuration['project_endpoint'] -> a partial config raised KeyError
instead of taking the normal (non-BYO) path. Require both markers, matching the
control-plane contract (BYO active iff both present). Adds coverage for the
byo_model-only and project_endpoint-only cases.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Fix: surface real finish_reason from the Responses result
The shim hardcoded finish_reason='stop', so a judge output truncated at
max_output_tokens was reported as complete and then failed json.loads downstream
with no signal. Map the Responses status/incomplete_details.reason to a
chat.completions finish_reason (max_output_tokens -> 'length', else 'stop').
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Fix: honor the per-request timeout in the BYO shim
with_options(timeout=...) was a no-op, so the prompty runner's per-request
timeout was dropped and a hung Responses call could block on openai's default
(~600s). Capture a concrete numeric timeout and forward it to responses.create
(openai's NotGiven sentinel, used when no timeout is set, is ignored).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Fix: compute total_tokens fallback in the usage adapter
_Usage reported total_tokens=0 when the Responses usage omits it. Fall back to
prompt_tokens + completion_tokens so token telemetry stays correct.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Fix: await-safe project client creation; drop dead responses property
get_openai_client() is synchronous in some azure-ai-projects versions and a
coroutine in others; the shim assumed sync, so a version bump would assign a
coroutine to self._client and break responses.create. Create the client in an
async helper that awaits it when needed. Also removes the unused 'responses'
property (leftover from the removed grader path).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Surface clear MissingRequiredPackage error when azure-ai-projects is absent
azure-ai-projects is an optional dependency (not in install_requires/extras),
so the BYO judge path's lazy `from azure.ai.projects.aio import AIProjectClient`
raised a raw ModuleNotFoundError when it was not installed. Wrap the import in
try/except and raise the SDK's MissingRequiredPackage with an actionable install
message, matching the existing optional-dependency idiom (azure-ai-inference,
promptflow, openai). Adds a unit test that simulates the missing package.
Addresses PR #48002 reviewer comment on _byo_judge.py.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Require BYO markers to be non-empty strings in is_byo_model_config
Truthiness alone accepted invalid configs such as {"byo_model": 1,
"project_endpoint": 2}: those markers are truthy, so the config was treated as
BYO, bypassed validate_model_config, and failed deep inside the project/OpenAI
client instead of producing the evaluator's normal validation error. Require
both markers to be non-empty strings before activating the BYO path. Extends the
is_byo_model_config unit test with non-string and empty-string cases.
Addresses PR #48002 reviewer comment on _byo_judge.py.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Support optional additional headers in AsyncByoProjectResponsesClient
ACA may forward additional request headers (e.g. correlation / telemetry
headers) when running continuous evaluations. Add an optional extra_headers
parameter to AsyncByoProjectResponsesClient that is forwarded on every Responses
API call via responses.create(extra_headers=...). The headers are copied on
construction so later caller mutations do not leak into the client. AsyncPrompty
threads them from the BYO model config (configuration["extra_headers"]) so the
end-to-end path stays unchanged for callers that do not supply headers.
Adds unit tests covering header forwarding and the no-headers default.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Preserve server-provided created timestamp in BYO chat-completion shim
_ChatCompletion always set `created` to the local wall-clock time, discarding
any server `created` timestamp on the Responses result. Preserve the server
value when it is a real number (isinstance-guarded so it falls back to now for
missing/non-numeric values), keeping the chat-completions shape faithful and
avoiding surprising timestamp drift in logs/traces. Adds a unit test.
Addresses PR #48002 reviewer comment on _byo_judge.py.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Preserve SDK default headers on the BYO prompty path
The BYO prompty branch forwarded only caller-supplied extra_headers to the
Responses API, dropping the SDK default headers (e.g. User-Agent) that the
non-BYO OpenAI/AzureOpenAI paths always set via default_headers. Merge
default_headers with the caller-supplied extra_headers (caller headers take
precedence) so BYO judge calls keep telemetry parity with the other paths.
Addresses PR #48002 reviewer comment on _prompty.py.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Cast BYO model config to Any in validate_model_config
The BYO branch cast the config to AzureOpenAIModelConfiguration, a differently
shaped TypedDict (no azure_endpoint/azure_deployment), which is misleading to
type checkers and readers. Cast to Any instead, accurately reflecting that BYO
configs bypass TypedDict validation.
Addresses PR #48002 reviewer comment on _common/utils.py.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Forward per-request extra_headers in the BYO chat-completions shim
Match the native OpenAI/AzureOpenAI client contract: headers passed per-request
to chat.completions.create(extra_headers=...) are now merged over the
client-level headers (per-request wins) and forwarded to responses.create as a
fresh copy, instead of being dropped by _map_params. Previously only the
client-level extra_headers were forwarded. Adds a unit test covering merge,
per-request precedence, and copy semantics (no mutation leak).
Addresses PR #48002 reviewer comments on _byo_judge.py and test_byo_judge.py.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Make test_evaluate_output_path xdist-safe (fixes sdist/whl CI legs)
test_evaluate_output_path[False] used a bare relative output_path
("eval_test_results.jsonl"), written and os.remove'd relative to the process
CWD. Under pytest-xdist (used by the whl/sdist test legs, unlike the serial
coverage leg) the CWD is shared across worker processes, so the write/exists/
remove of a relative file races with other workers and intermittently fails with
FileNotFoundError on cleanup. Adding the new BYO unit tests shifted the xdist
test distribution and deterministically exposed this pre-existing race.
chdir into the per-test tmpdir for the relative-path case so the bare filename
resolves to an isolated per-test directory, preserving the relative-path
behavior under test while removing the cross-worker race. The absolute-path
case is unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Fix BYO shim: read Responses created_at and disable client-side retries
- created timestamp: OpenAI Responses objects expose the server timestamp as
created_at, not created. The shim read created, so real responses always took
the local-time fallback (and the test mocked the wrong field). Read created_at
first, keep created as a fallback for older/mocked shapes.
- retries: AsyncPrompty disables the OpenAI client's built-in retries and runs
its own observable retry loop (max_retries=0). The BYO shim built the project
client without that, so each outer attempt could trigger hidden per-request
retries and exceed the intended budget. Pass max_retries=0 to get_openai_client
(kwargs are forwarded to the underlying AsyncOpenAI client).
Tests: cover created_at + created fallback via _ChatCompletion directly, and
assert get_openai_client is called with max_retries=0. Also add an AsyncPrompty
integration test that loads a prompty with a BYO configuration and verifies the
BYO branch routes through the project Responses API end-to-end (not just the shim
in isolation).
Addresses PR #48002 reviewer comments on _byo_judge.py and test_byo_judge.py.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Represent the BYO caller contract with a BYOModelConfiguration TypedDict
validate_model_config returned a BYO config cast to Any, which hid the newly
supported caller shape from type checkers. Add a BYOModelConfiguration TypedDict
(byo_model + project_endpoint, optional extra_headers) and include it in the
validate_model_config return union and the construct_prompty_model_config
parameter union, casting the BYO branch to it. Runtime behavior is unchanged
(TypedDicts are dicts; the cast is identity).
Addresses PR #48002 reviewer comment on _common/utils.py.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Add CHANGELOG entry for admin-connected (BYO) judge models
Document the new BYO model configuration (byo_model + project_endpoint), its
routing through the Foundry project Responses API, and its optional
azure-ai-projects requirement, under the upcoming 1.18.1 Features Added section.
Addresses PR #48002 reviewer comment.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Keep BYOModelConfiguration internal (_BYOModelConfiguration)
Admin-connected (BYO) model configs are constructed server-side by the
Foundry platform, not hand-authored by SDK users, and the feature is still
preview/gated. Rename the TypedDict to _BYOModelConfiguration and reframe its
docstring as internal plumbing so it is not advertised as a public, supported
type. It stays out of azure.ai.evaluation / __all__; the public model-config
union will be widened to advertise BYO once the feature reaches GA.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Drop BYO CHANGELOG entry; require azure-ai-projects>=2.0.0b1 for tests
Remove the admin-connected (BYO) Features Added entry from the 1.18.1 section
(the release entry will be added when the feature ships).
Bump the dev/test pin from azure-ai-projects<=1.0.0b10 to >=2.0.0b1: the BYO
judge path uses AIProjectClient.get_openai_client().responses (the Foundry
project Responses API), which the ACA data plane runs on 2.0.0b1, and Red Team
already documents azure-ai-projects>=2.0.0b1 as the minimum in TROUBLESHOOTING.md.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Make BYO inline comments concise
Tighten verbose inline comments across the BYO judge shim, prompty branch,
model-config validation, and their tests to one or two lines each, keeping the
essential rationale. Docstrings/function explanations are unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Revert azure-ai-projects test pin to <=1.0.0b10
The bump to >=2.0.0b1 broke the whl leg: (1) tests/e2etests/test_remote_evaluation.py
imports EvaluatorConfiguration/Evaluation/Dataset from azure.ai.projects.models,
which exist only in the 1.x API (removed in 2.x) -> collection ImportError; and
(2) azure-ai-projects 2.x requires openai>=2.8.0 and azure-core>=1.35.0, which
conflict with this SDK's pinned openai==1.108.0 / azure-core==1.31.0, making the
resolve unsatisfiable.
The test environment must stay on azure-ai-projects 1.x. BYO's 2.x runtime
requirement is a data-plane/ACA concern (already documented in TROUBLESHOOTING.md);
BYO unit tests mock azure-ai-projects, so they pass on 1.0.0b10.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Fix OTel event-logging test guard to detect the Events API submodule
The 22 TestLogEvents*/TestEmitEvalResultShutdown tests failed on the sk_ CI leg
with ModuleNotFoundError: No module named 'opentelemetry._events'. The App
Insights event-logging code they cover imports opentelemetry._events /
opentelemetry.sdk._events (the OpenTelemetry Events API, added in opentelemetry
1.26), but the MISSING_OPENTELEMETRY guard only checked the top-level
opentelemetry package. On the sk_ leg semantic-kernel pins an older opentelemetry
(<1.26) that lacks the Events API, so the guard reported present, the tests ran,
and the code crashed on the missing submodule.
Guard on the Events API submodules the code actually imports so the tests skip
cleanly where the API is unavailable (sk_ leg) and still run where it is present
(all other legs). Verified locally: 22 pass with the API present, 22 skip when it
is simulated absent.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
* Build Responses input via openai SDK EasyInputMessageParam
Address PR review (posaninagendra): construct the Responses API input with the
openai SDK's typed entity (openai.types.responses.EasyInputMessageParam) and
return the SDK's ResponseInputParam, instead of a hand-written dict. This keeps
the shim in sync with the SDK's input schema (type-checked against it) rather
than a hand-authored shape that drifts silently on SDK upgrades.
Runtime output is identical (EasyInputMessageParam is a TypedDict -> plain dict),
so existing input-shape assertions hold. The output side already reads the SDK's
typed Response via its output_text convenience aggregator. openai is a hard
dependency (openai>=1.108.0), so the import is safe. 29 byo tests pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b11202e6-6a52-4777-bed1-9c48730a63c3
---------
Co-authored-by: Shruti Iyer <you@example.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>1 parent cbc3f7f commit b4edba7
6 files changed
Lines changed: 723 additions & 31 deletions
File tree
- sdk/evaluation/azure-ai-evaluation
- azure/ai/evaluation
- _common
- _legacy/prompty
- tests/unittests
Lines changed: 268 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
Lines changed: 11 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| |||
222 | 223 | | |
223 | 224 | | |
224 | 225 | | |
225 | | - | |
| 226 | + | |
226 | 227 | | |
227 | 228 | | |
228 | 229 | | |
| |||
309 | 310 | | |
310 | 311 | | |
311 | 312 | | |
312 | | - | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
313 | 322 | | |
314 | 323 | | |
315 | 324 | | |
| |||
0 commit comments