This is an automated email from the ASF dual-hosted git repository.
oscerd pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new f3e8a71d27f5 CAMEL-23461: camel-aws-bedrock - applyGuardrail reads
guardrail identifier from wrong header (#23151)
f3e8a71d27f5 is described below
commit f3e8a71d27f57e36abe7721300f2ecd737683925
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue May 12 15:28:03 2026 +0200
CAMEL-23461: camel-aws-bedrock - applyGuardrail reads guardrail identifier
from wrong header (#23151)
The applyGuardrail producer was reading the guardrail identifier from
CamelAwsBedrockGuardrailConfig as a String, but that header is documented as
GuardrailConfiguration and is consumed as such by converse / converseStream.
Routes mixing the two operations silently produced a null identifier and
fell back to the endpoint configuration only.
Introduce a dedicated CamelAwsBedrockGuardrailIdentifier header
(BedrockConstants.GUARDRAIL_IDENTIFIER, typed String) for applyGuardrail.
GUARDRAIL_CONFIG stays reserved for converse / converseStream.
Adds a unit test (mocked BedrockRuntimeClient, no AWS access required)
covering endpoint fallback, the new header, the regression where
GUARDRAIL_CONFIG must not corrupt the applyGuardrail request, and the
missing-identifier failure path. Component docs, the 4.21 upgrade guide,
and the regenerated catalog/metadata/endpoint-DSL artifacts are updated
accordingly.
Closes #23151
---
.../camel/catalog/components/aws-bedrock.json | 13 +-
.../aws2/bedrock/runtime/aws-bedrock.json | 13 +-
.../src/main/docs/aws-bedrock-component.adoc | 1 +
.../aws2/bedrock/runtime/BedrockConstants.java | 2 +
.../aws2/bedrock/runtime/BedrockProducer.java | 3 +-
.../bedrock/runtime/BedrockApplyGuardrailTest.java | 172 +++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_21.adoc | 10 ++
.../dsl/BedrockEndpointBuilderFactory.java | 12 ++
8 files changed, 213 insertions(+), 13 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws-bedrock.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws-bedrock.json
index 90b1c4cba800..1c7145bfba86 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws-bedrock.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/aws-bedrock.json
@@ -71,12 +71,13 @@
"CamelAwsBedrockConverseUsage": { "index": 13, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "TokenUsage", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The usage metrics from
Converse API response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#CONVERSE_USAGE"
},
"CamelAwsBedrockConverseOutputMessage": { "index": 14, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Message", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The output message from Converse API
response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#CONVERSE_OUTPUT_MESSAGE"
},
"CamelAwsBedrockGuardrailConfig": { "index": 15, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailConfiguration", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "The guardrail
configuration to apply to the request", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_CONFIG"
},
- "CamelAwsBedrockGuardrailContent": { "index": 16, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "List<GuardrailContentBlock>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
content blocks for ApplyGuardrail operation", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_CONTENT"
},
- "CamelAwsBedrockGuardrailSource": { "index": 17, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The source type for ApplyGuardrail
operation (INPUT or OUTPUT)", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_SOURCE"
},
- "CamelAwsBedrockGuardrailOutput": { "index": 18, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailAssessment", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The guardrail assessment
output from the response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_OUTPUT"
},
- "CamelAwsBedrockGuardrailTrace": { "index": 19, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailTrace", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The trace information from
guardrail evaluation", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_TRACE"
},
- "CamelAwsBedrockGuardrailAssessments": { "index": 20, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "List<GuardrailAssessment>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
guardrail assessments from ApplyGuardrail response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_ASSESSMENTS"
},
- "CamelAwsBedrockGuardrailUsage": { "index": 21, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailUsage", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The guardrail usage
metrics from ApplyGuardrail response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_USAGE"
}
+ "CamelAwsBedrockGuardrailIdentifier": { "index": 16, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The guardrail identifier to use for the
ApplyGuardrail operation", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_IDENTIFIER"
},
+ "CamelAwsBedrockGuardrailContent": { "index": 17, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "List<GuardrailContentBlock>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
content blocks for ApplyGuardrail operation", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_CONTENT"
},
+ "CamelAwsBedrockGuardrailSource": { "index": 18, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The source type for ApplyGuardrail
operation (INPUT or OUTPUT)", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_SOURCE"
},
+ "CamelAwsBedrockGuardrailOutput": { "index": 19, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailAssessment", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The guardrail assessment
output from the response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_OUTPUT"
},
+ "CamelAwsBedrockGuardrailTrace": { "index": 20, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailTrace", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The trace information from
guardrail evaluation", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_TRACE"
},
+ "CamelAwsBedrockGuardrailAssessments": { "index": 21, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "List<GuardrailAssessment>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
guardrail assessments from ApplyGuardrail response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_ASSESSMENTS"
},
+ "CamelAwsBedrockGuardrailUsage": { "index": 22, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailUsage", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The guardrail usage
metrics from ApplyGuardrail response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_USAGE"
}
},
"properties": {
"label": { "index": 0, "kind": "path", "displayName": "Label", "group":
"producer", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration",
"configurationField": "configuration", "description": "Logical name" },
diff --git
a/components/camel-aws/camel-aws-bedrock/src/generated/resources/META-INF/org/apache/camel/component/aws2/bedrock/runtime/aws-bedrock.json
b/components/camel-aws/camel-aws-bedrock/src/generated/resources/META-INF/org/apache/camel/component/aws2/bedrock/runtime/aws-bedrock.json
index 90b1c4cba800..1c7145bfba86 100644
---
a/components/camel-aws/camel-aws-bedrock/src/generated/resources/META-INF/org/apache/camel/component/aws2/bedrock/runtime/aws-bedrock.json
+++
b/components/camel-aws/camel-aws-bedrock/src/generated/resources/META-INF/org/apache/camel/component/aws2/bedrock/runtime/aws-bedrock.json
@@ -71,12 +71,13 @@
"CamelAwsBedrockConverseUsage": { "index": 13, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "TokenUsage", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The usage metrics from
Converse API response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#CONVERSE_USAGE"
},
"CamelAwsBedrockConverseOutputMessage": { "index": 14, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "Message", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The output message from Converse API
response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#CONVERSE_OUTPUT_MESSAGE"
},
"CamelAwsBedrockGuardrailConfig": { "index": 15, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailConfiguration", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "The guardrail
configuration to apply to the request", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_CONFIG"
},
- "CamelAwsBedrockGuardrailContent": { "index": 16, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "List<GuardrailContentBlock>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
content blocks for ApplyGuardrail operation", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_CONTENT"
},
- "CamelAwsBedrockGuardrailSource": { "index": 17, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The source type for ApplyGuardrail
operation (INPUT or OUTPUT)", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_SOURCE"
},
- "CamelAwsBedrockGuardrailOutput": { "index": 18, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailAssessment", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The guardrail assessment
output from the response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_OUTPUT"
},
- "CamelAwsBedrockGuardrailTrace": { "index": 19, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailTrace", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The trace information from
guardrail evaluation", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_TRACE"
},
- "CamelAwsBedrockGuardrailAssessments": { "index": 20, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "List<GuardrailAssessment>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
guardrail assessments from ApplyGuardrail response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_ASSESSMENTS"
},
- "CamelAwsBedrockGuardrailUsage": { "index": 21, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailUsage", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The guardrail usage
metrics from ApplyGuardrail response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_USAGE"
}
+ "CamelAwsBedrockGuardrailIdentifier": { "index": 16, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The guardrail identifier to use for the
ApplyGuardrail operation", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_IDENTIFIER"
},
+ "CamelAwsBedrockGuardrailContent": { "index": 17, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "List<GuardrailContentBlock>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
content blocks for ApplyGuardrail operation", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_CONTENT"
},
+ "CamelAwsBedrockGuardrailSource": { "index": 18, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The source type for ApplyGuardrail
operation (INPUT or OUTPUT)", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_SOURCE"
},
+ "CamelAwsBedrockGuardrailOutput": { "index": 19, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailAssessment", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The guardrail assessment
output from the response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_OUTPUT"
},
+ "CamelAwsBedrockGuardrailTrace": { "index": 20, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailTrace", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The trace information from
guardrail evaluation", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_TRACE"
},
+ "CamelAwsBedrockGuardrailAssessments": { "index": 21, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "List<GuardrailAssessment>", "deprecated": false,
"deprecationNote": "", "autowired": false, "secret": false, "description": "The
guardrail assessments from ApplyGuardrail response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_ASSESSMENTS"
},
+ "CamelAwsBedrockGuardrailUsage": { "index": 22, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "GuardrailUsage", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The guardrail usage
metrics from ApplyGuardrail response", "constantName":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConstants#GUARDRAIL_USAGE"
}
},
"properties": {
"label": { "index": 0, "kind": "path", "displayName": "Label", "group":
"producer", "label": "", "required": true, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "configurationClass":
"org.apache.camel.component.aws2.bedrock.runtime.BedrockConfiguration",
"configurationField": "configuration", "description": "Logical name" },
diff --git
a/components/camel-aws/camel-aws-bedrock/src/main/docs/aws-bedrock-component.adoc
b/components/camel-aws/camel-aws-bedrock/src/main/docs/aws-bedrock-component.adoc
index 598590a6e8f8..591da05643ee 100644
---
a/components/camel-aws/camel-aws-bedrock/src/main/docs/aws-bedrock-component.adoc
+++
b/components/camel-aws/camel-aws-bedrock/src/main/docs/aws-bedrock-component.adoc
@@ -803,6 +803,7 @@ The Camel AWS Bedrock component provides comprehensive
support for guardrails th
*Message-level Configuration* (per-message override via headers):
- `CamelAwsBedrockGuardrailConfig`: GuardrailConfiguration object for converse
operations
+- `CamelAwsBedrockGuardrailIdentifier`: Guardrail identifier String for the
applyGuardrail operation (overrides the endpoint `guardrailIdentifier`)
- `CamelAwsBedrockGuardrailContent`: Content blocks for applyGuardrail
operation
- `CamelAwsBedrockGuardrailSource`: Source type - "INPUT" or "OUTPUT" for
applyGuardrail
diff --git
a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockConstants.java
b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockConstants.java
index 9feeb736cc3d..0ab3692e5775 100644
---
a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockConstants.java
+++
b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockConstants.java
@@ -55,6 +55,8 @@ public interface BedrockConstants {
String CONVERSE_OUTPUT_MESSAGE = "CamelAwsBedrockConverseOutputMessage";
@Metadata(description = "The guardrail configuration to apply to the
request", javaType = "GuardrailConfiguration")
String GUARDRAIL_CONFIG = "CamelAwsBedrockGuardrailConfig";
+ @Metadata(description = "The guardrail identifier to use for the
ApplyGuardrail operation", javaType = "String")
+ String GUARDRAIL_IDENTIFIER = "CamelAwsBedrockGuardrailIdentifier";
@Metadata(description = "The content blocks for ApplyGuardrail operation",
javaType = "List<GuardrailContentBlock>")
String GUARDRAIL_CONTENT = "CamelAwsBedrockGuardrailContent";
@Metadata(description = "The source type for ApplyGuardrail operation
(INPUT or OUTPUT)", javaType = "String")
diff --git
a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockProducer.java
b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockProducer.java
index edc967263986..1c40bc6cc730 100644
---
a/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockProducer.java
+++
b/components/camel-aws/camel-aws-bedrock/src/main/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockProducer.java
@@ -816,7 +816,8 @@ public class BedrockProducer extends DefaultProducer {
= ApplyGuardrailRequest.builder();
// Guardrail identifier from header or configuration
- String guardrailIdentifier =
exchange.getMessage().getHeader(BedrockConstants.GUARDRAIL_CONFIG,
String.class);
+ String guardrailIdentifier
+ =
exchange.getMessage().getHeader(BedrockConstants.GUARDRAIL_IDENTIFIER,
String.class);
if (ObjectHelper.isEmpty(guardrailIdentifier)) {
guardrailIdentifier =
getConfiguration().getGuardrailIdentifier();
}
diff --git
a/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockApplyGuardrailTest.java
b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockApplyGuardrailTest.java
new file mode 100644
index 000000000000..c0e0a05418cc
--- /dev/null
+++
b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockApplyGuardrailTest.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.aws2.bedrock.runtime;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.camel.support.SimpleRegistry;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClient;
+import
software.amazon.awssdk.services.bedrockruntime.model.ApplyGuardrailRequest;
+import
software.amazon.awssdk.services.bedrockruntime.model.ApplyGuardrailResponse;
+import software.amazon.awssdk.services.bedrockruntime.model.GuardrailAction;
+import
software.amazon.awssdk.services.bedrockruntime.model.GuardrailConfiguration;
+import
software.amazon.awssdk.services.bedrockruntime.model.GuardrailContentBlock;
+import software.amazon.awssdk.services.bedrockruntime.model.GuardrailTextBlock;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.lenient;
+
+/**
+ * Unit tests for the {@code applyGuardrail} producer operation. Uses a mocked
{@link BedrockRuntimeClient} so the test
+ * exercises only the producer's request-building logic — no AWS credentials
or network access required.
+ */
+@ExtendWith(MockitoExtension.class)
+public class BedrockApplyGuardrailTest {
+
+ @Mock
+ private BedrockRuntimeClient client;
+
+ private CamelContext camelContext;
+ private ProducerTemplate template;
+ private AtomicReference<ApplyGuardrailRequest> capturedRequest;
+
+ @BeforeEach
+ public void setup() throws Exception {
+ capturedRequest = new AtomicReference<>();
+ lenient().doAnswer(invocation -> {
+ capturedRequest.set(invocation.getArgument(0,
ApplyGuardrailRequest.class));
+ return
ApplyGuardrailResponse.builder().action(GuardrailAction.NONE).build();
+ }).when(client).applyGuardrail(any(ApplyGuardrailRequest.class));
+
+ SimpleRegistry registry = new SimpleRegistry();
+ registry.bind("bedrockClient", client);
+ camelContext = new DefaultCamelContext(registry);
+ camelContext.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:apply-guardrail-config")
+ .to("aws-bedrock://label"
+ + "?bedrockRuntimeClient=#bedrockClient"
+ + "&operation=applyGuardrail"
+ + "&guardrailIdentifier=endpoint-guardrail-id"
+ + "&guardrailVersion=1"
+ + "®ion=us-east-1"
+ + "&accessKey=unused"
+ + "&secretKey=unused");
+
+ from("direct:apply-guardrail-no-config")
+ .to("aws-bedrock://label2"
+ + "?bedrockRuntimeClient=#bedrockClient"
+ + "&operation=applyGuardrail"
+ + "®ion=us-east-1"
+ + "&accessKey=unused"
+ + "&secretKey=unused");
+ }
+ });
+ camelContext.start();
+ template = camelContext.createProducerTemplate();
+ }
+
+ @AfterEach
+ public void teardown() {
+ if (template != null) {
+ template.stop();
+ }
+ if (camelContext != null) {
+ camelContext.stop();
+ }
+ }
+
+ @Test
+ public void applyGuardrailUsesEndpointGuardrailIdentifierWhenNoHeader() {
+ template.send("direct:apply-guardrail-config",
+ exchange ->
exchange.getMessage().setHeader(BedrockConstants.GUARDRAIL_CONTENT,
sampleContent()));
+
+ ApplyGuardrailRequest request = capturedRequest.get();
+ assertNotNull(request, "Producer must have invoked the bedrock
client");
+ assertEquals("endpoint-guardrail-id", request.guardrailIdentifier(),
+ "Identifier should fall back to the endpoint configuration
when no header is set");
+ }
+
+ @Test
+ public void applyGuardrailUsesGuardrailIdentifierHeaderWhenSet() {
+ template.send("direct:apply-guardrail-config", exchange -> {
+
exchange.getMessage().setHeader(BedrockConstants.GUARDRAIL_IDENTIFIER,
"header-guardrail-id");
+
exchange.getMessage().setHeader(BedrockConstants.GUARDRAIL_CONTENT,
sampleContent());
+ });
+
+ ApplyGuardrailRequest request = capturedRequest.get();
+ assertNotNull(request);
+ assertEquals("header-guardrail-id", request.guardrailIdentifier(),
+ "Identifier should be read from the
CamelAwsBedrockGuardrailIdentifier header");
+ }
+
+ @Test
+ public void applyGuardrailDoesNotReadIdentifierFromGuardrailConfigHeader()
{
+ // GUARDRAIL_CONFIG carries a GuardrailConfiguration (used by
converse), not a String identifier.
+ // Setting it must not corrupt the applyGuardrail request — the
endpoint identifier must still be used.
+ template.send("direct:apply-guardrail-config", exchange -> {
+ GuardrailConfiguration converseConfig =
GuardrailConfiguration.builder()
+ .guardrailIdentifier("converse-guardrail-id")
+ .guardrailVersion("DRAFT")
+ .build();
+ exchange.getMessage().setHeader(BedrockConstants.GUARDRAIL_CONFIG,
converseConfig);
+
exchange.getMessage().setHeader(BedrockConstants.GUARDRAIL_CONTENT,
sampleContent());
+ });
+
+ ApplyGuardrailRequest request = capturedRequest.get();
+ assertNotNull(request);
+ assertEquals("endpoint-guardrail-id", request.guardrailIdentifier(),
+ "applyGuardrail must not consume the GuardrailConfiguration
from CamelAwsBedrockGuardrailConfig");
+ }
+
+ @Test
+ public void applyGuardrailFailsWhenGuardrailIdentifierIsMissing() {
+ Exchange exchange = template.send("direct:apply-guardrail-no-config",
+ ex ->
ex.getMessage().setHeader(BedrockConstants.GUARDRAIL_CONTENT, sampleContent()));
+ Throwable cause = exchange.getException();
+ assertNotNull(cause, "Producer should fail when no guardrailIdentifier
is provided");
+ assertInstanceOf(IllegalArgumentException.class, cause);
+ assertTrue(cause.getMessage().contains("guardrailIdentifier"),
+ "Error message should mention guardrailIdentifier — was: " +
cause.getMessage());
+ }
+
+ private static List<GuardrailContentBlock> sampleContent() {
+ List<GuardrailContentBlock> content = new ArrayList<>();
+ content.add(GuardrailContentBlock.builder()
+ .text(GuardrailTextBlock.builder().text("test").build())
+ .build());
+ return content;
+ }
+}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
index 02af0fb2e1fc..e2e840641219 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_21.adoc
@@ -268,6 +268,16 @@ What it means is that, from now on, the final user or any
third party dependency
This is just an informative note, there is not action expected by the final
user.
+=== camel-aws-bedrock
+
+The `applyGuardrail` producer operation now reads the guardrail identifier
from a new dedicated header
+`CamelAwsBedrockGuardrailIdentifier` (constant
`BedrockConstants.GUARDRAIL_IDENTIFIER`, typed `String`)
+instead of `CamelAwsBedrockGuardrailConfig`. The
`CamelAwsBedrockGuardrailConfig` header is typed
+`GuardrailConfiguration` and is reserved for the `converse` and
`converseStream` operations; the
+previous code path silently produced `null` whenever a route mixed `converse`
and `applyGuardrail`
+calls. If you were not setting the guardrail identifier via header, the
endpoint-level
+`guardrailIdentifier` option continues to work without changes.
+
=== camel-nats
Fixed the JetStream consumer pull subscription mode (which is the default) so
that messages are
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/BedrockEndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/BedrockEndpointBuilderFactory.java
index d7f8ed869fa4..a71c596ec0db 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/BedrockEndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/BedrockEndpointBuilderFactory.java
@@ -944,6 +944,18 @@ public interface BedrockEndpointBuilderFactory {
public String awsBedrockGuardrailConfig() {
return "CamelAwsBedrockGuardrailConfig";
}
+ /**
+ * The guardrail identifier to use for the ApplyGuardrail operation.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code
AwsBedrockGuardrailIdentifier}.
+ */
+ public String awsBedrockGuardrailIdentifier() {
+ return "CamelAwsBedrockGuardrailIdentifier";
+ }
/**
* The content blocks for ApplyGuardrail operation.
*