Skip to content

fix: report blocked Gemini prompts as content_filter - #2858

Open
jamesbuddrige wants to merge 10 commits into
theagentrouter:mainfrom
Digital-Insight-Technologies-Ltd:fix/gcp-vertexai-prompt-block
Open

jamesbuddrige wants to merge 10 commits into
theagentrouter:mainfrom
Digital-Insight-Technologies-Ltd:fix/gcp-vertexai-prompt-block

Conversation

@jamesbuddrige

Copy link
Copy Markdown
Contributor

Description

When Gemini on GCP Vertex AI blocks a prompt before generation, it returns promptFeedback.blockReason (for example BLOCKLIST or PROHIBITED_CONTENT) and no candidates. The OpenAI to GCP Vertex AI translator only converts candidates, and ChatCompletionResponse.Choices is omitempty, so the client receives a 200 with no choices field at all:

{"id":"...","created":1791992911,"model":"gemini-2.5-flash","object":"chat.completion","usage":{"prompt_tokens":7771,"total_tokens":7771,...}}

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-level SAFETY / BLOCKLIST / PROHIBITED_CONTENT finish 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 precommit and 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.

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>
@jamesbuddrige
jamesbuddrige requested a review from a team as a code owner October 14, 2026 16:36
@netlify

netlify Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for theagentrouter canceled.

Name Link
🔨 Latest commit d148b6c
🔍 Latest deploy log https://app.netlify.com/projects/theagentrouter/deploys/6aeddf048db39265f424e47c

@codecov

codecov Bot commented Oct 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@jamesbuddrige

Copy link
Copy Markdown
Contributor Author

/retest

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant