Surface a Gemini prompt-block (PromptFeedback.BlockReason) as ErrorContent instead of an empty success#743
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the Gemini provider to correctly surface “prompt blocked” responses (where Gemini returns zero candidates and instead sets PromptFeedback.BlockReason) as an error signal (message.ErrorContent) rather than emitting a successful-but-empty response update.
Changes:
- Added
promptBlockedContenthelper to translateresp.PromptFeedback.BlockReasoninto*message.ErrorContent(with a fallback message when absent). - Invoked the helper in both non-streaming and streaming response handling so blocked prompts are visible to callers.
- Added non-streaming and streaming tests to assert blocked prompts produce
ErrorContentwithErrorCode == "SAFETY".
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| provider/geminiprovider/agent.go | Adds and wires promptBlockedContent into both streaming and non-streaming response assembly. |
| provider/geminiprovider/agent_test.go | Adds tests (and a small helper) validating blocked prompts surface as ErrorContent in both modes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This comment has been minimized.
This comment has been minimized.
When Gemini blocks a prompt for a content or safety policy violation it returns zero candidates and sets PromptFeedback.BlockReason. All content extraction was gated behind len(resp.Candidates) > 0, so a blocked prompt yielded a successful, empty ResponseUpdate and the caller could not tell a policy block from a legitimately empty completion. Emit a message.ErrorContent carrying the BlockReason as ErrorCode and the BlockReasonMessage (or a default) in both the non-streaming and streaming paths, matching the .NET/Python providers which surface PromptFeedback as an error rather than an empty success.
5465fe1 to
faffcf9
Compare
Parity Review — Cross-Repo ConsistencyResult: No blocking parity issue, but one upstream alignment note. What changedThis PR adds Upstream parity checkPython ( The PR description states this change "aligns with the .NET/Python Gemini providers, which surface PromptFeedback as an error signal" — that claim is not currently accurate for the Python provider. The Go change is actually ahead of Python in this regard. No .NET Gemini provider was found in RecommendationThe Go behavior is reasonable and a genuine improvement. To keep cross-SDK semantics aligned, the Python Gemini provider should receive a parallel fix — likely in Label actionNo new exported Go API was added, so Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "awmgmcpg"See Network Configuration for more information.
|
What
When the Gemini API blocks a prompt for a content/safety policy violation, the response contains zero candidates and instead populates
resp.PromptFeedback.BlockReason(with an optionalBlockReasonMessage). The provider gated all content extraction behindif len(resp.Candidates) > 0in both the non-streaming and streaming paths and never inspectedPromptFeedback, so a blocked prompt produced a successful, emptyResponseUpdate. Callers had no way to distinguish a policy-blocked prompt from a legitimately empty completion.This adds a small
promptBlockedContenthelper that, whenPromptFeedback.BlockReasonis set, appends amessage.ErrorContentwith:ErrorCode= the block reason (e.g.SAFETY)Message=BlockReasonMessage, or"prompt blocked by Gemini content filter"when the API omits oneIt is invoked right after the candidate-extraction block in both paths; in the streaming loop the content is appended before the chunk is yielded so the block surfaces on the emitting update.
Why
This mirrors the existing
ErrorContentprecedent already used in this provider for non-OK code-execution outcomes, and aligns with the .NET/Python Gemini providers, which surfacePromptFeedbackas an error signal rather than silently returning an empty success. It is distinct from the candidateFinishReasonpath (different field, different failure mode).Tests
TestPromptBlocked_NonStreamingandTestPromptBlocked_Streaming(in the canonicalagent_test.go, reusing the existinghttptestharness) marshal a response with empty candidates andPromptFeedback{BlockReason: SAFETY, BlockReasonMessage: blocked}and assert the collected response contains an*message.ErrorContentwhoseErrorCode == "SAFETY"instead of an empty successful update.go build ./...,go vet ./provider/geminiprovider/..., andgo test ./provider/geminiprovider/...all pass.