This is an automated email from the ASF dual-hosted git repository.

davsclaus 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 96d952ecaf18 CAMEL-23462: camel-aws-bedrock - reject wrong-type body 
in invoke*Model POJO mode
96d952ecaf18 is described below

commit 96d952ecaf187bbf9deb1045c736fd34e1fd4938
Author: Andrea Cosentino <[email protected]>
AuthorDate: Fri Jun 26 12:31:53 2026 +0200

    CAMEL-23462: camel-aws-bedrock - reject wrong-type body in invoke*Model 
POJO mode
    
    When pojoRequest=true but the message body is not the expected request type,
    the invoke*Model operations now throw IllegalArgumentException instead of
    silently returning null. This makes them consistent with the converse,
    converseStream, and applyGuardrail operations. Only affects already-broken
    routes that previously produced a null body with no error.
    
    Closes #24269
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 .../aws2/bedrock/runtime/BedrockProducer.java      |  12 ++
 .../runtime/BedrockInvokeModelPojoRequestTest.java | 144 +++++++++++++++++++++
 2 files changed, 156 insertions(+)

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 d5e2ffa7f5a2..75e0cacb9c41 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
@@ -142,6 +142,9 @@ public class BedrockProducer extends DefaultProducer {
                 }
                 Message message = getMessageForResponse(exchange);
                 setResponseText(result, message);
