Skip to content

feat(guardrail): add aws bedrock#101

Merged
bzp2010 merged 3 commits into
mainfrom
bzp/feat-bedrock-guardrail
May 12, 2026
Merged

feat(guardrail): add aws bedrock#101
bzp2010 merged 3 commits into
mainfrom
bzp/feat-bedrock-guardrail

Conversation

@bzp2010
Copy link
Copy Markdown
Collaborator

@bzp2010 bzp2010 commented May 12, 2026

Summary by CodeRabbit

  • New Features

    • Added AWS Bedrock Guardrails integration with allow/rewrite/block outcomes and runtime configuration; guardrails module now exposed for use by other components.
  • Tests

    • Added unit tests covering request handling, outcome mapping, URL/credential handling, and credential redaction.
  • Chores

    • Updated crate dependency list to include AWS signing/runtime, HTTP/client, encoding, and JSON/error utilities.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b9d188fd-1b41-4ff8-aaaf-0153cdd9e52e

📥 Commits

Reviewing files that changed from the base of the PR and between 999a478 and 3f0247d.

📒 Files selected for processing (2)
  • crates/aisix-guardrail/src/guardrails/bedrock.rs
  • tests/package.json
✅ Files skipped from review due to trivial changes (1)
  • tests/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/aisix-guardrail/src/guardrails/bedrock.rs

📝 Walkthrough

Walkthrough

Implements a Bedrock guardrail runtime: adds dependencies, exposes guardrails module, defines Bedrock config/runtime types and wire formats, SigV4-signs requests, sends them via reqwest to Bedrock apply, maps responses to GuardrailOutcome, and adds unit tests.

Changes

Bedrock Guardrail Runtime

Layer / File(s) Summary
Dependencies and lib exposure
crates/aisix-guardrail/Cargo.toml, crates/aisix-guardrail/src/lib.rs, tests/package.json
Adds AWS SigV4/credential and HTTP/client/encoding dependencies; exposes guardrails module from crate root; updates test package manager lock string.
Module facade and public exports
crates/aisix-guardrail/src/guardrails/mod.rs
Publishes bedrock module, re-exports Bedrock runtime/config types, and exposes identifiers and configs submodules.
Config types and redacted Debug
crates/aisix-guardrail/src/guardrails/bedrock.rs
Adds IDENTIFIER, BedrockGuardrailConfig, BedrockRuntimeConfig (with redacted Debug), and BedrockGuardrailMeta.
Runtime constructors and request builders
crates/aisix-guardrail/src/guardrails/bedrock.rs
Implements BedrockGuardrailRuntime constructors, URL builder, and JSON request body construction from payload text blocks.
AWS SigV4 signing
crates/aisix-guardrail/src/guardrails/bedrock.rs
Builds required headers and signs Bedrock apply POST requests using aws-sigv4 and configured credentials (supports session token).
GuardrailRuntime check implementation
crates/aisix-guardrail/src/guardrails/bedrock.rs
Implements check: serialize request, sign, POST via reqwest, handle HTTP errors, parse JSON, and convert to GuardrailOutcome.
Error type and handling
crates/aisix-guardrail/src/guardrails/bedrock.rs
Introduces BedrockError variants for unsupported payloads, URL/signing failures, request/response JSON errors, and non-2xx responses with body.
Wire types and URL utilities
crates/aisix-guardrail/src/guardrails/bedrock.rs
Adds internal request/response wire structs, percent-encoding helpers, and endpoint path join logic preserving overlap.
Payload extraction
crates/aisix-guardrail/src/guardrails/bedrock.rs
Extracts non-empty text messages from input/output payloads into content blocks and retains indexes for rewrite mapping.
Response-to-outcome mapping
crates/aisix-guardrail/src/guardrails/bedrock.rs
Maps Bedrock actions: NONE→Allow, GUARDRAIL_INTERVENED→Rewrite (when counts match) or Block; errors on unknown actions or mismatches.
Unit tests
crates/aisix-guardrail/src/guardrails/bedrock.rs
Tests credential redaction, URL encoding/overlap behavior, presence of SigV4 Authorization header, and allow/rewrite/block/unknown action outcomes.

