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 f8c89e9d311c CAMEL-24538: camel-openai - fix operator precedence so an 
empty userMessage does not drop the body prompt
f8c89e9d311c is described below

commit f8c89e9d311c6a65d43558b0347165d6cd5a0f14
Author: Andrea Cosentino <[email protected]>
AuthorDate: Mon Aug 31 18:45:17 2026 +0200

    CAMEL-24538: camel-openai - fix operator precedence so an empty userMessage 
does not drop the body prompt
    
    OpenAIProducer.buildUserMessage() resolved the user prompt with
    userPrompt == null || userPrompt.isEmpty() && 
ObjectHelper.isNotEmpty(config.getUserMessage()).
    Because && binds tighter than ||, this parsed as
    userPrompt == null || (userPrompt.isEmpty() && configHasMessage), so when 
the
    CamelOpenAIUserMessage header was absent and the configured userMessage 
option
    was an empty string, the first branch was already true and userPrompt was 
set
    to that empty string. buildTextMessage then picked the empty string over the
    message body, failing with "No input provided to LLM".
    
    Parenthesize as (userPrompt == null || userPrompt.isEmpty()) && 
isNotEmpty(...)
    so the configured message is only substituted when one is actually set;
    otherwise the body is used. The same precedence bug is fixed in the
    systemPrompt and developerPrompt branches of buildMessages() for 
consistency,
    though neither is currently observable since their downstream isNotEmpty
    guard already drops a null before the message is added.
    
    Adds OpenAIEmptyUserMessageBodyPromptTest, which configures an empty
    userMessage and asserts the body prompt still reaches the model.
    
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
    
    Closes #25939
---
 .../camel/component/openai/OpenAIProducer.java     |  8 +--
 .../OpenAIEmptyUserMessageBodyPromptTest.java      | 69 ++++++++++++++++++++++
 2 files changed, 73 insertions(+), 4 deletions(-)

diff --git 
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java
 
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java
index 04b6cbafed05..6cb5d85c4dc3 100644
--- 
a/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java
+++ 
b/components/camel-ai/camel-openai/src/main/java/org/apache/camel/component/openai/OpenAIProducer.java
@@ -251,11 +251,11 @@ public class OpenAIProducer extends DefaultAsyncProducer {
 
         String systemPrompt = in.getHeader(OpenAIConstants.SYSTEM_MESSAGE, 
String.class);
         String developerPrompt = 
in.getHeader(OpenAIConstants.DEVELOPER_MESSAGE, String.class);
-        if (systemPrompt == null || systemPrompt.isEmpty() && 
ObjectHelper.isNotEmpty(config.getSystemMessage())) {
+        if ((systemPrompt == null || systemPrompt.isEmpty()) && 
ObjectHelper.isNotEmpty(config.getSystemMessage())) {
             systemPrompt = config.getSystemMessage();
         }
-        if (developerPrompt == null
-                || developerPrompt.isEmpty() && 
ObjectHelper.isNotEmpty(config.getDeveloperMessage())) {
+        if ((developerPrompt == null || developerPrompt.isEmpty())
+                && ObjectHelper.isNotEmpty(config.getDeveloperMessage())) {
             developerPrompt = config.getDeveloperMessage();
         }
 
@@ -308,7 +308,7 @@ public class OpenAIProducer extends DefaultAsyncProducer {
     private ChatCompletionMessageParam buildUserMessage(Message in, 
OpenAIConfiguration config) throws Exception {
         Object body = in.getBody();
         String userPrompt = in.getHeader(OpenAIConstants.USER_MESSAGE, 
String.class);
-        if (userPrompt == null || userPrompt.isEmpty() && 
ObjectHelper.isNotEmpty(config.getUserMessage())) {
+        if ((userPrompt == null || userPrompt.isEmpty()) && 
ObjectHelper.isNotEmpty(config.getUserMessage())) {
             userPrompt = config.getUserMessage();
         }
 
diff --git 
a/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIEmptyUserMessageBodyPromptTest.java
 
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIEmptyUserMessageBodyPromptTest.java
new file mode 100644
index 000000000000..b8a871657673
--- /dev/null
+++ 
b/components/camel-ai/camel-openai/src/test/java/org/apache/camel/component/openai/OpenAIEmptyUserMessageBodyPromptTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.openai;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.infra.openai.mock.OpenAIMock;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * When the userMessage option is configured to an empty string, it must not 
shadow the prompt supplied in the message
+ * body. An operator-precedence bug used to set the (empty) configured message 
as the prompt, dropping the body and
+ * failing with "No input provided".
+ */
+public class OpenAIEmptyUserMessageBodyPromptTest extends CamelTestSupport {
+
+    @RegisterExtension
+    public OpenAIMock openAIMock = new OpenAIMock().builder()
+            .when("hello from body")
+            .replyWith("mock reply")
+            .end()
+            .build();
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:empty-user-message")
+                        
.to("openai:chat-completion?model=gpt-4&apiKey=dummy&baseUrl="
+                            + openAIMock.getBaseUrl() + "/v1");
+            }
+        };
+    }
+
+    @Test
+    void bodyPromptIsUsedWhenConfiguredUserMessageIsEmpty() {
+        // Configure the route's endpoint with an empty userMessage - the 
exact case the precedence bug mishandled.
+        OpenAIEndpoint endpoint = context.getEndpoints().stream()
+                .filter(OpenAIEndpoint.class::isInstance)
+                .map(OpenAIEndpoint.class::cast)
+                .findFirst()
+                .orElseThrow(() -> new IllegalStateException("no 
OpenAIEndpoint found"));
+        endpoint.getConfiguration().setUserMessage("");
+
+        Exchange result = template.request("direct:empty-user-message", e -> 
e.getIn().setBody("hello from body"));
+
+        assertThat(result.getException()).isNull();
+        assertThat(result.getMessage().getBody(String.class)).contains("mock 
reply");
+    }
+}

Reply via email to