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

jamesnetherton 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 e5e2a3594dc CAMEL-22328: Avoid using JsonNode for langchain4j-tools 
parameter header values
e5e2a3594dc is described below

commit e5e2a3594dc533d1a42fc037abd645d93b9815fb
Author: James Netherton <[email protected]>
AuthorDate: Thu Aug 7 10:40:14 2025 +0100

    CAMEL-22328: Avoid using JsonNode for langchain4j-tools parameter header 
values
---
 .../camel-ai/camel-langchain4j-tools/pom.xml       | 10 ++--
 .../tools/LangChain4jToolsProducer.java            | 32 +++++++++++-
 ...ain4jToolParameterValueTypeConversionTest.java} | 57 +++++++++++++---------
 .../langchain4j/tools/LangChain4jToolTest.java     | 10 ++--
 4 files changed, 76 insertions(+), 33 deletions(-)

diff --git a/components/camel-ai/camel-langchain4j-tools/pom.xml 
b/components/camel-ai/camel-langchain4j-tools/pom.xml
index af8356c97df..771a9ae104f 100644
--- a/components/camel-ai/camel-langchain4j-tools/pom.xml
+++ b/components/camel-ai/camel-langchain4j-tools/pom.xml
@@ -45,6 +45,10 @@
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-support</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-jackson</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>dev.langchain4j</groupId>
@@ -56,11 +60,7 @@
             <artifactId>langchain4j-core</artifactId>
             <version>${langchain4j-version}</version>
         </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-databind</artifactId>
-            <version>${jackson2-version}</version>
-        </dependency>
+
         <!-- test -->
         <dependency>
             <groupId>org.apache.camel</groupId>
diff --git 
a/components/camel-ai/camel-langchain4j-tools/src/main/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolsProducer.java
 
b/components/camel-ai/camel-langchain4j-tools/src/main/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolsProducer.java
index 319b20441c9..1fd5da30e1e 100644
--- 
a/components/camel-ai/camel-langchain4j-tools/src/main/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolsProducer.java
+++ 
b/components/camel-ai/camel-langchain4j-tools/src/main/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolsProducer.java
@@ -23,6 +23,11 @@ import java.util.Set;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.BooleanNode;
+import com.fasterxml.jackson.databind.node.DoubleNode;
+import com.fasterxml.jackson.databind.node.IntNode;
+import com.fasterxml.jackson.databind.node.LongNode;
+import com.fasterxml.jackson.databind.node.TextNode;
 import dev.langchain4j.agent.tool.ToolExecutionRequest;
 import dev.langchain4j.agent.tool.ToolSpecification;
 import dev.langchain4j.data.message.AiMessage;
@@ -35,6 +40,7 @@ import dev.langchain4j.model.output.FinishReason;
 import dev.langchain4j.model.output.Response;
 import org.apache.camel.Exchange;
 import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.TypeConverter;
 import 
org.apache.camel.component.langchain4j.tools.spec.CamelToolExecutorCache;
 import 
org.apache.camel.component.langchain4j.tools.spec.CamelToolSpecification;
 import org.apache.camel.support.DefaultProducer;
