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-quarkus.git


The following commit(s) were added to refs/heads/main by this push:
     new b386ce31af Add tests and support for langchain4j-agent custom tools
b386ce31af is described below

commit b386ce31af0962464e4980656774b16337a17593
Author: James Netherton <[email protected]>
AuthorDate: Wed Oct 15 07:21:09 2025 +0100

    Add tests and support for langchain4j-agent custom tools
---
 .../deployment/SupportLangchain4jProcessor.java    | 21 ++++++++++++
 integration-tests/langchain4j-agent/pom.xml        |  4 +++
 .../langchain4j/agent/it/AgentProducers.java       |  9 +++++
 .../agent/it/Langchain4jAgentResource.java         | 19 ++++++++++
 .../agent/it/Langchain4jAgentRoutes.java           |  3 ++
 .../langchain4j/agent/it/tool/AdditionTool.java    | 40 ++++++++++++++++++++++
 .../langchain4j/agent/it/Langchain4jAgentTest.java | 12 +++++++
 ..._chat-891a47a3-531c-48fd-920c-b8eff546c7ff.json | 24 +++++++++++++
 ..._chat-a14f44e0-8921-47ab-a69e-cf038e7cb358.json | 24 +++++++++++++
 9 files changed, 156 insertions(+)

diff --git 
a/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/SupportLangchain4jProcessor.java
 
b/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/SupportLangchain4jProcessor.java
index 8d97bfef69..f7d0e243ff 100644
--- 
a/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/SupportLangchain4jProcessor.java
+++ 
b/extensions-support/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/deployment/SupportLangchain4jProcessor.java
@@ -25,6 +25,7 @@ import java.util.stream.Stream;
 import com.fasterxml.jackson.databind.JsonDeserializer;
 import com.fasterxml.jackson.databind.JsonSerializer;
 import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import dev.langchain4j.agent.tool.Tool;
 import dev.langchain4j.guardrail.InputGuardrail;
 import dev.langchain4j.guardrail.JsonExtractorOutputGuardrail;
 import dev.langchain4j.guardrail.OutputGuardrail;
@@ -188,6 +189,26 @@ class SupportLangchain4jProcessor {
         
reflectiveClass.produce(ReflectiveClassBuildItem.builder(guardrailTypes.toArray(new
 String[0])).build());
     }
 
+    @BuildStep
+    void registerCustomToolsForReflection(
+            CombinedIndexBuildItem combinedIndex,
+            BuildProducer<ReflectiveClassBuildItem> reflectiveClass) {
+
+        Set<String> customToolClasses = combinedIndex.getIndex()
+                .getAnnotations(Tool.class)
+                .stream()
+                .map(AnnotationInstance::target)
+                .map(AnnotationTarget::asMethod)
+                .map(MethodInfo::declaringClass)
+                .map(ClassInfo::name)
+                .map(DotName::toString)
+                .collect(Collectors.toSet());
+
+        
reflectiveClass.produce(ReflectiveClassBuildItem.builder(customToolClasses.toArray(new
 String[0]))
+                .methods()
+                .build());
+    }
+
     @BuildStep
     void 
