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 b6d7903dd4 LangChain4j: Fix AI services that could not be resolved by 
interface name #6838
b6d7903dd4 is described below

commit b6d7903dd4fe3695ddfd5e653bd8cfd683f0b421
Author: aldettinger <aldettin...@gmail.com>
AuthorDate: Tue Dec 3 14:59:28 2024 +0100

    LangChain4j: Fix AI services that could not be resolved by interface name 
#6838
---
 .../pages/reference/extensions/langchain4j.adoc    | 28 ++++++++++++++++++++
 .../chat/deployment/LangChain4jProcessor.java      | 16 ++++++++++++
 .../runtime/src/main/doc/configuration.adoc        | 29 ++++++++++++++++++++-
 ...oute.java => AiServiceResolvedByInterface.java} | 30 ++++++++++++++--------
 .../langchain/it/LangChain4jResource.java          |  8 ++++++
 .../component/langchain/it/LangChain4jRoute.java   |  3 +++
 .../component/langchain4jit/LangChain4jTest.java   |  8 ++++++
 7 files changed, 110 insertions(+), 12 deletions(-)

diff --git a/docs/modules/ROOT/pages/reference/extensions/langchain4j.adoc 
b/docs/modules/ROOT/pages/reference/extensions/langchain4j.adoc
index 53cf91d22f..0287231bda 100644
--- a/docs/modules/ROOT/pages/reference/extensions/langchain4j.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/langchain4j.adoc
@@ -83,3 +83,31 @@ public interface CustomAiService {
 
 You can find more details about Camel Parameter Binding annotations in the 
xref:manual::parameter-binding-annotations.adoc[manual].
 
+[id="extensions-langchain4j-configuration-resolving-ai-services-by-interface-name"]
+=== Resolving AI services by interface name
+
+With the `camel-quarkus-langchain4j` extension, the AI services are resolvable 
by interface name when called from a `bean` statement.
+
+For instance, let's define an AI service below:
+
+```
+@RegisterAiService
+@ApplicationScoped
+public interface MyAiService {
+
+    @UserMessage("My Prompt")
+    @Handler
+    String chat(String question);
+}
+```
+
+The AI service could then be invoked from a Camel route like this:
+
+```
+@Override
+public void configure() {
+  from("...")
+    .bean(MyAiService.class);
+}
+```
+
diff --git 
a/extensions/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/langchain/chat/deployment/LangChain4jProcessor.java
 
b/extensions/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/langchain/chat/deployment/LangChain4jProcessor.java
index 9709abdf52..4d40dc4c95 100644
--- 
a/extensions/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/langchain/chat/deployment/LangChain4jProcessor.java
+++ 
b/extensions/langchain4j/deployment/src/main/java/org/apache/camel/quarkus/component/langchain/chat/deployment/LangChain4jProcessor.java
@@ -16,10 +16,17 @@
  */
 package org.apache.camel.quarkus.component.langchain.chat.deployment;
 
+import java.util.List;
+
+import io.quarkiverse.langchain4j.deployment.DeclarativeAiServiceBuildItem;
 import 
io.quarkiverse.langchain4j.deployment.items.MethodParameterAllowedAnnotationsBuildItem;
+import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
+import io.quarkus.deployment.annotations.BuildProducer;
 import io.quarkus.deployment.annotations.BuildStep;
 import io.quarkus.deployment.builditem.FeatureBuildItem;
 
+import static 
io.quarkus.arc.deployment.UnremovableBeanBuildItem.beanClassNames;
+
 class LangChain4jProcessor {
     private static final String FEATURE = "camel-quarkus-langchain4j";
 
@@ -32,4 +39,13 @@ class LangChain4jProcessor {
     MethodParameterAllowedAnnotationsBuildItem 
camelAnnotatedParametersCouldBeUsedAsTemplateVariable() {
         return new MethodParameterAllowedAnnotationsBuildItem(anno -> 
anno.name().toString().startsWith("org.apache.camel"));
     };
+
+    @BuildStep
+    void markAiServicesAsUnremovable(
+            List<DeclarativeAiServiceBuildItem> aiServices, 
BuildProducer<UnremovableBeanBuildItem> unremovableBeans) {
+        aiServices.stream().forEach(ai -> {
+            unremovableBeans.produce(
+                    beanClassNames(ai.getServiceClassInfo().name().toString() 
+ "$$QuarkusImpl"));
+        });
+    };
 }
diff --git a/extensions/langchain4j/runtime/src/main/doc/configuration.adoc 
b/extensions/langchain4j/runtime/src/main/doc/configuration.adoc
index 51aa839b59..cf06882e5c 100644
--- a/extensions/langchain4j/runtime/src/main/doc/configuration.adoc
+++ b/extensions/langchain4j/runtime/src/main/doc/configuration.adoc
@@ -15,4 +15,31 @@ public interface CustomAiService {
 }
 ```
 
-You can find more details about Camel Parameter Binding annotations in the 
xref:manual::parameter-binding-annotations.adoc[manual].
\ No newline at end of file
+You can find more details about Camel Parameter Binding annotations in the 
xref:manual::parameter-binding-annotations.adoc[manual].
+
+=== Resolving AI services by interface name
+
+With the `camel-quarkus-langchain4j` extension, the AI services are resolvable 
by interface name when called from a `bean` statement.
+
+For instance, let's define an AI service below:
+
+```
+@RegisterAiService
+@ApplicationScoped
+public interface MyAiService {
+
+    @UserMessage("My Prompt")
+    @Handler
+    String chat(String question);
+}
+```
+
+The AI service could then be invoked from a Camel route like this:
+
+```
+@Override
+public void configure() {
+  from("...")
+    .bean(MyAiService.class);
+}
+```
\ No newline at end of file
diff --git 
a/integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/LangChain4jRoute.java
 
b/integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/AiServiceResolvedByInterface.java
similarity index 52%
copy from 
integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/LangChain4jRoute.java
copy to 
integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/AiServiceResolvedByInterface.java
index a7ee74f31a..ef81c28b2d 100644
--- 
a/integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/LangChain4jRoute.java
+++ 
b/integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/AiServiceResolvedByInterface.java
@@ -16,20 +16,28 @@
  */
 package org.apache.camel.quarkus.component.langchain.it;
 
+import java.util.function.Supplier;
+
+import dev.langchain4j.data.message.AiMessage;
+import dev.langchain4j.model.chat.ChatLanguageModel;
+import dev.langchain4j.model.output.Response;
+import dev.langchain4j.service.UserMessage;
+import io.quarkiverse.langchain4j.RegisterAiService;
 import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.inject.Inject;
-import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.Handler;
 
 @ApplicationScoped
-public class LangChain4jRoute extends RouteBuilder {
-
-    @Inject
-    MirrorAiService mirrorAiService;
+@RegisterAiService(chatLanguageModelSupplier = 
AiServiceResolvedByInterface.AiServiceResolvedByInterfaceModelSupplier.class)
+public interface AiServiceResolvedByInterface {
 
-    @Override
-    public void configure() {
-        from("direct:camel-annotations-should-work-as-expected")
-                .setHeader("headerName", constant("headerValue"))
-                .bean(mirrorAiService);
+    public static class AiServiceResolvedByInterfaceModelSupplier implements 
Supplier<ChatLanguageModel> {
+        @Override
+        public ChatLanguageModel get() {
+            return (messages) -> new Response<>(new 
AiMessage("AiServiceResolvedByInterface has been resolved"));
+        }
     }
+
+    @UserMessage("Any prompt")
+    @Handler
+    String chat(String input);
 }
diff --git 
a/integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/LangChain4jResource.java
 
b/integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/LangChain4jResource.java
index d3c9d30a68..bbe77035a4 100644
--- 
a/integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/LangChain4jResource.java
+++ 
b/integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/LangChain4jResource.java
@@ -18,6 +18,7 @@ package org.apache.camel.quarkus.component.langchain.it;
 
 import jakarta.enterprise.context.ApplicationScoped;
 import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
 import jakarta.ws.rs.POST;
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.Produces;
@@ -38,4 +39,11 @@ public class LangChain4jResource {
         return 
producerTemplate.requestBody("direct:camel-annotations-should-work-as-expected",
 json, String.class);
     }
 
+    @Path("/ai-service-should-be-resolvable-by-interface")
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String aiServiceShouldBeResolvableByInterface() {
+        return 
producerTemplate.requestBody("direct:ai-service-should-be-resolvable-by-interface",
 "dummy-body", String.class);
+    }
+
 }
diff --git 
a/integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/LangChain4jRoute.java
 
b/integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/LangChain4jRoute.java
index a7ee74f31a..c7aa1a8df6 100644
--- 
a/integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/LangChain4jRoute.java
+++ 
b/integration-tests/langchain4j/src/main/java/org/apache/camel/quarkus/component/langchain/it/LangChain4jRoute.java
@@ -31,5 +31,8 @@ public class LangChain4jRoute extends RouteBuilder {
         from("direct:camel-annotations-should-work-as-expected")
                 .setHeader("headerName", constant("headerValue"))
                 .bean(mirrorAiService);
+
+        from("direct:ai-service-should-be-resolvable-by-interface")
+                .bean(AiServiceResolvedByInterface.class);
     }
 }
diff --git 
a/integration-tests/langchain4j/src/test/java/org/apache/camel/quarkus/component/langchain4jit/LangChain4jTest.java
 
b/integration-tests/langchain4j/src/test/java/org/apache/camel/quarkus/component/langchain4jit/LangChain4jTest.java
index 26d5642894..85e73deb1f 100644
--- 
a/integration-tests/langchain4j/src/test/java/org/apache/camel/quarkus/component/langchain4jit/LangChain4jTest.java
+++ 
b/integration-tests/langchain4j/src/test/java/org/apache/camel/quarkus/component/langchain4jit/LangChain4jTest.java
@@ -36,4 +36,12 @@ class LangChain4jTest {
                 .body("fromHeader", is("headerValue"));
     }
 
+    @Test
+    void aiServiceShouldBeResolvedByInterface() {
+        RestAssured.given()
+                
.get("/langchain4j/ai-service-should-be-resolvable-by-interface")
+                .then()
+                .statusCode(200)
+                .body(is("AiServiceResolvedByInterface has been resolved"));
+    }
 }

Reply via email to