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¶meter.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¶meter.int=integer¶meter.intNumeric=number¶meter.long=number¶meter.double=number¶meter.boolean=boolean¶meter.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);
}
}