Handle OpenAI refusals and missing message content - #496
Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
1e98e04 to
f17989f
Compare
|
Your branch is 1 commits behind git fetch origin main
git merge origin/main
git pushNote: Enable "Allow edits by maintainers" to allow automatic updates. |
|
Your branch is 1 commits behind git fetch origin main
git merge origin/main
git pushNote: Enable "Allow edits by maintainers" to allow automatic updates. |
…orting success _process_single_prompt returned ScoredOutput(score=1.0, output=response.text) unconditionally. google.genai's response.text returns None (not an exception) when: the prompt was blocked before generation (prompt_feedback.block_reason set, no candidates), a candidate stopped for a non-STOP finish_reason (SAFETY, RECITATION, PROHIBITED_CONTENT, etc.) with no content, or the response contains only non-text parts. Every one of these was silently reported as a successful empty extraction, discarding the actual block/refusal reason. This is the same class of bug already fixed for the OpenAI realtime provider in google#491/PR google#496 -- that issue explicitly noted "The Gemini realtime path has the same class of gap... worth folding the same guard in there too," but PR google#496 only touched openai.py, leaving this side unaddressed. Verified directly against the installed google-genai 2.11.0 SDK source (GenerateContentResponse._get_text) that None is returned, not raised, in all three cases. Fix: check output_text is None after reading response.text, and raise InferenceRuntimeError with a diagnostic built from whichever information is available -- prompt_feedback.block_reason(+message) for prompt-level blocks, candidates[0].finish_reason for candidate-level stops, or a generic message otherwise. Also re-raises InferenceRuntimeError before the generic retry handler, matching the OpenAI fix's intent: retrying a safety-blocked prompt with identical content won't produce a different result. Added 5 tests to GeminiRefusalHandlingTest in tests/provider_schema_test.py: successful text, prompt-level block, candidate-level non-STOP finish reason, no-diagnostic-available fallback, and a check that a refusal is not retried. All 4 error-path tests confirmed to fail against the pre-fix code (git stash isolation of just gemini.py). Full verification: - tests/provider_schema_test.py: 32 passed (up from 27 baseline, exactly the 5 new tests), same 1 pre-existing unrelated failure as clean upstream/main. - tests -k gemini: 137 passed, 7 skipped (live API creds unavailable), 0 failed. - Full suite (-m "not live_api"): 673 passed (up from 668 baseline), same 39 pre-existing unrelated failures (OpenAI kwargs passthrough) as clean upstream/main -- no regressions. - tox -e format, tox -e lint-src, tox -e lint-tests (10.00/10): all clean.
|
Your branch is 1 commits behind git fetch origin main
git merge origin/main
git pushNote: Enable "Allow edits by maintainers" to allow automatic updates. |
Description
Fixes #491.
The realtime OpenAI provider previously returned
ScoredOutput(score=1.0, output=None)whenmessage.contentwasmissing, causing refusal or content-filtered responses to be reported
as successful extractions.
This change aligns the realtime provider with the batch provider by:
InferenceRuntimeErrorwhenmessage.contentisNone.message.refusalis present.How Has This Been Tested?
message.refusal)message.contentResult:
Checklist