registerLangChain4jNlpTypesForReflection(BuildProducer<ReflectiveClassBuildItem>
 reflectiveClass) {
         
reflectiveClass.produce(ReflectiveClassBuildItem.builder(SentenceDetectorFactory.class).build());
diff --git a/integration-tests/langchain4j-agent/pom.xml 
b/integration-tests/langchain4j-agent/pom.xml
index af0ee36572..7a4a9b02e7 100644
--- a/integration-tests/langchain4j-agent/pom.xml
+++ b/integration-tests/langchain4j-agent/pom.xml
@@ -47,6 +47,10 @@
             <groupId>io.quarkus</groupId>
             <artifactId>quarkus-resteasy</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-resteasy-jackson</artifactId>
+        </dependency>
         <dependency>
             <groupId>dev.langchain4j</groupId>
             <artifactId>langchain4j-ollama</artifactId>
diff --git 
a/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/AgentProducers.java
 
b/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/AgentProducers.java
index f60dd5ad05..ac1714d6b2 100644
--- 
a/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/AgentProducers.java
+++ 
b/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/AgentProducers.java
@@ -52,6 +52,7 @@ import 
org.apache.camel.quarkus.component.langchain4j.agent.it.guardrail.Validat
 import 
org.apache.camel.quarkus.component.langchain4j.agent.it.guardrail.ValidationSuccessInputGuardrail;
 import 
org.apache.camel.quarkus.component.langchain4j.agent.it.guardrail.ValidationSuccessOutputGuardrail;
 import 
org.apache.camel.quarkus.component.langchain4j.agent.it.service.TestPojoAiAgent;
+import 
org.apache.camel.quarkus.component.langchain4j.agent.it.tool.AdditionTool;
 import 
org.apache.camel.quarkus.component.langchain4j.agent.it.util.PersistentChatMemoryStore;
 import org.eclipse.microprofile.config.inject.ConfigProperty;
 
@@ -212,4 +213,12 @@ public class AgentProducers {
         return new TestPojoAiAgent(new AgentConfiguration()
                 .withChatModel(chatModel), objectMapper);
     }
+
+    @Produces
+    @Identifier("agentWithCustomTools")
+    Agent agentWithCustomTools(@Identifier("ollamaLlama31Model") ChatModel 
chatModel) {
+        return new AgentWithoutMemory(new AgentConfiguration()
+                .withChatModel(chatModel)
+                .withCustomTools(List.of(new AdditionTool())));
+    }
 }
diff --git 
a/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/Langchain4jAgentResource.java
 
b/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/Langchain4jAgentResource.java
index 311d9c1e5d..2edf87679d 100644
--- 
a/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/Langchain4jAgentResource.java
+++ 
b/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/Langchain4jAgentResource.java
@@ -33,6 +33,7 @@ import org.apache.camel.FluentProducerTemplate;
 import org.apache.camel.component.langchain4j.agent.api.AiAgentBody;
 import 
org.apache.camel.quarkus.component.langchain4j.agent.it.guardrail.ValidationSuccessInputGuardrail;
 import 
org.apache.camel.quarkus.component.langchain4j.agent.it.guardrail.ValidationSuccessOutputGuardrail;
+import 
org.apache.camel.quarkus.component.langchain4j.agent.it.tool.AdditionTool;
 
 import static 
org.apache.camel.component.langchain4j.agent.api.Headers.MEMORY_ID;
 import static 
org.apache.camel.component.langchain4j.agent.api.Headers.SYSTEM_MESSAGE;
@@ -206,4 +207,22 @@ public class Langchain4jAgentResource {
 
         return Response.ok(result.trim()).build();
     }
+
+    @Path("/custom/tools")
+    @POST
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response chatWithCustomTools(String userMessage) {
+        try {
+            String result = 
producerTemplate.to("direct:agent-with-custom-tools")
+                    .withBody(userMessage)
+                    .request(String.class);
+
+            return Response.ok(
+                    Map.of("result", result.trim(), "toolWasInvoked", 
AdditionTool.isToolWasInvoked()))
+                    .build();
+        } finally {
+            AdditionTool.reset();
+        }
+    }
 }
diff --git 
a/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/Langchain4jAgentRoutes.java
 
b/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/Langchain4jAgentRoutes.java
index 3c09c356e6..3d65065444 100644
--- 
a/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/Langchain4jAgentRoutes.java
+++ 
b/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/Langchain4jAgentRoutes.java
@@ -55,5 +55,8 @@ public class Langchain4jAgentRoutes extends RouteBuilder {
 
         from("langchain4j-tools:userDb?tags=users&description=Query user 
database by user ID&parameter.userId=integer")
                 .setBody().constant("{\"name\": \"" + USER_JOHN + "\", \"id\": 
\"123\"}");
+
+        from("direct:agent-with-custom-tools")
+                
.to("langchain4j-agent:test-agent-custom-tools?agent=#agentWithCustomTools");
     }
 }