+            } else {
+                throw new IllegalArgumentException(
+                        "InvokeTextModel operation requires InvokeModelRequest 
in POJO mode");
             }
         } else {
             InvokeModelRequest.Builder builder = InvokeModelRequest.builder();
@@ -186,6 +189,9 @@ public class BedrockProducer extends DefaultProducer {
                 }
                 Message message = getMessageForResponse(exchange);
                 message.setBody(result);
+            } else {
+                throw new IllegalArgumentException(
+                        "InvokeImageModel operation requires 
InvokeModelRequest in POJO mode");
             }
         } else {
             InvokeModelRequest.Builder builder = InvokeModelRequest.builder();
@@ -235,6 +241,9 @@ public class BedrockProducer extends DefaultProducer {
                 }
                 Message message = getMessageForResponse(exchange);
                 message.setBody(result);
+            } else {
+                throw new IllegalArgumentException(
+                        "InvokeEmbeddingsModel operation requires 
InvokeModelRequest in POJO mode");
             }
         } else {
             InvokeModelRequest.Builder builder = InvokeModelRequest.builder();
@@ -466,6 +475,9 @@ public class BedrockProducer extends DefaultProducer {
             Object payload = exchange.getMessage().getMandatoryBody();
             if (payload instanceof InvokeModelWithResponseStreamRequest 
streamRequest) {
                 processStreamingRequest(streamRequest, exchange);
+            } else {
+                throw new IllegalArgumentException(
+                        "Streaming invoke operations require 
InvokeModelWithResponseStreamRequest in POJO mode");
             }
         } else {
             InvokeModelWithResponseStreamRequest.Builder builder = 
InvokeModelWithResponseStreamRequest.builder();
diff --git 
a/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockInvokeModelPojoRequestTest.java
 
b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockInvokeModelPojoRequestTest.java
new file mode 100644
index 000000000000..309121899178
--- /dev/null
+++ 
b/components/camel-aws/camel-aws-bedrock/src/test/java/org/apache/camel/component/aws2/bedrock/runtime/BedrockInvokeModelPojoRequestTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+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.BedrockRuntimeAsyncClient;
+import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClient;
+
+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.assertThrows;
+
+/**
+ * Unit tests for the {@code invoke*Model} producer operations in POJO mode 
(CAMEL-23462).
+ * <p>
+ * When {@code pojoRequest=true} but the message body is not the expected 
request type, the producer must fail fast with
+ * a clear {@link IllegalArgumentException} — mirroring the {@code converse} 
and {@code applyGuardrail} operations —
+ * instead of silently returning a {@code null} body. A mocked client is used 
so the test exercises only the producer's
+ * payload-validation logic, with no AWS credentials or network access 
required (the exception is raised before any
+ * client call).
+ */
+@ExtendWith(MockitoExtension.class)
+public class BedrockInvokeModelPojoRequestTest {
+
+    @Mock
+    private BedrockRuntimeClient client;
+
+    @Mock
+    private BedrockRuntimeAsyncClient asyncClient;
+
+    private CamelContext camelContext;
+    private ProducerTemplate template;
+
+    @BeforeEach
+    public void setup() throws Exception {
+        SimpleRegistry registry = new SimpleRegistry();
+        registry.bind("bedrockClient", client);
+        registry.bind("bedrockAsyncClient", asyncClient);
+        camelContext = new DefaultCamelContext(registry);
+        camelContext.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() {
+                route("direct:invoke-text", "invokeTextModel");
+                route("direct:invoke-image", "invokeImageModel");
+                route("direct:invoke-embeddings", "invokeEmbeddingsModel");
+                route("direct:invoke-text-streaming", 
"invokeTextModelStreaming");
+                route("direct:invoke-image-streaming", 
"invokeImageModelStreaming");
+            }
+
+            private void route(String from, String operation) {
+                from(from)
+                        .to("aws-bedrock://label"
+                            + "?bedrockRuntimeClient=#bedrockClient"
+                            + "&bedrockRuntimeAsyncClient=#bedrockAsyncClient"
+                            + "&operation=" + operation
+                            + "&pojoRequest=true"
+                            + "&region=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 invokeTextModelFailsWhenPojoBodyHasWrongType() {
+        assertWrongPojoBodyFails("direct:invoke-text", "not an 
InvokeModelRequest",
+                "InvokeTextModel operation requires InvokeModelRequest in POJO 
mode");
+    }
+
+    @Test
+    public void invokeImageModelFailsWhenPojoBodyHasWrongType() {
+        assertWrongPojoBodyFails("direct:invoke-image", Map.of("prompt", "a 
cat"),
+                "InvokeImageModel operation requires InvokeModelRequest in 
POJO mode");
+    }
+
+    @Test
+    public void invokeEmbeddingsModelFailsWhenPojoBodyHasWrongType() {
+        assertWrongPojoBodyFails("direct:invoke-embeddings", "not an 
InvokeModelRequest",
+                "InvokeEmbeddingsModel operation requires InvokeModelRequest 
in POJO mode");
+    }
+
+    @Test
+    public void invokeTextModelStreamingFailsWhenPojoBodyHasWrongType() {
+        assertWrongPojoBodyFails("direct:invoke-text-streaming", "not a stream 
request",
+                "Streaming invoke operations require 
InvokeModelWithResponseStreamRequest in POJO mode");
+    }
+
+    @Test
+    public void invokeImageModelStreamingFailsWhenPojoBodyHasWrongType() {
+        // invokeImageModelStreaming delegates to invokeTextModelStreaming, so 
it must surface the same error.
+        assertWrongPojoBodyFails("direct:invoke-image-streaming", "not a 
stream request",
+                "Streaming invoke operations require 
InvokeModelWithResponseStreamRequest in POJO mode");
+    }
+
+    private void assertWrongPojoBodyFails(String endpoint, Object body, String 
expectedMessage) {
+        CamelExecutionException ex = 
assertThrows(CamelExecutionException.class,
+                () -> template.sendBody(endpoint, body));
+        Throwable cause = ex.getCause();
+        assertNotNull(cause, "A wrong-type POJO body must cause the exchange 
to fail");
+        assertInstanceOf(IllegalArgumentException.class, cause,
+                "Wrong-type POJO body must raise IllegalArgumentException, 
was: " + cause);
+        assertEquals(expectedMessage, cause.getMessage());
+    }
+}

Reply via email to