parseArguments(ToolExecutionRequest request) {
+ String jsonArguments = request.arguments();
+ if (jsonArguments == null || jsonArguments.trim().isEmpty()) {
+ return Map.of();
+ }
+ try {
+ return objectMapper.readValue(jsonArguments, new TypeReference<>() {
+ });
+ } catch (Exception e) {
+ LOG.debugf(e, "Failed to parse tool arguments: %s", jsonArguments);
+ return null;
+ }
+ }
+
+ private String toToolResponse(String toolName, AiToolResult result) {
+ if (result instanceof AiToolResult.Success success) {
+ return success.value();
+ } else if (result instanceof AiToolResult.ArgumentError error) {
+ return "Invalid arguments: " + error.message();
+ } else if (result instanceof AiToolResult.ExecutionError error) {
+ LOG.warnf("Tool '%s' execution failed: %s", toolName, error.message());
+ return "Tool execution failed";
+ }
+ return "Tool execution failed";
+ }
+
+}
diff --git a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiTools.java b/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiTools.java
new file mode 100644
index 000000000000..8be0eeacbff8
--- /dev/null
+++ b/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiTools.java
@@ -0,0 +1,48 @@
+/*
+ * 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.support.langchain4j;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import jakarta.enterprise.util.Nonbinding;
+import jakarta.interceptor.InterceptorBinding;
+
+/**
+ * Filters the Camel AI tools exposed to a {@code @RegisterAiService} by tag. When placed on a
+ * {@code @RegisterAiService} interface, only {@code ai-tool:} routes whose {@code tags} parameter includes the
+ * specified value (plus any routes in the default pool) are provided to the AI service.
+ *
+ *
+ * @RegisterAiService
+ * @CamelAiTools("support")
+ * public interface SupportAgent {
+ * String chat(@UserMessage String message);
+ * }
+ *
+ */
+@InterceptorBinding
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface CamelAiTools {
+ @Nonbinding
+ String value() default "";
+}
diff --git a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiToolsInterceptor.java b/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiToolsInterceptor.java
new file mode 100644
index 000000000000..239ab269762a
--- /dev/null
+++ b/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/CamelAiToolsInterceptor.java
@@ -0,0 +1,73 @@
+/*
+ * 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.support.langchain4j;
+
+import jakarta.annotation.Priority;
+import jakarta.interceptor.AroundInvoke;
+import jakarta.interceptor.Interceptor;
+import jakarta.interceptor.InvocationContext;
+
+/**
+ * CDI interceptor that sets the current Camel AI tool tag on a ThreadLocal before an AI service method executes.
+ * This allows {@link CamelAiToolProvider#provideTools} to filter tools by the tag associated with the calling AI
+ * service,
+ * enabling multiple {@code @RegisterAiService} interfaces with different {@code @CamelAiTools} tags in the same
+ * application.
+ */
+@Interceptor
+@CamelAiTools
+@Priority(Interceptor.Priority.LIBRARY_BEFORE + 100)
+public class CamelAiToolsInterceptor {
+
+ @AroundInvoke
+ Object aroundInvoke(InvocationContext ctx) throws Exception {
+ Class> targetClass = ctx.getTarget().getClass();
+ String tag = resolveTag(targetClass);
+ String previous = CamelAiToolProvider.getCurrentTag();
+ if (tag != null) {
+ CamelAiToolProvider.setCurrentTag(tag);
+ }
+ try {
+ return ctx.proceed();
+ } finally {
+ if (previous != null) {
+ CamelAiToolProvider.setCurrentTag(previous);
+ } else {
+ CamelAiToolProvider.clearCurrentTag();
+ }
+ }
+ }
+
+ private String resolveTag(Class> targetClass) {
+ // Walk the full class hierarchy: ArC creates $$QuarkusImpl_Subclass extending $$QuarkusImpl,
+ // and the @CamelAiTools interface is declared on $$QuarkusImpl (not the subclass), so we
+ // must check superclasses and all their interfaces.
+ for (Class> clazz = targetClass; clazz != null && clazz != Object.class; clazz = clazz.getSuperclass()) {
+ String tag = CamelAiToolProvider.TAG_MAP.get(clazz.getName());
+ if (tag != null) {
+ return tag;
+ }
+ for (Class> iface : clazz.getInterfaces()) {
+ tag = CamelAiToolProvider.TAG_MAP.get(iface.getName());
+ if (tag != null) {
+ return tag;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/QuarkusLangchain4jRecorder.java b/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/QuarkusLangchain4jRecorder.java
index af6301a6c939..ac23912b1bdf 100644
--- a/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/QuarkusLangchain4jRecorder.java
+++ b/extensions-support/langchain4j/runtime/src/main/java/org/apache/camel/quarkus/component/support/langchain4j/QuarkusLangchain4jRecorder.java
@@ -17,6 +17,7 @@
package org.apache.camel.quarkus.component.support.langchain4j;
import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
import dev.langchain4j.guardrail.Guardrail;
import io.quarkus.runtime.RuntimeValue;
@@ -26,6 +27,10 @@
@Recorder
public class QuarkusLangchain4jRecorder {
+ public void setCamelAiToolTagMap(Map tagMap) {
+ CamelAiToolProvider.TAG_MAP.putAll(tagMap);
+ }
+
public RuntimeValue> instantiateGuardrails(Class> guardrailClass) {
try {
return new RuntimeValue<>(guardrailClass.getConstructor().newInstance());
diff --git a/extensions/ai-tool/runtime/src/main/doc/usage.adoc b/extensions/ai-tool/runtime/src/main/doc/usage.adoc
index 677f114f7cb6..36359bf4106f 100644
--- a/extensions/ai-tool/runtime/src/main/doc/usage.adoc
+++ b/extensions/ai-tool/runtime/src/main/doc/usage.adoc
@@ -41,3 +41,57 @@ from("ai-tool:greet?description=Greet a user"
+ "¶meter.name=string¶meter.name.required=true")
.setBody(simple("Hello, ${header.name}!"));
----
+
+=== LangChain4j integration
+
+When both `camel-quarkus-ai-tool` and a Quarkus LangChain4j extension (`quarkus-langchain4j-ollama`, `quarkus-langchain4j-openai`, etc.) are on the classpath, a `ToolProvider` CDI bean is registered automatically at build time.
+All routes registered via `ai-tool:` endpoints become available to `@RegisterAiService` AI services without explicit wiring.
+
+You must also add `camel-langchain4j-agent` to your project dependencies. This artifact provides the `AiToolSpec` to `ToolSpecification` conversion and is not brought in transitively.
+
+[source,xml]
+----
+
+ org.apache.camel
+ camel-langchain4j-agent
+
+----
+
+With both dependencies in place, a `@RegisterAiService` interface can call Camel tools directly:
+
+[source,java]
+----
+@RegisterAiService
+public interface WeatherAiService {
+ String chat(@UserMessage String question);
+}
+----
+
+Any `ai-tool:` route is now callable by the LLM behind `WeatherAiService`. No `toolProvider` attribute or manual bean reference is needed.
+
+==== Tag filtering with `@CamelAiTools`
+
+By default, all registered `ai-tool:` routes are visible to every AI service. When you need different AI services to see different subsets of tools, annotate the service interface with `@CamelAiTools` and specify a tag value that matches the `tags` parameter on your `ai-tool:` routes.
+
+[source,java]
+----
+@RegisterAiService
+@CamelAiTools("weather")
+public interface WeatherAgent {
+ String chat(@UserMessage String question);
+}
+
+@RegisterAiService
+@CamelAiTools("support")
+public interface SupportAgent {
+ String chat(@UserMessage String question);
+}
+----
+
+With this setup:
+
+* `WeatherAgent` sees only tools tagged with `weather` (plus any untagged tools from the default pool).
+* `SupportAgent` sees only tools tagged with `support` (plus any untagged tools from the default pool).
+* A `@RegisterAiService` without `@CamelAiTools` sees all registered tools regardless of tags.
+
+NOTE: The `@CamelAiTools` value must match a tag declared in the `ai-tool:` endpoint URI (e.g. `from("ai-tool:getWeather?tags=weather&...")`). If the annotation value is blank or does not match any route tag, the AI service will only see untagged tools.
diff --git a/integration-tests/ai-tool-langchain4j/README.adoc b/integration-tests/ai-tool-langchain4j/README.adoc
new file mode 100644
index 000000000000..40a56d5e2416
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/README.adoc
@@ -0,0 +1,15 @@
+== Camel Quarkus AI Tool LangChain4j Integration Tests
+
+By default, the tests use a mock `ChatModel` that simulates tool calling without a real LLM.
+
+=== Running with a real Ollama LLM
+
+To run additional tests against a real Ollama model (qwen3:1.7b) via dev services, activate the `ollama` Maven profile.
+This requires Docker to be running.
+
+[source,shell]
+----
+./mvnw verify -f integration-tests/ai-tool-langchain4j -Pollama
+----
+
+The first run may take longer as the Ollama container image and model are downloaded.
diff --git a/integration-tests/ai-tool-langchain4j/pom.xml b/integration-tests/ai-tool-langchain4j/pom.xml
new file mode 100644
index 000000000000..433e1df1898c
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/pom.xml
@@ -0,0 +1,222 @@
+
+
+
+ 4.0.0
+
+ org.apache.camel.quarkus
+ camel-quarkus-build-parent-it
+ 3.39.0-SNAPSHOT
+ ../../poms/build-parent-it/pom.xml
+
+
+ camel-quarkus-integration-test-ai-tool-langchain4j
+ Camel Quarkus :: Integration Tests :: AI Tool LangChain4j
+ Integration tests for Camel AI Tool bridge with Quarkus LangChain4j
+
+
+
+
+ io.quarkiverse.langchain4j
+ quarkus-langchain4j-bom
+ ${quarkiverse-langchain4j.version}
+ pom
+ import
+
+
+
+
+
+
+ org.apache.camel.quarkus
+ camel-quarkus-ai-tool
+
+
+ org.apache.camel.quarkus
+ camel-quarkus-bean
+
+
+ org.apache.camel.quarkus
+ camel-quarkus-direct
+
+
+ io.quarkus
+ quarkus-rest
+
+
+
+
+ org.apache.camel.quarkus
+ camel-quarkus-support-langchain4j
+
+
+
+ org.apache.camel
+ camel-langchain4j-agent
+
+
+
+ io.quarkiverse.langchain4j
+ quarkus-langchain4j-ollama
+
+
+
+
+ io.quarkus
+ quarkus-junit
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+
+
+
+ native
+
+
+ native
+
+
+
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+
+ integration-test
+ verify
+
+
+
+
+
+
+
+
+ ollama
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+ true
+
+
+
+
+
+
+
+ virtualDependencies
+
+
+ !noVirtualDependencies
+
+
+
+
+
+ org.apache.camel.quarkus
+ camel-quarkus-ai-tool-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ org.apache.camel.quarkus
+ camel-quarkus-bean-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ org.apache.camel.quarkus
+ camel-quarkus-direct-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+ org.apache.camel.quarkus
+ camel-quarkus-support-langchain4j-deployment
+ ${project.version}
+ pom
+ test
+
+
+ *
+ *
+
+
+
+
+
+
+ skip-testcontainers-tests
+
+
+ skip-testcontainers-tests
+
+
+
+ true
+
+
+
+
diff --git a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jOllamaResource.java b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jOllamaResource.java
new file mode 100644
index 000000000000..60fdd44967e6
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jOllamaResource.java
@@ -0,0 +1,54 @@
+/*
+ * 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.ai.tool.langchain4j.it;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import org.apache.camel.quarkus.component.ai.tool.langchain4j.it.service.WeatherAiServiceOllama;
+import org.apache.camel.quarkus.component.ai.tool.langchain4j.it.service.WeatherAiServiceOllamaWrongTag;
+
+@Path("/ai-tool-langchain4j-ollama")
+@ApplicationScoped
+public class AiToolLangchain4jOllamaResource {
+
+ @Inject
+ WeatherAiServiceOllama weatherAiService;
+
+ @Inject
+ WeatherAiServiceOllamaWrongTag weatherAiServiceWrongTag;
+
+ @Path("/chat")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.TEXT_PLAIN)
+ public String chat(String message) {
+ return weatherAiService.chat(message);
+ }
+
+ @Path("/chat-wrong-tag")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.TEXT_PLAIN)
+ public String chatWrongTag(String message) {
+ return weatherAiServiceWrongTag.chat(message);
+ }
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jResource.java b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jResource.java
new file mode 100644
index 000000000000..82770665ac2a
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jResource.java
@@ -0,0 +1,90 @@
+/*
+ * 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.ai.tool.langchain4j.it;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.ai.tool.AiToolRegistry;
+import org.apache.camel.component.ai.tool.AiToolSpec;
+import org.apache.camel.quarkus.component.ai.tool.langchain4j.it.service.AdminAiService;
+import org.apache.camel.quarkus.component.ai.tool.langchain4j.it.service.WeatherAiService;
+import org.apache.camel.quarkus.component.ai.tool.langchain4j.it.service.WeatherAiServiceWrongTag;
+
+@Path("/ai-tool-langchain4j")
+@ApplicationScoped
+public class AiToolLangchain4jResource {
+
+ @Inject
+ CamelContext camelContext;
+
+ @Inject
+ WeatherAiService weatherAiService;
+
+ @Inject
+ AdminAiService adminAiService;
+
+ @Inject
+ WeatherAiServiceWrongTag weatherAiServiceWrongTag;
+
+ @Path("/weather/chat")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.TEXT_PLAIN)
+ public String weatherChat(String message) {
+ return weatherAiService.chat(message);
+ }
+
+ @Path("/admin/chat")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.TEXT_PLAIN)
+ public String adminChat(String message) {
+ return adminAiService.chat(message);
+ }
+
+ @Path("/weather-wrong-tag/chat")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.TEXT_PLAIN)
+ public String weatherChatWrongTag(String message) {
+ return weatherAiServiceWrongTag.chat(message);
+ }
+
+ @Path("/tools/{tag}")
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public String listToolsByTag(@PathParam("tag") String tag) {
+ AiToolRegistry registry = AiToolRegistry.getOrCreate(camelContext);
+ Set tools = registry.getToolsByTag(tag);
+ return tools.stream()
+ .map(AiToolSpec::getName)
+ .sorted()
+ .collect(Collectors.joining(","));
+ }
+
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jRoutes.java b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jRoutes.java
new file mode 100644
index 000000000000..8f8b45acf66b
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jRoutes.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.ai.tool.langchain4j.it;
+
+import org.apache.camel.builder.RouteBuilder;
+
+public class AiToolLangchain4jRoutes extends RouteBuilder {
+ @Override
+ public void configure() {
+ from("ai-tool:getWeather?"
+ + "tags=weatherTag"
+ + "&description=Get the current weather for a city"
+ + "¶meter.city=string"
+ + "¶meter.city.required=true"
+ + "¶meter.city.description=The city name")
+ .setBody(simple("Sunny in ${header.city}, 1111 celsius"));
+
+ from("ai-tool:getNews?"
+ + "tags=adminTag"
+ + "&description=Get the latest news about a topic"
+ + "¶meter.topic=string"
+ + "¶meter.topic.required=true"
+ + "¶meter.topic.description=The news topic")
+ .setBody(simple("Latest news about ${header.topic}"));
+ }
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/model/AbstractToolCallingChatModel.java b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/model/AbstractToolCallingChatModel.java
new file mode 100644
index 000000000000..a4be0986b90c
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/model/AbstractToolCallingChatModel.java
@@ -0,0 +1,93 @@
+/*
+ * 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.ai.tool.langchain4j.it.model;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+import dev.langchain4j.agent.tool.ToolExecutionRequest;
+import dev.langchain4j.data.message.AiMessage;
+import dev.langchain4j.data.message.ChatMessage;
+import dev.langchain4j.data.message.ToolExecutionResultMessage;
+import dev.langchain4j.model.chat.ChatModel;
+import dev.langchain4j.model.chat.request.ChatRequest;
+import dev.langchain4j.model.chat.response.ChatResponse;
+
+/**
+ * Simulates the two-turn tool-calling protocol used by langchain4j:
+ *
+ *
+ * - Turn 1 — langchain4j sends the user message and available tool specifications to the model.
+ * The model responds with a {@link ToolExecutionRequest} ("I want to call tool X with args Y").
+ * No tool is executed yet.
+ * - Tool execution — langchain4j sees the request, invokes the matching {@code ToolExecutor}
+ * (in our case the Camel {@code ai-tool:} route), and captures the result.
+ * - Turn 2 — langchain4j calls the model again, appending a {@link ToolExecutionResultMessage}
+ * with the tool's output. The model returns a final text answer.
+ *
+ *
+ * Subclasses only define which tool to call and how to format the final response.
+ */
+public abstract class AbstractToolCallingChatModel implements Supplier {
+
+ // Correlation ID for matching ToolExecutionResultMessage back to the request — required by the
+ // builder but the actual value is irrelevant in tests with a single tool call per turn.
+ protected abstract String toolCallId();
+
+ protected abstract String toolName();
+
+ protected abstract String toolArguments();
+
+ protected abstract String formatResponse(String toolResult);
+
+ protected abstract String getNameOfService();
+
+ @Override
+ public ChatModel get() {
+ return new ChatModel() {
+ @Override
+ public ChatResponse doChat(ChatRequest chatRequest) {
+ List messages = chatRequest.messages();
+
+ boolean hasToolResult = messages.stream()
+ .anyMatch(m -> m instanceof ToolExecutionResultMessage);
+
+ if (!hasToolResult) {
+ ToolExecutionRequest toolRequest = ToolExecutionRequest.builder()
+ .id(toolCallId())
+ .name(toolName())
+ .arguments(toolArguments())
+ .build();
+ return ChatResponse.builder()
+ .aiMessage(AiMessage.from(toolRequest))
+ .build();
+ }
+
+ // #2 tool's response already present
+ String toolResult = messages.stream()
+ .filter(m -> m instanceof ToolExecutionResultMessage)
+ .map(m -> ((ToolExecutionResultMessage) m).text())
+ .findFirst()
+ .orElse("unknown");
+
+ return ChatResponse.builder()
+ .aiMessage(new AiMessage(formatResponse(toolResult)))
+ .build();
+ }
+ };
+ }
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/model/AdminToolCallingChatModel.java b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/model/AdminToolCallingChatModel.java
new file mode 100644
index 000000000000..2bff6c1e1260
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/model/AdminToolCallingChatModel.java
@@ -0,0 +1,45 @@
+/*
+ * 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.ai.tool.langchain4j.it.model;
+
+public class AdminToolCallingChatModel extends AbstractToolCallingChatModel {
+
+ @Override
+ protected String getNameOfService() {
+ return "adminTag";
+ }
+
+ @Override
+ protected String toolCallId() {
+ return "call_admin_1";
+ }
+
+ @Override
+ protected String toolName() {
+ return "getNews";
+ }
+
+ @Override
+ protected String toolArguments() {
+ return "{\"topic\":\"camel\"}";
+ }
+
+ @Override
+ protected String formatResponse(String toolResult) {
+ return "News result: " + toolResult;
+ }
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/model/WeatherToolCallingChatModel.java b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/model/WeatherToolCallingChatModel.java
new file mode 100644
index 000000000000..4a52902d80cd
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/model/WeatherToolCallingChatModel.java
@@ -0,0 +1,45 @@
+/*
+ * 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.ai.tool.langchain4j.it.model;
+
+public class WeatherToolCallingChatModel extends AbstractToolCallingChatModel {
+
+ @Override
+ protected String getNameOfService() {
+ return "weatherTag";
+ }
+
+ @Override
+ protected String toolCallId() {
+ return "call_1";
+ }
+
+ @Override
+ protected String toolName() {
+ return "getWeather";
+ }
+
+ @Override
+ protected String toolArguments() {
+ return "{\"city\":\"Prague\"}";
+ }
+
+ @Override
+ protected String formatResponse(String toolResult) {
+ return toolResult;
+ }
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/model/WrongTagToolCallingChatModel.java b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/model/WrongTagToolCallingChatModel.java
new file mode 100644
index 000000000000..a4c1803a46e9
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/model/WrongTagToolCallingChatModel.java
@@ -0,0 +1,78 @@
+/*
+ * 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.ai.tool.langchain4j.it.model;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+import dev.langchain4j.agent.tool.ToolExecutionRequest;
+import dev.langchain4j.data.message.AiMessage;
+import dev.langchain4j.data.message.ChatMessage;
+import dev.langchain4j.data.message.ToolExecutionResultMessage;
+import dev.langchain4j.model.chat.ChatModel;
+import dev.langchain4j.model.chat.request.ChatRequest;
+import dev.langchain4j.model.chat.response.ChatResponse;
+
+/**
+ * Mock model for testing tag isolation. Calls {@code getWeather} only if it appears in the
+ * available tool specifications — when the interceptor works correctly and this service is
+ * tagged with {@code adminTag}, the weather tool should NOT be visible.
+ */
+public class WrongTagToolCallingChatModel implements Supplier {
+
+ @Override
+ public ChatModel get() {
+ return new ChatModel() {
+ @Override
+ public ChatResponse doChat(ChatRequest chatRequest) {
+ List messages = chatRequest.messages();
+ boolean hasToolResult = messages.stream()
+ .anyMatch(m -> m instanceof ToolExecutionResultMessage);
+
+ if (!hasToolResult) {
+ boolean weatherToolAvailable = chatRequest.toolSpecifications() != null
+ && chatRequest.toolSpecifications().stream()
+ .anyMatch(ts -> "getWeather".equals(ts.name()));
+
+ if (weatherToolAvailable) {
+ ToolExecutionRequest request = ToolExecutionRequest.builder()
+ .id("call_wrong_tag")
+ .name("getWeather")
+ .arguments("{\"city\":\"Prague\"}")
+ .build();
+ return ChatResponse.builder()
+ .aiMessage(AiMessage.from(request))
+ .build();
+ }
+
+ return ChatResponse.builder()
+ .aiMessage(new AiMessage("No weather tool available"))
+ .build();
+ }
+
+ String toolResult = messages.stream()
+ .filter(m -> m instanceof ToolExecutionResultMessage)
+ .map(m -> ((ToolExecutionResultMessage) m).text())
+ .findFirst()
+ .orElse("unknown");
+ return ChatResponse.builder()
+ .aiMessage(new AiMessage(toolResult))
+ .build();
+ }
+ };
+ }
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/AdminAiService.java b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/AdminAiService.java
new file mode 100644
index 000000000000..b54e0d4d1286
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/AdminAiService.java
@@ -0,0 +1,31 @@
+/*
+ * 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.ai.tool.langchain4j.it.service;
+
+import dev.langchain4j.service.UserMessage;
+import io.quarkiverse.langchain4j.RegisterAiService;
+import jakarta.enterprise.context.ApplicationScoped;
+import org.apache.camel.quarkus.component.ai.tool.langchain4j.it.model.AdminToolCallingChatModel;
+import org.apache.camel.quarkus.component.support.langchain4j.CamelAiTools;
+
+@ApplicationScoped
+@RegisterAiService(chatLanguageModelSupplier = AdminToolCallingChatModel.class)
+@CamelAiTools("adminTag")
+public interface AdminAiService {
+
+ String chat(@UserMessage String message);
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiService.java b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiService.java
new file mode 100644
index 000000000000..9a1e8501a1ee
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiService.java
@@ -0,0 +1,31 @@
+/*
+ * 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.ai.tool.langchain4j.it.service;
+
+import dev.langchain4j.service.UserMessage;
+import io.quarkiverse.langchain4j.RegisterAiService;
+import jakarta.enterprise.context.ApplicationScoped;
+import org.apache.camel.quarkus.component.ai.tool.langchain4j.it.model.WeatherToolCallingChatModel;
+import org.apache.camel.quarkus.component.support.langchain4j.CamelAiTools;
+
+@ApplicationScoped
+@RegisterAiService(chatLanguageModelSupplier = WeatherToolCallingChatModel.class)
+@CamelAiTools("weatherTag")
+public interface WeatherAiService {
+
+ String chat(@UserMessage String message);
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllama.java b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllama.java
new file mode 100644
index 000000000000..76829813cb69
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllama.java
@@ -0,0 +1,30 @@
+/*
+ * 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.ai.tool.langchain4j.it.service;
+
+import dev.langchain4j.service.UserMessage;
+import io.quarkiverse.langchain4j.RegisterAiService;
+import jakarta.enterprise.context.ApplicationScoped;
+import org.apache.camel.quarkus.component.support.langchain4j.CamelAiTools;
+
+@ApplicationScoped
+@RegisterAiService
+@CamelAiTools("weatherTag")
+public interface WeatherAiServiceOllama {
+
+ String chat(@UserMessage String message);
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllamaWrongTag.java b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllamaWrongTag.java
new file mode 100644
index 000000000000..1545589a3375
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceOllamaWrongTag.java
@@ -0,0 +1,30 @@
+/*
+ * 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.ai.tool.langchain4j.it.service;
+
+import dev.langchain4j.service.UserMessage;
+import io.quarkiverse.langchain4j.RegisterAiService;
+import jakarta.enterprise.context.ApplicationScoped;
+import org.apache.camel.quarkus.component.support.langchain4j.CamelAiTools;
+
+@ApplicationScoped
+@RegisterAiService
+@CamelAiTools("adminTag")
+public interface WeatherAiServiceOllamaWrongTag {
+
+ String chat(@UserMessage String message);
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceWrongTag.java b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceWrongTag.java
new file mode 100644
index 000000000000..7e4a78d7b6a8
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/main/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/service/WeatherAiServiceWrongTag.java
@@ -0,0 +1,31 @@
+/*
+ * 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.ai.tool.langchain4j.it.service;
+
+import dev.langchain4j.service.UserMessage;
+import io.quarkiverse.langchain4j.RegisterAiService;
+import jakarta.enterprise.context.ApplicationScoped;
+import org.apache.camel.quarkus.component.ai.tool.langchain4j.it.model.WrongTagToolCallingChatModel;
+import org.apache.camel.quarkus.component.support.langchain4j.CamelAiTools;
+
+@ApplicationScoped
+@RegisterAiService(chatLanguageModelSupplier = WrongTagToolCallingChatModel.class)
+@CamelAiTools("adminTag")
+public interface WeatherAiServiceWrongTag {
+
+ String chat(@UserMessage String message);
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/main/resources/application.properties b/integration-tests/ai-tool-langchain4j/src/main/resources/application.properties
new file mode 100644
index 000000000000..a58bbb874a93
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/main/resources/application.properties
@@ -0,0 +1,18 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+quarkus.devservices.enabled=false
+quarkus.langchain4j.ollama.chat-model.model-id=qwen3:1.7b
diff --git a/integration-tests/ai-tool-langchain4j/src/test/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jIT.java b/integration-tests/ai-tool-langchain4j/src/test/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jIT.java
new file mode 100644
index 000000000000..40dd473753c9
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/test/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jIT.java
@@ -0,0 +1,23 @@
+/*
+ * 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.ai.tool.langchain4j.it;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+class AiToolLangchain4jIT extends AiToolLangchain4jTest {
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/test/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jOllamaIT.java b/integration-tests/ai-tool-langchain4j/src/test/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jOllamaIT.java
new file mode 100644
index 000000000000..b263b5abc2d3
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/test/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jOllamaIT.java
@@ -0,0 +1,25 @@
+/*
+ * 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.ai.tool.langchain4j.it;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+
+@QuarkusIntegrationTest
+@EnabledIfSystemProperty(named = "ollama.test", matches = "true")
+class AiToolLangchain4jOllamaIT extends AiToolLangchain4jOllamaTest {
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/test/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jOllamaTest.java b/integration-tests/ai-tool-langchain4j/src/test/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jOllamaTest.java
new file mode 100644
index 000000000000..d4b5fef08d2a
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/test/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jOllamaTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.ai.tool.langchain4j.it;
+
+import java.util.Map;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.QuarkusTestProfile;
+import io.quarkus.test.junit.TestProfile;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+
+@QuarkusTest
+@TestProfile(AiToolLangchain4jOllamaTest.OllamaProfile.class)
+@EnabledIfSystemProperty(named = "ollama.test", matches = "true")
+class AiToolLangchain4jOllamaTest {
+
+ @Test
+ void weatherFromToolTest() {
+ RestAssured.given()
+ .contentType("text/plain")
+ .body("What is the weather in Prague?")
+ .post("/ai-tool-langchain4j-ollama/chat")
+ .then()
+ .statusCode(200)
+ .body(containsString("1111"));
+ }
+
+ // Verifies that the @CamelAiTools interceptor filters tools by tag: a service tagged "adminTag"
+ // should not see the getWeather tool (tagged "weatherTag"), so the real LLM won't call it
+ // and "1111" won't appear in the response. Fails if the interceptor is broken and all tools are visible.
+ @Test
+ void weatherWithWrongTag() {
+ RestAssured.given()
+ .contentType("text/plain")
+ .body("What is the weather in Prague?")
+ .post("/ai-tool-langchain4j-ollama/chat-wrong-tag")
+ .then()
+ .statusCode(200)
+ .body(not(containsString("1111")));
+ }
+
+ public static class OllamaProfile implements QuarkusTestProfile {
+ @Override
+ public Map getConfigOverrides() {
+ return Map.of(
+ "quarkus.devservices.enabled", "true");
+ }
+ }
+}
diff --git a/integration-tests/ai-tool-langchain4j/src/test/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jTest.java b/integration-tests/ai-tool-langchain4j/src/test/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jTest.java
new file mode 100644
index 000000000000..2b7cf401a679
--- /dev/null
+++ b/integration-tests/ai-tool-langchain4j/src/test/java/org/apache/camel/quarkus/component/ai/tool/langchain4j/it/AiToolLangchain4jTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.ai.tool.langchain4j.it;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.RestAssured;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+
+@QuarkusTest
+class AiToolLangchain4jTest {
+
+ @Test
+ void weatherServiceTest() {
+ RestAssured.given()
+ .contentType("text/plain")
+ .body("What is the weather in Prague?")
+ .post("/ai-tool-langchain4j/weather/chat")
+ .then()
+ .statusCode(200)
+ .body(containsString("1111"));
+ }
+
+ @Test
+ void weatherServiceTestWithWrongTag() {
+ RestAssured.given()
+ .contentType("text/plain")
+ .body("What is the weather in Prague?")
+ .post("/ai-tool-langchain4j/admin/chat")
+ .then()
+ .statusCode(200)
+ .body(containsString("Latest news about camel"));
+ }
+
+ @Test
+ void aiToolRegistryToolsByWeatherTagTest() {
+ RestAssured.given()
+ .get("/ai-tool-langchain4j/tools/weatherTag")
+ .then()
+ .statusCode(200)
+ .body(containsString("getWeather"))
+ .body(not(containsString("getNews")));
+ }
+
+ @Test
+ void aiToolRegistryToolsByAdminTagTest() {
+ RestAssured.given()
+ .get("/ai-tool-langchain4j/tools/adminTag")
+ .then()
+ .statusCode(200)
+ .body(containsString("getNews"))
+ .body(not(containsString("getWeather")));
+ }
+
+ @Test
+ void adminServiceTest() {
+ RestAssured.given()
+ .contentType("text/plain")
+ .body("What are the latest news about camel?")
+ .post("/ai-tool-langchain4j/admin/chat")
+ .then()
+ .statusCode(200)
+ .body(containsString("camel"))
+ .body(containsString("news"));
+ }
+
+ // Verifies that the @CamelAiTools interceptor filters tools by tag: a service tagged "adminTag"
+ // should not see the getWeather tool (tagged "weatherTag"), so the mock model won't call it
+ // and "1111" won't appear in the response. Fails if the interceptor is broken and all tools are visible.
+ @Test
+ void weatherWithWrongTagTest() {
+ RestAssured.given()
+ .contentType("text/plain")
+ .body("What is the weather in Prague?")
+ .post("/ai-tool-langchain4j/weather-wrong-tag/chat")
+ .then()
+ .statusCode(200)
+ .body(not(containsString("1111")));
+ }
+
+}
diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
index 8ffee5a4dd62..ce58500c8370 100644
--- a/integration-tests/pom.xml
+++ b/integration-tests/pom.xml
@@ -52,6 +52,7 @@
activemq
activemq6
ai-tool
+ ai-tool-langchain4j
amqp
arangodb
as2
diff --git a/tooling/scripts/test-categories.yaml b/tooling/scripts/test-categories.yaml
index a4df39026536..97b679b994ca 100644
--- a/tooling/scripts/test-categories.yaml
+++ b/tooling/scripts/test-categories.yaml
@@ -232,6 +232,7 @@ group-11:
- xslt-saxon
group-12:
- ai-tool
+ - ai-tool-langchain4j
- aws2-grouped
- csimple
- github2