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

orpiske 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 2885c7de6b4 CAMEL-22475: Camel LangChain4j Agent: custom tools feature
2885c7de6b4 is described below

commit 2885c7de6b40cd2445f8a553b13017caf7d781f9
Author: Zineb Bendhiba <[email protected]>
AuthorDate: Wed Oct 1 09:54:43 2025 +0200

    CAMEL-22475: Camel LangChain4j Agent: custom tools feature
---
 .../langchain4j/agent/api/AgentConfiguration.java  |  21 +++
 .../langchain4j/agent/api/AgentWithMemory.java     |  10 +-
 .../langchain4j/agent/api/AgentWithoutMemory.java  |  12 +-
 .../src/main/docs/langchain4j-agent-component.adoc | 128 ++++++++++++++++-
 .../integration/LangChain4jAgentCustomToolsIT.java | 150 ++++++++++++++++++++
 .../integration/LangChain4jAgentMixedToolsIT.java  | 156 +++++++++++++++++++++
 .../langchain4j/agent/pojos/CalculatorTool.java    |  41 ++++++
 .../langchain4j/agent/pojos/StringTool.java        |  36 +++++
 .../langchain4j/agent/pojos/WeatherTool.java       |  39 ++++++
 9 files changed, 587 insertions(+), 6 deletions(-)

diff --git 
a/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentConfiguration.java
 
