Skip to content

fix: map Bedrock content_filter/guardrail_intervened stop reasons to incomplete status with content_filter incomplete details - #6103

Merged
akshaydeo merged 1 commit into
devfrom
08-12-fix_content_filtered_status_fallback
Aug 13, 2026
Merged

fix: map Bedrock content_filter/guardrail_intervened stop reasons to incomplete status with content_filter incomplete details#6103
akshaydeo merged 1 commit into
devfrom
08-12-fix_content_filtered_status_fallback

Conversation

@BearTS

@BearTS BearTS commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

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

  • Bug fix

Affected areas

  • Core (Go)
  • Providers/Integrations

How to test

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
  • 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
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: bd0cbc70-987c-4c87-88bc-e11a3edbc2d8

📥 Commits

Reviewing files that changed from the base of the PR and between ce1b2a6 and 3dfc3bc.

📒 Files selected for processing (5)
  • core/providers/bedrock/bedrock_test.go
  • core/providers/bedrock/responses.go
  • core/providers/bedrock/utils.go
  • core/schemas/mux.go
  • core/schemas/mux_test.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • core/schemas/mux_test.go
  • core/schemas/mux.go
  • core/providers/bedrock/utils.go
  • core/providers/bedrock/bedrock_test.go
  • core/providers/bedrock/responses.go

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Content-filtered and guardrail-intervened responses are now correctly marked as incomplete.
    • Responses include content_filter details when generation is stopped by safety controls.
    • Streaming responses now emit the appropriate incomplete terminal event.
    • Non-streaming responses consistently report safety-related interruptions.
    • Unknown stop reasons continue to complete without an assigned status, avoiding misleading response classifications.
    • Safety-related response handling is now more consistent across supported response formats.

Walkthrough

Bedrock content-filter and guardrail-intervened stop reasons now map to incomplete Responses with content_filter details. Streaming and non-streaming paths use this mapping, while unknown stop reasons remain unmapped.

Changes

Bedrock content-filter mapping

Layer / File(s) Summary
Response status mapping
core/schemas/mux.go, core/schemas/mux_test.go
The shared mapper handles content_filter and guardrail_intervened as incomplete responses and preserves the stop reason. Unknown reasons remain unmapped.
Bedrock conversion paths
core/providers/bedrock/utils.go, core/providers/bedrock/responses.go, core/providers/bedrock/bedrock_test.go
Bedrock streaming and non-streaming conversions emit incomplete responses with content_filter details for filtered and guardrail-intervened outcomes. Tests cover both paths.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Mergeability Score: ⚪ Minimal · up to 3dfc3

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

  • maximhq/bifrost#5936: Both changes update Bedrock handling of content-filter and incomplete stop reasons, but this PR covers response conversion while the issue concerns egress enum conversion.

Possibly related PRs

  • maximhq/bifrost#6113: Both PRs modify Bedrock streaming response handling, but this PR handles content filtering and guardrails while that PR handles max_tokens truncation.

Suggested reviewers: tejasghatte, akshaydeo

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary Bedrock status-mapping fix.
Description check ✅ Passed The description covers the purpose, changes, affected areas, testing, breaking changes, security, and checklist items.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 08-12-fix_content_filtered_status_fallback

Comment @coderabbitai help to get the list of available commands.

BearTS commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

@BearTS BearTS changed the title fix: content_filtered status fallback fix: map Bedrock content_filter/guardrail_intervened stop reasons to incomplete status with content_filter incomplete details Aug 12, 2026
@BearTS
BearTS marked this pull request as ready for review August 12, 2026 12:32
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 12, 2026
@BearTS
BearTS changed the base branch from main to graphite-base/6103 August 13, 2026 05:52
@BearTS
BearTS force-pushed the graphite-base/6103 branch from fd5f55a to c01a0a2 Compare August 13, 2026 05:52
@BearTS
BearTS force-pushed the 08-12-fix_content_filtered_status_fallback branch from 29daea2 to bca4163 Compare August 13, 2026 05:52
@BearTS
BearTS changed the base branch from graphite-base/6103 to dev August 13, 2026 05:52
@BearTS
BearTS dismissed coderabbitai[bot]’s stale review August 13, 2026 05:52

The base branch was changed.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

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.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 13, 2026
@BearTS
BearTS changed the base branch from dev to graphite-base/6103 August 13, 2026 08:09
@BearTS
BearTS force-pushed the 08-12-fix_content_filtered_status_fallback branch from bca4163 to 6b2d42c Compare August 13, 2026 08:10
@BearTS
BearTS changed the base branch from graphite-base/6103 to dev August 13, 2026 08:10
@BearTS
BearTS dismissed coderabbitai[bot]’s stale review August 13, 2026 08:10

The base branch was changed.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 13, 2026
@BearTS
BearTS changed the base branch from dev to graphite-base/6103 August 13, 2026 08:25
@BearTS
BearTS force-pushed the 08-12-fix_content_filtered_status_fallback branch from 6b2d42c to 1deb849 Compare August 13, 2026 08:25
@BearTS
BearTS force-pushed the graphite-base/6103 branch from c556c60 to 2ba9beb Compare August 13, 2026 08:25
@BearTS
BearTS changed the base branch from graphite-base/6103 to main August 13, 2026 08:25
@BearTS
BearTS dismissed coderabbitai[bot]’s stale review August 13, 2026 08:25

The base branch was changed.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 13, 2026
@BearTS
BearTS changed the base branch from main to graphite-base/6103 August 13, 2026 11:27
@BearTS
BearTS force-pushed the 08-12-fix_content_filtered_status_fallback branch from 1deb849 to d62aeed Compare August 13, 2026 11:28
@BearTS
BearTS changed the base branch from graphite-base/6103 to dev August 13, 2026 11:28
@BearTS
BearTS dismissed coderabbitai[bot]’s stale review August 13, 2026 11:28

The base branch was changed.

@BearTS
BearTS requested a review from a team as a code owner August 13, 2026 11:28
@CLAassistant

CLAassistant commented Aug 13, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@BearTS
BearTS force-pushed the 08-12-fix_content_filtered_status_fallback branch from d62aeed to 3dfc3bc Compare August 13, 2026 11:31
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

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.

akshaydeo commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Merge activity

  • Aug 13, 11:35 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Aug 13, 11:36 AM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo
akshaydeo merged commit 748b869 into dev Aug 13, 2026
14 checks passed
@akshaydeo
akshaydeo deleted the 08-12-fix_content_filtered_status_fallback branch August 13, 2026 11:36
akshaydeo pushed a commit that referenced this pull request Aug 14, 2026
… 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
akshaydeo pushed a commit that referenced this pull request Aug 19, 2026
… 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
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.

3 participants