Skip to content

feat: add native AWS Bedrock Guardrails support to ModelConfig#2323

Open
sanskar-singh-2403 wants to merge 5 commits into
kagent-dev:mainfrom
sanskar-singh-2403:feat/bedrock-guardrails
Open

feat: add native AWS Bedrock Guardrails support to ModelConfig#2323
sanskar-singh-2403 wants to merge 5 commits into
kagent-dev:mainfrom
sanskar-singh-2403:feat/bedrock-guardrails

Conversation

@sanskar-singh-2403

Copy link
Copy Markdown

What this PR does

Adds an optional guardrail block to the Bedrock provider in ModelConfig, so the
controller passes the guardrail config to the Bedrock Converse and ConverseStream
APIs natively. This removes the need to stand up a separate OpenAI-compatible proxy just
to apply AWS Bedrock Guardrails (content filtering, denied topics, PII redaction) to an
agent.

Example config:

provider: Bedrock
bedrock:
  region: "eu-central-1"
  guardrail:
    identifier: "abc123def456"   # guardrail ID or ARN
    version: "1"
    trace: "enabled"             # disabled (default) | enabled | enabled_full

Changes

  • api/v1alpha2: new BedrockGuardrailConfig type on BedrockConfig. identifier
    and version are required within the block (MinLength=1); the whole block is optional.
    trace is a typed enum (disabled default / enabled / enabled_full) mapping to the
    SDK's GuardrailTrace.
  • api/adk: matching BedrockGuardrail transport struct on Bedrock, omitempty.
  • Controller translator + adk agent: wire the field through v1alpha2 -> adk ->
    runtime BedrockConfig, nil-guarded so existing configs are unaffected.
  • adk/pkg/models/bedrock.go: build the guardrail config and attach it to both API
    calls. Note the SDK uses two different types here Converse takes
    types.GuardrailConfiguration, while ConverseStream takes
    types.GuardrailStreamConfiguration. The stream path uses StreamProcessingMode: sync
    so guardrail interventions are applied before content is streamed to the caller,
    rather than leaking blocked content. Per the issue discussion, interventions surface in
    the response content (not as a hard error) so the agent loop isn't broken.
  • Regenerated zz_generated.deepcopy.go and the ModelConfig CRD (controller-gen
    v0.19.0), and synced the helm/kagent-crds copy.

Tests

  • Unit tests for both guardrail builders (bedrockGuardrailConfig,
    bedrockGuardrailStreamConfig) covering the nil / set / empty-trace / stream-mode cases.
  • ADK JSON marshalling tests for the guardrail block (present and omitted-when-nil).
  • go build, go vet, and go test pass for api/adk, adk/pkg/models, and the
    controller translator package; gofmt clean.

Notes for reviewers

  • Scoped to the Converse / ConverseStream path only the current Bedrock provider doesn't
    use InvokeModel, so it was intentionally left out (matches the issue scope).
  • @GiGi-cloud-76 offered to validate against real Bedrock guardrail configurations
    (content filters, topic denial, PII redaction) in their environment happy to
    coordinate on that.
  • cc @peterj

Fixes #2320

@sanskar-singh-2403
sanskar-singh-2403 requested review from a team and supreme-gg-gg as code owners July 23, 2026 15:15
@github-actions github-actions Bot added the enhancement New feature or request label Jul 23, 2026
@sanskar-singh-2403
sanskar-singh-2403 force-pushed the feat/bedrock-guardrails branch 2 times, most recently from 66094cc to ff7ec17 Compare July 23, 2026 17:12
@sanskar-singh-2403

Copy link
Copy Markdown
Author

@peterj @dmitri-d @davidkarlsen can we review this? Thanks!