@@ -149,11 +155,33 @@ public class LangChain4jToolsProducer extends 
DefaultProducer {
                     .filter(c -> 
c.getToolSpecification().name().equals(toolName)).findFirst().get();
 
             try {
+                TypeConverter typeConverter = 
endpoint.getCamelContext().getTypeConverter();
+
                 // Map Json to Header
                 JsonNode jsonNode = 
objectMapper.readValue(toolExecutionRequest.arguments(), JsonNode.class);
-
                 jsonNode.fieldNames()
-                        .forEachRemaining(name -> 
exchange.getMessage().setHeader(name, jsonNode.get(name)));
+                        .forEachRemaining(name -> {
+                            final JsonNode value = jsonNode.get(name);
+                            Object headerValue;
+
+                            // Try to get values for the known tool parameter 
types
+                            if (value instanceof TextNode) {
+                                headerValue = 
typeConverter.convertTo(String.class, value);
+                            } else if (value instanceof IntNode) {
+                                headerValue = 
typeConverter.convertTo(Integer.class, value);
+                            } else if (value instanceof LongNode) {
+                                headerValue = 
typeConverter.convertTo(Long.class, value);
+                            } else if (value instanceof DoubleNode) {
+                                headerValue = 
typeConverter.convertTo(Double.class, value);
+                            } else if (value instanceof BooleanNode) {
+                                headerValue = 
typeConverter.convertTo(Boolean.class, value);
+                            } else {
+                                // Fallback to JsonNode to enable the value to 
be extracted elsewhere
+                                headerValue = value;
+                            }
+
+                            exchange.getMessage().setHeader(name, headerValue);
+                        });
 
                 // Execute the consumer route
 
diff --git 
a/components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolTest.java
 
b/components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolParameterValueTypeConversionTest.java
similarity index 58%
copy from 
components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolTest.java
copy to 
components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolParameterValueTypeConversionTest.java
index cae003e175e..be03fae61f6 100644
--- 
a/components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolTest.java
+++ 
b/components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolParameterValueTypeConversionTest.java
@@ -25,6 +25,7 @@ import dev.langchain4j.data.message.UserMessage;
 import dev.langchain4j.model.chat.ChatModel;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
+import org.apache.camel.Message;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.test.infra.openai.mock.OpenAIMock;
 import org.apache.camel.test.junit5.CamelTestSupport;
@@ -32,16 +33,19 @@ import org.assertj.core.api.Assertions;
 import org.junit.jupiter.api.RepeatedTest;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
-public class LangChain4jToolTest extends CamelTestSupport {
-
-    protected final String nameFromDB = "pippo";
+class LangChain4jToolParameterValueTypeConversionTest extends CamelTestSupport 
{
     protected ChatModel chatModel;
 
     @RegisterExtension
     static OpenAIMock openAIMock = new OpenAIMock().builder()
-            .when("What is the name of the user 1?\n")
-            .invokeTool("QueryUserByNumber")
-            .withParam("number", 1)
+            .when("A test user message\n")
+            .invokeTool("TestTool")
+            .withParam("int", 1)
+            .withParam("intNumeric", 2)
+            .withParam("long", Long.MIN_VALUE)
+            .withParam("double", 1.0)
+            .withParam("boolean", true)
+            .withParam("string", "1")
             .build();
 
     @Override
@@ -69,37 +73,46 @@ public class LangChain4jToolTest extends CamelTestSupport {
             public void configure() {
 
                 from("direct:test")
-                        .to("langchain4j-tools:test1?tags=user")
+                        .to("langchain4j-tools:test?tags=test")
                         .log("response is: ${body}");
 
-                
from("langchain4j-tools:test1?tags=user&name=QueryUserByNumber&description=Query
 user database by number&parameter.number=integer")
-                        .setBody(simple("{\"name\": \"pippo\"}"));
-
-                from("langchain4j-tools:test1?tags=user&description=Does not 
really do anything")
-                        .setBody(constant("Hello World"));
-
-                
from("langchain4j-tools:test1?tags=user&name=DoesNothing&description=Also does 
not really do anything, but has a name")
-                        .setBody(constant("Hello World"));
-
+                
from("langchain4j-tools:test?tags=test&name=TestTool&description=Test 
Tool&parameter.int=integer&parameter.intNumeric=number&parameter.long=number&parameter.double=number&parameter.boolean=boolean&parameter.string=string")
+                        .setBody(simple("{\"content\": \"fake response\"}"));
             }
         };
     }
 
     @RepeatedTest(1)
-    public void testSimpleInvocation() {
+    void parameterValueTypeConversion() {
         List<ChatMessage> messages = new ArrayList<>();
         messages.add(new SystemMessage(
                 """
                         You provide the requested information using the 
functions you hava available. You can invoke the functions to obtain the 
information you need to complete the answer.
                         """));
         messages.add(new UserMessage("""
-                What is the name of the user 1?
+                A test user message
                 """));
 
-        Exchange message = 
fluentTemplate.to("direct:test").withBody(messages).request(Exchange.class);
+        Exchange exchange = 
fluentTemplate.to("direct:test").withBody(messages).request(Exchange.class);
+
+        Assertions.assertThat(exchange).isNotNull();
+        Message message = exchange.getMessage();
+        
Assertions.assertThat(message.getHeader("int")).isInstanceOf(Integer.class);
+        Assertions.assertThat(message.getHeader("int")).isEqualTo(1);
+
+        
Assertions.assertThat(message.getHeader("intNumeric")).isInstanceOf(Integer.class);
+        Assertions.assertThat(message.getHeader("intNumeric")).isEqualTo(2);
+
+        
Assertions.assertThat(message.getHeader("long")).isInstanceOf(Long.class);
+        
Assertions.assertThat(message.getHeader("long")).isEqualTo(Long.MIN_VALUE);
+
+        
Assertions.assertThat(message.getHeader("double")).isInstanceOf(Double.class);
+        Assertions.assertThat(message.getHeader("double")).isEqualTo(1.0);
+
+        
Assertions.assertThat(message.getHeader("boolean")).isInstanceOf(Boolean.class);
+        Assertions.assertThat(message.getHeader("boolean")).isEqualTo(true);
 
-        Assertions.assertThat(message).isNotNull();
-        final String responseContent = 
message.getMessage().getBody().toString();
-        
Assertions.assertThat(responseContent).containsIgnoringCase(nameFromDB);
+        
Assertions.assertThat(message.getHeader("string")).isInstanceOf(String.class);
+        Assertions.assertThat(message.getHeader("string")).isEqualTo("1");
     }
 }
diff --git 
a/components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolTest.java
 
b/components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolTest.java
index cae003e175e..82047da2ea6 100644
--- 
a/components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolTest.java
+++ 
b/components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolTest.java
@@ -25,6 +25,7 @@ import dev.langchain4j.data.message.UserMessage;
 import dev.langchain4j.model.chat.ChatModel;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
+import org.apache.camel.Message;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.test.infra.openai.mock.OpenAIMock;
 import org.apache.camel.test.junit5.CamelTestSupport;
@@ -96,10 +97,11 @@ public class LangChain4jToolTest extends CamelTestSupport {
                 What is the name of the user 1?
                 """));
 
-        Exchange message = 
fluentTemplate.to("direct:test").withBody(messages).request(Exchange.class);
+        Exchange exchange = 
fluentTemplate.to("direct:test").withBody(messages).request(Exchange.class);
 
-        Assertions.assertThat(message).isNotNull();
-        final String responseContent = 
message.getMessage().getBody().toString();
-        
Assertions.assertThat(responseContent).containsIgnoringCase(nameFromDB);
+        Assertions.assertThat(exchange).isNotNull();
+        Message message = exchange.getMessage();
+        
Assertions.assertThat(message.getBody(String.class)).containsIgnoringCase(nameFromDB);
+        
Assertions.assertThat(message.getHeader("number")).isInstanceOf(Integer.class);
     }
 }

Reply via email to