Repository navigation
fix: report blocked Gemini prompts as content_filter - #2858
Open
jamesbuddrige wants to merge 10 commits into
Open
jamesbuddrige wants to merge 10 commits into
jamesbuddrige wants to merge 10 commits into
Conversation
When Gemini blocks a prompt before generation it returns promptFeedback.blockReason with no candidates. The OpenAI to GCP Vertex AI translator only converted candidates, so the client got a 200 with no choices field at all (choices is omitempty), with no sign that the prompt was rejected. OpenAI SDK clients then fail on response.choices[0], and retrying does not help because the block is deterministic for the same prompt. Return a single choice with finish_reason content_filter instead, in both the non-streaming and streaming paths. This matches how the translator already maps candidate-level SAFETY/BLOCKLIST/ PROHIBITED_CONTENT finish reasons. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: James Buddrige <104356223+jamesbuddrige@users.noreply.github.com>
✅ Deploy Preview for theagentrouter canceled.
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Contributor
Author
|
/retest |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
When Gemini on GCP Vertex AI blocks a prompt before generation, it returns
promptFeedback.blockReason(for exampleBLOCKLISTorPROHIBITED_CONTENT) and nocandidates. The OpenAI to GCP Vertex AI translator only converts candidates, andChatCompletionResponse.Choicesisomitempty, so the client receives a 200 with nochoicesfield at all:Nothing in that response says the prompt was rejected. OpenAI SDK clients then fail on
response.choices[0], and because the block is deterministic for a given prompt, generic retry logic loops on it. We hit this in production: one blocked prompt was retried thousands of times until the caller's activity timed out.This change returns a single choice with
finish_reason: content_filter(assistant role, no content) when the prompt is blocked, in both the non-streaming and streaming paths. That matches how the translator already maps candidate-levelSAFETY/BLOCKLIST/PROHIBITED_CONTENTfinish reasons, and it is what OpenAI-compatible clients already check for.Tests cover the non-streaming and streaming translation using the response shape Vertex returns for a blocked prompt, plus the helper's edge cases (no feedback, empty or unspecified reason, block reason alongside candidates). The new test cases fail without the change.
make precommitand the translator package tests pass.Special notes for reviewers (if applicable)
I used Claude Code (generative AI) to help investigate the issue and write this change. I have reviewed and understand the code and will handle review feedback.
An alternative would be to turn the block into a 4xx error. I did not do that here because translators can only rewrite the response body, so changing the status would mean extproc changes to status handling, metrics and spans. A content_filter choice keeps the fix inside the translator and consistent with the existing candidate-level handling.