Sequence Diagram

sequenceDiagram
  participant Runtime as BedrockGuardrailRuntime
  participant Signer as aws-sigv4 Signer
  participant Client as reqwest::Client
  participant Bedrock as Bedrock Apply API
  Runtime->>Signer: build headers and SigV4 sign (POST /model/.../apply)
  Signer-->>Runtime: signed headers + signed url
  Runtime->>Client: POST signed URL with JSON body (content blocks)
  Client->>Bedrock: HTTP POST (signed, JSON)
  Bedrock-->>Client: HTTP response (JSON action + outputs)
  Client-->>Runtime: response body + status
  Runtime->>Runtime: map action -> GuardrailOutcome (Allow/Rewrite/Block) or BedrockError
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • api7/aisix#100: Added the aisix-guardrail crate with core traits and types (GuardrailCheckPayload, GuardrailOutcome, GuardrailRuntime) that this PR's Bedrock runtime implements and uses.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Security Check ❌ Error BedrockRuntimeConfig derives Serialize/ToSchema without protecting credential fields. No #[serde(skip)] on access_key_id or secret_access_key allows exposure via serialization and OpenAPI docs. Add #[serde(skip_serializing)] to access_key_id, secret_access_key, and session_token fields. Consider removing ToSchema derive or implementing custom schema that excludes sensitive fields.
E2e Test Quality Review ⚠️ Warning No E2E tests found. PR adds Bedrock Guardrails with 9 unit tests but lacks E2E tests covering full business flow with external dependencies. Add E2E integration tests exercising complete flow: build → sign → HTTP POST → parse response. Include error scenarios and mocking of Bedrock service.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(guardrail): add aws bedrock' clearly and concisely describes the main change: adding AWS Bedrock integration to the guardrails module.
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
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bzp/feat-bedrock-guardrail

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
crates/aisix-guardrail/src/guardrails/bedrock.rs (1)

22-270: 🏗️ Heavy lift

Document newly exposed public API items with /// comments.

This file introduces multiple public constants/types/constructors without doc comments, which makes the crate API harder to consume and maintain.

As per coding guidelines: "Use /// for doc comments on public items in Rust".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aisix-guardrail/src/guardrails/bedrock.rs` around lines 22 - 270,
Several newly exported items lack doc comments; add /// documentation for each
public constant, struct, method, and enum introduced here (so the crate API is
documented). Specifically, add concise /// comments for IDENTIFIER,
DEFAULT_OUTPUT_SCOPE, DEFAULT_RUNTIME_HOST_PREFIX, DEFAULT_RUNTIME_HOST_SUFFIX,
the BedrockGuardrailConfig and BedrockRuntimeConfig structs (documenting fields
and purpose), BedrockGuardrailMeta, BedrockGuardrailRuntime and its public
constructors new and with_client, and the BedrockError enum (documenting error
variants semantics). Keep comments short and descriptive, mention expected
usage/semantics for constructors (e.g., what BedrockRuntimeConfig fields mean
and optional endpoint/session_token), and document what each BedrockError
variant represents.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/aisix-guardrail/src/guardrails/bedrock.rs`:
- Around line 379-415: The match on response.action should handle the
"GUARDRAIL_BLOCKED" action explicitly instead of falling through to the
UnsupportedPayload error; update the match in the function that maps bedrock
responses (the match on response.action.as_str()) to add a "GUARDRAIL_BLOCKED"
arm which returns Ok(GuardrailOutcome::Block { reason:
response.action_reason.unwrap_or_else(|| "bedrock guardrail blocked".into()) })
so valid blocked responses produce GuardrailOutcome::Block, leaving the existing
"GUARDRAIL_INTERVENED" and fallback arms unchanged.
- Around line 236-242: The Bedrock guardrail HTTP call currently uses
self.client.post(signed.url)...body(body).send().await which relies on the
client's default timeout; modify the RequestBuilder to set an explicit
per-request timeout by calling the RequestBuilder.timeout(...) with a
std::time::Duration (e.g., Duration::from_secs(5) or other acceptable bound)
before .send(), and add the necessary Duration import; update the call sequence
around self.client.post(...) to include this timeout so the await is bounded.

