fix: map Bedrock content_filter/guardrail_intervened stop reasons to incomplete status with content_filter incomplete details - #6103
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (5)
📝 WalkthroughSummary by CodeRabbit
WalkthroughBedrock content-filter and guardrail-intervened stop reasons now map to incomplete Responses with ChangesBedrock content-filter mapping
Estimated code review effort: 3 (Moderate) | ~20 minutes Mergeability Score: ⚪ Minimal · up to This change consistently reports Bedrock content-filtered and guardrail-blocked responses as incomplete instead of successful empty completions, with dedicated coverage for the affected paths. No actionable merge-blocking risk remains beyond normal checks and review. Possibly related issues
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
This stack of pull requests is managed by Graphite. Learn more about stacking. |
content_filter/guardrail_intervened stop reasons to incomplete status with content_filter incomplete details
fd5f55a to
c01a0a2
Compare
29daea2 to
bca4163
Compare
The base branch was changed.
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
bca4163 to
6b2d42c
Compare
The base branch was changed.
6b2d42c to
1deb849
Compare
c556c60 to
2ba9beb
Compare
The base branch was changed.
1deb849 to
d62aeed
Compare
The base branch was changed.
d62aeed to
3dfc3bc
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
Merge activity
|
… to `incomplete` status with `content_filter` incomplete details (#6103) ## Summary When Bedrock returns a `content_filtered` or `guardrail_intervened` stop reason, the response was previously passed through without setting `Status` or `IncompleteDetails`, making it indistinguishable from a genuine empty completion. This PR ensures that content-filtered and guardrail-blocked turns are surfaced as `status: "incomplete"` with `incomplete_details.reason: "content_filter"` in both the non-streaming and streaming Responses API paths, as well as in the Chat-to-Responses conversion layer. ## Changes - Added a `bedrockStopReasonContentFilter` / `bedrockStopReasonGuardrailIntervened` constant pair in `utils.go` to avoid magic strings across the Bedrock provider. - In `ToBifrostResponsesResponse`, extended the stop-reason switch to handle `content_filter` and `guardrail_intervened` by setting `Status = "incomplete"` and `IncompleteDetails.Reason = "content_filter"`, matching the same pattern already used for `max_tokens` truncation. - In `FinalizeBedrockStream`, added the same case to the streaming finalization switch so the terminal SSE event is emitted as `response.incomplete` rather than `response.completed`. - In `responsesStatusFromChatFinishReason` (mux layer), added `content_filter` and `guardrail_intervened` as mapped reasons that resolve to `incomplete` + `content_filter`, so the fix applies uniformly when Chat responses are converted to Responses format. - Updated existing tests that previously treated `content_filter` as an unmapped/pass-through reason to use a genuinely unmapped reason (`some_unknown_reason` / `some_unmapped_reason`), and added new dedicated tests covering both the non-streaming and streaming content-filter paths. ## Type of change - [x] Bug fix ## Affected areas - [x] Core (Go) - [x] Providers/Integrations ## How to test ```sh go test ./core/providers/bedrock/... ./core/schemas/... ``` The new tests assert: - `ToBifrostResponsesResponse` with `content_filtered` or `guardrail_intervened` stop reasons produces `Status = "incomplete"` and `IncompleteDetails.Reason = "content_filter"`. - `FinalizeBedrockStream` with those stop reasons emits a `response.incomplete` terminal event with the same fields. - `ToBifrostResponsesResponse` (mux) with `content_filter` or `guardrail_intervened` finish reasons maps to `incomplete` + `content_filter`. - Genuinely unmapped stop reasons still leave `Status` unset. ## Breaking changes - [ ] Yes - [x] No ## Security considerations This change ensures content-filtered responses are never silently presented as successful empty completions, which reduces the risk of downstream agents treating a blocked turn as a valid empty output. ## Checklist - [ ] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [ ] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [ ] I verified the CI pipeline passes locally if applicable
… to `incomplete` status with `content_filter` incomplete details (#6103) ## Summary When Bedrock returns a `content_filtered` or `guardrail_intervened` stop reason, the response was previously passed through without setting `Status` or `IncompleteDetails`, making it indistinguishable from a genuine empty completion. This PR ensures that content-filtered and guardrail-blocked turns are surfaced as `status: "incomplete"` with `incomplete_details.reason: "content_filter"` in both the non-streaming and streaming Responses API paths, as well as in the Chat-to-Responses conversion layer. ## Changes - Added a `bedrockStopReasonContentFilter` / `bedrockStopReasonGuardrailIntervened` constant pair in `utils.go` to avoid magic strings across the Bedrock provider. - In `ToBifrostResponsesResponse`, extended the stop-reason switch to handle `content_filter` and `guardrail_intervened` by setting `Status = "incomplete"` and `IncompleteDetails.Reason = "content_filter"`, matching the same pattern already used for `max_tokens` truncation. - In `FinalizeBedrockStream`, added the same case to the streaming finalization switch so the terminal SSE event is emitted as `response.incomplete` rather than `response.completed`. - In `responsesStatusFromChatFinishReason` (mux layer), added `content_filter` and `guardrail_intervened` as mapped reasons that resolve to `incomplete` + `content_filter`, so the fix applies uniformly when Chat responses are converted to Responses format. - Updated existing tests that previously treated `content_filter` as an unmapped/pass-through reason to use a genuinely unmapped reason (`some_unknown_reason` / `some_unmapped_reason`), and added new dedicated tests covering both the non-streaming and streaming content-filter paths. ## Type of change - [x] Bug fix ## Affected areas - [x] Core (Go) - [x] Providers/Integrations ## How to test ```sh go test ./core/providers/bedrock/... ./core/schemas/... ``` The new tests assert: - `ToBifrostResponsesResponse` with `content_filtered` or `guardrail_intervened` stop reasons produces `Status = "incomplete"` and `IncompleteDetails.Reason = "content_filter"`. - `FinalizeBedrockStream` with those stop reasons emits a `response.incomplete` terminal event with the same fields. - `ToBifrostResponsesResponse` (mux) with `content_filter` or `guardrail_intervened` finish reasons maps to `incomplete` + `content_filter`. - Genuinely unmapped stop reasons still leave `Status` unset. ## Breaking changes - [ ] Yes - [x] No ## Security considerations This change ensures content-filtered responses are never silently presented as successful empty completions, which reduces the risk of downstream agents treating a blocked turn as a valid empty output. ## Checklist - [ ] I read `docs/contributing/README.md` and followed the guidelines - [x] I added/updated tests where appropriate - [ ] I updated documentation where needed - [x] I verified builds succeed (Go and UI) - [ ] I verified the CI pipeline passes locally if applicable

Summary
When Bedrock returns a
content_filteredorguardrail_intervenedstop reason, the response was previously passed through without settingStatusorIncompleteDetails, making it indistinguishable from a genuine empty completion. This PR ensures that content-filtered and guardrail-blocked turns are surfaced asstatus: "incomplete"withincomplete_details.reason: "content_filter"in both the non-streaming and streaming Responses API paths, as well as in the Chat-to-Responses conversion layer.Changes
bedrockStopReasonContentFilter/bedrockStopReasonGuardrailIntervenedconstant pair inutils.goto avoid magic strings across the Bedrock provider.ToBifrostResponsesResponse, extended the stop-reason switch to handlecontent_filterandguardrail_intervenedby settingStatus = "incomplete"andIncompleteDetails.Reason = "content_filter", matching the same pattern already used formax_tokenstruncation.FinalizeBedrockStream, added the same case to the streaming finalization switch so the terminal SSE event is emitted asresponse.incompleterather thanresponse.completed.responsesStatusFromChatFinishReason(mux layer), addedcontent_filterandguardrail_intervenedas mapped reasons that resolve toincomplete+content_filter, so the fix applies uniformly when Chat responses are converted to Responses format.content_filteras an unmapped/pass-through reason to use a genuinely unmapped reason (some_unknown_reason/some_unmapped_reason), and added new dedicated tests covering both the non-streaming and streaming content-filter paths.Type of change
Affected areas
How to test
go test ./core/providers/bedrock/... ./core/schemas/...The new tests assert:
ToBifrostResponsesResponsewithcontent_filteredorguardrail_intervenedstop reasons producesStatus = "incomplete"andIncompleteDetails.Reason = "content_filter".FinalizeBedrockStreamwith those stop reasons emits aresponse.incompleteterminal event with the same fields.ToBifrostResponsesResponse(mux) withcontent_filterorguardrail_intervenedfinish reasons maps toincomplete+content_filter.Statusunset.Breaking changes
Security considerations
This change ensures content-filtered responses are never silently presented as successful empty completions, which reduces the risk of downstream agents treating a blocked turn as a valid empty output.
Checklist
docs/contributing/README.mdand followed the guidelines