type BedrockGuardrailConfig struct {
// +required
// +kubebuilder:validation:MinLength=1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add a max length too, since it's set on the aws config.

@peterj peterj Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(i wonder if we'd need the actual regex for the full arn); but i see it's either a string OR arn, so that might look too complex

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @peterj

Good call, added MaxLength to both fields:

  • identifier: MaxLength=2048 (AWS's cap on guardrailIdentifier)
  • version: MaxLength=8 (numeric version or DRAFT)

Regenerated the CRD manifests and synced the helm copy.

On the ARN regex: you are right I left it out on purpose. AWS's real pattern is a
bare-ID branch OR a full-ARN branch:

^(([a-z0-9]+)|(arn:aws(-[^:]+)?:bedrock:[a-z0-9-]{1,20}:[0-9]{12}:guardrail/[a-z0-9]+))$

Baking that into a kubebuilder Pattern would reject at admission, but
AWS already validates the identifier on the Converse call and returns a
clear error. A strict regex mostly adds risk of false rejections (GovCloud
/China partitions, future ARN shapes) for little gain, and as you noted it
reads as noise. The MinLength=1 / MaxLength=2048 bound catches the
empty/garbage cases cheaply and defers real validation to AWS.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we re-run the CI?

Add an optional guardrail block to the Bedrock provider in ModelConfig so the controller passes GuardrailConfiguration to the Converse and ConverseStream APIs natively, without requiring an external proxy.

- v1alpha2: new BedrockGuardrailConfig (identifier+version required, trace enum disabled/enabled/enabled_full, default disabled)

- adk types + translator + agent wiring

- Converse uses GuardrailConfiguration; ConverseStream uses GuardrailStreamConfiguration with sync processing so interventions apply before content is streamed

- regenerated deepcopy + CRD manifests, synced helm CRD copy

- unit tests for both guardrail builders and ADK JSON marshalling

Fixes kagent-dev#2320

Signed-off-by: Sanskar Singh <sanskarsinghty1234@gmail.com>
@sanskar-singh-2403
sanskar-singh-2403 force-pushed the feat/bedrock-guardrails branch from 964014b to 932fbc4 Compare July 24, 2026 12:20
@sanskar-singh-2403

Copy link
Copy Markdown
Author

@peterj should we merge? If everything seems fine 😸

@EItanya EItanya left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there, to be honest I don't think guardrails belong inside of kagent. If we start adding guardrails the scope may blow up. I think this belongs at the network level. i would look into https://agentgateway.dev/

@sanskar-singh-2403

Copy link
Copy Markdown
Author

Hey there, to be honest I don't think guardrails belong inside of kagent. If we start adding guardrails the scope may blow up. I think this belongs at the network level. i would look into https://agentgateway.dev/

hey @EItanya thanks for taking a look, and totally fair concern. I want to make sure I am not
arguing past your point, so let me reframe what this PR actually does and then
defer to your call.

This isn't adding a guardrails engine or any filtering logic to kagent. It exposes
two fields (identifier + version) that the Bedrock Converse / ConverseStream
APIs already accept in the request body. kagent does zero enforcement here, it just
forwards those values to AWS and AWS does all the filtering. In that sense it is the
same category as region, cacheTTL, and promptCaching, which already live on
BedrockConfig as native Bedrock pass-through params.

So my worry with pushing this to the network layer only: for someone who is on the
native Bedrock provider and not fronting it with agentgateway, there is currently no
way to turn on a guardrail they have already configured in their AWS account,
without standing up an OpenAI-compatible proxy just for that (which is what the
original issue #2320 was asking to avoid).

That said, I hear you on keeping traffic-plane concerns in agentgateway, and I don't
want to open the door to per-provider guardrail schemas creeping in. If the
pass-through framing doesn't change your view, I am happy to close this and point
the issue at agentgateway instead. Your call, just let me know.

Comment thread go/adk/pkg/models/bedrock.go
sanskar-singh-2403 and others added 3 commits July 25, 2026 18:03
Add an optional guardrail block to the Bedrock provider in ModelConfig so the controller passes GuardrailConfiguration to the Converse and ConverseStream APIs natively, without requiring an external proxy.

- v1alpha2: new BedrockGuardrailConfig (identifier+version required, trace enum disabled/enabled/enabled_full, default disabled)

- adk types + translator + agent wiring

- Converse uses GuardrailConfiguration; ConverseStream uses GuardrailStreamConfiguration with sync processing so interventions apply before content is streamed

- regenerated deepcopy + CRD manifests, synced helm CRD copy

- unit tests for both guardrail builders and ADK JSON marshalling

Fixes kagent-dev#2320

Signed-off-by: Sanskar Singh <sanskarsinghty1234@gmail.com>
@sanskar-singh-2403

Copy link
Copy Markdown
Author

Hey there, to be honest I don't think guardrails belong inside of kagent. If we start adding guardrails the scope may blow up. I think this belongs at the network level. i would look into https://agentgateway.dev/

hey @EItanya thanks for taking a look, and totally fair concern. I want to make sure I am not arguing past your point, so let me reframe what this PR actually does and then defer to your call.

This isn't adding a guardrails engine or any filtering logic to kagent. It exposes two fields (identifier + version) that the Bedrock Converse / ConverseStream APIs already accept in the request body. kagent does zero enforcement here, it just forwards those values to AWS and AWS does all the filtering. In that sense it is the same category as region, cacheTTL, and promptCaching, which already live on BedrockConfig as native Bedrock pass-through params.

So my worry with pushing this to the network layer only: for someone who is on the native Bedrock provider and not fronting it with agentgateway, there is currently no way to turn on a guardrail they have already configured in their AWS account, without standing up an OpenAI-compatible proxy just for that (which is what the original issue #2320 was asking to avoid).

That said, I hear you on keeping traffic-plane concerns in agentgateway, and I don't want to open the door to per-provider guardrail schemas creeping in. If the pass-through framing doesn't change your view, I am happy to close this and point the issue at agentgateway instead. Your call, just let me know.

hey @EItanya any thoughts on this?

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Native AWS Bedrock Guardrails support in ModelConfig

4 participants