---

Nitpick comments:
In `@crates/aisix-guardrail/src/guardrails/bedrock.rs`:
- Around line 22-270: Several newly exported items lack doc comments; add ///
documentation for each public constant, struct, method, and enum introduced here
(so the crate API is documented). Specifically, add concise /// comments for
IDENTIFIER, DEFAULT_OUTPUT_SCOPE, DEFAULT_RUNTIME_HOST_PREFIX,
DEFAULT_RUNTIME_HOST_SUFFIX, the BedrockGuardrailConfig and BedrockRuntimeConfig
structs (documenting fields and purpose), BedrockGuardrailMeta,
BedrockGuardrailRuntime and its public constructors new and with_client, and the
BedrockError enum (documenting error variants semantics). Keep comments short
and descriptive, mention expected usage/semantics for constructors (e.g., what
BedrockRuntimeConfig fields mean and optional endpoint/session_token), and
document what each BedrockError variant represents.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d524fcef-501d-4457-b3b2-68dd7f311617

📥 Commits

Reviewing files that changed from the base of the PR and between 05feff6 and f4c4cfd.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • crates/aisix-guardrail/Cargo.toml
  • crates/aisix-guardrail/src/guardrails/bedrock.rs
  • crates/aisix-guardrail/src/guardrails/mod.rs
  • crates/aisix-guardrail/src/lib.rs

Comment on lines +236 to +242
let response = self
.client
.post(signed.url)
.headers(signed.headers)
.body(body)
.send()
.await?;
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 12, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In reqwest (current stable), does Client::new() include a default request timeout, and is per-request .timeout(...) the recommended way to enforce bounded wait time?

💡 Result:

Yes—reqwest’s stable behavior includes a default overall request timeout when using Client::new (through ClientBuilder defaults), and it is 30 seconds. Reasoning and details: 1) ClientBuilder::timeout documents that it “Set[s] a timeout for connect, read and write operations of a Client” and that “Default is 30 seconds,” with “Pass None to disable timeout.” [1] 2) The reqwest issue tracker also reports that with Client::new (and ClientBuilder::new.build) “requests will time out after 30 seconds,” despite some docs claiming “Default is no timeout.” [2] Is per-request.timeout(...) the recommended way to enforce a bounded wait time? - The per-request reqwest::RequestBuilder::timeout(Duration) is explicitly described as “applied … from when the request starts connecting until the response body has finished,” and it “affects only this request and overrides the timeout configured using ClientBuilder::timeout.” [3] - Therefore, if you want different bounded wait times per request, using per-request.timeout(...) is appropriate (it overrides the client default) and is the direct mechanism to do so. [3] - If you want a single consistent bound for most requests, setting the timeout on the Client via ClientBuilder::timeout(...) (or.timeout(...) on a blocking/async ClientBuilder) is the mechanism; per-request.timeout(...) is then for exceptions because it overrides the client’s timeout. [1][3] Example pattern (per-request override): client.get(url).timeout(Duration::from_secs(10)).send.await

Citations:


🏁 Script executed:

# Check the broader context around the Bedrock HTTP request to understand if there's specific timeout requirements
cat -n crates/aisix-guardrail/src/guardrails/bedrock.rs | head -260 | tail -40

Repository: api7/aisix

Length of output: 1717


🏁 Script executed:

# Search for any existing timeout configurations elsewhere in the codebase for Bedrock or similar HTTP requests
rg "timeout" crates/aisix-guardrail/src/guardrails/ -A 2 -B 2

Repository: api7/aisix

Length of output: 36


🏁 Script executed:

# Check if there are any tests or documentation about Bedrock request expectations
rg -i "bedrock" crates/aisix-guardrail/ --type rs -l

Repository: api7/aisix

Length of output: 81


🏁 Script executed:

# Search for BedrockGuardrailRuntime struct definition and client field initialization
rg "struct BedrockGuardrailRuntime" crates/aisix-guardrail/src/guardrails/bedrock.rs -A 20

Repository: api7/aisix