b/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentConfiguration.java
index fcdeaf0f8f8..c0de0e01fe1 100644
--- 
a/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentConfiguration.java
+++ 
b/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentConfiguration.java
@@ -57,6 +57,7 @@ public class AgentConfiguration {
     private RetrievalAugmentor retrievalAugmentor;
     private List<Class<?>> inputGuardrailClasses;
     private List<Class<?>> outputGuardrailClasses;
+    private List<Object> customTools; // Custom LangChain4j tools
 
     /**
      * Gets the configured chat model.
@@ -265,6 +266,26 @@ public class AgentConfiguration {
                 .collect(java.util.stream.Collectors.toList());
     }
 
+    /**
+     * Gets the configured custom tools.
+     *
+     * @return the custom tools list, or {@code null} if not configured
+     */
+    public List<Object> getCustomTools() {
+        return customTools;
+    }
+
+    /**
+     * Sets the custom tools for this agent configuration.
+     *
+     * @param  customTools the list of tool instances with @Tool methods
+     * @return             this configuration instance for method chaining
+     */
+    public AgentConfiguration withCustomTools(List<Object> customTools) {
+        this.customTools = customTools;
+        return this;
+    }
+
     /**
      * Loads a guardrail class by its fully qualified name using reflection.
      *
diff --git 
a/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentWithMemory.java
 
b/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentWithMemory.java
index ba1ad3f6211..cf0bb64e7b6 100644
--- 
a/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentWithMemory.java
+++ 
b/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentWithMemory.java
@@ -39,7 +39,7 @@ public class AgentWithMemory implements Agent {
     }
 
     @Override
-    public String chat(AiAgentBody aiAgentBody, ToolProvider toolProvider) {
+    public String chat(AiAgentBody<?> aiAgentBody, ToolProvider toolProvider) {
         AiAgentWithMemoryService agentService = 
createAiAgentService(toolProvider);
 
         return aiAgentBody.getSystemMessage() != null
@@ -48,7 +48,8 @@ public class AgentWithMemory implements Agent {
     }
 
     /**
-     * Create AI service with a single universal tool that handles multiple 
Camel routes and Memory Provider
+     * Create AI service with a single universal tool that handles multiple 
Camel routes, Memory Provider, and
+     * additional tools
      */
     private AiAgentWithMemoryService createAiAgentService(ToolProvider 
toolProvider) {
         var builder = AiServices.builder(AiAgentWithMemoryService.class)
@@ -60,6 +61,11 @@ public class AgentWithMemory implements Agent {
             builder.toolProvider(toolProvider);
         }
 
+        // Additional custom LangChain4j Tool Instances (objects with @Tool 
methods)
+        if (configuration.getCustomTools() != null && 
!configuration.getCustomTools().isEmpty()) {
+            builder.tools(configuration.getCustomTools());
+        }
+
         // RAG
         if (configuration.getRetrievalAugmentor() != null) {
             builder.retrievalAugmentor(configuration.getRetrievalAugmentor());
diff --git 
a/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentWithoutMemory.java
 
b/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentWithoutMemory.java
index dca18d96d98..b8ca88abee2 100644
--- 
a/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentWithoutMemory.java
+++ 
b/components/camel-ai/camel-langchain4j-agent-api/src/main/java/org/apache/camel/component/langchain4j/agent/api/AgentWithoutMemory.java
@@ -36,7 +36,7 @@ public class AgentWithoutMemory implements Agent {
     }
 
     @Override
-    public String chat(AiAgentBody aiAgentBody, ToolProvider toolProvider) {
+    public String chat(AiAgentBody<?> aiAgentBody, ToolProvider toolProvider) {
         AiAgentWithoutMemoryService agentService = 
createAiAgentService(toolProvider);
 
         return aiAgentBody.getSystemMessage() != null
@@ -45,9 +45,10 @@ public class AgentWithoutMemory implements Agent {
     }
 
     /**
-     * Create AI service with a single universal tool that handles multiple 
Camel routes
+     * Create AI service with a single universal tool that handles multiple 
Camel routes and additional tools
      */
-    private AiAgentWithoutMemoryService createAiAgentService(ToolProvider 
toolProvider) {
+    private AiAgentWithoutMemoryService createAiAgentService(
+            ToolProvider toolProvider) {
         var builder = AiServices.builder(AiAgentWithoutMemoryService.class)
                 .chatModel(configuration.getChatModel());
 
@@ -56,6 +57,11 @@ public class AgentWithoutMemory implements Agent {
             builder.toolProvider(toolProvider);
         }
 
+        // Additional custom LangChain4j Tool Instances (objects with @Tool 
methods)
+        if (configuration.getCustomTools() != null && 
!configuration.getCustomTools().isEmpty()) {
+            builder.tools(configuration.getCustomTools());
+        }
+
         // RAG
         if (configuration.getRetrievalAugmentor() != null) {
             builder.retrievalAugmentor(configuration.getRetrievalAugmentor());
diff --git 
a/components/camel-ai/camel-langchain4j-agent/src/main/docs/langchain4j-agent-component.adoc
 
b/components/camel-ai/camel-langchain4j-agent/src/main/docs/langchain4j-agent-component.adoc
index 58438c12f7e..668341d8a7a 100644
--- 
a/components/camel-ai/camel-langchain4j-agent/src/main/docs/langchain4j-agent-component.adoc
+++ 
b/components/camel-ai/camel-langchain4j-agent/src/main/docs/langchain4j-agent-component.adoc
@@ -35,7 +35,7 @@ The component has been simplified to use only two main 
options:
 * **agent**: Reference to an `Agent` implementation registered in the Camel 
registry
 * **tags**: Tags for discovering and calling Camel route tools (optional)
 
-All other configuration (chat models, memory, RAG, guardrails) is now handled 
through the `AgentConfiguration` when creating agents.
+All other configuration (chat models, memory, RAG, guardrails, custom tools) 
is now handled through the `AgentConfiguration` when creating agents.
 
 == URI format
 
@@ -257,6 +257,132 @@ String response = 
template.requestBodyAndHeader("direct:chat",
 There's no need to add Camel LangChain4j Tools component as a dependency when 
using the tools with LangChain4j Agent component.
 ====
 
+=== Custom LangChain4j Tools
+
+You can also add custom LangChain4j tools using the `@Tool` annotation. These 
tools are passed directly to the agent via the `customTools` configuration 
parameter.
+
+==== Creating Custom LangChain4j Tools
+
+Create a class with methods annotated with `@Tool`:
+
+[source,java]
+----
+import dev.langchain4j.agent.tool.P;
+import dev.langchain4j.agent.tool.Tool;
+
+public class CalculatorTool {
+    
+    @Tool("Adds two numbers")
+    public int add(@P("First number") int a, @P("Second number") int b) {
+        return a + b;
+    }
+    
+    @Tool("Multiplies two numbers")
+    public int multiply(@P("First number") int a, @P("Second number") int b) {
+        return a * b;
+    }
+    
+    @Tool("Gets the square root of a number")
+    public double sqrt(@P("Number") double x) {
+        return Math.sqrt(x);
+    }
+}
+----
+
+==== Using Custom LangChain4j Tools with Agent
+
+Pass your custom tool instances to the agent configuration:
+
+[source,java]
+----
+// Create tool instances
+CalculatorTool calculator = new CalculatorTool();
+WeatherTool weather = new WeatherTool();
+
+// Create agent configuration with custom tools
+AgentConfiguration config = new AgentConfiguration()
+    .withChatModel(chatModel)
+    .withCustomTools(Arrays.asList(calculator, weather));
+
+// Create agent
+Agent agent = new AgentWithoutMemory(config);
+
+// Register agent in Camel context
+context.getRegistry().bind("customToolsAgent", agent);
+----
+
+==== Route Configuration
+
+Use the agent with custom tools in your routes:
+
+[source,java]
+----
+from("direct:chat")
+    .to("langchain4j-agent:assistant?agent=#customToolsAgent")
+    .to("mock:agent-response");
+----
+
+==== Usage Example
+
+[source,java]
+----
+String response = template.requestBody("direct:chat", 
+    "Calculate 10 * 5 and tell me the weather in Paris", String.class);
+----
+
+[NOTE]
+====
+Custom LangChain4j tools are executed directly by the LangChain4j framework. 
No additional configuration or tool Executor is needed for tool execution.
+====
+
+=== Mixed Tools (Camel Routes + Custom LangChain4j Tools)
+
+You can combine both Camel route tools (via `tags`) and custom LangChain4j 
tools (via `customTools`) in the same agent:
+
+[source,java]
+----
+// Define Camel route tools
+from("langchain4j-tools:weatherService?tags=weather&description=Get current 
weather information&parameter.location=string")
+    .setBody(constant("{\"weather\": \"sunny\", \"location\": \"Current 
Location\"}"));
+
+// Create custom tool instances
+CalculatorTool calculator = new CalculatorTool();
+StringTool stringTool = new StringTool();
+
+// Create agent configuration with both types of tools
+AgentConfiguration config = new AgentConfiguration()
+    .withChatModel(chatModel)
+    .withCustomTools(Arrays.asList(calculator, stringTool));
+
+// Create agent
+Agent agent = new AgentWithMemory(config);
+
+// Register agent in Camel context
+context.getRegistry().bind("mixedToolsAgent", agent);
+----
+
+==== Route Configuration with Mixed Tools
+
+[source,java]
+----
+from("direct:chat")
+    .to("langchain4j-agent:assistant?agent=#mixedToolsAgent&tags=weather")
+    .to("mock:agent-response");
+----
+
+==== Usage Example
+
+[source,java]
+----
+String response = template.requestBody("direct:chat", 
+    "Calculate 10 * 5 and tell me the weather in London", String.class);
+----
+
+[NOTE]
+====
+When using mixed tools, Camel route tools are discovered dynamically via the 
`tags` parameter, while custom LangChain4j tools are provided statically via 
the `customTools` configuration.
+====
+
 === RAG Integration
 
 RAG (Retrieval-Augmented Generation) is supported by configuring a 
`RetrievalAugmentor` in the `AgentConfiguration`. Create an agent with RAG 
capabilities:
diff --git 
a/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/integration/LangChain4jAgentCustomToolsIT.java
 
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/integration/LangChain4jAgentCustomToolsIT.java
new file mode 100644
index 00000000000..5b085ad3ce7
--- /dev/null
+++ 
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/integration/LangChain4jAgentCustomToolsIT.java
@@ -0,0 +1,150 @@
+/*
+ * 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.langchain4j.agent.integration;
+
+import java.util.Arrays;
+import java.util.List;
+
+import dev.langchain4j.model.chat.ChatModel;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.langchain4j.agent.api.Agent;
+import org.apache.camel.component.langchain4j.agent.api.AgentConfiguration;
+import org.apache.camel.component.langchain4j.agent.api.AgentWithoutMemory;
+import org.apache.camel.component.langchain4j.agent.pojos.CalculatorTool;
+import org.apache.camel.component.langchain4j.agent.pojos.StringTool;
+import org.apache.camel.component.langchain4j.agent.pojos.WeatherTool;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIf;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Integration test for LangChain4j Agent component with custom tools only (no 
Camel route tools).
+ */
+@EnabledIf("org.apache.camel.component.langchain4j.agent.integration.ModelHelper#environmentWithoutEmbeddings")
+public class LangChain4jAgentCustomToolsIT extends CamelTestSupport {
+
+    private static final String CALCULATION_RESULT = "8";
+    private static final String WEATHER_INFO = "sunny";
+    private static final String WEATHER_TEMP = "22";
+
+    protected ChatModel chatModel;
+
+    @Override
+    protected void setupResources() throws Exception {
+        super.setupResources();
+        chatModel = ModelHelper.loadFromEnv();
+    }
+
+    @Test
+    void testAgentWithAdditionalToolsOnly() throws InterruptedException {
+        MockEndpoint mockEndpoint = 
this.context.getEndpoint("mock:agent-response", MockEndpoint.class);
+        mockEndpoint.expectedMessageCount(1);
+
+        String response = template.requestBody("direct:chat", "What is 5 + 
3?", String.class);
+
+        mockEndpoint.assertIsSatisfied();
+        assertNotNull(response, "AI response should not be null");
+        assertTrue(response.contains(CALCULATION_RESULT) || 
response.contains("eight"),
+                "Response should contain the calculation result from the 
calculator tool");
+    }
+
+    @Test
+    void testMultipleToolInstances() throws InterruptedException {
+        MockEndpoint mockEndpoint = 
this.context.getEndpoint("mock:agent-response", MockEndpoint.class);
+        mockEndpoint.expectedMessageCount(1);
+
+        String response
+                = template.requestBody("direct:chat", "Calculate 10 * 5 and 
tell me the weather in Paris", String.class);
+
+        mockEndpoint.assertIsSatisfied();
+        assertNotNull(response, "AI response should not be null");
+        assertTrue(response.contains("50") || response.contains("fifty"),
+                "Response should contain the multiplication result");
+        // Weather tool might not be used by the AI, so we make this assertion 
more flexible
+        assertTrue(
+                response.toLowerCase().contains(WEATHER_INFO) || 
response.toLowerCase().contains("weather")
+                        || response.toLowerCase().contains("paris"),
+                "Response should contain weather information or reference to 
weather/Paris");
+    }
+
+    @Test
+    void testWeatherTool() throws InterruptedException {
+        MockEndpoint mockEndpoint = 
this.context.getEndpoint("mock:agent-response", MockEndpoint.class);
+        mockEndpoint.expectedMessageCount(1);
+
+        String response = template.requestBody("direct:chat",
+                "Call the getWeather function for Paris", String.class);
+
+        mockEndpoint.assertIsSatisfied();
+        assertNotNull(response, "AI response should not be null");
+
+        boolean weatherToolUsed = response.contains("Weather in Paris: " + 
WEATHER_INFO + ", " + WEATHER_TEMP + "°C") ||
+                response.toLowerCase().contains("sunny") ||
+                response.toLowerCase().contains("22");
+
+        assertTrue(weatherToolUsed,
+                "Response should contain weather information from the weather 
tool. Response was: " + response);
+    }
+
+    @Test
+    void testStringManipulationTool() throws InterruptedException {
+        MockEndpoint mockEndpoint = 
this.context.getEndpoint("mock:agent-response", MockEndpoint.class);
+        mockEndpoint.expectedMessageCount(1);
+
+        String response = template.requestBody("direct:chat", "Convert 'hello 
world' to uppercase", String.class);
+
+        mockEndpoint.assertIsSatisfied();
+        assertNotNull(response, "AI response should not be null");
+        assertTrue(response.contains("HELLO WORLD"),
+                "Response should contain the uppercase conversion result");
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        // Create LangChain4j tool instances
+        CalculatorTool calculator = new CalculatorTool();
+        WeatherTool weather = new WeatherTool();
+        StringTool stringTool = new StringTool();
+
+        List<Object> customTools = Arrays.asList(calculator, weather, 
stringTool);
+
+        // Create agent configuration with custom tools
+        AgentConfiguration config = new AgentConfiguration()
+                .withChatModel(chatModel)
+                .withCustomTools(customTools);
+
+        // Create agent
+        Agent agent = new AgentWithoutMemory(config);
+
+        // Register agent in Camel context
+        this.context.getRegistry().bind("additionalToolsAgent", agent);
+
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                // Route with custom tools only
+                from("direct:chat")
+                        
.to("langchain4j-agent:assistant?agent=#additionalToolsAgent")
+                        .to("mock:agent-response");
+            }
+        };
+    }
+}
diff --git 
a/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/integration/LangChain4jAgentMixedToolsIT.java
 
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/integration/LangChain4jAgentMixedToolsIT.java
new file mode 100644
index 00000000000..abe135190bc
--- /dev/null
+++ 
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/integration/LangChain4jAgentMixedToolsIT.java
@@ -0,0 +1,156 @@
+/*
+ * 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.langchain4j.agent.integration;
+
+import java.util.Arrays;
+import java.util.List;
+
+import dev.langchain4j.model.chat.ChatModel;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.langchain4j.agent.api.Agent;
+import org.apache.camel.component.langchain4j.agent.api.AgentConfiguration;
+import org.apache.camel.component.langchain4j.agent.api.AgentWithoutMemory;
+import org.apache.camel.component.langchain4j.agent.pojos.CalculatorTool;
+import org.apache.camel.component.langchain4j.agent.pojos.StringTool;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIf;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Integration test for LangChain4j Agent component mixing Camel route tools 
(tags) and custom LangChain4j tools.
+ */
+@EnabledIf("org.apache.camel.component.langchain4j.agent.integration.ModelHelper#environmentWithoutEmbeddings")
+public class LangChain4jAgentMixedToolsIT extends CamelTestSupport {
+
+    private static final String USER_DATABASE = """
+            {"id": "123", "name": "John Smith", "membership": "Gold", 
"rentals": 15, "preferredVehicle": "SUV"}
+            """;
+
+    private static final String USER_DB_NAME = "John Smith";
+    private static final String WEATHER_INFO = "sunny";
+    private static final String CALCULATION_RESULT = "10";
+
+    protected ChatModel chatModel;
+
+    @Override
+    protected void setupResources() throws Exception {
+        super.setupResources();
+        chatModel = ModelHelper.loadFromEnv();
+    }
+
+    @Test
+    void testAgentWithMixedTools() throws InterruptedException {
+        MockEndpoint mockEndpoint = 
this.context.getEndpoint("mock:agent-response", MockEndpoint.class);
+        mockEndpoint.expectedMessageCount(1);
+
+        String response = template.requestBody("direct:mixedTools", "Calculate 
7 + 3", String.class);
+
+        mockEndpoint.assertIsSatisfied();
+        assertNotNull(response, "AI response should not be null");
+        assertTrue(response.contains(CALCULATION_RESULT) || 
response.contains("ten"),
+                "Response should contain the calculation result from the 
additional calculator tool");
+    }
+
+    @Test
+    void testAgentWithMultipleTagsAndAdditionalTools() throws 
InterruptedException {
+        MockEndpoint mockEndpoint = 
this.context.getEndpoint("mock:agent-response", MockEndpoint.class);
+        mockEndpoint.expectedMessageCount(1);
+
+        String response = template.requestBody("direct:mixedTools",
+                "Calculate 15 * 4 and convert 'hello' to uppercase",
+                String.class);
+
+        mockEndpoint.assertIsSatisfied();
+        assertNotNull(response, "AI response should not be null");
+        assertTrue(response.contains("60") || response.contains("sixty"),
+                "Response should contain the multiplication result from 
additional tools");
+        assertTrue(response.contains("HELLO"),
+                "Response should contain the uppercase conversion result from 
additional tools");
+    }
+
+    @Test
+    void testAgentWithCamelAndAdditionalTools() throws InterruptedException {
+        MockEndpoint mockEndpoint = 
this.context.getEndpoint("mock:agent-response", MockEndpoint.class);
+        mockEndpoint.expectedMessageCount(1);
+
+        String response = template.requestBody("direct:mixedTools",
+                "What is the name of user ID 123 and calculate 5 * 6?",
+                String.class);
+
+        mockEndpoint.assertIsSatisfied();
+        assertNotNull(response, "AI response should not be null");
+        assertTrue(response.contains(USER_DB_NAME),
+                "Response should contain the user name from the Camel route 
tool");
+        assertTrue(response.contains("30") || response.contains("thirty"),
+                "Response should contain the calculation result from the 
additional calculator tool");
+    }
+
+    @Test
+    void testAgentWithOnlyCamelRouteTools() throws InterruptedException {
+        MockEndpoint mockEndpoint = 
this.context.getEndpoint("mock:agent-response", MockEndpoint.class);
+        mockEndpoint.expectedMessageCount(1);
+
+        String response = template.requestBody("direct:mixedTools", "What's 
the weather in New York?", String.class);
+
+        mockEndpoint.assertIsSatisfied();
+        assertNotNull(response, "AI response should not be null");
+        assertTrue(response.toLowerCase().contains(WEATHER_INFO),
+                "Response should contain weather information from the Camel 
route tool");
+
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        // Create LangChain4jtool instances
+        CalculatorTool calculator = new CalculatorTool();
+        StringTool stringTool = new StringTool();
+
+        List<Object> customTools = Arrays.asList(calculator, stringTool);
+
+        // Create agent configuration with custom tools
+        AgentConfiguration config = new AgentConfiguration()
+                .withChatModel(chatModel)
+                .withCustomTools(customTools);
+
+        // Create agent
+        Agent agent = new AgentWithoutMemory(config);
+
+        // Register agent in Camel context
+        this.context.getRegistry().bind("mixedToolsAgent", agent);
+
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                // Route with mixed tools : custom tools (via agent) + camel 
routes
+                from("direct:mixedTools")
+                        
.to("langchain4j-agent:assistant?agent=#mixedToolsAgent&tags=users,weather")
+                        .to("mock:agent-response");
+
+                // Tool routes for function calling
+                from("langchain4j-tools:userDb?tags=users&description=Query 
user database by user ID&parameter.userId=string")
+                        .setBody(constant(USER_DATABASE));
+
+                
from("langchain4j-tools:weatherService?tags=weather&description=Get current 
weather information&parameter.location=string")
+                        .setBody(constant("{\"weather\": \"" + WEATHER_INFO + 
"\", \"location\": \"Current Location\"}"));
+            }
+        };
+    }
+}
diff --git 
a/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/pojos/CalculatorTool.java
 
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/pojos/CalculatorTool.java
new file mode 100644
index 00000000000..e64a64af66a
--- /dev/null
+++ 
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/pojos/CalculatorTool.java
@@ -0,0 +1,41 @@
+/*
+ * 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.langchain4j.agent.pojos;
+
+import dev.langchain4j.agent.tool.P;
+import dev.langchain4j.agent.tool.Tool;
+
+/**
+ * Calculator tools
+ */
+public class CalculatorTool {
+
+    @Tool("Adds two numbers")
+    public int add(@P("First number") int a, @P("Second number") int b) {
+        return a + b;
+    }
+
+    @Tool("Multiplies two numbers")
+    public int multiply(@P("First number") int a, @P("Second number") int b) {
+        return a * b;
+    }
+
+    @Tool("Gets the square root of a number")
+    public double sqrt(@P("Number") double x) {
+        return Math.sqrt(x);
+    }
+}
diff --git 
a/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/pojos/StringTool.java
 
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/pojos/StringTool.java
new file mode 100644
index 00000000000..7700061d9c9
--- /dev/null
+++ 
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/pojos/StringTool.java
@@ -0,0 +1,36 @@
+/*
+ * 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.langchain4j.agent.pojos;
+
+import dev.langchain4j.agent.tool.P;
+import dev.langchain4j.agent.tool.Tool;
+
+/**
+ * String tools
+ */
+public class StringTool {
+
+    @Tool("Converts text to uppercase")
+    public String toUpperCase(@P("Text to convert") String text) {
+        return text.toUpperCase();
+    }
+
+    @Tool("Gets the length of a string")
+    public int getLength(@P("Text") String text) {
+        return text.length();
+    }
+}
diff --git 
a/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/pojos/WeatherTool.java
 
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/pojos/WeatherTool.java
new file mode 100644
index 00000000000..f4230b55d46
--- /dev/null
+++ 
b/components/camel-ai/camel-langchain4j-agent/src/test/java/org/apache/camel/component/langchain4j/agent/pojos/WeatherTool.java
@@ -0,0 +1,39 @@
+/*
+ * 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.langchain4j.agent.pojos;
+
+import dev.langchain4j.agent.tool.P;
+import dev.langchain4j.agent.tool.Tool;
+
+/**
+ * Weather LangChain4j tools
+ */
+public class WeatherTool {
+
+    private static final String WEATHER_INFO = "sunny";
+    private static final String WEATHER_TEMP = "22";
+
+    @Tool("Gets weather information for a city")
+    public String getWeather(@P("City name") String city) {
+        return "Weather in " + city + ": " + WEATHER_INFO + ", " + 
WEATHER_TEMP + "°C";
+    }
+
+    @Tool("Gets temperature for a city")
+    public int getTemperature(@P("City name") String city) {
+        return Integer.parseInt(WEATHER_TEMP);
+    }
+}

Reply via email to