diff --git 
a/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/tool/AdditionTool.java
 
b/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/tool/AdditionTool.java
new file mode 100644
index 0000000000..64c2a72c0d
--- /dev/null
+++ 
b/integration-tests/langchain4j-agent/src/main/java/org/apache/camel/quarkus/component/langchain4j/agent/it/tool/AdditionTool.java
@@ -0,0 +1,40 @@
+/*
+ * 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.quarkus.component.langchain4j.agent.it.tool;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import dev.langchain4j.agent.tool.P;
+import dev.langchain4j.agent.tool.Tool;
+
+public class AdditionTool {
+    private static final AtomicBoolean TOOL_WAS_INVOKED = new 
AtomicBoolean(false);
+
+    @Tool("Adds two numbers")
+    public Integer addNumbers(@P("First number") Integer a, @P("Second 
number") Integer b) {
+        TOOL_WAS_INVOKED.set(true);
+        return a + b;
+    }
+
+    public static boolean isToolWasInvoked() {
+        return TOOL_WAS_INVOKED.get();
+    }
+
+    public static void reset() {
+        TOOL_WAS_INVOKED.set(false);
+    }
+}
diff --git 
a/integration-tests/langchain4j-agent/src/test/java/org/apache/camel/quarkus/component/langchain4j/agent/it/Langchain4jAgentTest.java
 
b/integration-tests/langchain4j-agent/src/test/java/org/apache/camel/quarkus/component/langchain4j/agent/it/Langchain4jAgentTest.java
index fce52dbc17..bc73e83ed6 100644
--- 
a/integration-tests/langchain4j-agent/src/test/java/org/apache/camel/quarkus/component/langchain4j/agent/it/Langchain4jAgentTest.java
+++ 
b/integration-tests/langchain4j-agent/src/test/java/org/apache/camel/quarkus/component/langchain4j/agent/it/Langchain4jAgentTest.java
@@ -211,4 +211,16 @@ class Langchain4jAgentTest {
                         "name", is(USER_JOHN),
                         "description", notNullValue());
     }
+
+    @Test
+    void agentWithCustomTools() {
+        RestAssured.given()
+                .body("Calculate the addition of 10 + 5")
+                .post("/langchain4j-agent/custom/tools")
+                .then()
+                .statusCode(200)
+                .body(
+                        "result", containsStringIgnoringCase("15"),
+                        "toolWasInvoked", is(true));
+    }
 }
diff --git 
a/integration-tests/langchain4j-agent/src/test/resources/mappings/api_chat-891a47a3-531c-48fd-920c-b8eff546c7ff.json
 
b/integration-tests/langchain4j-agent/src/test/resources/mappings/api_chat-891a47a3-531c-48fd-920c-b8eff546c7ff.json
new file mode 100644
index 0000000000..24a597189e
--- /dev/null
+++ 
b/integration-tests/langchain4j-agent/src/test/resources/mappings/api_chat-891a47a3-531c-48fd-920c-b8eff546c7ff.json
@@ -0,0 +1,24 @@
+{
+  "id" : "891a47a3-531c-48fd-920c-b8eff546c7ff",
+  "name" : "api_chat",
+  "request" : {
+    "url" : "/api/chat",
+    "method" : "POST",
+    "bodyPatterns" : [ {
+      "equalToJson" : "{\n  \"model\" : \"llama3.1:latest\",\n  \"messages\" : 
[ {\n    \"role\" : \"user\",\n    \"content\" : \"Calculate the addition of 10 
+ 5\"\n  }, {\n    \"role\" : \"assistant\",\n    \"tool_calls\" : [ {\n      
\"function\" : {\n        \"name\" : \"addNumbers\",\n        \"arguments\" : 
{\n          \"arg1\" : 5,\n          \"arg0\" : 10\n        }\n      }\n    } 
]\n  }, {\n    \"role\" : \"tool\",\n    \"content\" : \"15\"\n  } ],\n  
\"options\" : {\n    \"te [...]
+      "ignoreArrayOrder" : true,
+      "ignoreExtraElements" : false
+    } ]
+  },
+  "response" : {
+    "status" : 200,
+    "body" : 
"{\"model\":\"llama3.1:latest\",\"created_at\":\"2025-10-14T13:43:19.525346678Z\",\"message\":{\"role\":\"assistant\",\"content\":\"The
 result of the calculation is 
15.\"},\"done_reason\":\"stop\",\"done\":true,\"total_duration\":2396046130,\"load_duration\":18256574,\"prompt_eval_count\":108,\"prompt_eval_duration\":1392355389,\"eval_count\":10,\"eval_duration\":984595879}",
+    "headers" : {
+      "Date" : "Tue, 14 Oct 2025 13:43:19 GMT",
+      "Content-Type" : "application/json; charset=utf-8"
+    }
+  },
+  "uuid" : "891a47a3-531c-48fd-920c-b8eff546c7ff",
+  "persistent" : true,
+  "insertionIndex" : 20
+}
\ No newline at end of file
diff --git 
a/integration-tests/langchain4j-agent/src/test/resources/mappings/api_chat-a14f44e0-8921-47ab-a69e-cf038e7cb358.json
 
b/integration-tests/langchain4j-agent/src/test/resources/mappings/api_chat-a14f44e0-8921-47ab-a69e-cf038e7cb358.json
new file mode 100644
index 0000000000..3d85f6c1cd
--- /dev/null
+++ 
b/integration-tests/langchain4j-agent/src/test/resources/mappings/api_chat-a14f44e0-8921-47ab-a69e-cf038e7cb358.json
@@ -0,0 +1,24 @@
+{
+  "id" : "a14f44e0-8921-47ab-a69e-cf038e7cb358",
+  "name" : "api_chat",
+  "request" : {
+    "url" : "/api/chat",
+    "method" : "POST",
+    "bodyPatterns" : [ {
+      "equalToJson" : "{\n  \"model\" : \"llama3.1:latest\",\n  \"messages\" : 
[ {\n    \"role\" : \"user\",\n    \"content\" : \"Calculate the addition of 10 
+ 5\"\n  } ],\n  \"options\" : {\n    \"temperature\" : 0.3,\n    \"stop\" : [ 
]\n  },\n  \"stream\" : false,\n  \"tools\" : [ {\n    \"type\" : 
\"function\",\n    \"function\" : {\n      \"name\" : \"addNumbers\",\n      
\"description\" : \"Adds two numbers\",\n      \"parameters\" : {\n        
\"type\" : \"object\",\n        \"pr [...]
+      "ignoreArrayOrder" : true,
+      "ignoreExtraElements" : false
+    } ]
+  },
+  "response" : {
+    "status" : 200,
+    "body" : 
"{\"model\":\"llama3.1:latest\",\"created_at\":\"2025-10-14T13:43:17.079209926Z\",\"message\":{\"role\":\"assistant\",\"content\":\"\",\"tool_calls\":[{\"function\":{\"name\":\"addNumbers\",\"arguments\":{\"arg0\":10,\"arg1\":5}}}]},\"done_reason\":\"stop\",\"done\":true,\"total_duration\":20461789347,\"load_duration\":13165365726,\"prompt_eval_count\":188,\"prompt_eval_duration\":4650715215,\"eval_count\":25,\"eval_duration\":2644447381}",
+    "headers" : {
+      "Date" : "Tue, 14 Oct 2025 13:43:17 GMT",
+      "Content-Type" : "application/json; charset=utf-8"
+    }
+  },
+  "uuid" : "a14f44e0-8921-47ab-a69e-cf038e7cb358",
+  "persistent" : true,
+  "insertionIndex" : 21
+}
\ No newline at end of file

Reply via email to