Length of output: 612


🏁 Script executed:

# Search for impl blocks that initialize the client (new, from_config, etc.)
rg "impl.*BedrockGuardrailRuntime" crates/aisix-guardrail/src/guardrails/bedrock.rs -A 30

Repository: api7/aisix

Length of output: 2339


🏁 Script executed:

# Look for any timeout-related constants or configuration in the file
rg -i "timeout|duration" crates/aisix-guardrail/src/guardrails/bedrock.rs

Repository: api7/aisix

Length of output: 162


Add an explicit per-request timeout for the Bedrock HTTP call.

The .send().await call relies on reqwest's default 30-second timeout (from Client::new()). Since this guardrail check is latency-sensitive, add an explicit per-request timeout override to ensure bounded wait time:

Suggested fix
         let response = self
             .client
             .post(signed.url)
             .headers(signed.headers)
+            .timeout(std::time::Duration::from_secs(10))
             .body(body)
             .send()
             .await?;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let response = self
.client
.post(signed.url)
.headers(signed.headers)
.body(body)
.send()
.await?;
let response = self
.client
.post(signed.url)
.headers(signed.headers)
.timeout(std::time::Duration::from_secs(10))
.body(body)
.send()
.await?;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/aisix-guardrail/src/guardrails/bedrock.rs` around lines 236 - 242, The
Bedrock guardrail HTTP call currently uses
self.client.post(signed.url)...body(body).send().await which relies on the
client's default timeout; modify the RequestBuilder to set an explicit
per-request timeout by calling the RequestBuilder.timeout(...) with a
std::time::Duration (e.g., Duration::from_secs(5) or other acceptable bound)
before .send(), and add the necessary Duration import; update the call sequence
around self.client.post(...) to include this timeout so the await is bounded.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Create an issue for this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

gh issue create \
  --repo api7/aisix \
  --title "feat(guardrail): add explicit per-request timeout for Bedrock HTTP call" \
  --body "## Summary

The Bedrock guardrail HTTP call in \`crates/aisix-guardrail/src/guardrails/bedrock.rs\` currently relies on reqwest's default timeout from \`Client::new()\` without an explicit per-request timeout override. Since this guardrail check is latency-sensitive, an explicit timeout should be configured to ensure bounded wait time.

## Suggested Fix

Add a per-request \`.timeout(...)\` call before \`.send().await\`:

\`\`\`rust
let response = self
    .client
    .post(signed.url)
    .headers(signed.headers)
    .timeout(std::time::Duration::from_secs(10))
    .body(body)
    .send()
    .await?;
\`\`\`

The specific timeout value (e.g., 5s, 10s) should be decided based on acceptable latency bounds for guardrail checks.

## References

- PR: https://github.com/api7/aisix/pull/101
- Review comment: https://github.com/api7/aisix/pull/101#discussion_r3224587569

Reported by `@bzp2010`" \
  --assignee bzp2010

Length of output: 41


@bzp2010, I've created the follow-up issue: #102 — assigned to you for tracking the explicit per-request timeout on the Bedrock HTTP call.


🧠 Learnings used
Learnt from: bzp2010
Repo: api7/aisix PR: 34
File: src/proxy/hooks2/authorization/mod.rs:1-1
Timestamp: 2026-04-10T09:35:05.745Z
Learning: In Rust code using the `anyhow` crate, treat `use anyhow::Result;` as potentially used even if the code uses non-default error types like `Result<T, SomeOtherError>` (i.e., `Result<T, E>` where `E` is not `anyhow::Error`). Because `anyhow::Result` is an alias for `std::result::Result<T, E=anyhow::Error>`, the import is referenced by the `Result<..., ...>` syntax. Do not flag `use anyhow::Result;` as unused solely due to the presence of `Result<T, SomeOtherError>` usages.

Comment thread crates/aisix-guardrail/src/guardrails/bedrock.rs
@bzp2010 bzp2010 merged commit 70e5f1a into main May 12, 2026
3 checks passed
@bzp2010 bzp2010 deleted the bzp/feat-bedrock-guardrail branch May 12, 2026 08:02
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