From 05b5f0452ee6c6a10ced4c33224b87b6091edd73 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Thu, 5 Sep 2024 14:38:27 +0200 Subject: [PATCH 01/35] Liberty feature for SmallRye LLM. --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index a4777a5..0207c4f 100644 --- a/pom.xml +++ b/pom.xml @@ -84,6 +84,7 @@ smallrye-llm-langchain4j-buildcompatible-extension smallrye-llm-langchain4j-core smallrye-llm-langchain4j-config-mpconfig + smallrye-llm-langchain4j-liberty-bundle From 5ff505c240b606f4d34337200920277da2654ff9 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Thu, 5 Sep 2024 14:43:11 +0200 Subject: [PATCH 02/35] Removing submodule. --- smallrye-llm-langchain4j-liberty-bundle | 1 + 1 file changed, 1 insertion(+) create mode 160000 smallrye-llm-langchain4j-liberty-bundle diff --git a/smallrye-llm-langchain4j-liberty-bundle b/smallrye-llm-langchain4j-liberty-bundle new file mode 160000 index 0000000..6e1a2ec --- /dev/null +++ b/smallrye-llm-langchain4j-liberty-bundle @@ -0,0 +1 @@ +Subproject commit 6e1a2ec8a91432e56390f84368c987df5b12bba2 From 325c2068a92715572452eb6ab06d9f3340b15d28 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Thu, 5 Sep 2024 15:28:07 +0200 Subject: [PATCH 03/35] Fix git. --- smallrye-llm-langchain4j-liberty-bundle | 1 - 1 file changed, 1 deletion(-) delete mode 160000 smallrye-llm-langchain4j-liberty-bundle diff --git a/smallrye-llm-langchain4j-liberty-bundle b/smallrye-llm-langchain4j-liberty-bundle deleted file mode 160000 index 6e1a2ec..0000000 --- a/smallrye-llm-langchain4j-liberty-bundle +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6e1a2ec8a91432e56390f84368c987df5b12bba2 From 7147ac4880c712cb9ea6af13140755aa410ef5b3 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Thu, 5 Sep 2024 16:28:45 +0200 Subject: [PATCH 04/35] Update... --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0207c4f..53ccc37 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ smallrye-llm-langchain4j-buildcompatible-extension smallrye-llm-langchain4j-core smallrye-llm-langchain4j-config-mpconfig - smallrye-llm-langchain4j-liberty-bundle + smallrye-llm-langchain4j-liberty-bundle From 7bd453dadceb167cca24b4f611d8b5f1c8c29eb1 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Thu, 5 Sep 2024 16:34:28 +0200 Subject: [PATCH 05/35] Commit. --- .../pom.xml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 smallrye-llm-langchain4j-liberty-bundle/pom.xml diff --git a/smallrye-llm-langchain4j-liberty-bundle/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/pom.xml new file mode 100644 index 0000000..6397787 --- /dev/null +++ b/smallrye-llm-langchain4j-liberty-bundle/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + io.smallrye.llm + smallrye-llm-parent + 1.0.0-SNAPSHOT + + smallrye-llm-langchain4j-liberty-bundle + pom + + + mpLLM-1.0 + smallrye-llm-langchain4j-features-bom + smallrye-llm-langchain4j-feature + smallrye-llm-langchain4j-bundle + io.smallrye.llm.core.langchain4j.spi.liberty.cdi.internal + + \ No newline at end of file From 70117106dc8495652612792d843ac578b514d67d Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Thu, 5 Sep 2024 16:40:34 +0200 Subject: [PATCH 06/35] Adding BOM, ESA & integration --- .../BOM/pom.xml | 26 +++++ .../ESA/pom.xml | 70 ++++++++++++ .../integration/pom.xml | 108 ++++++++++++++++++ .../Langchain4JCDIExtensionMetadata.java | 43 +++++++ .../pom.xml | 6 + 5 files changed, 253 insertions(+) create mode 100644 smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml create mode 100644 smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml create mode 100644 smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml create mode 100644 smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java diff --git a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml new file mode 100644 index 0000000..8e66dfc --- /dev/null +++ b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml @@ -0,0 +1,26 @@ + + 4.0.0 + + io.smallrye.llm + smallyre-llm-langchain4j-liberty-bundle + 1.0.0-SNAPSHOT + ../pom.xml + + ${bom.artifact.id} + pom + https://openliberty.io/ + + + + + io.smallrye.llm + smallrye-llm-langchain4j-feature + 0.0.1-SNAPSHOT + runtime + esa + + + + \ No newline at end of file diff --git a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml new file mode 100644 index 0000000..5113183 --- /dev/null +++ b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml @@ -0,0 +1,70 @@ + + 4.0.0 + + io.smallrye.llm + smallyre-llm-langchain4j-liberty-bundle + 1.0.0-SNAPSHOT + ../pom.xml + + ${esa.artifact.id} + esa + + + + io.smallrye.llm + ${integration.artifact.id} + ${project.version} + + + + io.openliberty.features + microProfile-6.1 + 24.0.0.8 + esa + provided + + + + + + + + org.apache.aries + esa-maven-plugin + 1.0.2 + true + + true + all + + IBM + 2 + ${feature.name} + + + ${integration.artifact.id};visibility:=public + + osgi.subsystem.feature + 1.0.0 + + smallrye-llm-langchain4j-core;version="1.0.0-SNAPSHOT" + + jakarta.enterprise; type="spec",io.smallrye.llm.spi; type="third-party" + + + + + + + + + org.apache.aries + esa-maven-plugin + + + + \ No newline at end of file diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml new file mode 100644 index 0000000..a08896b --- /dev/null +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml @@ -0,0 +1,108 @@ + + + 4.0.0 + + io.smallrye.llm + smallyre-llm-langchain4j-liberty-bundle + 1.0.0-SNAPSHOT + ../pom.xml + + ${integration.artifact.id} + bundle + + + org.osgi + osgi.core + 8.0.0 + provided + + + org.osgi + org.osgi.service.component.annotations + 1.5.1 + provided + + + org.osgi + org.osgi.service.cm + 1.6.1 + provided + + + + io.openliberty.spi + io.openliberty.cdi.spi + 1.1.92 + provided + + + org.eclipse.microprofile + microprofile + ${microprofile-api.version} + pom + provided + + + io.smallrye.llm + smallrye-llm-langchain4j-core + 1.0.0-SNAPSHOT + + + io.smallrye.llm + smallrye-llm-langchain4j-portable-extension + 1.0.0-SNAPSHOT + + + + + + + org.apache.felix + maven-bundle-plugin + 5.1.9 + true + + + ${project.artifactId} + ${project.artifactId}; singleton:=true + 1.0.0 + Sindi LangChain4J Liberty Bundle + *;scope=compile|runtime + true + + io.smallrye.llm.spi;version="1.0.0", + io.smallrye.llm.core.langchain4j.spi.cdi.extension;version="1.0.0", + ${new.integration.code.private.package};version="1.0.0" + + + + + + + + org.apache.felix + org.apache.felix.dependencymanager.annotation + 5.0.2 + + + + + + org.apache.felix + maven-bundle-plugin + + + org.apache.felix + org.apache.felix.dependencymanager.annotation + + + + diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java new file mode 100644 index 0000000..966f57b --- /dev/null +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java @@ -0,0 +1,43 @@ +package io.smallrye.llm.core.langchain4j.spi.liberty.cdi.internal; + +import java.lang.annotation.Annotation; +import java.util.Set; + +import jakarta.enterprise.inject.spi.Extension; + +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; + +import io.openliberty.cdi.spi.CDIExtensionMetadata; +import io.smallrye.llm.core.langchain4j.portableextension; +import io.smallrye.llm.spi; + +/** + * @author Buhake Sindi + * @since 26 August 2024 + */ +@Component(service = CDIExtensionMetadata.class, configurationPolicy = ConfigurationPolicy.IGNORE, immediate = true) +public class Langchain4JCDIExtensionMetadata implements CDIExtensionMetadata { + + /* + * (non-Javadoc) + * + * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getBeanDefiningAnnotationClasses() + */ + @Override + public Set> getBeanDefiningAnnotationClasses() { + // TODO Auto-generated method stub + return Set.of(RegisterAIService.class); + } + + /* + * (non-Javadoc) + * + * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getExtensions() + */ + @Override + public Set> getExtensions() { + // TODO Auto-generated method stub + return Set.of(LangChain4JAIServicePortableExtension.class); + } +} diff --git a/smallrye-llm-langchain4j-liberty-bundle/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/pom.xml index 6397787..1363940 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/pom.xml @@ -17,4 +17,10 @@ smallrye-llm-langchain4j-bundle io.smallrye.llm.core.langchain4j.spi.liberty.cdi.internal + + + BOM + ESA + integration + \ No newline at end of file From c125f1d06f15f78b8c5f347310030bf6ac9de480 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Thu, 5 Sep 2024 17:04:48 +0200 Subject: [PATCH 07/35] Update POM --- smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml | 2 +- smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml | 2 +- .../integration/pom.xml | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml index 8e66dfc..2684a6a 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml @@ -17,7 +17,7 @@ io.smallrye.llm smallrye-llm-langchain4j-feature - 0.0.1-SNAPSHOT + ${project.version} runtime esa diff --git a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml index 5113183..926f897 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml @@ -51,7 +51,7 @@ osgi.subsystem.feature 1.0.0 - smallrye-llm-langchain4j-core;version="1.0.0-SNAPSHOT" + smallrye-llm-langchain4j-core;version="${project.version}" jakarta.enterprise; type="spec",io.smallrye.llm.spi; type="third-party" diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml index a08896b..ee06523 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml @@ -48,12 +48,12 @@ io.smallrye.llm smallrye-llm-langchain4j-core - 1.0.0-SNAPSHOT + ${project.version} io.smallrye.llm smallrye-llm-langchain4j-portable-extension - 1.0.0-SNAPSHOT + ${project.version} @@ -74,13 +74,12 @@ true io.smallrye.llm.spi;version="1.0.0", - io.smallrye.llm.core.langchain4j.spi.cdi.extension;version="1.0.0", ${new.integration.code.private.package};version="1.0.0" From 7aa894080283eca23fa3b20dbf4d9b001f0a111a Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Thu, 5 Sep 2024 17:15:42 +0200 Subject: [PATCH 08/35] Update POM --- ...adata.java => Langchain4JAIServiceCDIExtensionMetadata.java} | 2 +- smallrye-llm-langchain4j-liberty-bundle/pom.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) rename smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/{Langchain4JCDIExtensionMetadata.java => Langchain4JAIServiceCDIExtensionMetadata.java} (93%) diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java similarity index 93% rename from smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java rename to smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java index 966f57b..e4ed52d 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java @@ -17,7 +17,7 @@ * @since 26 August 2024 */ @Component(service = CDIExtensionMetadata.class, configurationPolicy = ConfigurationPolicy.IGNORE, immediate = true) -public class Langchain4JCDIExtensionMetadata implements CDIExtensionMetadata { +public class Langchain4JAIServiceCDIExtensionMetadata implements CDIExtensionMetadata { /* * (non-Javadoc) diff --git a/smallrye-llm-langchain4j-liberty-bundle/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/pom.xml index 1363940..1aa39e8 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/pom.xml @@ -6,6 +6,7 @@ io.smallrye.llm smallrye-llm-parent 1.0.0-SNAPSHOT + ../pom.xml smallrye-llm-langchain4j-liberty-bundle pom From 7b9909caf59d12889a411be516676ede02850e99 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Thu, 5 Sep 2024 17:26:26 +0200 Subject: [PATCH 09/35] Update POM --- smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml index ee06523..a2a96eb 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml @@ -69,7 +69,7 @@ ${project.artifactId} ${project.artifactId}; singleton:=true 1.0.0 - Sindi LangChain4J Liberty Bundle + SmallRye LLM LangChain4J Liberty Bundle *;scope=compile|runtime true From 5e451ea1dcbdf24b75dc32417e77f15b578b42b0 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Thu, 5 Sep 2024 22:53:51 +0200 Subject: [PATCH 10/35] Update POM --- smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml | 2 +- .../cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml index a2a96eb..8921f50 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml @@ -73,12 +73,12 @@ *;scope=compile|runtime true - io.smallrye.llm.spi;version="1.0.0", ${new.integration.code.private.package};version="1.0.0" diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java index e4ed52d..a53a8af 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java @@ -38,6 +38,6 @@ public Set> getBeanDefiningAnnotationClasses() { @Override public Set> getExtensions() { // TODO Auto-generated method stub - return Set.of(LangChain4JAIServicePortableExtension.class); + return Set.of(LangChain4JAIServicePortableExtension.class, LangChain4JPluginsPortableExtension.class); } } From ad85556b76216c1f8a2284518624844b9814f883 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Fri, 6 Sep 2024 09:28:02 +0200 Subject: [PATCH 11/35] Fixed code import and spelling. --- smallrye-llm-langchain4j-core/pom.xml | 2 -- smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml | 2 +- .../internal/Langchain4JAIServiceCDIExtensionMetadata.java | 5 +++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/smallrye-llm-langchain4j-core/pom.xml b/smallrye-llm-langchain4j-core/pom.xml index 4607191..9f58454 100644 --- a/smallrye-llm-langchain4j-core/pom.xml +++ b/smallrye-llm-langchain4j-core/pom.xml @@ -11,7 +11,6 @@ http://maven.apache.org - org.jboss.logging jboss-logging @@ -20,7 +19,6 @@ jakarta.enterprise jakarta.enterprise.cdi-api - dev.langchain4j langchain4j diff --git a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml index 2684a6a..48cb147 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml @@ -16,7 +16,7 @@ io.smallrye.llm - smallrye-llm-langchain4j-feature + ${esa.artifact.id} ${project.version} runtime esa diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java index a53a8af..6a4d72a 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java @@ -9,8 +9,9 @@ import org.osgi.service.component.annotations.ConfigurationPolicy; import io.openliberty.cdi.spi.CDIExtensionMetadata; -import io.smallrye.llm.core.langchain4j.portableextension; -import io.smallrye.llm.spi; +import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JAIServicePortableExtension; +import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JPluginsPortableExtension; +import io.smallrye.llm.spi.RegisterAIService; /** * @author Buhake Sindi From b6f467f5ddf38ff13ea4db376054d6edb7af4196 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Fri, 6 Sep 2024 10:07:44 +0200 Subject: [PATCH 12/35] Update POM --- smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml | 2 +- smallrye-llm-langchain4j-liberty-bundle/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml index 926f897..e333b9b 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml @@ -41,7 +41,7 @@ IBM 2 - ${feature.name} + ${liberty.feature.name} diff --git a/smallrye-llm-langchain4j-liberty-bundle/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/pom.xml index 1aa39e8..f46533e 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/pom.xml @@ -12,7 +12,7 @@ pom - mpLLM-1.0 + mpLLM-1.0 smallrye-llm-langchain4j-features-bom smallrye-llm-langchain4j-feature smallrye-llm-langchain4j-bundle From 38929e7f0cb4f203df19b09720eb9dd0eddeec88 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Sun, 8 Sep 2024 11:00:57 +0200 Subject: [PATCH 13/35] Update POM --- smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml | 5 ++--- smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml | 5 ++--- smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml | 3 +-- smallrye-llm-langchain4j-liberty-bundle/pom.xml | 1 + 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml index 48cb147..4465f93 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml @@ -3,10 +3,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - io.smallrye.llm + io.smallrye.llm.liberty-bundle smallyre-llm-langchain4j-liberty-bundle 1.0.0-SNAPSHOT - ../pom.xml ${bom.artifact.id} pom @@ -15,7 +14,7 @@ - io.smallrye.llm + io.smallrye.llm.liberty-bundle ${esa.artifact.id} ${project.version} runtime diff --git a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml index e333b9b..717ecc3 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml @@ -3,17 +3,16 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - io.smallrye.llm + io.smallrye.llm.liberty-bundle smallyre-llm-langchain4j-liberty-bundle 1.0.0-SNAPSHOT - ../pom.xml ${esa.artifact.id} esa - io.smallrye.llm + io.smallrye.llm.liberty-bundle ${integration.artifact.id} ${project.version} diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml index 8921f50..d7a6501 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml @@ -5,10 +5,9 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 4.0.0 - io.smallrye.llm + io.smallrye.llm.liberty-bundle smallyre-llm-langchain4j-liberty-bundle 1.0.0-SNAPSHOT - ../pom.xml ${integration.artifact.id} bundle diff --git a/smallrye-llm-langchain4j-liberty-bundle/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/pom.xml index f46533e..5de2a18 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/pom.xml @@ -8,6 +8,7 @@ 1.0.0-SNAPSHOT ../pom.xml + io.smallrye.llm.liberty-bundle smallrye-llm-langchain4j-liberty-bundle pom From 350eee911dc09286c5472e7f4f7a08df39345a53 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Sun, 8 Sep 2024 13:05:20 +0200 Subject: [PATCH 14/35] Update POM --- smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml | 3 ++- smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml | 3 ++- smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml | 3 ++- smallrye-llm-langchain4j-liberty-bundle/pom.xml | 1 + 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml index 4465f93..e0eda69 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml @@ -4,8 +4,9 @@ 4.0.0 io.smallrye.llm.liberty-bundle - smallyre-llm-langchain4j-liberty-bundle + smallrye-llm-langchain4j-liberty-bundle 1.0.0-SNAPSHOT + ../pom.xml ${bom.artifact.id} pom diff --git a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml index 717ecc3..c2b7e40 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml @@ -4,8 +4,9 @@ 4.0.0 io.smallrye.llm.liberty-bundle - smallyre-llm-langchain4j-liberty-bundle + smallrye-llm-langchain4j-liberty-bundle 1.0.0-SNAPSHOT + ../pom.xml ${esa.artifact.id} esa diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml index d7a6501..d41e6ed 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml @@ -6,8 +6,9 @@ 4.0.0 io.smallrye.llm.liberty-bundle - smallyre-llm-langchain4j-liberty-bundle + smallrye-llm-langchain4j-liberty-bundle 1.0.0-SNAPSHOT + ../pom.xml ${integration.artifact.id} bundle diff --git a/smallrye-llm-langchain4j-liberty-bundle/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/pom.xml index 5de2a18..11b58df 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/pom.xml @@ -13,6 +13,7 @@ pom + 6.1 mpLLM-1.0 smallrye-llm-langchain4j-features-bom smallrye-llm-langchain4j-feature From 2e2b4e1adb00da48c1b4b972f363add26ab294bc Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Mon, 16 Sep 2024 11:11:18 +0200 Subject: [PATCH 15/35] Adding Liberty examples. --- ....eclipse.wst.common.project.facet.core.xml | 7 + examples/liberty-car-booking/pom.xml | 194 ++++++++++++++++++ .../java/io/jefrajames/booking/Booking.java | 21 ++ .../BookingAlreadyCanceledException.java | 9 + .../BookingCannotBeCanceledException.java | 9 + .../booking/BookingNotFoundException.java | 7 + .../io/jefrajames/booking/BookingService.java | 95 +++++++++ .../booking/CarBookingResource.java | 48 +++++ .../io/jefrajames/booking/ChatAiService.java | 42 ++++ .../java/io/jefrajames/booking/Customer.java | 15 ++ .../io/jefrajames/booking/DocRagIngestor.java | 61 ++++++ .../io/jefrajames/booking/DummyLLConfig.java | 56 +++++ .../io/jefrajames/booking/FraudAiService.java | 55 +++++ .../io/jefrajames/booking/FraudResponse.java | 14 ++ .../java/io/jefrajames/booking/JaxRSApp.java | 8 + .../src/main/liberty/config/server.xml | 28 +++ .../src/main/resources/META-INF/beans.xml | 7 + .../src/main/webapp/WEB-INF/web.xml | 10 + examples/pom.xml | 1 + pom.xml | 2 - .../BOM/pom.xml | 6 +- .../ESA/pom.xml | 17 +- .../integration/pom.xml | 24 +++ ...gchain4JAIServiceCDIExtensionMetadata.java | 44 ---- .../core/PortableExtensionInstanceTest.java | 22 +- 25 files changed, 738 insertions(+), 64 deletions(-) create mode 100644 examples/liberty-car-booking/.settings/org.eclipse.wst.common.project.facet.core.xml create mode 100644 examples/liberty-car-booking/pom.xml create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Booking.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingAlreadyCanceledException.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingCannotBeCanceledException.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingNotFoundException.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingService.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/CarBookingResource.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/ChatAiService.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Customer.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DummyLLConfig.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudAiService.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudResponse.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/JaxRSApp.java create mode 100644 examples/liberty-car-booking/src/main/liberty/config/server.xml create mode 100644 examples/liberty-car-booking/src/main/resources/META-INF/beans.xml create mode 100644 examples/liberty-car-booking/src/main/webapp/WEB-INF/web.xml delete mode 100644 smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java diff --git a/examples/liberty-car-booking/.settings/org.eclipse.wst.common.project.facet.core.xml b/examples/liberty-car-booking/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 0000000..a571972 --- /dev/null +++ b/examples/liberty-car-booking/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/examples/liberty-car-booking/pom.xml b/examples/liberty-car-booking/pom.xml new file mode 100644 index 0000000..5266377 --- /dev/null +++ b/examples/liberty-car-booking/pom.xml @@ -0,0 +1,194 @@ + + 4.0.0 + io.smallrye.llm.examples + 1.0.0-SNAPSHOT + liberty-car-booking + war + + + + Buhake Sindi + +2 + + PROJECT LEAD + + + + + + UTF-8 + UTF-8 + 17 + 10.0.0 + 6.1 + 3.13.0 + 3.4.0 + 0.33.0 + 3.13.0 + + + + + + org.eclipse.microprofile + microprofile + ${microprofile-api.version} + pom + provided + + + + io.smallrye.llm.liberty-bundle + smallrye-llm-langchain4j-features-bom + 1.0.0-SNAPSHOT + pom + + + + io.smallrye.llm + smallrye-llm-langchain4j-core + 1.0.0-SNAPSHOT + provided + + + + io.smallrye.llm + smallrye-llm-langchain4j-portable-extension + 1.0.0-SNAPSHOT + provided + + + + dev.langchain4j + langchain4j-open-ai + ${dev.langchain4j.version} + + + + dev.langchain4j + langchain4j-embeddings-all-minilm-l6-v2 + ${dev.langchain4j.version} + + + + + org.slf4j + slf4j-jdk14 + runtime + 2.0.9 + + + + + + + org.eclipse.microprofile + microprofile + pom + + + + io.smallrye.llm + smallrye-llm-langchain4j-core + provided + + + + io.smallrye.llm + smallrye-llm-langchain4j-portable-extension + provided + + + + io.smallrye.llm.liberty-bundle + smallrye-llm-langchain4j-feature + 1.0.0-SNAPSHOT + pom + provided + + + + org.projectlombok + lombok + 1.18.32 + provided + + + + + dev.langchain4j + langchain4j + ${dev.langchain4j.version} + + + + dev.langchain4j + langchain4j-hugging-face + ${dev.langchain4j.version} + + + + dev.langchain4j + langchain4j-open-ai + ${dev.langchain4j.version} + + + + dev.langchain4j + langchain4j-embeddings-all-minilm-l6-v2 + ${dev.langchain4j.version} + + + + + org.slf4j + slf4j-jdk14 + runtime + + + + + ${project.artifactId} + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compiler.release} + + + + + io.openliberty.tools + liberty-maven-plugin + 3.10.3 + + + io.openliberty + openliberty-runtime + 24.0.0.9 + zip + + + ${project.build.finalName} + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.openliberty.tools + liberty-maven-plugin + + + + \ No newline at end of file diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Booking.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Booking.java new file mode 100644 index 0000000..a8e03c1 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Booking.java @@ -0,0 +1,21 @@ +package io.jefrajames.booking; + +import java.time.LocalDate; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Booking { + + private String bookingNumber; + private LocalDate start; + private LocalDate end; + private Customer customer; + private boolean canceled = false; + private String carModel; + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingAlreadyCanceledException.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingAlreadyCanceledException.java new file mode 100644 index 0000000..740a903 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingAlreadyCanceledException.java @@ -0,0 +1,9 @@ +package io.jefrajames.booking; + +public class BookingAlreadyCanceledException extends RuntimeException { + + public BookingAlreadyCanceledException(String bookingNumber) { + super("Booking " + bookingNumber + " already canceled"); + } + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingCannotBeCanceledException.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingCannotBeCanceledException.java new file mode 100644 index 0000000..5bafda7 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingCannotBeCanceledException.java @@ -0,0 +1,9 @@ +package io.jefrajames.booking; + +public class BookingCannotBeCanceledException extends RuntimeException { + + public BookingCannotBeCanceledException(String bookingNumber) { + super("Booking " + bookingNumber + " cannot be canceled"); + } + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingNotFoundException.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingNotFoundException.java new file mode 100644 index 0000000..3b8165e --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingNotFoundException.java @@ -0,0 +1,7 @@ +package io.jefrajames.booking; + +public class BookingNotFoundException extends RuntimeException { + public BookingNotFoundException(String bookingNumber) { + super("Booking " + bookingNumber + " not found"); + } +} \ No newline at end of file diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingService.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingService.java new file mode 100644 index 0000000..4ee5ff5 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingService.java @@ -0,0 +1,95 @@ +package io.jefrajames.booking; + +import java.time.LocalDate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import jakarta.enterprise.context.ApplicationScoped; + +import dev.langchain4j.agent.tool.Tool; +import lombok.extern.java.Log; + +@ApplicationScoped +@Log +public class BookingService { + + // Pseudo database + private static final Map BOOKINGS = new HashMap<>(); + static { + // James Bond: hero customer! + BOOKINGS.put("123-456", new Booking("123-456", LocalDate.now().plusDays(1), LocalDate.now().plusDays(7), + new Customer("James", "Bond"), false, "Aston Martin")); // Not cancelable: too late + BOOKINGS.put("234-567", new Booking("234-567", LocalDate.now().plusDays(10), LocalDate.now().plusDays(12), + new Customer("James", "Bond"), false, "Renault")); // Not cancelable: too short + BOOKINGS.put("345-678", new Booking("345-678", LocalDate.now().plusDays(14), LocalDate.now().plusDays(20), + new Customer("James", "Bond"), false, "Porsche")); // Cancelable + // Emilio Largo: villain frauder! + BOOKINGS.put("456-789", new Booking("456-789", LocalDate.now().plusDays(10), LocalDate.now().plusDays(20), + new Customer("Largo", "Emilio"), false, "Porsche")); // Cancelable + BOOKINGS.put("567-890", new Booking("567-890", LocalDate.now().plusDays(11), LocalDate.now().plusDays(16), + new Customer("Largo", "Emilio"), false, "BMW")); // Cancelable + } + + // Simulate database accesses + private Booking checkBookingExists(String bookingNumber, String name, String surname) { + Booking booking = BOOKINGS.get(bookingNumber); + if (booking == null || !booking.getCustomer().getName().equals(name) + || !booking.getCustomer().getSurname().equals(surname)) { + throw new BookingNotFoundException(bookingNumber); + } + return booking; + } + + @Tool("Get booking details given a booking number and customer name and surname") + public Booking getBookingDetails(String bookingNumber, String name, String surname) { + log.info("DEMO: Calling Tool-getBookingDetails: " + bookingNumber + " and customer: " + + name + " " + surname); + return checkBookingExists(bookingNumber, name, surname); + } + + @Tool("Get all booking ids for a customer given his name and surname") + public List getBookingsForCustomer(String name, String surname) { + log.info("DEMO: Calling Tool-getBookingsForCustomer: " + name + " " + surname); + Customer customer = new Customer(name, surname); + return BOOKINGS.values() + .stream() + .filter(booking -> booking.getCustomer().equals(customer)) + .map(Booking::getBookingNumber) + .collect(Collectors.toList()); + } + + public void checkCancelPolicy(Booking booking) { + + // Reservations can be cancelled up to 7 days prior to the start of the booking + // period + if (LocalDate.now().plusDays(7).isAfter(booking.getStart())) { + throw new BookingCannotBeCanceledException(booking.getBookingNumber() + " Too late"); + } + + // If the booking period is less than 3 days, cancellations are not permitted. + if (booking.getEnd().compareTo(booking.getStart().plusDays(3)) < 0) { + throw new BookingCannotBeCanceledException(booking.getBookingNumber() + " Too short"); + } + + } + + @Tool("Cancel a booking given its booking number and customer name and surname") + public Booking cancelBooking(String bookingNumber, String name, String surname) { + log.info("DEMO: Calling Tool-cancelBooking " + bookingNumber + " for customer: " + name + + " " + surname); + + Booking booking = checkBookingExists(bookingNumber, name, surname); + + if (booking.isCanceled()) + throw new BookingCannotBeCanceledException(bookingNumber); + + checkCancelPolicy(booking); + + booking.setCanceled(true); + + return booking; + } + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/CarBookingResource.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/CarBookingResource.java new file mode 100644 index 0000000..8faf5f9 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/CarBookingResource.java @@ -0,0 +1,48 @@ +package io.jefrajames.booking; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; + +@ApplicationScoped +@Path("/car-booking") +public class CarBookingResource { + + @Inject + private ChatAiService aiService; + + @Inject + private FraudAiService fraudService; + + @GET + @Produces(MediaType.TEXT_PLAIN) + @Path("/chat") + public String chatWithAssistant( + @QueryParam("question") String question) { + + String answer; + try { + answer = aiService.chat(question); + } catch (Exception e) { + e.printStackTrace(); + answer = "My failure reason is:\n\n" + e.getMessage(); + } + + return answer; + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/fraud") + public FraudResponse detectFraudForCustomer( + @QueryParam("name") String name, + + @QueryParam("surname") String surname) { + return fraudService.detectFraudForCustomer(name, surname); + } + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/ChatAiService.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/ChatAiService.java new file mode 100644 index 0000000..bc6892f --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/ChatAiService.java @@ -0,0 +1,42 @@ +package io.jefrajames.booking; + +import java.time.temporal.ChronoUnit; + +import org.eclipse.microprofile.faulttolerance.Fallback; +import org.eclipse.microprofile.faulttolerance.Retry; +import org.eclipse.microprofile.faulttolerance.Timeout; + +import dev.langchain4j.service.SystemMessage; +import io.smallrye.llm.spi.RegisterAIService; + +//@SuppressWarnings("CdiManagedBeanInconsistencyInspection") +@RegisterAIService(tools = BookingService.class, chatMemoryMaxMessages = 10) +public interface ChatAiService { + + @SystemMessage(""" + You are a customer support agent of a car rental company named 'Miles of Smiles'. + Before providing information about booking or canceling a booking, you MUST always check: + booking number, customer name and surname. + You should not answer to any request not related to car booking or Miles of Smiles company general information. + When a customer wants to cancel a booking, you must check his name and the Miles of Smiles cancellation policy first. + Any cancelation request must comply with cancellation policy both for the delay and the duration. + Today is {{current_date}}. + """) + @Timeout(unit = ChronoUnit.MINUTES, value = 5) + @Retry(abortOn = { BookingCannotBeCanceledException.class, + BookingAlreadyCanceledException.class, + BookingNotFoundException.class }, maxRetries = 2) + @Fallback(fallbackMethod = "chatFallback", skipOn = { + BookingCannotBeCanceledException.class, + BookingAlreadyCanceledException.class, + BookingNotFoundException.class }) + // String chat(@V("question") @UserMessage String question); + String chat(String question); + + default String chatFallback(String question) { + return String.format( + "Sorry, I am not able to answer your request %s at the moment. Please try again later.", + question); + } + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Customer.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Customer.java new file mode 100644 index 0000000..f2488f0 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Customer.java @@ -0,0 +1,15 @@ +package io.jefrajames.booking; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(of = { "name", "surname" }) +public class Customer { + private String name; + private String surname; +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java new file mode 100644 index 0000000..d0d0af4 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java @@ -0,0 +1,61 @@ +package io.jefrajames.booking; + +import static dev.langchain4j.data.document.loader.FileSystemDocumentLoader.loadDocuments; + +import java.io.File; +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Initialized; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.Produces; + +import dev.langchain4j.data.document.Document; +import dev.langchain4j.data.document.parser.TextDocumentParser; +import dev.langchain4j.data.document.splitter.DocumentSplitters; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.model.embedding.onnx.allminilml6v2.AllMiniLmL6V2EmbeddingModel; +import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; +import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; +import lombok.extern.java.Log; + +@Log +@ApplicationScoped +public class DocRagIngestor { + + // Used by ContentRetriever + @Produces + private EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel(); + + // Used by ContentRetriever + @Produces + private InMemoryEmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); + + private File docs = new File(System.getProperty("docragdir")); + + private List loadDocs() { + return loadDocuments(docs.getPath(), new TextDocumentParser()); + } + + public void ingest(@Observes @Initialized(ApplicationScoped.class) Object pointless) { + + long start = System.currentTimeMillis(); + + EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() + .documentSplitter(DocumentSplitters.recursive(300, 30)) + .embeddingModel(embeddingModel) + .embeddingStore(embeddingStore) + .build(); + + List docs = loadDocs(); + ingestor.ingest(docs); + + log.info(String.format("DEMO %d documents ingested in %d msec", docs.size(), + System.currentTimeMillis() - start)); + } + + public static void main(String[] args) { + + System.out.println(InMemoryEmbeddingStore.class.getInterfaces()[0]); + } +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DummyLLConfig.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DummyLLConfig.java new file mode 100644 index 0000000..e120c91 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DummyLLConfig.java @@ -0,0 +1,56 @@ +package io.jefrajames.booking; + +import java.io.FileReader; +import java.io.IOException; +import java.time.Duration; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; + +import io.smallrye.llm.core.langchain4j.core.config.spi.LLMConfig; + +public class DummyLLConfig implements LLMConfig { + Properties properties = new Properties(); + + @Override + public void init() { + try (FileReader fileReader = new FileReader(System.getProperty("llmconfigfile"))) { + properties.load(fileReader); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public Set getBeanNames() { + return properties.keySet().stream().map(Object::toString) + .filter(prop -> prop.startsWith(PREFIX)) + .map(prop -> prop.substring(PREFIX.length() + 1, prop.indexOf(".", PREFIX.length() + 2))) + .collect(Collectors.toSet()); + } + + @Override + public T getBeanPropertyValue(String beanName, String propertyName, Class type) { + String value = properties.getProperty(PREFIX + "." + beanName + "." + propertyName); + if (value == null) + return null; + if (type == String.class) + return (T) value; + if (type == Duration.class) + return (T) Duration.parse(value); + try { + return type.getConstructor(String.class).newInstance(value); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public Set getPropertyNamesForBean(String beanName) { + String configPrefix = PREFIX + "." + beanName + ".config."; + return properties.keySet().stream().map(Object::toString) + .filter(prop -> prop.startsWith(configPrefix)) + .map(prop -> prop.substring(configPrefix.length())) + .collect(Collectors.toSet()); + } +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudAiService.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudAiService.java new file mode 100644 index 0000000..68488de --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudAiService.java @@ -0,0 +1,55 @@ +package io.jefrajames.booking; + +import dev.langchain4j.service.SystemMessage; +import dev.langchain4j.service.UserMessage; +import dev.langchain4j.service.V; +import io.smallrye.llm.spi.RegisterAIService; + +@SuppressWarnings("CdiManagedBeanInconsistencyInspection") +@RegisterAIService(chatMemoryMaxMessages = 5, + + chatLanguageModelName = "chat-model") +public interface FraudAiService { + + @SystemMessage(""" + You are a car booking fraud detection AI for Miles of Smiles. + You have to detect customer fraud in bookings. + """) + @UserMessage(""" + Your task is to detect whether a fraud was committed for the customer {{name}} {{surname}}. + + To detect a fraud, perform the following actions: + 1 - Retrieve all bookings for the customer with name {{name}} and surname {{surname}}. + 2 - If there are no bookings, return the fraud status as 'false'. + 3 - Otherwise, Determine if there is an overlap between several bookings. + 4 - If there is an overlap, a fraud is detected. + 5 - If a fraud is detected, return the fraud status and the bookings that overlap. + + A booking overlap (and hence a fraud) occurs when there are several bookings for a given date. + For instance: + -there is no overlap if a given customer has the following bookings: + - Booking number 345-678 with the period from 2024-03-25 to 2024-03-31. + - Booking number 234-567 with the period from 2024-03-21 to 2024-03-23. + -there is an overlap if a given customer has the following bookings: + - Booking number 456-789 with the period from 2024-03-21 to 2024-03-31. + - Booking number 567-890 with the period from 2024-03-22 to 2024-03-27. + + Answer with the following information in a valid JSON document: + - the customer-name key set to {{name}} + - the customer-surname key set to {{surname}} + - the fraud-detected key set to 'true' or 'false' + - in case of fraud, the explanation of the fraud in the fraud-explanation key + - in case of fraud, the reservation ids that overlap. + You must respond in a valid JSON format. + + You must not wrap JSON response in backticks, markdown, or in any other way, but return it as plain text. + """) + FraudResponse detectFraudForCustomer(@V("name") String name, @V("surname") String surname); + + default FraudResponse fraudFallback(String name, String surname) { + throw new RuntimeException( + "Sorry, I am not able to detect fraud for customer " + name + " " + surname + + " at the moment. Please try again later."); + } + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudResponse.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudResponse.java new file mode 100644 index 0000000..a1663ec --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudResponse.java @@ -0,0 +1,14 @@ +package io.jefrajames.booking; + +import java.util.List; + +import lombok.Data; + +// Warning: Java Record not supported by Google JSON (used by LangChain4J) +@Data +public class FraudResponse { + private String customerName; + private String customerSurname; + private boolean fraudDetected; + private List bookingIds; +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/JaxRSApp.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/JaxRSApp.java new file mode 100644 index 0000000..f478b17 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/JaxRSApp.java @@ -0,0 +1,8 @@ +package io.jefrajames.booking; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; + +@ApplicationPath("/api") +public class JaxRSApp extends Application { +} diff --git a/examples/liberty-car-booking/src/main/liberty/config/server.xml b/examples/liberty-car-booking/src/main/liberty/config/server.xml new file mode 100644 index 0000000..25f4aee --- /dev/null +++ b/examples/liberty-car-booking/src/main/liberty/config/server.xml @@ -0,0 +1,28 @@ + + + + + jakartaee-10.0 + + + microProfile-6.1 + bells-1.0 + true + usr:mpLLM-1.0 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/liberty-car-booking/src/main/resources/META-INF/beans.xml b/examples/liberty-car-booking/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000..c2a9245 --- /dev/null +++ b/examples/liberty-car-booking/src/main/resources/META-INF/beans.xml @@ -0,0 +1,7 @@ + + + + diff --git a/examples/liberty-car-booking/src/main/webapp/WEB-INF/web.xml b/examples/liberty-car-booking/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..a3823f1 --- /dev/null +++ b/examples/liberty-car-booking/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,10 @@ + + + Liberty Project + + + index.html + + \ No newline at end of file diff --git a/examples/pom.xml b/examples/pom.xml index c1af84f..b6fb2da 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -17,5 +17,6 @@ helidon-car-booking-portable-ext quarkus-car-booking glassfish-car-booking + liberty-car-booking diff --git a/pom.xml b/pom.xml index 53ccc37..61b9152 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,6 @@ https://smallrye.io - 11 3.1 2.4.0 9.7 @@ -52,7 +51,6 @@ 3.13.0 3.5.0 3.4.0 - 11 SmallRye LLM diff --git a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml index e0eda69..1856f93 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml @@ -16,9 +16,9 @@ io.smallrye.llm.liberty-bundle - ${esa.artifact.id} - ${project.version} - runtime + smallrye-llm-langchain4j-feature + 1.0.0-SNAPSHOT + provided esa diff --git a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml index c2b7e40..1482f4f 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml @@ -21,10 +21,20 @@ io.openliberty.features microProfile-6.1 - 24.0.0.8 + 24.0.0.9 esa provided + + @@ -50,10 +60,7 @@ osgi.subsystem.feature 1.0.0 - - smallrye-llm-langchain4j-core;version="${project.version}" - - jakarta.enterprise; type="spec",io.smallrye.llm.spi; type="third-party" + dev.langchain4j.*;version="${dev.langchain4j.version}",io.smallrye.llm.spi;version="1.0.0",io.smallrye.llm.core.langchain4j.portableextension;version="1.0.0" diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml index d41e6ed..c6bb32b 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml @@ -38,6 +38,11 @@ 1.1.92 provided + + jakarta.enterprise + jakarta.enterprise.cdi-api + provided + org.eclipse.microprofile microprofile @@ -45,16 +50,30 @@ pom provided + io.smallrye.llm smallrye-llm-langchain4j-portable-extension ${project.version} + provided + + + dev.langchain4j + langchain4j + + + + org.jboss.logging + jboss-logging + @@ -73,6 +92,8 @@ *;scope=compile|runtime true + io.smallrye.llm.spi;version="1.0.0", + io.smallrye.llm.core.langchain4j.portableextension;version="1.0.0", ${new.integration.code.private.package};version="1.0.0" + + !io.smallrye.llm.aiservice; + diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java deleted file mode 100644 index 6a4d72a..0000000 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.smallrye.llm.core.langchain4j.spi.liberty.cdi.internal; - -import java.lang.annotation.Annotation; -import java.util.Set; - -import jakarta.enterprise.inject.spi.Extension; - -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.ConfigurationPolicy; - -import io.openliberty.cdi.spi.CDIExtensionMetadata; -import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JAIServicePortableExtension; -import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JPluginsPortableExtension; -import io.smallrye.llm.spi.RegisterAIService; - -/** - * @author Buhake Sindi - * @since 26 August 2024 - */ -@Component(service = CDIExtensionMetadata.class, configurationPolicy = ConfigurationPolicy.IGNORE, immediate = true) -public class Langchain4JAIServiceCDIExtensionMetadata implements CDIExtensionMetadata { - - /* - * (non-Javadoc) - * - * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getBeanDefiningAnnotationClasses() - */ - @Override - public Set> getBeanDefiningAnnotationClasses() { - // TODO Auto-generated method stub - return Set.of(RegisterAIService.class); - } - - /* - * (non-Javadoc) - * - * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getExtensions() - */ - @Override - public Set> getExtensions() { - // TODO Auto-generated method stub - return Set.of(LangChain4JAIServicePortableExtension.class, LangChain4JPluginsPortableExtension.class); - } -} diff --git a/smallrye-llm-langchain4j-portable-extension/src/test/java/io/smallrye/llm/core/PortableExtensionInstanceTest.java b/smallrye-llm-langchain4j-portable-extension/src/test/java/io/smallrye/llm/core/PortableExtensionInstanceTest.java index 782fca3..189d8ef 100644 --- a/smallrye-llm-langchain4j-portable-extension/src/test/java/io/smallrye/llm/core/PortableExtensionInstanceTest.java +++ b/smallrye-llm-langchain4j-portable-extension/src/test/java/io/smallrye/llm/core/PortableExtensionInstanceTest.java @@ -1,15 +1,16 @@ package io.smallrye.llm.core; -import dev.langchain4j.model.chat.ChatLanguageModel; -import io.smallrye.config.inject.ConfigExtension; -import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JAIServicePortableExtension; -import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JPluginsPortableExtension; +import java.lang.annotation.Annotation; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.concurrent.Callable; + import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.RequestScoped; import jakarta.enterprise.context.control.ActivateRequestContext; import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.spi.BeanManager; import jakarta.inject.Inject; + import org.jboss.weld.junit5.WeldInitiator; import org.jboss.weld.junit5.WeldJunit5Extension; import org.jboss.weld.junit5.WeldSetup; @@ -17,9 +18,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import java.lang.annotation.Annotation; -import java.lang.reflect.UndeclaredThrowableException; -import java.util.concurrent.Callable; +import dev.langchain4j.model.chat.ChatLanguageModel; +import io.smallrye.config.inject.ConfigExtension; +import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JAIServicePortableExtension; +import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JPluginsPortableExtension; @ExtendWith(WeldJunit5Extension.class) public class PortableExtensionInstanceTest { @@ -73,8 +75,8 @@ void detectAIServiceInterface() { @Test void ensureInjectAndScope() { - MyDummyAIService myDummyAIService=myDummyAIServiceInstance.get(); - MyDummyApplicationScopedAIService myDummyApplicationScopedAIService=myDummyApplicationScopedAIServiceInstance.get(); + MyDummyAIService myDummyAIService = myDummyAIServiceInstance.get(); + MyDummyApplicationScopedAIService myDummyApplicationScopedAIService = myDummyApplicationScopedAIServiceInstance.get(); Assertions.assertNotNull(myDummyAIService); Assertions.assertNotNull(myDummyApplicationScopedAIService); assertBeanScope(MyDummyAIService.class, RequestScoped.class); @@ -83,7 +85,7 @@ void ensureInjectAndScope() { @Test void callEffectiveCreation() { - MyDummyAIService myDummyAIService=myDummyAIServiceInstance.get(); + MyDummyAIService myDummyAIService = myDummyAIServiceInstance.get(); Assertions.assertNotNull(requestContextCaller.run(() -> myDummyAIService.toString())); } From 29e40a81326122d6e71e364922f631bdbdf912f4 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Mon, 16 Sep 2024 11:14:54 +0200 Subject: [PATCH 16/35] Adding Liberty examples. --- examples/liberty-car-booking/README.md | 0 .../docs-for-rag/apple-pie-recipe.txt | 22 + .../docs-for-rag/general-information.txt | 6 + .../docs-for-rag/list-of-cars.txt | 11 + .../docs-for-rag/terms-of-use.txt | 37 ++ .../src/main/resources/META-INF/MANIFEST.MF | 3 + .../META-INF/microprofile-config.properties | 33 ++ .../src/main/webapp/chatroom.js | 57 ++ .../src/main/webapp/css/styles.css | 544 ++++++++++++++++++ .../webapp/fonts/BunueloCleanPro-Light.otf | Bin 0 -> 113536 bytes .../webapp/fonts/BunueloCleanPro-SemiBold.otf | Bin 0 -> 116504 bytes .../main/webapp/img/small_logo_dark_gray.svg | 1 + .../src/main/webapp/img/sysProps.svg | 20 + .../src/main/webapp/index.html | 47 ++ .../Langchain4JCDIExtensionMetadata.java | 39 ++ 15 files changed, 820 insertions(+) create mode 100644 examples/liberty-car-booking/README.md create mode 100644 examples/liberty-car-booking/docs-for-rag/apple-pie-recipe.txt create mode 100644 examples/liberty-car-booking/docs-for-rag/general-information.txt create mode 100644 examples/liberty-car-booking/docs-for-rag/list-of-cars.txt create mode 100644 examples/liberty-car-booking/docs-for-rag/terms-of-use.txt create mode 100644 examples/liberty-car-booking/src/main/resources/META-INF/MANIFEST.MF create mode 100644 examples/liberty-car-booking/src/main/resources/META-INF/microprofile-config.properties create mode 100644 examples/liberty-car-booking/src/main/webapp/chatroom.js create mode 100644 examples/liberty-car-booking/src/main/webapp/css/styles.css create mode 100644 examples/liberty-car-booking/src/main/webapp/fonts/BunueloCleanPro-Light.otf create mode 100644 examples/liberty-car-booking/src/main/webapp/fonts/BunueloCleanPro-SemiBold.otf create mode 100644 examples/liberty-car-booking/src/main/webapp/img/small_logo_dark_gray.svg create mode 100644 examples/liberty-car-booking/src/main/webapp/img/sysProps.svg create mode 100644 examples/liberty-car-booking/src/main/webapp/index.html create mode 100644 smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java diff --git a/examples/liberty-car-booking/README.md b/examples/liberty-car-booking/README.md new file mode 100644 index 0000000..e69de29 diff --git a/examples/liberty-car-booking/docs-for-rag/apple-pie-recipe.txt b/examples/liberty-car-booking/docs-for-rag/apple-pie-recipe.txt new file mode 100644 index 0000000..a64deba --- /dev/null +++ b/examples/liberty-car-booking/docs-for-rag/apple-pie-recipe.txt @@ -0,0 +1,22 @@ +Special Apple Pie Recipe + +Here's a very brief overview of what you can expect when you make this special apple pie at home: + +1.Make the filling: +On the stove, make a paste with flour and butter. +Add the sugar and water and bring to a boil. +Simmer, then remove from heat. + +2.Assemble the pie: +Press one crust into a pie plate. +Place the sliced apples on the bottom crust. +Use the top crust to make a lattice crust according to the recipe below. +Pour the butter-sugar mixture over the lattice crust. + +3.Bake the pie: +Bake the pie in a preheated oven until the apples are soft and the crust is golden brown. + +4.How Long to Bake Apple Pie +You'll bake the pie at 425 degrees F for 15 minutes, then you'll reduce the temperature to 350 degrees F and continue baking for 35-45 minutes. +All in all, the pie will bake for about one hour, give or take a few minutes. +You'll know the pie is done when the apples are soft and the crust is a beautiful golden brown color. \ No newline at end of file diff --git a/examples/liberty-car-booking/docs-for-rag/general-information.txt b/examples/liberty-car-booking/docs-for-rag/general-information.txt new file mode 100644 index 0000000..f0dbb2c --- /dev/null +++ b/examples/liberty-car-booking/docs-for-rag/general-information.txt @@ -0,0 +1,6 @@ +Miles of Smiles general information and main figures + +1) Population: 500 employees +2) Geography: 40 countries accross Europe, America and Asia +3) Fleet: 1000 cars, 10% electric +4) Satisfaction rating: 99% \ No newline at end of file diff --git a/examples/liberty-car-booking/docs-for-rag/list-of-cars.txt b/examples/liberty-car-booking/docs-for-rag/list-of-cars.txt new file mode 100644 index 0000000..6d65c11 --- /dev/null +++ b/examples/liberty-car-booking/docs-for-rag/list-of-cars.txt @@ -0,0 +1,11 @@ +Miles of Smiles Car Rental list of cars + +Aston Martin +BMW +DS +Mercedes +Renault +Peugeot +Porsche +Tesla +Toyota \ No newline at end of file diff --git a/examples/liberty-car-booking/docs-for-rag/terms-of-use.txt b/examples/liberty-car-booking/docs-for-rag/terms-of-use.txt new file mode 100644 index 0000000..e06772f --- /dev/null +++ b/examples/liberty-car-booking/docs-for-rag/terms-of-use.txt @@ -0,0 +1,37 @@ +Miles of Smiles Car Rental Services Terms of Use + +1. Introduction +These Terms of Service (“Terms”) govern the access or use by you, an individual, from within any country in the world, of applications, websites, content, products, and services (“Services”) made available by Miles of Smiles Car Rental Services, a company registered in the United States of America. + +2. The Services +Miles of Smiles rents out vehicles to the end user. We reserve the right to temporarily or permanently discontinue the Services at any time and are not liable for any modification, suspension or discontinuation of the Services. + +3. Bookings +3.1 Users may make a booking through our website or mobile application. +3.2 You must provide accurate, current and complete information during the reservation process. You are responsible for all charges incurred under your account. +3.3 All bookings are subject to vehicle availability. + +4. Cancellation Policy +4.1 Reservations lasting less than three days cannot be cancelled +4.2 Reservations can be cancelled up to 7 days prior to the start of the booking period. + +5. Use of Vehicle +5.1 All cars rented from Miles of Smiles must not be used: +for any illegal purpose or in connection with any criminal offense. +for teaching someone to drive. +in any race, rally or contest. +while under the influence of alcohol or drugs. + +6. Liability +6.1 Users will be held liable for any damage, loss, or theft that occurs during the rental period. +6.2 We do not accept liability for any indirect or consequential loss, damage, or expense including but not limited to loss of profits. + +7. Governing Law +These terms will be governed by and construed in accordance with the laws of the United States of America, and any disputes relating to these terms will be subject to the exclusive jurisdiction of the courts of United States. + +8. Changes to These Terms +We may revise these terms of use at any time by amending this page. You are expected to check this page from time to time to take notice of any changes we made. + +9. Acceptance of These Terms +By using the Services, you acknowledge that you have read and understand these Terms and agree to be bound by them. +If you do not agree to these Terms, please do not use or access our Services. \ No newline at end of file diff --git a/examples/liberty-car-booking/src/main/resources/META-INF/MANIFEST.MF b/examples/liberty-car-booking/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..254272e --- /dev/null +++ b/examples/liberty-car-booking/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/examples/liberty-car-booking/src/main/resources/META-INF/microprofile-config.properties b/examples/liberty-car-booking/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 0000000..c793531 --- /dev/null +++ b/examples/liberty-car-booking/src/main/resources/META-INF/microprofile-config.properties @@ -0,0 +1,33 @@ +# Microprofile server properties +server.port=9080 + +smallrye.llm.plugin.chat-model.class=dev.langchain4j.model.azure.AzureOpenAiChatModel +smallrye.llm.plugin.chat-model.config.api-key=${azure.openai.api.key} +smallrye.llm.plugin.chat-model.config.endpoint=${azure.openai.endpoint} +smallrye.llm.plugin.chat-model.config.service-version=2024-02-15-preview +smallrye.llm.plugin.chat-model.config.deployment-name=${azure.openai.deployment.name} +smallrye.llm.plugin.chat-model.config.temperature=0.1 +smallrye.llm.plugin.chat-model.config.topP=0.1 +smallrye.llm.plugin.chat-model.config.timeout=120s +smallrye.llm.plugin.chat-model.config.max-retries=2 +#smallrye.llm.plugin.chat-model.config.logRequestsAndResponsess=false + + +smallrye.llm.plugin.docRagRetriever.class=dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever +smallrye.llm.plugin.docRagRetriever.config.embeddingStore=lookup:default +smallrye.llm.plugin.docRagRetriever.config.embeddingModel=lookup:default +smallrye.llm.plugin.docRagRetriever.config.maxResults=3 +smallrye.llm.plugin.docRagRetriever.config.minScore=0.6 + + + +smallrye.llm.embedding.store.in-memory.file=embedding.json + +# Chat memory configuration, used by ChatAiFactory +chat.memory.max.messages=20 + +# Fraud detection configuration, used by FraudAiFactory +fraud.memory.max.messages=20 + +# Location of documents to RAG +app.docs-for-rag.dir=docs-for-rag diff --git a/examples/liberty-car-booking/src/main/webapp/chatroom.js b/examples/liberty-car-booking/src/main/webapp/chatroom.js new file mode 100644 index 0000000..e351dcd --- /dev/null +++ b/examples/liberty-car-booking/src/main/webapp/chatroom.js @@ -0,0 +1,57 @@ + var messagesTableBody = document.getElementById('messagesTableBody'); + var thinkingRow = document.createElement('tr'); + thinkingRow.setAttribute('id', 'thinking'); + thinkingRow.innerHTML = '

thinking...

' + + ''; + + function getTime() { + var now = new Date(); + var hours = now.getHours(); + hours = hours < 10 ? '0' + hours : hours; + var minutes = now.getMinutes(); + minutes = minutes < 10 ? '0' + minutes : minutes; + var seconds = now.getSeconds(); + seconds = seconds < 10 ? '0' + seconds : seconds; + var time = hours + ":" + minutes + ":" + seconds; + return time; + } + + function sendMessage() { + var myMessageRow = document.createElement('tr'); + var myMessage = document.getElementById('myMessage').value; + myMessageRow.innerHTML = '

' + myMessage + '

' + + '' + getTime() + ''; + messagesTableBody.appendChild(myMessageRow); + messagesTableBody.appendChild(thinkingRow); + webSocket.send(myMessage); + document.getElementById('myMessage').value = ""; + } + + // Getting the used url from browser + var loc = window.location, uri; + if (loc.protocol === "https:") { + uri = "wss:"; + } else { + uri = "ws:"; + } + uri += "//" + loc.host; + uri += loc.pathname + "chat"; + // buildign websocket + const webSocket = new WebSocket(uri); + + webSocket.onopen = function (event) { + console.log(event); + }; + + webSocket.onmessage = function (event) { + var data = event.data; + messagesTableBody.removeChild(thinkingRow); + var agentMessageRow = document.createElement('tr'); + agentMessageRow.innerHTML = '

' + data + '

' + + '' + getTime() + ''; + messagesTableBody.appendChild(agentMessageRow); + }; + + webSocket.onerror = function (event) { + console.log(event); + }; \ No newline at end of file diff --git a/examples/liberty-car-booking/src/main/webapp/css/styles.css b/examples/liberty-car-booking/src/main/webapp/css/styles.css new file mode 100644 index 0000000..1871f58 --- /dev/null +++ b/examples/liberty-car-booking/src/main/webapp/css/styles.css @@ -0,0 +1,544 @@ +@import url("https://fonts.googleapis.com/css?family=Asap:300,400,500"); + +body{ + font-family:Asap; + font-size: 16px; + color:#24243b; + background-color: white; + margin: 0px; +} + +section { + padding-top: 55px; + padding-left: 8%; + padding-right: 8%; + /* font-weight: 400; */ + letter-spacing:0; + text-align:left; +} + +.line { + margin-right: 200px; + height: 1px; + background-color: #C8D3D3; +} + +.headerImage { + background-image: url(/img/helidon_logo_dark.svg); + background-repeat: no-repeat; + background-position: top 20px right 15px; + height: 103px; + margin-top: -94px; +} + +#whereTo { + padding-bottom: 80px; + width: 50%; +} + +p { + line-height: 22px; + margin-top: 0px; +} +h1 { + font-family:BunueloSemiBold; + font-size: 40px; + font-weight: 400; + letter-spacing:0; + text-align:left; +} +h2 { + font-size: 24px; + font-weight: 400; +} +h4 { + margin-top: 52px; +} +a { + text-decoration: none; +} + +#appIntro { + background-image:linear-gradient(#141427 0%, #2c2e50 100%); + background-size: 100% calc(100% - 70px); + background-repeat: no-repeat; +} + +#titleSection { + color: white; + margin-bottom: 80px; +} + +#appTitle { + font-family:BunueloLight; + font-size:55px; +} + +.headerRow { + height: 100px; + position:relative; + z-index:2; + box-shadow: 0 2px 4px 0 rgba(0,0,0,0.50); +} +.headerRow > div { + display: inline-block; +} + +.collapsibleRow { + transition: border 400ms ease-out, box-shadow 200ms linear; + cursor: pointer; +} +.collapsibleRow:hover .headerTitle { + background-color: #f4f4f4; + transition: background-color 0.1s; +} +.collapsed .collapsibleRow { + box-shadow: none; + border-bottom: 4px solid; +} +.collapsed#healthSection > .headerRow { + border-bottom-color: #D6D9E4; +} +.collapsed#configSection > .headerRow { + border-bottom-color: #F8D7C1; +} +.collapsed#metricsSection > .headerRow { + border-bottom-color: #EEF3C3; +} + +.collapsed .collapsibleContent { /* collapsing animation */ + transition: all 400ms ease-out, opacity 300ms ease-in; +} +.expanded .collapsibleContent { /* expanding animation */ + transition: all 400ms ease-out, opacity 450ms ease-out; +} +.collapsed .collapsibleContent { + opacity: 0; + max-height: 0; + visibility: hidden; +} +.expanded .collapsibleContent { + opacity: 1; + max-height: 1000px; + visibility: visible; +} + +.headerIcon { + width: 160px; + height: 100%; + float: left; + background-color: #E8EAEF; +} +.headerIcon img { + display:block; + margin:auto; + margin-top: 20px; +} + +#healthSection .headerIcon { + background-color: #E8EAEF; +} +#configSection .headerIcon { + background-color: #FDE4D1; +} +#metricsSection .headerIcon { + background-color: #F5F8DA; +} + +.headerTitle { + background-color: white; + color:#5d6a8e; + letter-spacing:0; + text-align:left; + padding-left: 40px; + padding-top: 10px; + width: calc(100% - 200px); /* 160 from icon, 40 from padding */ +} +#healthSection h2 { + color: #5D6A8E; +} +#configSection h2 { + color: #E57000; +} +#metricsSection h2 { + color: #4F6700; +} + +#sysPropTitle { + padding-top: 28px; +} + +.headerTitle > h2 { + font-family: BunueloLight; + font-size:40px; + margin: 0; +} + +.caret { + position: absolute; + right: 45px; + top: 45px; +} + +.collapsed#configSection .caret { + background-image: url("../img/carets/caret_down_orange.svg") +} +.expanded#configSection .caret { + background-image: url("../img/carets/caret_up_orange.svg") +} + +.msSection { + background: white; + box-shadow: 0 2px 4px 0 rgba(63,70,89,0.31); +} + +.sectionContent { + margin-left: 160px; +} + +#messagesTable { + padding-left: 160px; + background: white; +} + +button { + border-radius:100px; + height:44px; + color:#24253a; + text-align:center; + font-family: Asap; + margin-top: 25px; + margin-bottom: 70px; + cursor: pointer; + border: none; +} + +button a { + text-decoration: none; + color:#F4914D; +} + +#guidesButton { + background-color:#abd155; + width:269px; + font-weight: 500; + font-size:16px; + transition: background-color .2s; +} +#guidesButton:hover { + background-color: #C7EE63; +} + +section#openLibertyAndMp { + background:#f4f4f5; + background-size: 100% calc(100% - 70px); + background-repeat: no-repeat; +} + +#healthBox { + text-align: left; + display: table-cell; + vertical-align: middle; + width: 47%; +} + +#healthBox > div { + display: table-cell; + vertical-align: middle; +} + +#healthIcon { + padding-left: 73px; + padding-top: 56px; + padding-bottom: 56px; +} +#healthStatusIcon { + width: 104px; + height: 104px; +} + +#healthText { + padding: 50px; +} + +#serviceStatus { + font-size: 50px; + font-family:BunueloLight; + margin-top: 30px; +} + +#healthNote { + text-align: left; + display: table-cell; + vertical-align: middle; + padding-left: 43px; + line-height: 26px; + width: 53%; +} + +table { + width: 100%; + font-size: 14px; + text-align: left; + border-collapse: collapse; +} + +th { + height: 63px; + padding-left: 41px; + font-size: 16px; +} +tr { + height: 45px; +} +td { + padding-left: 41px; +} +#messagesTable tr:first-child { + background: #D6D9E4; +} +#configTable tr:first-child { + background: #F8D7C1;; +} +#metricsTable tr:first-child { + background: #EEF3C3; +} + +#messagesTable tr:nth-child(2n+3) { + background: #EEEFF3; +} +#configTable tr:nth-child(2n+2) { + background: #FEF8F4; +} +#metricsTable tr:nth-child(2n+2) { + background: #FBFCEE; +} + +#messagesTable .sourceRow, +#healthTable .sourceRow { + border-top: 4px solid #D6D9E4; +} +#messagesTable .sourceRow a, +#healthTable .sourceRow a { + color: #5D6A8E; +} +#configTable .sourceRow { + border-top: 4px solid #F8D7C1; +} +#configTable .sourceRow a { + color: #E57000; +} +#metricsTable .sourceRow { + border-top: 4px solid #EEF3C3; +} +#metricsTable .sourceRow a { + color: #4F6700; +} +.sourceRow a { + font-weight: 500; +} + +#learnMore { + margin-top: 120px; + padding: 0px 200px 100px; +} + +#learnMore > h2 { + color:#5e6b8d; +} + +.bodyFooter { + padding: 5px 8%; + background-repeat: no-repeat; + background-position: top 20px right 110px; + margin-bottom: 40px; + margin-top: 50px; + color: #3F4659; +} + +.bodyFooterLink { + font-family: Asap; + font-weight: 300; + font-size: 14px; + letter-spacing: 0; + height: 60px; + margin-top: 30px; + margin-left: 10px; + margin-right: 10px; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 20px; + padding-right: 20px; + text-align: right; + background: #F9F9F9; +} + +.my_message_container { + font-family: Asap; + font-weight: 300; + font-size: 14px; + letter-spacing: 0; + margin-top: 30px; + margin-right: 130px; + padding-bottom: 5px; + padding-right: 50px; + text-align: right; +} + +.bodyFooterLink > a { + text-decoration: none; + padding: 10px; + color: #96bc32; +} + +#licenseLink { + color: #5E6B8D; + text-align: left; +} + +#footer_text { + margin-top: 4px; + margin-bottom: 4px; + font-size: 16px; +} + +#myMsgLabel { + margin-top: 4px; + margin-bottom: 4px; + font-size: 18px; + margin-right: 10px; +} + +.refreshSection { + padding-top: 20px; + padding-left: 160px; + color: #3A73B4; +} + +label { + margin-right: 30px; +} + +#sendButton { + background-color:#abd155; + width:80px; + height:30px; + font-weight: 500; + font-size:16px; + transition: background-color .2s; + margin-left: 15px; +} + +#sendButton:hover { + background-color: #C7EE63; +} + +.agent-msg { + border: 1px solid black; + border-radius: 20px; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; +} + +.my-msg { + border-radius: 20px; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 20px; + padding-right: 10px; + margin-left: 60%; + background-color: #F0F0F0; + text-align: left; +} + +.thinking-msg { + border: 1px solid black; + border-radius: 20px; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; + margin-right: 60%; +} + +.footer_round_btn { + display: inline-block; + width: 20px; + height: 20px; + background-repeat: no-repeat; + background-size: cover; +} + +#footer_github_link { + background-image: url("/img/Footer_GitCat.svg"); + &:hover { + background-image: url("/img/Footer_GitCat_Hover.svg"); + } +} + +#footer_twitter_link { + background-image: url("/img/Footer_TwitterBird.svg"); + &:hover { + background-image: url("/img/Footer_TwitterBird_Hover.svg"); + } +} + +#footer_groupsio_link { + background-image: url("/img/Footer_GroupsIO.svg"); + &:hover { + background-image: url("/img/Footer_GroupsIO_Hover.svg"); + } +} + +#footer_gitter_link { + background-image: url("/img/footer_gitter.svg"); + &:hover { + background-image: url("/img/footer_gitter_hover.svg"); + } +} + +#footer_project_container { + overflow: hidden; + float: left; +} + +#footer_copyright { + font-size: 13px; + padding-left: 30px; + float: left; + clear: left; +} + +.footer_ol_io { + font-size: 15px; + padding-right: 220px; + float: right; +} + +.footer_ol_io_others { + font-size: 15px; + padding-right: 20px; + float: right; +} + +#footer_project { + font-weight: 500; + font-size: 16px; + color: #6A7070; + letter-spacing: 0; + float: left; + clear: left; +} + + +#footer_open_liberty { + width: 100px; + padding: 25px 25px 10px 25px; + background-image: url(/img/small_logo_dark_gray.svg); + background-repeat: no-repeat; + transition: background-image .2s; + float: left; + clear: left; +} diff --git a/examples/liberty-car-booking/src/main/webapp/fonts/BunueloCleanPro-Light.otf b/examples/liberty-car-booking/src/main/webapp/fonts/BunueloCleanPro-Light.otf new file mode 100644 index 0000000000000000000000000000000000000000..bcb8cfb50618a1f27568ca848c32213212520c50 GIT binary patch literal 113536 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 THA598yDCa{Q \ No newline at end of file diff --git a/examples/liberty-car-booking/src/main/webapp/img/sysProps.svg b/examples/liberty-car-booking/src/main/webapp/img/sysProps.svg new file mode 100644 index 0000000..3ba129f --- /dev/null +++ b/examples/liberty-car-booking/src/main/webapp/img/sysProps.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/examples/liberty-car-booking/src/main/webapp/index.html b/examples/liberty-car-booking/src/main/webapp/index.html new file mode 100644 index 0000000..4d557d9 --- /dev/null +++ b/examples/liberty-car-booking/src/main/webapp/index.html @@ -0,0 +1,47 @@ + + + + + Chat Room + + + + + +
+
+

Chat Room implemented with Helidon

+
+ +
+
+
+
+ +
+
+

Conversation with an AI Agent

+
+
+
+ + + + + + + + +
Chat MessagesTime
+
+
+
+
+ + + +
+ + + diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java new file mode 100644 index 0000000..f6d8505 --- /dev/null +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java @@ -0,0 +1,39 @@ +package io.smallrye.llm.core.langchain4j.spi.liberty.cdi.internal; + +import java.lang.annotation.Annotation; +import java.util.Set; + +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; + +import io.openliberty.cdi.spi.CDIExtensionMetadata; +import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JAIServicePortableExtension; +import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JPluginsPortableExtension; +import io.smallrye.llm.spi.RegisterAIService; +import jakarta.enterprise.inject.spi.Extension; + +/** + * @author Buhake Sindi + * @since 26 August 2024 + */ +@Component(service = CDIExtensionMetadata.class, configurationPolicy = ConfigurationPolicy.IGNORE, immediate = true) +public class Langchain4JCDIExtensionMetadata implements CDIExtensionMetadata { + + /* (non-Javadoc) + * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getBeanDefiningAnnotationClasses() + */ + @Override + public Set> getBeanDefiningAnnotationClasses() { + // TODO Auto-generated method stub + return Set.of(RegisterAIService.class); + } + + /* (non-Javadoc) + * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getExtensions() + */ + @Override + public Set> getExtensions() { + // TODO Auto-generated method stub + return Set.of(LangChain4JAIServicePortableExtension.class, LangChain4JPluginsPortableExtension.class); + } +} From 8967e17f0430a37d036a717d1070013f752f3a9c Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Mon, 16 Sep 2024 13:56:19 +0200 Subject: [PATCH 17/35] Adding Liberty examples. --- .../integration/pom.xml | 3 -- .../Langchain4JCDIExtensionMetadata.java | 39 +++++++++++-------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml index c6bb32b..cca6df3 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml @@ -103,9 +103,6 @@ ${new.integration.code.private.package};version="1.0.0" --> - - !io.smallrye.llm.aiservice; - diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java index f6d8505..e750a4e 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java @@ -3,6 +3,8 @@ import java.lang.annotation.Annotation; import java.util.Set; +import jakarta.enterprise.inject.spi.Extension; + import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.ConfigurationPolicy; @@ -10,7 +12,6 @@ import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JAIServicePortableExtension; import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JPluginsPortableExtension; import io.smallrye.llm.spi.RegisterAIService; -import jakarta.enterprise.inject.spi.Extension; /** * @author Buhake Sindi @@ -19,21 +20,25 @@ @Component(service = CDIExtensionMetadata.class, configurationPolicy = ConfigurationPolicy.IGNORE, immediate = true) public class Langchain4JCDIExtensionMetadata implements CDIExtensionMetadata { - /* (non-Javadoc) - * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getBeanDefiningAnnotationClasses() - */ - @Override - public Set> getBeanDefiningAnnotationClasses() { - // TODO Auto-generated method stub - return Set.of(RegisterAIService.class); - } + /* + * (non-Javadoc) + * + * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getBeanDefiningAnnotationClasses() + */ + @Override + public Set> getBeanDefiningAnnotationClasses() { + // TODO Auto-generated method stub + return Set.of(RegisterAIService.class); + } - /* (non-Javadoc) - * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getExtensions() - */ - @Override - public Set> getExtensions() { - // TODO Auto-generated method stub - return Set.of(LangChain4JAIServicePortableExtension.class, LangChain4JPluginsPortableExtension.class); - } + /* + * (non-Javadoc) + * + * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getExtensions() + */ + @Override + public Set> getExtensions() { + // TODO Auto-generated method stub + return Set.of(LangChain4JAIServicePortableExtension.class, LangChain4JPluginsPortableExtension.class); + } } From 2d6763148ef36faa7263aa5b8a277d10fb536182 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Mon, 16 Sep 2024 20:36:06 +0200 Subject: [PATCH 18/35] Update Car Booking Liberty README --- examples/liberty-car-booking/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/examples/liberty-car-booking/README.md b/examples/liberty-car-booking/README.md index e69de29..d3b4d29 100644 --- a/examples/liberty-car-booking/README.md +++ b/examples/liberty-car-booking/README.md @@ -0,0 +1,10 @@ +# Car Booking (Open Liberty) + +These are the steps to run this service. + + 1. Go to the root `smallrye-llm` directory, then build it using the command: + > `mvn clean install -e` + 2. Once the project is built, move to directory `cd examples/liberty-car-booking` and run the following command to install the bundle as a user liberty feature: + > `mvn clean liberty:create liberty:prepare-feature liberty:install-feature liberty:dev -e` + +Then you can access the application through the browser of your choice. From dc269bfdefcb0265d6215003d6f5670563e7c281 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Mon, 16 Sep 2024 20:40:43 +0200 Subject: [PATCH 19/35] Update Car Booking Liberty README --- examples/liberty-car-booking/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/liberty-car-booking/README.md b/examples/liberty-car-booking/README.md index d3b4d29..ca045f6 100644 --- a/examples/liberty-car-booking/README.md +++ b/examples/liberty-car-booking/README.md @@ -7,4 +7,8 @@ These are the steps to run this service. 2. Once the project is built, move to directory `cd examples/liberty-car-booking` and run the following command to install the bundle as a user liberty feature: > `mvn clean liberty:create liberty:prepare-feature liberty:install-feature liberty:dev -e` +Application requirements: + - JDK 17 and higher + - Maven 3.9.9 and higher + Then you can access the application through the browser of your choice. From edc43425190deb9aff8381f64b44f3ade750875b Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Thu, 19 Sep 2024 16:15:02 +0200 Subject: [PATCH 20/35] Update Liberty example. --- examples/helidon-car-booking/README.md | 18 ++--- examples/liberty-car-booking/README.md | 73 +++++++++++++++++-- examples/liberty-car-booking/pom.xml | 53 ++++++++++---- .../io/jefrajames/booking/DocRagIngestor.java | 10 ++- .../src/main/liberty/config/server.xml | 6 +- examples/quarkus-car-booking/README.md | 16 ++-- .../core/config/spi/LLMConfigProvider.java | 4 +- 7 files changed, 135 insertions(+), 45 deletions(-) diff --git a/examples/helidon-car-booking/README.md b/examples/helidon-car-booking/README.md index a012fa3..53a17d0 100644 --- a/examples/helidon-car-booking/README.md +++ b/examples/helidon-car-booking/README.md @@ -9,7 +9,7 @@ It is derived from my [Quarkus-LangChain4j](https://github.com/jefrajames/car-bo It is based on a simplified car booking application inspired from the [Java meets AI](https://www.youtube.com/watch?v=BD1MSLbs9KE) talk from Lize Raes at Devoxx Belgium 2023. The car booking company is called "Miles of Smiles" and the application exposes two AI services: . a chat service to freely discuss with a customer assistant -. a fraud service to determine if a customer is a frauder. +. a fraud service to determine if a customer is a fraudster. For the sake of simplicity, there is no database interaction, the application is standalone and can be used "as is". Of course thanks to Quarkus, it can easily be extended according to your needs. @@ -17,7 +17,7 @@ Warning: you must first configure the application to connect to an LLM that supp ## Technical context -The project has been developped and tested with: +The project has been developed and tested with: * Java 22 (Temurin OpenJDK distro) * Helidon 4.0.7 @@ -32,11 +32,11 @@ During my tests, GPT 3.5 has proved to be faster but less precise en consistent Quarkus provides a deep integration with LangChain4j thanks to a specific [extension](https://docs.quarkiverse.io/quarkus-langchain4j/dev/index.html). -In particular, it provides a powerful @RegisterAiService annotation and network interractions with LLMs are managed with its own RestClient. +In particular, it provides a powerful @RegisterAiService annotation and network interactions with LLMs are managed with its own RestClient. This example is based on a standard usage of LangChain4j with Helidon. There is no such deep integration. -I've added 3 technical classes to manage "the glue" (more or less the equivallent of @RegisterAiService): +I've added 3 technical classes to manage "the glue" (more or less the equivalent of `@RegisterAiService`): * ModelFactory: generates an OpenAI Chat model * ChatAiServiceFactory: generates a Chat assistant @@ -44,7 +44,7 @@ I've added 3 technical classes to manage "the glue" (more or less the equivallen I've been obliged to turn FraudResponse in a POJO. It seems that Google GSON, used to deserialize OpenAI responses does not support Java Record. -In contrast with Quarkus, network interractions with LLMs are based on standard LangChain4j. For instance, the Azure SDK is used with Azure OpenAI. +In contrast with Quarkus, network interactions with LLMs are based on standard LangChain4j. For instance, the Azure SDK is used with Azure OpenAI. ## Packaging the application @@ -66,19 +66,19 @@ Note: native mode not yet tested. The application exposes a REST API documented with OpenAPI. -To interract with the application go to: http://localhost:8080/openapi/ui. +To interact with the application go to: http://localhost:8080/openapi/ui. Typical questions you can ask in the Chat: * Hello, how can you help me? * What is your list of cars? -* What is your cancelation policy? +* What is your cancellation policy? * What is your fleet size? Be short please. * How many electric cars do you have? * My name is James Bond, please list my bookings -* Is my booking 123-456 cancelable? -* Is my booking 234-567 cancelable? +* Is my booking 123-456 cancellable? +* Is my booking 234-567 cancellable? * Can you check the duration please? * I'm James Bond, can I cancel all my booking 345-678? * Can you provide the details of all my bookings? diff --git a/examples/liberty-car-booking/README.md b/examples/liberty-car-booking/README.md index ca045f6..9d6c77f 100644 --- a/examples/liberty-car-booking/README.md +++ b/examples/liberty-car-booking/README.md @@ -2,13 +2,74 @@ These are the steps to run this service. - 1. Go to the root `smallrye-llm` directory, then build it using the command: +1. Go to the root `smallrye-llm` directory, then build it using the command: > `mvn clean install -e` - 2. Once the project is built, move to directory `cd examples/liberty-car-booking` and run the following command to install the bundle as a user liberty feature: +2. Once the project is built, move to directory `cd examples/liberty-car-booking` and run the following command to install the bundle as a user liberty feature: > `mvn clean liberty:create liberty:prepare-feature liberty:install-feature liberty:dev -e` -Application requirements: - - JDK 17 and higher - - Maven 3.9.9 and higher - +## Application requirements: +- JDK 17 and higher +- Maven 3.9.9 and higher +- LangChain4j 0.33.0 or higher. +- Testing against GPT 3.5 and 4.0 on a dedicated Azure instance (to be customized in your context). + Then you can access the application through the browser of your choice. + +## Differences with Quarkus-LangChain4j + +Quarkus provides a deep integration with LangChain4j thanks to a specific [extension](https://docs.quarkiverse.io/quarkus-langchain4j/dev/index.html). + +In particular, it provides a powerful `@RegisterAiService` annotation and network interactions with LLMs are managed with its own RestClient. + +This example is based on a standard usage of LangChain4j with Helidon. There is no such deep integration. + +I've added 3 technical classes to manage "the glue" (more or less the equivalent of `@RegisterAiService`): + +* ModelFactory: generates an OpenAI Chat model +* ChatAiServiceFactory: generates a Chat assistant +* FraudAiServiceFactory: generates a Fraud assistant. + +I've been obliged to turn FraudResponse in a POJO. It seems that Google GSON, used to deserialize OpenAI responses does not support Java Record. + +In contrast with Quarkus, network interactions with LLMs are based on standard LangChain4j. For instance, the Azure SDK is used with Azure OpenAI. + +## Packaging the application + +To package the application in JVM mode run: `mvn package`. + +## Configuration + +All configuration is centralized in `microprofile-config.properties`(found is META-INF folder) and can be redefined using environment variables. + +## Running the application + +To run in dev mode with OpenLiberty: Run the following command in the CLI: `mvn liberty:dev`. + +To run in JVM mode (after packaging): `mvn liberty:start`. To stop the running server: `mvn liberty:stop`. + + +## Playing with the application + +The application exposes a REST API documented with OpenAPI. + +To interact with the application go to: [http://localhost:9080/openapi/ui](http://localhost:8080/openapi/ui). + + +Typical questions you can ask in the Chat: + +* Hello, how can you help me? +* What is your list of cars? +* What is your cancellation policy? +* What is your fleet size? Be short please. +* How many electric cars do you have? +* My name is James Bond, please list my bookings +* Is my booking 123-456 cancelable? +* Is my booking 234-567 cancelable? +* Can you check the duration please? +* I'm James Bond, can I cancel all my booking 345-678? +* Can you provide the details of all my bookings? + +You can ask fraud for: + +* James Bond +* Emilio Largo diff --git a/examples/liberty-car-booking/pom.xml b/examples/liberty-car-booking/pom.xml index 5266377..528c048 100644 --- a/examples/liberty-car-booking/pom.xml +++ b/examples/liberty-car-booking/pom.xml @@ -43,19 +43,39 @@ 1.0.0-SNAPSHOT pom
+ + + io.smallrye.llm + smallrye-llm-langchain4j-config-mpconfig + 1.0.0-SNAPSHOT + io.smallrye.llm - smallrye-llm-langchain4j-core + smallrye-llm-langchain4j-portable-extension 1.0.0-SNAPSHOT + + + dev.langchain4j + langchain4j + ${dev.langchain4j.version} + + + + dev.langchain4j + langchain4j-hugging-face + ${dev.langchain4j.version} + + + - io.smallrye.llm - smallrye-llm-langchain4j-portable-extension - 1.0.0-SNAPSHOT - provided + dev.langchain4j + langchain4j-azure-open-ai + ${dev.langchain4j.version} @@ -87,16 +107,14 @@ pom - - io.smallrye.llm - smallrye-llm-langchain4j-core - provided - + + io.smallrye.llm + smallrye-llm-langchain4j-config-mpconfig + io.smallrye.llm smallrye-llm-langchain4j-portable-extension - provided @@ -113,30 +131,32 @@ 1.18.32 provided - + dev.langchain4j langchain4j - ${dev.langchain4j.version} dev.langchain4j langchain4j-hugging-face - ${dev.langchain4j.version} + + + + dev.langchain4j + langchain4j-azure-open-ai + dev.langchain4j langchain4j-open-ai - ${dev.langchain4j.version} dev.langchain4j langchain4j-embeddings-all-minilm-l6-v2 - ${dev.langchain4j.version} @@ -173,6 +193,7 @@ ${project.build.finalName} + ${project.basedir}/docs-for-rag diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java index d0d0af4..007a0af 100644 --- a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java @@ -9,15 +9,17 @@ import jakarta.enterprise.context.Initialized; import jakarta.enterprise.event.Observes; import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; import dev.langchain4j.data.document.Document; import dev.langchain4j.data.document.parser.TextDocumentParser; import dev.langchain4j.data.document.splitter.DocumentSplitters; -import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.model.embedding.onnx.allminilml6v2.AllMiniLmL6V2EmbeddingModel; +import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; import lombok.extern.java.Log; +import org.eclipse.microprofile.config.inject.ConfigProperty; @Log @ApplicationScoped @@ -31,7 +33,11 @@ public class DocRagIngestor { @Produces private InMemoryEmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); - private File docs = new File(System.getProperty("docragdir")); +// private File docs = new File(System.getProperty("docragdir")); + + @Inject + @ConfigProperty(name = "app.docs-for-rag.dir") + private File docs; private List loadDocs() { return loadDocuments(docs.getPath(), new TextDocumentParser()); diff --git a/examples/liberty-car-booking/src/main/liberty/config/server.xml b/examples/liberty-car-booking/src/main/liberty/config/server.xml index 25f4aee..ab5e086 100644 --- a/examples/liberty-car-booking/src/main/liberty/config/server.xml +++ b/examples/liberty-car-booking/src/main/liberty/config/server.xml @@ -14,7 +14,11 @@ - + + + + + diff --git a/examples/quarkus-car-booking/README.md b/examples/quarkus-car-booking/README.md index a012fa3..797c361 100644 --- a/examples/quarkus-car-booking/README.md +++ b/examples/quarkus-car-booking/README.md @@ -32,27 +32,27 @@ During my tests, GPT 3.5 has proved to be faster but less precise en consistent Quarkus provides a deep integration with LangChain4j thanks to a specific [extension](https://docs.quarkiverse.io/quarkus-langchain4j/dev/index.html). -In particular, it provides a powerful @RegisterAiService annotation and network interractions with LLMs are managed with its own RestClient. +In particular, it provides a powerful `@RegisterAiService` annotation and network interactions with LLMs are managed with its own RestClient. This example is based on a standard usage of LangChain4j with Helidon. There is no such deep integration. -I've added 3 technical classes to manage "the glue" (more or less the equivallent of @RegisterAiService): +I've added 3 technical classes to manage "the glue" (more or less the equivalent of `@RegisterAiService`): * ModelFactory: generates an OpenAI Chat model * ChatAiServiceFactory: generates a Chat assistant -* FraudAiServiceFactrory: generates a Fraud assistant. +* FraudAiServiceFactory: generates a Fraud assistant. I've been obliged to turn FraudResponse in a POJO. It seems that Google GSON, used to deserialize OpenAI responses does not support Java Record. -In contrast with Quarkus, network interractions with LLMs are based on standard LangChain4j. For instance, the Azure SDK is used with Azure OpenAI. +In contrast with Quarkus, network interactions with LLMs are based on standard LangChain4j. For instance, the Azure SDK is used with Azure OpenAI. ## Packaging the application -To package the application in JVM mode run: _mvn package_. +To package the application in JVM mode run: `mvn package`. ## Configuration -All configuration is centralized in microprofile-config.properties and can be redefined using environment variables. +All configuration is centralized in `microprofile-config.properties` and can be redefined using environment variables. ## Running the application @@ -66,14 +66,14 @@ Note: native mode not yet tested. The application exposes a REST API documented with OpenAPI. -To interract with the application go to: http://localhost:8080/openapi/ui. +To interact with the application go to: [http://localhost:8080/openapi/ui](http://localhost:8080/openapi/ui). Typical questions you can ask in the Chat: * Hello, how can you help me? * What is your list of cars? -* What is your cancelation policy? +* What is your cancellation policy? * What is your fleet size? Be short please. * How many electric cars do you have? * My name is James Bond, please list my bookings diff --git a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfigProvider.java b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfigProvider.java index afb9408..748547c 100644 --- a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfigProvider.java +++ b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfigProvider.java @@ -5,7 +5,6 @@ import java.util.List; import java.util.ServiceLoader; -@SuppressWarnings("unchecked") public class LLMConfigProvider { private static LLMConfig llmConfig; @@ -19,7 +18,7 @@ public class LLMConfigProvider { if (factories.isEmpty()) { throw new RuntimeException("No service Found for LLMConfig interface"); } else { - llmConfig = loader.findFirst().orElse(null); + llmConfig = loader.findFirst().get(); } } @@ -31,5 +30,4 @@ public static LLMConfig getLlmConfig() { return llmConfig; } - } From 6e01b08230d1776e8909d9be96f7c02ce085a33a Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Mon, 23 Sep 2024 14:12:57 +0200 Subject: [PATCH 21/35] Update POM --- examples/liberty-car-booking/pom.xml | 64 ++++++++++++++++--- .../io/jefrajames/booking/DocRagIngestor.java | 15 +++++ .../src/main/liberty/config/server.xml | 7 +- pom.xml | 8 +-- .../pom.xml | 1 + smallrye-llm-langchain4j-core/pom.xml | 1 + .../pom.xml | 1 - 7 files changed, 73 insertions(+), 24 deletions(-) diff --git a/examples/liberty-car-booking/pom.xml b/examples/liberty-car-booking/pom.xml index 528c048..794f36a 100644 --- a/examples/liberty-car-booking/pom.xml +++ b/examples/liberty-car-booking/pom.xml @@ -1,9 +1,9 @@ - 4.0.0 - io.smallrye.llm.examples - 1.0.0-SNAPSHOT - liberty-car-booking - war + 4.0.0 + io.smallrye.llm.examples + 1.0.0-SNAPSHOT + liberty-car-booking + war @@ -29,6 +29,14 @@ + + + jakarta.platform + jakarta.jakartaee-api + ${jakartaee-api.version} + provided + + org.eclipse.microprofile microprofile @@ -37,27 +45,33 @@ provided + + io.smallrye.llm smallrye-llm-langchain4j-config-mpconfig 1.0.0-SNAPSHOT - + io.smallrye.llm smallrye-llm-langchain4j-portable-extension 1.0.0-SNAPSHOT - + dev.langchain4j @@ -101,6 +115,15 @@ + + + org.eclipse.microprofile microprofile @@ -110,13 +133,33 @@ io.smallrye.llm smallrye-llm-langchain4j-config-mpconfig + + + org.eclipse.microprofile.config + microprofile-config-api + + io.smallrye.llm smallrye-llm-langchain4j-portable-extension + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + + + + org.projectlombok diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java index 007a0af..bf29d05 100644 --- a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java @@ -19,11 +19,26 @@ import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; import lombok.extern.java.Log; + import org.eclipse.microprofile.config.inject.ConfigProperty; @Log @ApplicationScoped public class DocRagIngestor { + +// static { +// ClassLoader cl = Thread.currentThread().getContextClassLoader(); +// +// if (cl == null) { +// cl = DocRagIngestor.class.getClassLoader(); +// } +// +// if (cl == null) { +// cl = ClassLoader.getSystemClassLoader(); +// } +// +// Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader()); +// } // Used by ContentRetriever @Produces diff --git a/examples/liberty-car-booking/src/main/liberty/config/server.xml b/examples/liberty-car-booking/src/main/liberty/config/server.xml index ab5e086..8e19039 100644 --- a/examples/liberty-car-booking/src/main/liberty/config/server.xml +++ b/examples/liberty-car-booking/src/main/liberty/config/server.xml @@ -1,14 +1,8 @@ - - - jakartaee-10.0 microProfile-6.1 - bells-1.0 - true - usr:mpLLM-1.0 @@ -18,6 +12,7 @@ + diff --git a/pom.xml b/pom.xml index 61b9152..e81f127 100644 --- a/pom.xml +++ b/pom.xml @@ -41,11 +41,10 @@ UTF-8 UTF-8 UTF-8 - 4.0.1 + 4.1.0 4.0.0.Final 5.1.0.Final 3.5.3.Final - 3.0.3 0.33.0 3.9.9 3.13.0 @@ -135,11 +134,6 @@ langchain4j ${dev.langchain4j.version} - - org.eclipse.microprofile.config - microprofile-config-api - ${microprofile-config-api.version} -
diff --git a/smallrye-llm-langchain4j-config-mpconfig/pom.xml b/smallrye-llm-langchain4j-config-mpconfig/pom.xml index f943aee..84680df 100644 --- a/smallrye-llm-langchain4j-config-mpconfig/pom.xml +++ b/smallrye-llm-langchain4j-config-mpconfig/pom.xml @@ -20,6 +20,7 @@ org.eclipse.microprofile.config microprofile-config-api + provided io.smallrye.llm diff --git a/smallrye-llm-langchain4j-core/pom.xml b/smallrye-llm-langchain4j-core/pom.xml index 9f58454..014aadc 100644 --- a/smallrye-llm-langchain4j-core/pom.xml +++ b/smallrye-llm-langchain4j-core/pom.xml @@ -18,6 +18,7 @@ jakarta.enterprise jakarta.enterprise.cdi-api + provided dev.langchain4j diff --git a/smallrye-llm-langchain4j-portable-extension/pom.xml b/smallrye-llm-langchain4j-portable-extension/pom.xml index 3da1ede..169d82e 100644 --- a/smallrye-llm-langchain4j-portable-extension/pom.xml +++ b/smallrye-llm-langchain4j-portable-extension/pom.xml @@ -24,7 +24,6 @@ jakarta.enterprise jakarta.enterprise.cdi-api - provided org.jboss.logging From cfbe1545ac42f53ed3ce87c340d1eea70033712a Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Mon, 23 Sep 2024 14:58:50 +0200 Subject: [PATCH 22/35] Finally got OL to work. --- examples/liberty-car-booking/pom.xml | 9 ++++++++- examples/liberty-car-booking/src/main/webapp/index.html | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/liberty-car-booking/pom.xml b/examples/liberty-car-booking/pom.xml index 794f36a..c2ee0e7 100644 --- a/examples/liberty-car-booking/pom.xml +++ b/examples/liberty-car-booking/pom.xml @@ -23,7 +23,7 @@ 6.1 3.13.0 3.4.0 - 0.33.0 + 0.34.0 3.13.0 @@ -203,6 +203,13 @@ langchain4j-embeddings-all-minilm-l6-v2 + + + ai.djl.huggingface + tokenizers + 0.30.0 + + org.slf4j diff --git a/examples/liberty-car-booking/src/main/webapp/index.html b/examples/liberty-car-booking/src/main/webapp/index.html index 4d557d9..4f190be 100644 --- a/examples/liberty-car-booking/src/main/webapp/index.html +++ b/examples/liberty-car-booking/src/main/webapp/index.html @@ -11,7 +11,7 @@
-

Chat Room implemented with Helidon

+

Chat Room implemented with OpenLiberty.

From 5b860a46a374080d4cd7b975cc2962d9533ad983 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Mon, 23 Sep 2024 15:09:17 +0200 Subject: [PATCH 23/35] Update POM --- examples/helidon-car-booking-portable-ext/pom.xml | 2 +- examples/helidon-car-booking/pom.xml | 2 +- pom.xml | 2 +- smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml | 6 ++++++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/helidon-car-booking-portable-ext/pom.xml b/examples/helidon-car-booking-portable-ext/pom.xml index 10bef10..64edfe9 100644 --- a/examples/helidon-car-booking-portable-ext/pom.xml +++ b/examples/helidon-car-booking-portable-ext/pom.xml @@ -17,7 +17,7 @@ 17 UTF-8 - 0.33.0 + 0.34.0 diff --git a/examples/helidon-car-booking/pom.xml b/examples/helidon-car-booking/pom.xml index 472e2c7..604369a 100644 --- a/examples/helidon-car-booking/pom.xml +++ b/examples/helidon-car-booking/pom.xml @@ -17,7 +17,7 @@ 17 UTF-8 - 0.33.0 + 0.34.0 diff --git a/pom.xml b/pom.xml index e81f127..729526a 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ 4.0.0.Final 5.1.0.Final 3.5.3.Final - 0.33.0 + 0.34.0 3.9.9 3.13.0 3.5.0 diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml index cca6df3..3e16a99 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml @@ -63,6 +63,12 @@ smallrye-llm-langchain4j-portable-extension ${project.version} provided + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + From 22079350438737f936f378e3199a841bf9b8e3bd Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Mon, 23 Sep 2024 15:21:27 +0200 Subject: [PATCH 24/35] Remove unnecessary files. --- .../org.eclipse.wst.common.project.facet.core.xml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 examples/liberty-car-booking/.settings/org.eclipse.wst.common.project.facet.core.xml diff --git a/examples/liberty-car-booking/.settings/org.eclipse.wst.common.project.facet.core.xml b/examples/liberty-car-booking/.settings/org.eclipse.wst.common.project.facet.core.xml deleted file mode 100644 index a571972..0000000 --- a/examples/liberty-car-booking/.settings/org.eclipse.wst.common.project.facet.core.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - From 4854ccea5d49bd4c94e70dbe1435157d6eb4d98f Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Mon, 23 Sep 2024 15:24:13 +0200 Subject: [PATCH 25/35] Remove unnecessary files. --- .../src/main/resources/META-INF/MANIFEST.MF | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 examples/liberty-car-booking/src/main/resources/META-INF/MANIFEST.MF diff --git a/examples/liberty-car-booking/src/main/resources/META-INF/MANIFEST.MF b/examples/liberty-car-booking/src/main/resources/META-INF/MANIFEST.MF deleted file mode 100644 index 254272e..0000000 --- a/examples/liberty-car-booking/src/main/resources/META-INF/MANIFEST.MF +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -Class-Path: - From bbd5ad6dcc60a751bcdc80c8c049af93d8e49200 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Mon, 23 Sep 2024 15:38:08 +0200 Subject: [PATCH 26/35] Making component libs scope provided. --- smallrye-llm-langchain4j-portable-extension/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/smallrye-llm-langchain4j-portable-extension/pom.xml b/smallrye-llm-langchain4j-portable-extension/pom.xml index b1102ab..4690195 100644 --- a/smallrye-llm-langchain4j-portable-extension/pom.xml +++ b/smallrye-llm-langchain4j-portable-extension/pom.xml @@ -24,6 +24,7 @@ jakarta.enterprise jakarta.enterprise.cdi-api + provided org.jboss.logging From 944d9dba71d37500be2b2c244538c9f5e82594e2 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Tue, 24 Sep 2024 01:57:42 +0200 Subject: [PATCH 27/35] More refactoring. --- examples/liberty-car-booking/pom.xml | 31 +++++++++++-------- .../io/jefrajames/booking/DocRagIngestor.java | 15 +-------- .../src/main/liberty/config/server.xml | 6 +++- .../integration/pom.xml | 6 ---- 4 files changed, 24 insertions(+), 34 deletions(-) diff --git a/examples/liberty-car-booking/pom.xml b/examples/liberty-car-booking/pom.xml index c2ee0e7..8e1a3a0 100644 --- a/examples/liberty-car-booking/pom.xml +++ b/examples/liberty-car-booking/pom.xml @@ -25,6 +25,13 @@ 3.4.0 0.34.0 3.13.0 + + + + ${project.build.directory}/liberty/wlp/usr/shared/resources/lib/ @@ -103,6 +110,15 @@ langchain4j-embeddings-all-minilm-l6-v2 ${dev.langchain4j.version} + + + @@ -133,23 +149,11 @@ io.smallrye.llm smallrye-llm-langchain4j-config-mpconfig - - - org.eclipse.microprofile.config - microprofile-config-api - - io.smallrye.llm smallrye-llm-langchain4j-portable-extension - - - jakarta.enterprise - jakarta.enterprise.cdi-api - - + diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java index bf29d05..b31de6f 100644 --- a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java @@ -18,6 +18,7 @@ import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; +import io.jefrajames.utils.LibUtilsNativeHelper; import lombok.extern.java.Log; import org.eclipse.microprofile.config.inject.ConfigProperty; @@ -26,20 +27,6 @@ @ApplicationScoped public class DocRagIngestor { -// static { -// ClassLoader cl = Thread.currentThread().getContextClassLoader(); -// -// if (cl == null) { -// cl = DocRagIngestor.class.getClassLoader(); -// } -// -// if (cl == null) { -// cl = ClassLoader.getSystemClassLoader(); -// } -// -// Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader()); -// } - // Used by ContentRetriever @Produces private EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel(); diff --git a/examples/liberty-car-booking/src/main/liberty/config/server.xml b/examples/liberty-car-booking/src/main/liberty/config/server.xml index 8e19039..1d30382 100644 --- a/examples/liberty-car-booking/src/main/liberty/config/server.xml +++ b/examples/liberty-car-booking/src/main/liberty/config/server.xml @@ -12,8 +12,12 @@ - + + + + + diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml index 3e16a99..cca6df3 100644 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml +++ b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml @@ -63,12 +63,6 @@ smallrye-llm-langchain4j-portable-extension ${project.version} provided - - - jakarta.enterprise - jakarta.enterprise.cdi-api - - From 568303941d7b310c5c3bb653a8f005e9ec29b31c Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Tue, 24 Sep 2024 02:10:03 +0200 Subject: [PATCH 28/35] Update the LLMConfigProvider --- .../llm/core/langchain4j/core/config/spi/LLMConfigProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfigProvider.java b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfigProvider.java index 748547c..e825565 100644 --- a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfigProvider.java +++ b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfigProvider.java @@ -18,7 +18,7 @@ public class LLMConfigProvider { if (factories.isEmpty()) { throw new RuntimeException("No service Found for LLMConfig interface"); } else { - llmConfig = loader.findFirst().get(); + llmConfig = factories.iterator().next(); //loader.findFirst().orElse(null); } } From 7727a91b1c47f5df107c56e35b9ee680a5ccf23c Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Tue, 24 Sep 2024 15:19:24 +0200 Subject: [PATCH 29/35] Fix import issue. --- .../src/main/java/io/jefrajames/booking/DocRagIngestor.java | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java index b31de6f..58cb628 100644 --- a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java @@ -18,7 +18,6 @@ import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; -import io.jefrajames.utils.LibUtilsNativeHelper; import lombok.extern.java.Log; import org.eclipse.microprofile.config.inject.ConfigProperty; From 5a5325d2dc2cc5d80ca393b22bc6056bf005a763 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Tue, 24 Sep 2024 16:09:26 +0200 Subject: [PATCH 30/35] Making Liberty example simpler. Thanks Emily. --- examples/liberty-car-booking/pom.xml | 6 - .../src/main/liberty/config/server.xml | 12 -- .../BOM/pom.xml | 26 ---- .../ESA/pom.xml | 77 ----------- .../integration/pom.xml | 128 ------------------ .../Langchain4JCDIExtensionMetadata.java | 44 ------ .../pom.xml | 29 ---- 7 files changed, 322 deletions(-) delete mode 100644 smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml delete mode 100644 smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml delete mode 100644 smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml delete mode 100644 smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java delete mode 100644 smallrye-llm-langchain4j-liberty-bundle/pom.xml diff --git a/examples/liberty-car-booking/pom.xml b/examples/liberty-car-booking/pom.xml index 8e1a3a0..7781d95 100644 --- a/examples/liberty-car-booking/pom.xml +++ b/examples/liberty-car-booking/pom.xml @@ -241,12 +241,6 @@ liberty-maven-plugin 3.10.3 - - io.openliberty - openliberty-runtime - 24.0.0.9 - zip - ${project.build.finalName} ${project.basedir}/docs-for-rag diff --git a/examples/liberty-car-booking/src/main/liberty/config/server.xml b/examples/liberty-car-booking/src/main/liberty/config/server.xml index 1d30382..f515f8a 100644 --- a/examples/liberty-car-booking/src/main/liberty/config/server.xml +++ b/examples/liberty-car-booking/src/main/liberty/config/server.xml @@ -5,9 +5,6 @@ microProfile-6.1 - - - @@ -19,13 +16,4 @@ - - - - - - - - \ No newline at end of file diff --git a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml deleted file mode 100644 index 1856f93..0000000 --- a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml +++ /dev/null @@ -1,26 +0,0 @@ - - 4.0.0 - - io.smallrye.llm.liberty-bundle - smallrye-llm-langchain4j-liberty-bundle - 1.0.0-SNAPSHOT - ../pom.xml - - ${bom.artifact.id} - pom - https://openliberty.io/ - - - - - io.smallrye.llm.liberty-bundle - smallrye-llm-langchain4j-feature - 1.0.0-SNAPSHOT - provided - esa - - - - \ No newline at end of file diff --git a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml deleted file mode 100644 index 1482f4f..0000000 --- a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml +++ /dev/null @@ -1,77 +0,0 @@ - - 4.0.0 - - io.smallrye.llm.liberty-bundle - smallrye-llm-langchain4j-liberty-bundle - 1.0.0-SNAPSHOT - ../pom.xml - - ${esa.artifact.id} - esa - - - - io.smallrye.llm.liberty-bundle - ${integration.artifact.id} - ${project.version} - - - - io.openliberty.features - microProfile-6.1 - 24.0.0.9 - esa - provided - - - - - - - - - - org.apache.aries - esa-maven-plugin - 1.0.2 - true - - true - all - - IBM - 2 - ${liberty.feature.name} - - - ${integration.artifact.id};visibility:=public - - osgi.subsystem.feature - 1.0.0 - dev.langchain4j.*;version="${dev.langchain4j.version}",io.smallrye.llm.spi;version="1.0.0",io.smallrye.llm.core.langchain4j.portableextension;version="1.0.0" - - - - - - - - - org.apache.aries - esa-maven-plugin - - - - \ No newline at end of file diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml deleted file mode 100644 index cca6df3..0000000 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml +++ /dev/null @@ -1,128 +0,0 @@ - - - 4.0.0 - - io.smallrye.llm.liberty-bundle - smallrye-llm-langchain4j-liberty-bundle - 1.0.0-SNAPSHOT - ../pom.xml - - ${integration.artifact.id} - bundle - - - org.osgi - osgi.core - 8.0.0 - provided - - - org.osgi - org.osgi.service.component.annotations - 1.5.1 - provided - - - org.osgi - org.osgi.service.cm - 1.6.1 - provided - - - - io.openliberty.spi - io.openliberty.cdi.spi - 1.1.92 - provided - - - jakarta.enterprise - jakarta.enterprise.cdi-api - provided - - - org.eclipse.microprofile - microprofile - ${microprofile-api.version} - pom - provided - - - - io.smallrye.llm - smallrye-llm-langchain4j-portable-extension - ${project.version} - provided - - - - dev.langchain4j - langchain4j - - - - org.jboss.logging - jboss-logging - - - - - - - org.apache.felix - maven-bundle-plugin - 5.1.9 - true - - - ${project.artifactId} - ${project.artifactId}; singleton:=true - 1.0.0 - SmallRye LLM LangChain4J Liberty Bundle - *;scope=compile|runtime - true - - io.smallrye.llm.spi;version="1.0.0", - io.smallrye.llm.core.langchain4j.portableextension;version="1.0.0", - ${new.integration.code.private.package};version="1.0.0" - - - - - - - - org.apache.felix - org.apache.felix.dependencymanager.annotation - 5.0.2 - - - - - - org.apache.felix - maven-bundle-plugin - - - org.apache.felix - org.apache.felix.dependencymanager.annotation - - - - diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java deleted file mode 100644 index e750a4e..0000000 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JCDIExtensionMetadata.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.smallrye.llm.core.langchain4j.spi.liberty.cdi.internal; - -import java.lang.annotation.Annotation; -import java.util.Set; - -import jakarta.enterprise.inject.spi.Extension; - -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.ConfigurationPolicy; - -import io.openliberty.cdi.spi.CDIExtensionMetadata; -import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JAIServicePortableExtension; -import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JPluginsPortableExtension; -import io.smallrye.llm.spi.RegisterAIService; - -/** - * @author Buhake Sindi - * @since 26 August 2024 - */ -@Component(service = CDIExtensionMetadata.class, configurationPolicy = ConfigurationPolicy.IGNORE, immediate = true) -public class Langchain4JCDIExtensionMetadata implements CDIExtensionMetadata { - - /* - * (non-Javadoc) - * - * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getBeanDefiningAnnotationClasses() - */ - @Override - public Set> getBeanDefiningAnnotationClasses() { - // TODO Auto-generated method stub - return Set.of(RegisterAIService.class); - } - - /* - * (non-Javadoc) - * - * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getExtensions() - */ - @Override - public Set> getExtensions() { - // TODO Auto-generated method stub - return Set.of(LangChain4JAIServicePortableExtension.class, LangChain4JPluginsPortableExtension.class); - } -} diff --git a/smallrye-llm-langchain4j-liberty-bundle/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/pom.xml deleted file mode 100644 index 11b58df..0000000 --- a/smallrye-llm-langchain4j-liberty-bundle/pom.xml +++ /dev/null @@ -1,29 +0,0 @@ - - 4.0.0 - - io.smallrye.llm - smallrye-llm-parent - 1.0.0-SNAPSHOT - ../pom.xml - - io.smallrye.llm.liberty-bundle - smallrye-llm-langchain4j-liberty-bundle - pom - - - 6.1 - mpLLM-1.0 - smallrye-llm-langchain4j-features-bom - smallrye-llm-langchain4j-feature - smallrye-llm-langchain4j-bundle - io.smallrye.llm.core.langchain4j.spi.liberty.cdi.internal - - - - BOM - ESA - integration - - \ No newline at end of file From 8922cc05c16ea3de9cb781a3f3294ad2b6d6526a Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Wed, 25 Sep 2024 03:24:57 +0200 Subject: [PATCH 31/35] Fix the issue where OpenLiberty deploys application twice due to liberty-maven-plugin started in dev mode. --- examples/liberty-car-booking/README.md | 2 +- examples/liberty-car-booking/pom.xml | 8 -------- .../main/java/io/jefrajames/booking/DocRagIngestor.java | 1 + .../src/main/liberty/config/server.xml | 9 ++++----- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/examples/liberty-car-booking/README.md b/examples/liberty-car-booking/README.md index 9d6c77f..5d66d76 100644 --- a/examples/liberty-car-booking/README.md +++ b/examples/liberty-car-booking/README.md @@ -5,7 +5,7 @@ These are the steps to run this service. 1. Go to the root `smallrye-llm` directory, then build it using the command: > `mvn clean install -e` 2. Once the project is built, move to directory `cd examples/liberty-car-booking` and run the following command to install the bundle as a user liberty feature: - > `mvn clean liberty:create liberty:prepare-feature liberty:install-feature liberty:dev -e` + > `mvn liberty:dev -e` ## Application requirements: - JDK 17 and higher diff --git a/examples/liberty-car-booking/pom.xml b/examples/liberty-car-booking/pom.xml index 7781d95..f5ef78b 100644 --- a/examples/liberty-car-booking/pom.xml +++ b/examples/liberty-car-booking/pom.xml @@ -27,10 +27,6 @@ 3.13.0 - ${project.build.directory}/liberty/wlp/usr/shared/resources/lib/ @@ -112,13 +108,11 @@ - @@ -208,12 +202,10 @@ - diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java index 58cb628..b31de6f 100644 --- a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java @@ -18,6 +18,7 @@ import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; +import io.jefrajames.utils.LibUtilsNativeHelper; import lombok.extern.java.Log; import org.eclipse.microprofile.config.inject.ConfigProperty; diff --git a/examples/liberty-car-booking/src/main/liberty/config/server.xml b/examples/liberty-car-booking/src/main/liberty/config/server.xml index f515f8a..2653bc7 100644 --- a/examples/liberty-car-booking/src/main/liberty/config/server.xml +++ b/examples/liberty-car-booking/src/main/liberty/config/server.xml @@ -4,16 +4,15 @@ microProfile-6.1 - + - - - - + + + \ No newline at end of file From dec5db6cab5b1d830cb5fac82e7d1690196e2961 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Wed, 25 Sep 2024 03:26:31 +0200 Subject: [PATCH 32/35] Fix the issue where OpenLiberty deploys application twice due to liberty-maven-plugin started in dev mode. --- examples/liberty-car-booking/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/liberty-car-booking/README.md b/examples/liberty-car-booking/README.md index 5d66d76..92561c7 100644 --- a/examples/liberty-car-booking/README.md +++ b/examples/liberty-car-booking/README.md @@ -39,7 +39,7 @@ To package the application in JVM mode run: `mvn package`. ## Configuration -All configuration is centralized in `microprofile-config.properties`(found is META-INF folder) and can be redefined using environment variables. +All configuration is centralized in `microprofile-config.properties`(found is `resources\META-INF` folder) and can be redefined using environment variables. ## Running the application From e73ade55e063e4cec6824d425eb08c93e8cbf7b4 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Wed, 25 Sep 2024 03:33:15 +0200 Subject: [PATCH 33/35] Fix the issue where OpenLiberty deploys application twice due to liberty-maven-plugin started in dev mode. --- .../src/main/java/io/jefrajames/booking/DocRagIngestor.java | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java index b31de6f..58cb628 100644 --- a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java @@ -18,7 +18,6 @@ import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; -import io.jefrajames.utils.LibUtilsNativeHelper; import lombok.extern.java.Log; import org.eclipse.microprofile.config.inject.ConfigProperty; From 47bda65f6cb7ee26853e162e4c02a2a791407f89 Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Wed, 25 Sep 2024 03:58:29 +0200 Subject: [PATCH 34/35] Making Liberty example simpler. Thanks Emily. --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index b4c2c4d..88a3405 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,6 @@ smallrye-llm-langchain4j-buildcompatible-extension smallrye-llm-langchain4j-core smallrye-llm-langchain4j-config-mpconfig - smallrye-llm-langchain4j-liberty-bundle From 29aa4ad18a1d60e008f0d28331d9bee60192c34a Mon Sep 17 00:00:00 2001 From: Buhake Sindi Date: Mon, 16 Sep 2024 11:11:18 +0200 Subject: [PATCH 35/35] Car Booking example, running on OpenLiberty. Adding Liberty examples. Adding Liberty examples. Adding Liberty examples. Update Car Booking Liberty README Update Car Booking Liberty README Update Liberty example. Update POM Finally got OL to work. Update POM Updating the code to be able to support WildFly Adding a ChatMemoryFactoryProvider to enable the creation of ChatMemory as needed. Signed-off-by: Emmanuel Hugonnet Cleaning the dependencies tree to avoid bringing in JakartaEE APIs Fixing GlassFish example Signed-off-by: Emmanuel Hugonnet Remove unnecessary files. Remove unnecessary files. Making component libs scope provided. More refactoring. Update the LLMConfigProvider Fix import issue. Making Liberty example simpler. Thanks Emily. Fix the issue where OpenLiberty deploys application twice due to liberty-maven-plugin started in dev mode. Fix the issue where OpenLiberty deploys application twice due to liberty-maven-plugin started in dev mode. Fix the issue where OpenLiberty deploys application twice due to liberty-maven-plugin started in dev mode. Making Liberty example simpler. Thanks Emily. --- .gitignore | 1 + .../config/llm-config.properties | 3 +- examples/glassfish-car-booking/pom.xml | 27 + .../io/jefrajames/booking/DummyLLConfig.java | 2 +- examples/glassfish-car-booking/test.sh | 56 +- .../helidon-car-booking-portable-ext/pom.xml | 2 +- examples/helidon-car-booking/README.md | 18 +- examples/helidon-car-booking/pom.xml | 2 +- examples/liberty-car-booking/README.md | 75 ++ .../docs-for-rag/apple-pie-recipe.txt | 22 + .../docs-for-rag/general-information.txt | 6 + .../docs-for-rag/list-of-cars.txt | 11 + .../docs-for-rag/terms-of-use.txt | 37 + examples/liberty-car-booking/pom.xml | 257 ++++ .../java/io/jefrajames/booking/Booking.java | 21 + .../BookingAlreadyCanceledException.java | 9 + .../BookingCannotBeCanceledException.java | 9 + .../booking/BookingNotFoundException.java | 7 + .../io/jefrajames/booking/BookingService.java | 95 ++ .../booking/CarBookingResource.java | 48 + .../io/jefrajames/booking/ChatAiService.java | 42 + .../java/io/jefrajames/booking/Customer.java | 15 + .../io/jefrajames/booking/DocRagIngestor.java | 68 + .../io/jefrajames/booking/DummyLLConfig.java | 56 + .../io/jefrajames/booking/FraudAiService.java | 55 + .../io/jefrajames/booking/FraudResponse.java | 14 + .../java/io/jefrajames/booking/JaxRSApp.java | 8 + .../src/main/liberty/config/server.xml | 18 + .../src/main/resources/META-INF/beans.xml | 7 + .../META-INF/microprofile-config.properties | 33 + .../src/main/webapp/WEB-INF/web.xml | 10 + .../src/main/webapp/chatroom.js | 57 + .../src/main/webapp/css/styles.css | 544 ++++++++ .../webapp/fonts/BunueloCleanPro-Light.otf | Bin 0 -> 113536 bytes .../webapp/fonts/BunueloCleanPro-SemiBold.otf | Bin 0 -> 116504 bytes .../main/webapp/img/small_logo_dark_gray.svg | 1 + .../src/main/webapp/img/sysProps.svg | 20 + .../src/main/webapp/index.html | 47 + examples/pom.xml | 1 + examples/quarkus-car-booking/README.md | 16 +- pom.xml | 22 +- .../pom.xml | 14 +- .../pom.xml | 1 + .../mpconfig/LLMConfigMPConfig.java | 8 +- smallrye-llm-langchain4j-core/pom.xml | 1 + .../llm/aiservice/CommonAIServiceCreator.java | 72 +- .../core/config/spi/ChatMemoryFactory.java | 10 + .../config/spi/ChatMemoryFactoryProvider.java | 29 + .../core/config/spi/LLMConfig.java | 5 + .../core/config/spi/LLMConfigProvider.java | 4 +- .../llm/plugin/CommonLLMPluginCreator.java | 51 +- .../smallrye/llm/spi/RegisterAIService.java | 6 + .../BOM/pom.xml | 26 - .../ESA/pom.xml | 70 - .../integration/pom.xml | 107 -- ...gchain4JAIServiceCDIExtensionMetadata.java | 44 - .../pom.xml | 29 - .../effective-pom.xml | 1156 +++++++++++++++++ .../pom.xml | 6 +- .../LangChain4JPluginsPortableExtension.java | 6 +- .../jakarta.enterprise.inject.spi.Extension | 2 +- .../core/PortableExtensionInstanceTest.java | 22 +- 62 files changed, 3027 insertions(+), 384 deletions(-) create mode 100644 examples/liberty-car-booking/README.md create mode 100644 examples/liberty-car-booking/docs-for-rag/apple-pie-recipe.txt create mode 100644 examples/liberty-car-booking/docs-for-rag/general-information.txt create mode 100644 examples/liberty-car-booking/docs-for-rag/list-of-cars.txt create mode 100644 examples/liberty-car-booking/docs-for-rag/terms-of-use.txt create mode 100644 examples/liberty-car-booking/pom.xml create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Booking.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingAlreadyCanceledException.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingCannotBeCanceledException.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingNotFoundException.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingService.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/CarBookingResource.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/ChatAiService.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Customer.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DummyLLConfig.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudAiService.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudResponse.java create mode 100644 examples/liberty-car-booking/src/main/java/io/jefrajames/booking/JaxRSApp.java create mode 100644 examples/liberty-car-booking/src/main/liberty/config/server.xml create mode 100644 examples/liberty-car-booking/src/main/resources/META-INF/beans.xml create mode 100644 examples/liberty-car-booking/src/main/resources/META-INF/microprofile-config.properties create mode 100644 examples/liberty-car-booking/src/main/webapp/WEB-INF/web.xml create mode 100644 examples/liberty-car-booking/src/main/webapp/chatroom.js create mode 100644 examples/liberty-car-booking/src/main/webapp/css/styles.css create mode 100644 examples/liberty-car-booking/src/main/webapp/fonts/BunueloCleanPro-Light.otf create mode 100644 examples/liberty-car-booking/src/main/webapp/fonts/BunueloCleanPro-SemiBold.otf create mode 100644 examples/liberty-car-booking/src/main/webapp/img/small_logo_dark_gray.svg create mode 100644 examples/liberty-car-booking/src/main/webapp/img/sysProps.svg create mode 100644 examples/liberty-car-booking/src/main/webapp/index.html create mode 100644 smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/ChatMemoryFactory.java create mode 100644 smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/ChatMemoryFactoryProvider.java delete mode 100644 smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml delete mode 100644 smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml delete mode 100644 smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml delete mode 100644 smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java delete mode 100644 smallrye-llm-langchain4j-liberty-bundle/pom.xml create mode 100644 smallrye-llm-langchain4j-portable-extension/effective-pom.xml diff --git a/.gitignore b/.gitignore index df41e31..7879fe1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ target/ +installs/ pom.xml.tag pom.xml.releaseBackup pom.xml.versionsBackup diff --git a/examples/glassfish-car-booking/config/llm-config.properties b/examples/glassfish-car-booking/config/llm-config.properties index 3548a2c..745b123 100644 --- a/examples/glassfish-car-booking/config/llm-config.properties +++ b/examples/glassfish-car-booking/config/llm-config.properties @@ -11,7 +11,8 @@ #smallrye.llm.plugin.chat-model.config.logRequestsAndResponses=true smallrye.llm.plugin.chat-model.class=dev.langchain4j.model.openai.OpenAiChatModel -smallrye.llm.plugin.chat-model.config.base-url=http://localhost:1234/v1 +smallrye.llm.plugin.chat-model.config.base-url=http://localhost:11434/v1 +smallrye.llm.plugin.chat-model.config.model-name=llama3.1 smallrye.llm.plugin.chat-model.config.api-key=not-needed smallrye.llm.plugin.chat-model.config.temperature=0.1 smallrye.llm.plugin.chat-model.config.topP=0.1 diff --git a/examples/glassfish-car-booking/pom.xml b/examples/glassfish-car-booking/pom.xml index 8f58558..a8942c3 100644 --- a/examples/glassfish-car-booking/pom.xml +++ b/examples/glassfish-car-booking/pom.xml @@ -29,6 +29,32 @@ io.smallrye.llm smallrye-llm-langchain4j-portable-extension 1.0.0-SNAPSHOT + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + jakarta.annotation + jakarta.annotation-api + + + jakarta.el + jakarta.el-api + + + jakarta.interceptor + jakarta.interceptor-api + + + org.jboss.weld + weld-api + + + org.jboss.weld + weld-core-impl + + org.projectlombok @@ -91,6 +117,7 @@ ${project.build.directory}/glassfish7x-home ${cargo.servlet.port} + true diff --git a/examples/glassfish-car-booking/src/main/java/io/jefrajames/booking/DummyLLConfig.java b/examples/glassfish-car-booking/src/main/java/io/jefrajames/booking/DummyLLConfig.java index 0d0b6a1..25234c5 100644 --- a/examples/glassfish-car-booking/src/main/java/io/jefrajames/booking/DummyLLConfig.java +++ b/examples/glassfish-car-booking/src/main/java/io/jefrajames/booking/DummyLLConfig.java @@ -43,7 +43,7 @@ public T getBeanPropertyValue(String beanName, String propertyName, Class try { return type.getConstructor(String.class).newInstance(value); } catch (Exception e) { - throw new RuntimeException(e); + throw new IllegalArgumentException(); } } diff --git a/examples/glassfish-car-booking/test.sh b/examples/glassfish-car-booking/test.sh index 9964f85..17f9f8f 100755 --- a/examples/glassfish-car-booking/test.sh +++ b/examples/glassfish-car-booking/test.sh @@ -1 +1,55 @@ -/opt/homebrew/Cellar/openjdk/22.0.1/libexec/openjdk.jdk/Contents/Home/bin/java -cp /Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/cargo/installs/glassfish-7.0.16/glassfish7/glassfish/modules/glassfish.jar -DWALL_CLOCK_START=2024-08-19T22:08:15.207415Z -XX:+UnlockDiagnosticVMOptions -XX:NewRatio=2 -Xmx512m --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/sun.nio.fs=ALL-UNNAMED --add-opens=java.base/sun.net.www.protocol.jrt=ALL-UNNAMED --add-opens=java.naming/javax.naming.spi=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-exports=java.base/jdk.internal.vm.annotation=ALL-UNNAMED --add-opens=java.base/jdk.internal.vm.annotation=ALL-UNNAMED -javaagent:/Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/cargo/installs/glassfish-7.0.16/glassfish7/glassfish/lib/monitor/flashlight-agent.jar -Djava.awt.headless=true -Djdk.corba.allowOutputStreamSubclass=true -Djdk.tls.rejectClientInitiatedRenegotiation=true -Djavax.xml.accessExternalSchema=all -Djava.security.policy=/Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/glassfish7x-home/cargo-domain/config/server.policy -Djava.security.auth.login.config=/Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/glassfish7x-home/cargo-domain/config/login.conf -Dcom.sun.enterprise.security.httpsOutboundKeyAlias=s1as -Djavax.net.ssl.keyStore=/Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/glassfish7x-home/cargo-domain/config/keystore.jks -Djavax.net.ssl.trustStore=/Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/glassfish7x-home/cargo-domain/config/cacerts.jks -Djdbc.drivers=org.apache.derby.jdbc.ClientDriver -DANTLR_USE_DIRECT_CLASS_LOADING=true -Dcom.sun.enterprise.config.config_environment_factory_class=com.sun.enterprise.config.serverbeans.AppserverConfigEnvironmentFactory -Dorg.glassfish.additionalOSGiBundlesToStart=org.apache.felix.shell,org.apache.felix.gogo.runtime,org.apache.felix.gogo.shell,org.apache.felix.gogo.command,org.apache.felix.shell.remote,org.apache.felix.fileinstall -Dosgi.shell.telnet.port=6666 -Dosgi.shell.telnet.maxconn=1 -Dosgi.shell.telnet.ip=127.0.0.1 -Dgosh.args=--nointeractive -Dfelix.fileinstall.dir=/Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/cargo/installs/glassfish-7.0.16/glassfish7/glassfish/modules/autostart/ -Dfelix.fileinstall.poll=5000 -Dfelix.fileinstall.log.level=2 -Dfelix.fileinstall.bundles.new.start=true -Dfelix.fileinstall.bundles.startTransient=true -Dfelix.fileinstall.disableConfigSave=false -Dorg.glassfish.gmbal.no.multipleUpperBoundsException=true -Dcom.ctc.wstx.returnNullForDefaultNamespace=true -Djdk.attach.allowAttachSelf=true -Dcom.sun.aas.instanceRoot=/Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/glassfish7x-home/cargo-domain -Dcom.sun.aas.installRoot=/Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/cargo/installs/glassfish-7.0.16/glassfish7/glassfish -Djava.library.path=/Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/cargo/installs/glassfish-7.0.16/glassfish7/glassfish/lib:/Users/yblazart/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:/Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/glassfish7x-home com.sun.enterprise.glassfish.bootstrap.ASMain -upgrade false -domaindir /Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/glassfish7x-home/cargo-domain -read-stdin true -asadmin-args --host,,,localhost,,,--port,,,4848,,,--secure=false,,,--terse=false,,,--echo=false,,,--interactive=false,,,start-domain,,,--verbose=false,,,--watchdog=false,,,--debug=false,,,--domaindir,,,/Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/glassfish7x-home,,,cargo-domain -domainname cargo-domain -instancename server -type DAS -verbose false -asadmin-classpath /Users/yblazart/projects/smallrye-llm/examples/glassfish-car-booking/target/cargo/installs/glassfish-7.0.16/glassfish7/glassfish/modules/admin-cli.jar -debug false -asadmin-classname com.sun.enterprise.admin.cli.AdminMain \ No newline at end of file +#!/bin/bash +WORKING_DIR="$(dirname "${BASH_SOURCE[0]}")" +GLASSFISH_DIR=$WORKING_DIR/target/cargo/installs/glassfish-7.0.16/glassfish7/ +echo $WORKING_DIR + +if [[ ! -d $GLASSFISH_DIR ]];then + echo "Installing Glassfish" + mvn cargo:install +fi + +mvn cargo:run & + +$JAVA_HOME/bin/java -cp $GLASSFISH_DIR/glassfish/modules/glassfish.jar -DWALL_CLOCK_START=2024-08-19T22:08:15.207415Z -XX:+UnlockDiagnosticVMOptions -XX:NewRatio=2 -Xmx512m \ +--add-opens=java.base/java.io=ALL-UNNAMED \ +--add-opens=java.base/java.lang=ALL-UNNAMED \ +--add-opens=java.base/java.util=ALL-UNNAMED \ +--add-opens=java.base/sun.nio.fs=ALL-UNNAMED \ +--add-opens=java.base/sun.net.www.protocol.jrt=ALL-UNNAMED \ +--add-opens=java.naming/javax.naming.spi=ALL-UNNAMED \ +--add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED \ +--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED \ +--add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED \ +--add-exports=java.base/jdk.internal.vm.annotation=ALL-UNNAMED \ +--add-opens=java.base/jdk.internal.vm.annotation=ALL-UNNAMED \ +-javaagent:$GLASSFISH_DIR/glassfish/lib/monitor/flashlight-agent.jar \ +-Djava.awt.headless=true \ +-Djdk.corba.allowOutputStreamSubclass=true \ +-Djdk.tls.rejectClientInitiatedRenegotiation=true \ +-Djavax.xml.accessExternalSchema=all \ +-Djava.security.policy=$WORKING_DIR/target/glassfish7x-home/cargo-domain/config/server.policy \ +-Djava.security.auth.login.config=$WORKING_DIR/target/glassfish7x-home/cargo-domain/config/login.conf \ +-Dcom.sun.enterprise.security.httpsOutboundKeyAlias=s1as \ +-Djavax.net.ssl.keyStore=$WORKING_DIR/target/glassfish7x-home/cargo-domain/config/keystore.jks \ +-Djavax.net.ssl.trustStore=$WORKING_DIR/target/glassfish7x-home/cargo-domain/config/cacerts.jks \ +-Djdbc.drivers=org.apache.derby.jdbc.ClientDriver \ +-DANTLR_USE_DIRECT_CLASS_LOADING=true \ +-Dcom.sun.enterprise.config.config_environment_factory_class=com.sun.enterprise.config.serverbeans.AppserverConfigEnvironmentFactory \ +-Dorg.glassfish.additionalOSGiBundlesToStart=org.apache.felix.shell,org.apache.felix.gogo.runtime,org.apache.felix.gogo.shell,org.apache.felix.gogo.command,org.apache.felix.shell.remote,org.apache.felix.fileinstall \ +-Dosgi.shell.telnet.port=6666 -Dosgi.shell.telnet.maxconn=1 -Dosgi.shell.telnet.ip=127.0.0.1 \ +-Dgosh.args=--nointeractive \ +-Dfelix.fileinstall.dir=$GLASSFISH_DIR/glassfish/modules/autostart/ \ +-Dfelix.fileinstall.poll=5000 \ +-Dfelix.fileinstall.log.level=2 \ +-Dfelix.fileinstall.bundles.new.start=true \ +-Dfelix.fileinstall.bundles.startTransient=true \ +-Dfelix.fileinstall.disableConfigSave=false \ +-Dorg.glassfish.gmbal.no.multipleUpperBoundsException=true \ +-Dcom.ctc.wstx.returnNullForDefaultNamespace=true \ +-Djdk.attach.allowAttachSelf=true \ +-Dcom.sun.aas.instanceRoot=$WORKING_DIR/target/glassfish7x-home/cargo-domain \ +-Dcom.sun.aas.installRoot=$GLASSFISH_DIR/glassfish \ +-Djava.library.path=$GLASSFISH_DIR/glassfish/lib:/usr/lib/java:$WORKING_DIR/target/glassfish7x-home com.sun.enterprise.glassfish.bootstrap.ASMain \ +-upgrade false -domaindir $WORKING_DIR/target/glassfish7x-home/cargo-domain \ +-read-stdin true -asadmin-args --host,,,localhost,,,--port,,,4848,,,--secure=false,,,--terse=false,,,--echo=false,,,--interactive=false,,,start-domain,,,--verbose=false,,,--watchdog=false,,,--debug=false,,,--domaindir,,,$WORKING_DIR/target/glassfish7x-home,,,cargo-domain -domainname cargo-domain -instancename server \ +-type DAS -verbose false -asadmin-classpath $GLASSFISH_DIR/glassfish/modules/admin-cli.jar -debug false -asadmin-classname com.sun.enterprise.admin.cli.AdminMain diff --git a/examples/helidon-car-booking-portable-ext/pom.xml b/examples/helidon-car-booking-portable-ext/pom.xml index 10bef10..64edfe9 100644 --- a/examples/helidon-car-booking-portable-ext/pom.xml +++ b/examples/helidon-car-booking-portable-ext/pom.xml @@ -17,7 +17,7 @@ 17 UTF-8 - 0.33.0 + 0.34.0 diff --git a/examples/helidon-car-booking/README.md b/examples/helidon-car-booking/README.md index a012fa3..53a17d0 100644 --- a/examples/helidon-car-booking/README.md +++ b/examples/helidon-car-booking/README.md @@ -9,7 +9,7 @@ It is derived from my [Quarkus-LangChain4j](https://github.com/jefrajames/car-bo It is based on a simplified car booking application inspired from the [Java meets AI](https://www.youtube.com/watch?v=BD1MSLbs9KE) talk from Lize Raes at Devoxx Belgium 2023. The car booking company is called "Miles of Smiles" and the application exposes two AI services: . a chat service to freely discuss with a customer assistant -. a fraud service to determine if a customer is a frauder. +. a fraud service to determine if a customer is a fraudster. For the sake of simplicity, there is no database interaction, the application is standalone and can be used "as is". Of course thanks to Quarkus, it can easily be extended according to your needs. @@ -17,7 +17,7 @@ Warning: you must first configure the application to connect to an LLM that supp ## Technical context -The project has been developped and tested with: +The project has been developed and tested with: * Java 22 (Temurin OpenJDK distro) * Helidon 4.0.7 @@ -32,11 +32,11 @@ During my tests, GPT 3.5 has proved to be faster but less precise en consistent Quarkus provides a deep integration with LangChain4j thanks to a specific [extension](https://docs.quarkiverse.io/quarkus-langchain4j/dev/index.html). -In particular, it provides a powerful @RegisterAiService annotation and network interractions with LLMs are managed with its own RestClient. +In particular, it provides a powerful @RegisterAiService annotation and network interactions with LLMs are managed with its own RestClient. This example is based on a standard usage of LangChain4j with Helidon. There is no such deep integration. -I've added 3 technical classes to manage "the glue" (more or less the equivallent of @RegisterAiService): +I've added 3 technical classes to manage "the glue" (more or less the equivalent of `@RegisterAiService`): * ModelFactory: generates an OpenAI Chat model * ChatAiServiceFactory: generates a Chat assistant @@ -44,7 +44,7 @@ I've added 3 technical classes to manage "the glue" (more or less the equivallen I've been obliged to turn FraudResponse in a POJO. It seems that Google GSON, used to deserialize OpenAI responses does not support Java Record. -In contrast with Quarkus, network interractions with LLMs are based on standard LangChain4j. For instance, the Azure SDK is used with Azure OpenAI. +In contrast with Quarkus, network interactions with LLMs are based on standard LangChain4j. For instance, the Azure SDK is used with Azure OpenAI. ## Packaging the application @@ -66,19 +66,19 @@ Note: native mode not yet tested. The application exposes a REST API documented with OpenAPI. -To interract with the application go to: http://localhost:8080/openapi/ui. +To interact with the application go to: http://localhost:8080/openapi/ui. Typical questions you can ask in the Chat: * Hello, how can you help me? * What is your list of cars? -* What is your cancelation policy? +* What is your cancellation policy? * What is your fleet size? Be short please. * How many electric cars do you have? * My name is James Bond, please list my bookings -* Is my booking 123-456 cancelable? -* Is my booking 234-567 cancelable? +* Is my booking 123-456 cancellable? +* Is my booking 234-567 cancellable? * Can you check the duration please? * I'm James Bond, can I cancel all my booking 345-678? * Can you provide the details of all my bookings? diff --git a/examples/helidon-car-booking/pom.xml b/examples/helidon-car-booking/pom.xml index 472e2c7..604369a 100644 --- a/examples/helidon-car-booking/pom.xml +++ b/examples/helidon-car-booking/pom.xml @@ -17,7 +17,7 @@ 17 UTF-8 - 0.33.0 + 0.34.0 diff --git a/examples/liberty-car-booking/README.md b/examples/liberty-car-booking/README.md new file mode 100644 index 0000000..92561c7 --- /dev/null +++ b/examples/liberty-car-booking/README.md @@ -0,0 +1,75 @@ +# Car Booking (Open Liberty) + +These are the steps to run this service. + +1. Go to the root `smallrye-llm` directory, then build it using the command: + > `mvn clean install -e` +2. Once the project is built, move to directory `cd examples/liberty-car-booking` and run the following command to install the bundle as a user liberty feature: + > `mvn liberty:dev -e` + +## Application requirements: +- JDK 17 and higher +- Maven 3.9.9 and higher +- LangChain4j 0.33.0 or higher. +- Testing against GPT 3.5 and 4.0 on a dedicated Azure instance (to be customized in your context). + +Then you can access the application through the browser of your choice. + +## Differences with Quarkus-LangChain4j + +Quarkus provides a deep integration with LangChain4j thanks to a specific [extension](https://docs.quarkiverse.io/quarkus-langchain4j/dev/index.html). + +In particular, it provides a powerful `@RegisterAiService` annotation and network interactions with LLMs are managed with its own RestClient. + +This example is based on a standard usage of LangChain4j with Helidon. There is no such deep integration. + +I've added 3 technical classes to manage "the glue" (more or less the equivalent of `@RegisterAiService`): + +* ModelFactory: generates an OpenAI Chat model +* ChatAiServiceFactory: generates a Chat assistant +* FraudAiServiceFactory: generates a Fraud assistant. + +I've been obliged to turn FraudResponse in a POJO. It seems that Google GSON, used to deserialize OpenAI responses does not support Java Record. + +In contrast with Quarkus, network interactions with LLMs are based on standard LangChain4j. For instance, the Azure SDK is used with Azure OpenAI. + +## Packaging the application + +To package the application in JVM mode run: `mvn package`. + +## Configuration + +All configuration is centralized in `microprofile-config.properties`(found is `resources\META-INF` folder) and can be redefined using environment variables. + +## Running the application + +To run in dev mode with OpenLiberty: Run the following command in the CLI: `mvn liberty:dev`. + +To run in JVM mode (after packaging): `mvn liberty:start`. To stop the running server: `mvn liberty:stop`. + + +## Playing with the application + +The application exposes a REST API documented with OpenAPI. + +To interact with the application go to: [http://localhost:9080/openapi/ui](http://localhost:8080/openapi/ui). + + +Typical questions you can ask in the Chat: + +* Hello, how can you help me? +* What is your list of cars? +* What is your cancellation policy? +* What is your fleet size? Be short please. +* How many electric cars do you have? +* My name is James Bond, please list my bookings +* Is my booking 123-456 cancelable? +* Is my booking 234-567 cancelable? +* Can you check the duration please? +* I'm James Bond, can I cancel all my booking 345-678? +* Can you provide the details of all my bookings? + +You can ask fraud for: + +* James Bond +* Emilio Largo diff --git a/examples/liberty-car-booking/docs-for-rag/apple-pie-recipe.txt b/examples/liberty-car-booking/docs-for-rag/apple-pie-recipe.txt new file mode 100644 index 0000000..a64deba --- /dev/null +++ b/examples/liberty-car-booking/docs-for-rag/apple-pie-recipe.txt @@ -0,0 +1,22 @@ +Special Apple Pie Recipe + +Here's a very brief overview of what you can expect when you make this special apple pie at home: + +1.Make the filling: +On the stove, make a paste with flour and butter. +Add the sugar and water and bring to a boil. +Simmer, then remove from heat. + +2.Assemble the pie: +Press one crust into a pie plate. +Place the sliced apples on the bottom crust. +Use the top crust to make a lattice crust according to the recipe below. +Pour the butter-sugar mixture over the lattice crust. + +3.Bake the pie: +Bake the pie in a preheated oven until the apples are soft and the crust is golden brown. + +4.How Long to Bake Apple Pie +You'll bake the pie at 425 degrees F for 15 minutes, then you'll reduce the temperature to 350 degrees F and continue baking for 35-45 minutes. +All in all, the pie will bake for about one hour, give or take a few minutes. +You'll know the pie is done when the apples are soft and the crust is a beautiful golden brown color. \ No newline at end of file diff --git a/examples/liberty-car-booking/docs-for-rag/general-information.txt b/examples/liberty-car-booking/docs-for-rag/general-information.txt new file mode 100644 index 0000000..f0dbb2c --- /dev/null +++ b/examples/liberty-car-booking/docs-for-rag/general-information.txt @@ -0,0 +1,6 @@ +Miles of Smiles general information and main figures + +1) Population: 500 employees +2) Geography: 40 countries accross Europe, America and Asia +3) Fleet: 1000 cars, 10% electric +4) Satisfaction rating: 99% \ No newline at end of file diff --git a/examples/liberty-car-booking/docs-for-rag/list-of-cars.txt b/examples/liberty-car-booking/docs-for-rag/list-of-cars.txt new file mode 100644 index 0000000..6d65c11 --- /dev/null +++ b/examples/liberty-car-booking/docs-for-rag/list-of-cars.txt @@ -0,0 +1,11 @@ +Miles of Smiles Car Rental list of cars + +Aston Martin +BMW +DS +Mercedes +Renault +Peugeot +Porsche +Tesla +Toyota \ No newline at end of file diff --git a/examples/liberty-car-booking/docs-for-rag/terms-of-use.txt b/examples/liberty-car-booking/docs-for-rag/terms-of-use.txt new file mode 100644 index 0000000..e06772f --- /dev/null +++ b/examples/liberty-car-booking/docs-for-rag/terms-of-use.txt @@ -0,0 +1,37 @@ +Miles of Smiles Car Rental Services Terms of Use + +1. Introduction +These Terms of Service (“Terms”) govern the access or use by you, an individual, from within any country in the world, of applications, websites, content, products, and services (“Services”) made available by Miles of Smiles Car Rental Services, a company registered in the United States of America. + +2. The Services +Miles of Smiles rents out vehicles to the end user. We reserve the right to temporarily or permanently discontinue the Services at any time and are not liable for any modification, suspension or discontinuation of the Services. + +3. Bookings +3.1 Users may make a booking through our website or mobile application. +3.2 You must provide accurate, current and complete information during the reservation process. You are responsible for all charges incurred under your account. +3.3 All bookings are subject to vehicle availability. + +4. Cancellation Policy +4.1 Reservations lasting less than three days cannot be cancelled +4.2 Reservations can be cancelled up to 7 days prior to the start of the booking period. + +5. Use of Vehicle +5.1 All cars rented from Miles of Smiles must not be used: +for any illegal purpose or in connection with any criminal offense. +for teaching someone to drive. +in any race, rally or contest. +while under the influence of alcohol or drugs. + +6. Liability +6.1 Users will be held liable for any damage, loss, or theft that occurs during the rental period. +6.2 We do not accept liability for any indirect or consequential loss, damage, or expense including but not limited to loss of profits. + +7. Governing Law +These terms will be governed by and construed in accordance with the laws of the United States of America, and any disputes relating to these terms will be subject to the exclusive jurisdiction of the courts of United States. + +8. Changes to These Terms +We may revise these terms of use at any time by amending this page. You are expected to check this page from time to time to take notice of any changes we made. + +9. Acceptance of These Terms +By using the Services, you acknowledge that you have read and understand these Terms and agree to be bound by them. +If you do not agree to these Terms, please do not use or access our Services. \ No newline at end of file diff --git a/examples/liberty-car-booking/pom.xml b/examples/liberty-car-booking/pom.xml new file mode 100644 index 0000000..f5ef78b --- /dev/null +++ b/examples/liberty-car-booking/pom.xml @@ -0,0 +1,257 @@ + + 4.0.0 + io.smallrye.llm.examples + 1.0.0-SNAPSHOT + liberty-car-booking + war + + + + Buhake Sindi + +2 + + PROJECT LEAD + + + + + + UTF-8 + UTF-8 + 17 + 10.0.0 + 6.1 + 3.13.0 + 3.4.0 + 0.34.0 + 3.13.0 + + + ${project.build.directory}/liberty/wlp/usr/shared/resources/lib/ + + + + + + + jakarta.platform + jakarta.jakartaee-api + ${jakartaee-api.version} + provided + + + + org.eclipse.microprofile + microprofile + ${microprofile-api.version} + pom + provided + + + + + + io.smallrye.llm + smallrye-llm-langchain4j-config-mpconfig + 1.0.0-SNAPSHOT + + + + io.smallrye.llm + smallrye-llm-langchain4j-portable-extension + 1.0.0-SNAPSHOT + + + + + dev.langchain4j + langchain4j + ${dev.langchain4j.version} + + + + dev.langchain4j + langchain4j-hugging-face + ${dev.langchain4j.version} + + + + + dev.langchain4j + langchain4j-azure-open-ai + ${dev.langchain4j.version} + + + + dev.langchain4j + langchain4j-open-ai + ${dev.langchain4j.version} + + + + dev.langchain4j + langchain4j-embeddings-all-minilm-l6-v2 + ${dev.langchain4j.version} + + + + + ai.djl.huggingface + tokenizers + 0.30.0 + + + + + org.slf4j + slf4j-jdk14 + runtime + 2.0.9 + + + + + + + + + + org.eclipse.microprofile + microprofile + pom + + + + io.smallrye.llm + smallrye-llm-langchain4j-config-mpconfig + + + + io.smallrye.llm + smallrye-llm-langchain4j-portable-extension + + + + + + + + org.projectlombok + lombok + 1.18.32 + provided + + + + + dev.langchain4j + langchain4j + + + + dev.langchain4j + langchain4j-hugging-face + + + + + dev.langchain4j + langchain4j-azure-open-ai + + + + dev.langchain4j + langchain4j-open-ai + + + + dev.langchain4j + langchain4j-embeddings-all-minilm-l6-v2 + + + + + ai.djl.huggingface + tokenizers + + + + + org.slf4j + slf4j-jdk14 + runtime + + + + + ${project.artifactId} + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${maven.compiler.release} + + + + + io.openliberty.tools + liberty-maven-plugin + 3.10.3 + + + ${project.build.finalName} + ${project.basedir}/docs-for-rag + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + io.openliberty.tools + liberty-maven-plugin + + + + \ No newline at end of file diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Booking.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Booking.java new file mode 100644 index 0000000..a8e03c1 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Booking.java @@ -0,0 +1,21 @@ +package io.jefrajames.booking; + +import java.time.LocalDate; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Booking { + + private String bookingNumber; + private LocalDate start; + private LocalDate end; + private Customer customer; + private boolean canceled = false; + private String carModel; + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingAlreadyCanceledException.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingAlreadyCanceledException.java new file mode 100644 index 0000000..740a903 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingAlreadyCanceledException.java @@ -0,0 +1,9 @@ +package io.jefrajames.booking; + +public class BookingAlreadyCanceledException extends RuntimeException { + + public BookingAlreadyCanceledException(String bookingNumber) { + super("Booking " + bookingNumber + " already canceled"); + } + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingCannotBeCanceledException.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingCannotBeCanceledException.java new file mode 100644 index 0000000..5bafda7 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingCannotBeCanceledException.java @@ -0,0 +1,9 @@ +package io.jefrajames.booking; + +public class BookingCannotBeCanceledException extends RuntimeException { + + public BookingCannotBeCanceledException(String bookingNumber) { + super("Booking " + bookingNumber + " cannot be canceled"); + } + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingNotFoundException.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingNotFoundException.java new file mode 100644 index 0000000..3b8165e --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingNotFoundException.java @@ -0,0 +1,7 @@ +package io.jefrajames.booking; + +public class BookingNotFoundException extends RuntimeException { + public BookingNotFoundException(String bookingNumber) { + super("Booking " + bookingNumber + " not found"); + } +} \ No newline at end of file diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingService.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingService.java new file mode 100644 index 0000000..4ee5ff5 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/BookingService.java @@ -0,0 +1,95 @@ +package io.jefrajames.booking; + +import java.time.LocalDate; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import jakarta.enterprise.context.ApplicationScoped; + +import dev.langchain4j.agent.tool.Tool; +import lombok.extern.java.Log; + +@ApplicationScoped +@Log +public class BookingService { + + // Pseudo database + private static final Map BOOKINGS = new HashMap<>(); + static { + // James Bond: hero customer! + BOOKINGS.put("123-456", new Booking("123-456", LocalDate.now().plusDays(1), LocalDate.now().plusDays(7), + new Customer("James", "Bond"), false, "Aston Martin")); // Not cancelable: too late + BOOKINGS.put("234-567", new Booking("234-567", LocalDate.now().plusDays(10), LocalDate.now().plusDays(12), + new Customer("James", "Bond"), false, "Renault")); // Not cancelable: too short + BOOKINGS.put("345-678", new Booking("345-678", LocalDate.now().plusDays(14), LocalDate.now().plusDays(20), + new Customer("James", "Bond"), false, "Porsche")); // Cancelable + // Emilio Largo: villain frauder! + BOOKINGS.put("456-789", new Booking("456-789", LocalDate.now().plusDays(10), LocalDate.now().plusDays(20), + new Customer("Largo", "Emilio"), false, "Porsche")); // Cancelable + BOOKINGS.put("567-890", new Booking("567-890", LocalDate.now().plusDays(11), LocalDate.now().plusDays(16), + new Customer("Largo", "Emilio"), false, "BMW")); // Cancelable + } + + // Simulate database accesses + private Booking checkBookingExists(String bookingNumber, String name, String surname) { + Booking booking = BOOKINGS.get(bookingNumber); + if (booking == null || !booking.getCustomer().getName().equals(name) + || !booking.getCustomer().getSurname().equals(surname)) { + throw new BookingNotFoundException(bookingNumber); + } + return booking; + } + + @Tool("Get booking details given a booking number and customer name and surname") + public Booking getBookingDetails(String bookingNumber, String name, String surname) { + log.info("DEMO: Calling Tool-getBookingDetails: " + bookingNumber + " and customer: " + + name + " " + surname); + return checkBookingExists(bookingNumber, name, surname); + } + + @Tool("Get all booking ids for a customer given his name and surname") + public List getBookingsForCustomer(String name, String surname) { + log.info("DEMO: Calling Tool-getBookingsForCustomer: " + name + " " + surname); + Customer customer = new Customer(name, surname); + return BOOKINGS.values() + .stream() + .filter(booking -> booking.getCustomer().equals(customer)) + .map(Booking::getBookingNumber) + .collect(Collectors.toList()); + } + + public void checkCancelPolicy(Booking booking) { + + // Reservations can be cancelled up to 7 days prior to the start of the booking + // period + if (LocalDate.now().plusDays(7).isAfter(booking.getStart())) { + throw new BookingCannotBeCanceledException(booking.getBookingNumber() + " Too late"); + } + + // If the booking period is less than 3 days, cancellations are not permitted. + if (booking.getEnd().compareTo(booking.getStart().plusDays(3)) < 0) { + throw new BookingCannotBeCanceledException(booking.getBookingNumber() + " Too short"); + } + + } + + @Tool("Cancel a booking given its booking number and customer name and surname") + public Booking cancelBooking(String bookingNumber, String name, String surname) { + log.info("DEMO: Calling Tool-cancelBooking " + bookingNumber + " for customer: " + name + + " " + surname); + + Booking booking = checkBookingExists(bookingNumber, name, surname); + + if (booking.isCanceled()) + throw new BookingCannotBeCanceledException(bookingNumber); + + checkCancelPolicy(booking); + + booking.setCanceled(true); + + return booking; + } + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/CarBookingResource.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/CarBookingResource.java new file mode 100644 index 0000000..8faf5f9 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/CarBookingResource.java @@ -0,0 +1,48 @@ +package io.jefrajames.booking; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; + +@ApplicationScoped +@Path("/car-booking") +public class CarBookingResource { + + @Inject + private ChatAiService aiService; + + @Inject + private FraudAiService fraudService; + + @GET + @Produces(MediaType.TEXT_PLAIN) + @Path("/chat") + public String chatWithAssistant( + @QueryParam("question") String question) { + + String answer; + try { + answer = aiService.chat(question); + } catch (Exception e) { + e.printStackTrace(); + answer = "My failure reason is:\n\n" + e.getMessage(); + } + + return answer; + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/fraud") + public FraudResponse detectFraudForCustomer( + @QueryParam("name") String name, + + @QueryParam("surname") String surname) { + return fraudService.detectFraudForCustomer(name, surname); + } + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/ChatAiService.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/ChatAiService.java new file mode 100644 index 0000000..bc6892f --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/ChatAiService.java @@ -0,0 +1,42 @@ +package io.jefrajames.booking; + +import java.time.temporal.ChronoUnit; + +import org.eclipse.microprofile.faulttolerance.Fallback; +import org.eclipse.microprofile.faulttolerance.Retry; +import org.eclipse.microprofile.faulttolerance.Timeout; + +import dev.langchain4j.service.SystemMessage; +import io.smallrye.llm.spi.RegisterAIService; + +//@SuppressWarnings("CdiManagedBeanInconsistencyInspection") +@RegisterAIService(tools = BookingService.class, chatMemoryMaxMessages = 10) +public interface ChatAiService { + + @SystemMessage(""" + You are a customer support agent of a car rental company named 'Miles of Smiles'. + Before providing information about booking or canceling a booking, you MUST always check: + booking number, customer name and surname. + You should not answer to any request not related to car booking or Miles of Smiles company general information. + When a customer wants to cancel a booking, you must check his name and the Miles of Smiles cancellation policy first. + Any cancelation request must comply with cancellation policy both for the delay and the duration. + Today is {{current_date}}. + """) + @Timeout(unit = ChronoUnit.MINUTES, value = 5) + @Retry(abortOn = { BookingCannotBeCanceledException.class, + BookingAlreadyCanceledException.class, + BookingNotFoundException.class }, maxRetries = 2) + @Fallback(fallbackMethod = "chatFallback", skipOn = { + BookingCannotBeCanceledException.class, + BookingAlreadyCanceledException.class, + BookingNotFoundException.class }) + // String chat(@V("question") @UserMessage String question); + String chat(String question); + + default String chatFallback(String question) { + return String.format( + "Sorry, I am not able to answer your request %s at the moment. Please try again later.", + question); + } + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Customer.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Customer.java new file mode 100644 index 0000000..f2488f0 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/Customer.java @@ -0,0 +1,15 @@ +package io.jefrajames.booking; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(of = { "name", "surname" }) +public class Customer { + private String name; + private String surname; +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java new file mode 100644 index 0000000..58cb628 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DocRagIngestor.java @@ -0,0 +1,68 @@ +package io.jefrajames.booking; + +import static dev.langchain4j.data.document.loader.FileSystemDocumentLoader.loadDocuments; + +import java.io.File; +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Initialized; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; + +import dev.langchain4j.data.document.Document; +import dev.langchain4j.data.document.parser.TextDocumentParser; +import dev.langchain4j.data.document.splitter.DocumentSplitters; +import dev.langchain4j.model.embedding.onnx.allminilml6v2.AllMiniLmL6V2EmbeddingModel; +import dev.langchain4j.model.embedding.EmbeddingModel; +import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; +import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; +import lombok.extern.java.Log; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +@Log +@ApplicationScoped +public class DocRagIngestor { + + // Used by ContentRetriever + @Produces + private EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel(); + + // Used by ContentRetriever + @Produces + private InMemoryEmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>(); + +// private File docs = new File(System.getProperty("docragdir")); + + @Inject + @ConfigProperty(name = "app.docs-for-rag.dir") + private File docs; + + private List loadDocs() { + return loadDocuments(docs.getPath(), new TextDocumentParser()); + } + + public void ingest(@Observes @Initialized(ApplicationScoped.class) Object pointless) { + + long start = System.currentTimeMillis(); + + EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() + .documentSplitter(DocumentSplitters.recursive(300, 30)) + .embeddingModel(embeddingModel) + .embeddingStore(embeddingStore) + .build(); + + List docs = loadDocs(); + ingestor.ingest(docs); + + log.info(String.format("DEMO %d documents ingested in %d msec", docs.size(), + System.currentTimeMillis() - start)); + } + + public static void main(String[] args) { + + System.out.println(InMemoryEmbeddingStore.class.getInterfaces()[0]); + } +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DummyLLConfig.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DummyLLConfig.java new file mode 100644 index 0000000..e120c91 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/DummyLLConfig.java @@ -0,0 +1,56 @@ +package io.jefrajames.booking; + +import java.io.FileReader; +import java.io.IOException; +import java.time.Duration; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; + +import io.smallrye.llm.core.langchain4j.core.config.spi.LLMConfig; + +public class DummyLLConfig implements LLMConfig { + Properties properties = new Properties(); + + @Override + public void init() { + try (FileReader fileReader = new FileReader(System.getProperty("llmconfigfile"))) { + properties.load(fileReader); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public Set getBeanNames() { + return properties.keySet().stream().map(Object::toString) + .filter(prop -> prop.startsWith(PREFIX)) + .map(prop -> prop.substring(PREFIX.length() + 1, prop.indexOf(".", PREFIX.length() + 2))) + .collect(Collectors.toSet()); + } + + @Override + public T getBeanPropertyValue(String beanName, String propertyName, Class type) { + String value = properties.getProperty(PREFIX + "." + beanName + "." + propertyName); + if (value == null) + return null; + if (type == String.class) + return (T) value; + if (type == Duration.class) + return (T) Duration.parse(value); + try { + return type.getConstructor(String.class).newInstance(value); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public Set getPropertyNamesForBean(String beanName) { + String configPrefix = PREFIX + "." + beanName + ".config."; + return properties.keySet().stream().map(Object::toString) + .filter(prop -> prop.startsWith(configPrefix)) + .map(prop -> prop.substring(configPrefix.length())) + .collect(Collectors.toSet()); + } +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudAiService.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudAiService.java new file mode 100644 index 0000000..68488de --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudAiService.java @@ -0,0 +1,55 @@ +package io.jefrajames.booking; + +import dev.langchain4j.service.SystemMessage; +import dev.langchain4j.service.UserMessage; +import dev.langchain4j.service.V; +import io.smallrye.llm.spi.RegisterAIService; + +@SuppressWarnings("CdiManagedBeanInconsistencyInspection") +@RegisterAIService(chatMemoryMaxMessages = 5, + + chatLanguageModelName = "chat-model") +public interface FraudAiService { + + @SystemMessage(""" + You are a car booking fraud detection AI for Miles of Smiles. + You have to detect customer fraud in bookings. + """) + @UserMessage(""" + Your task is to detect whether a fraud was committed for the customer {{name}} {{surname}}. + + To detect a fraud, perform the following actions: + 1 - Retrieve all bookings for the customer with name {{name}} and surname {{surname}}. + 2 - If there are no bookings, return the fraud status as 'false'. + 3 - Otherwise, Determine if there is an overlap between several bookings. + 4 - If there is an overlap, a fraud is detected. + 5 - If a fraud is detected, return the fraud status and the bookings that overlap. + + A booking overlap (and hence a fraud) occurs when there are several bookings for a given date. + For instance: + -there is no overlap if a given customer has the following bookings: + - Booking number 345-678 with the period from 2024-03-25 to 2024-03-31. + - Booking number 234-567 with the period from 2024-03-21 to 2024-03-23. + -there is an overlap if a given customer has the following bookings: + - Booking number 456-789 with the period from 2024-03-21 to 2024-03-31. + - Booking number 567-890 with the period from 2024-03-22 to 2024-03-27. + + Answer with the following information in a valid JSON document: + - the customer-name key set to {{name}} + - the customer-surname key set to {{surname}} + - the fraud-detected key set to 'true' or 'false' + - in case of fraud, the explanation of the fraud in the fraud-explanation key + - in case of fraud, the reservation ids that overlap. + You must respond in a valid JSON format. + + You must not wrap JSON response in backticks, markdown, or in any other way, but return it as plain text. + """) + FraudResponse detectFraudForCustomer(@V("name") String name, @V("surname") String surname); + + default FraudResponse fraudFallback(String name, String surname) { + throw new RuntimeException( + "Sorry, I am not able to detect fraud for customer " + name + " " + surname + + " at the moment. Please try again later."); + } + +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudResponse.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudResponse.java new file mode 100644 index 0000000..a1663ec --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/FraudResponse.java @@ -0,0 +1,14 @@ +package io.jefrajames.booking; + +import java.util.List; + +import lombok.Data; + +// Warning: Java Record not supported by Google JSON (used by LangChain4J) +@Data +public class FraudResponse { + private String customerName; + private String customerSurname; + private boolean fraudDetected; + private List bookingIds; +} diff --git a/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/JaxRSApp.java b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/JaxRSApp.java new file mode 100644 index 0000000..f478b17 --- /dev/null +++ b/examples/liberty-car-booking/src/main/java/io/jefrajames/booking/JaxRSApp.java @@ -0,0 +1,8 @@ +package io.jefrajames.booking; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; + +@ApplicationPath("/api") +public class JaxRSApp extends Application { +} diff --git a/examples/liberty-car-booking/src/main/liberty/config/server.xml b/examples/liberty-car-booking/src/main/liberty/config/server.xml new file mode 100644 index 0000000..2653bc7 --- /dev/null +++ b/examples/liberty-car-booking/src/main/liberty/config/server.xml @@ -0,0 +1,18 @@ + + + + + microProfile-6.1 + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/liberty-car-booking/src/main/resources/META-INF/beans.xml b/examples/liberty-car-booking/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000..c2a9245 --- /dev/null +++ b/examples/liberty-car-booking/src/main/resources/META-INF/beans.xml @@ -0,0 +1,7 @@ + + + + diff --git a/examples/liberty-car-booking/src/main/resources/META-INF/microprofile-config.properties b/examples/liberty-car-booking/src/main/resources/META-INF/microprofile-config.properties new file mode 100644 index 0000000..c793531 --- /dev/null +++ b/examples/liberty-car-booking/src/main/resources/META-INF/microprofile-config.properties @@ -0,0 +1,33 @@ +# Microprofile server properties +server.port=9080 + +smallrye.llm.plugin.chat-model.class=dev.langchain4j.model.azure.AzureOpenAiChatModel +smallrye.llm.plugin.chat-model.config.api-key=${azure.openai.api.key} +smallrye.llm.plugin.chat-model.config.endpoint=${azure.openai.endpoint} +smallrye.llm.plugin.chat-model.config.service-version=2024-02-15-preview +smallrye.llm.plugin.chat-model.config.deployment-name=${azure.openai.deployment.name} +smallrye.llm.plugin.chat-model.config.temperature=0.1 +smallrye.llm.plugin.chat-model.config.topP=0.1 +smallrye.llm.plugin.chat-model.config.timeout=120s +smallrye.llm.plugin.chat-model.config.max-retries=2 +#smallrye.llm.plugin.chat-model.config.logRequestsAndResponsess=false + + +smallrye.llm.plugin.docRagRetriever.class=dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever +smallrye.llm.plugin.docRagRetriever.config.embeddingStore=lookup:default +smallrye.llm.plugin.docRagRetriever.config.embeddingModel=lookup:default +smallrye.llm.plugin.docRagRetriever.config.maxResults=3 +smallrye.llm.plugin.docRagRetriever.config.minScore=0.6 + + + +smallrye.llm.embedding.store.in-memory.file=embedding.json + +# Chat memory configuration, used by ChatAiFactory +chat.memory.max.messages=20 + +# Fraud detection configuration, used by FraudAiFactory +fraud.memory.max.messages=20 + +# Location of documents to RAG +app.docs-for-rag.dir=docs-for-rag diff --git a/examples/liberty-car-booking/src/main/webapp/WEB-INF/web.xml b/examples/liberty-car-booking/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..a3823f1 --- /dev/null +++ b/examples/liberty-car-booking/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,10 @@ + + + Liberty Project + + + index.html + + \ No newline at end of file diff --git a/examples/liberty-car-booking/src/main/webapp/chatroom.js b/examples/liberty-car-booking/src/main/webapp/chatroom.js new file mode 100644 index 0000000..e351dcd --- /dev/null +++ b/examples/liberty-car-booking/src/main/webapp/chatroom.js @@ -0,0 +1,57 @@ + var messagesTableBody = document.getElementById('messagesTableBody'); + var thinkingRow = document.createElement('tr'); + thinkingRow.setAttribute('id', 'thinking'); + thinkingRow.innerHTML = '

thinking...

' + + ''; + + function getTime() { + var now = new Date(); + var hours = now.getHours(); + hours = hours < 10 ? '0' + hours : hours; + var minutes = now.getMinutes(); + minutes = minutes < 10 ? '0' + minutes : minutes; + var seconds = now.getSeconds(); + seconds = seconds < 10 ? '0' + seconds : seconds; + var time = hours + ":" + minutes + ":" + seconds; + return time; + } + + function sendMessage() { + var myMessageRow = document.createElement('tr'); + var myMessage = document.getElementById('myMessage').value; + myMessageRow.innerHTML = '

' + myMessage + '

' + + '' + getTime() + ''; + messagesTableBody.appendChild(myMessageRow); + messagesTableBody.appendChild(thinkingRow); + webSocket.send(myMessage); + document.getElementById('myMessage').value = ""; + } + + // Getting the used url from browser + var loc = window.location, uri; + if (loc.protocol === "https:") { + uri = "wss:"; + } else { + uri = "ws:"; + } + uri += "//" + loc.host; + uri += loc.pathname + "chat"; + // buildign websocket + const webSocket = new WebSocket(uri); + + webSocket.onopen = function (event) { + console.log(event); + }; + + webSocket.onmessage = function (event) { + var data = event.data; + messagesTableBody.removeChild(thinkingRow); + var agentMessageRow = document.createElement('tr'); + agentMessageRow.innerHTML = '

' + data + '

' + + '' + getTime() + ''; + messagesTableBody.appendChild(agentMessageRow); + }; + + webSocket.onerror = function (event) { + console.log(event); + }; \ No newline at end of file diff --git a/examples/liberty-car-booking/src/main/webapp/css/styles.css b/examples/liberty-car-booking/src/main/webapp/css/styles.css new file mode 100644 index 0000000..1871f58 --- /dev/null +++ b/examples/liberty-car-booking/src/main/webapp/css/styles.css @@ -0,0 +1,544 @@ +@import url("https://fonts.googleapis.com/css?family=Asap:300,400,500"); + +body{ + font-family:Asap; + font-size: 16px; + color:#24243b; + background-color: white; + margin: 0px; +} + +section { + padding-top: 55px; + padding-left: 8%; + padding-right: 8%; + /* font-weight: 400; */ + letter-spacing:0; + text-align:left; +} + +.line { + margin-right: 200px; + height: 1px; + background-color: #C8D3D3; +} + +.headerImage { + background-image: url(/img/helidon_logo_dark.svg); + background-repeat: no-repeat; + background-position: top 20px right 15px; + height: 103px; + margin-top: -94px; +} + +#whereTo { + padding-bottom: 80px; + width: 50%; +} + +p { + line-height: 22px; + margin-top: 0px; +} +h1 { + font-family:BunueloSemiBold; + font-size: 40px; + font-weight: 400; + letter-spacing:0; + text-align:left; +} +h2 { + font-size: 24px; + font-weight: 400; +} +h4 { + margin-top: 52px; +} +a { + text-decoration: none; +} + +#appIntro { + background-image:linear-gradient(#141427 0%, #2c2e50 100%); + background-size: 100% calc(100% - 70px); + background-repeat: no-repeat; +} + +#titleSection { + color: white; + margin-bottom: 80px; +} + +#appTitle { + font-family:BunueloLight; + font-size:55px; +} + +.headerRow { + height: 100px; + position:relative; + z-index:2; + box-shadow: 0 2px 4px 0 rgba(0,0,0,0.50); +} +.headerRow > div { + display: inline-block; +} + +.collapsibleRow { + transition: border 400ms ease-out, box-shadow 200ms linear; + cursor: pointer; +} +.collapsibleRow:hover .headerTitle { + background-color: #f4f4f4; + transition: background-color 0.1s; +} +.collapsed .collapsibleRow { + box-shadow: none; + border-bottom: 4px solid; +} +.collapsed#healthSection > .headerRow { + border-bottom-color: #D6D9E4; +} +.collapsed#configSection > .headerRow { + border-bottom-color: #F8D7C1; +} +.collapsed#metricsSection > .headerRow { + border-bottom-color: #EEF3C3; +} + +.collapsed .collapsibleContent { /* collapsing animation */ + transition: all 400ms ease-out, opacity 300ms ease-in; +} +.expanded .collapsibleContent { /* expanding animation */ + transition: all 400ms ease-out, opacity 450ms ease-out; +} +.collapsed .collapsibleContent { + opacity: 0; + max-height: 0; + visibility: hidden; +} +.expanded .collapsibleContent { + opacity: 1; + max-height: 1000px; + visibility: visible; +} + +.headerIcon { + width: 160px; + height: 100%; + float: left; + background-color: #E8EAEF; +} +.headerIcon img { + display:block; + margin:auto; + margin-top: 20px; +} + +#healthSection .headerIcon { + background-color: #E8EAEF; +} +#configSection .headerIcon { + background-color: #FDE4D1; +} +#metricsSection .headerIcon { + background-color: #F5F8DA; +} + +.headerTitle { + background-color: white; + color:#5d6a8e; + letter-spacing:0; + text-align:left; + padding-left: 40px; + padding-top: 10px; + width: calc(100% - 200px); /* 160 from icon, 40 from padding */ +} +#healthSection h2 { + color: #5D6A8E; +} +#configSection h2 { + color: #E57000; +} +#metricsSection h2 { + color: #4F6700; +} + +#sysPropTitle { + padding-top: 28px; +} + +.headerTitle > h2 { + font-family: BunueloLight; + font-size:40px; + margin: 0; +} + +.caret { + position: absolute; + right: 45px; + top: 45px; +} + +.collapsed#configSection .caret { + background-image: url("../img/carets/caret_down_orange.svg") +} +.expanded#configSection .caret { + background-image: url("../img/carets/caret_up_orange.svg") +} + +.msSection { + background: white; + box-shadow: 0 2px 4px 0 rgba(63,70,89,0.31); +} + +.sectionContent { + margin-left: 160px; +} + +#messagesTable { + padding-left: 160px; + background: white; +} + +button { + border-radius:100px; + height:44px; + color:#24253a; + text-align:center; + font-family: Asap; + margin-top: 25px; + margin-bottom: 70px; + cursor: pointer; + border: none; +} + +button a { + text-decoration: none; + color:#F4914D; +} + +#guidesButton { + background-color:#abd155; + width:269px; + font-weight: 500; + font-size:16px; + transition: background-color .2s; +} +#guidesButton:hover { + background-color: #C7EE63; +} + +section#openLibertyAndMp { + background:#f4f4f5; + background-size: 100% calc(100% - 70px); + background-repeat: no-repeat; +} + +#healthBox { + text-align: left; + display: table-cell; + vertical-align: middle; + width: 47%; +} + +#healthBox > div { + display: table-cell; + vertical-align: middle; +} + +#healthIcon { + padding-left: 73px; + padding-top: 56px; + padding-bottom: 56px; +} +#healthStatusIcon { + width: 104px; + height: 104px; +} + +#healthText { + padding: 50px; +} + +#serviceStatus { + font-size: 50px; + font-family:BunueloLight; + margin-top: 30px; +} + +#healthNote { + text-align: left; + display: table-cell; + vertical-align: middle; + padding-left: 43px; + line-height: 26px; + width: 53%; +} + +table { + width: 100%; + font-size: 14px; + text-align: left; + border-collapse: collapse; +} + +th { + height: 63px; + padding-left: 41px; + font-size: 16px; +} +tr { + height: 45px; +} +td { + padding-left: 41px; +} +#messagesTable tr:first-child { + background: #D6D9E4; +} +#configTable tr:first-child { + background: #F8D7C1;; +} +#metricsTable tr:first-child { + background: #EEF3C3; +} + +#messagesTable tr:nth-child(2n+3) { + background: #EEEFF3; +} +#configTable tr:nth-child(2n+2) { + background: #FEF8F4; +} +#metricsTable tr:nth-child(2n+2) { + background: #FBFCEE; +} + +#messagesTable .sourceRow, +#healthTable .sourceRow { + border-top: 4px solid #D6D9E4; +} +#messagesTable .sourceRow a, +#healthTable .sourceRow a { + color: #5D6A8E; +} +#configTable .sourceRow { + border-top: 4px solid #F8D7C1; +} +#configTable .sourceRow a { + color: #E57000; +} +#metricsTable .sourceRow { + border-top: 4px solid #EEF3C3; +} +#metricsTable .sourceRow a { + color: #4F6700; +} +.sourceRow a { + font-weight: 500; +} + +#learnMore { + margin-top: 120px; + padding: 0px 200px 100px; +} + +#learnMore > h2 { + color:#5e6b8d; +} + +.bodyFooter { + padding: 5px 8%; + background-repeat: no-repeat; + background-position: top 20px right 110px; + margin-bottom: 40px; + margin-top: 50px; + color: #3F4659; +} + +.bodyFooterLink { + font-family: Asap; + font-weight: 300; + font-size: 14px; + letter-spacing: 0; + height: 60px; + margin-top: 30px; + margin-left: 10px; + margin-right: 10px; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 20px; + padding-right: 20px; + text-align: right; + background: #F9F9F9; +} + +.my_message_container { + font-family: Asap; + font-weight: 300; + font-size: 14px; + letter-spacing: 0; + margin-top: 30px; + margin-right: 130px; + padding-bottom: 5px; + padding-right: 50px; + text-align: right; +} + +.bodyFooterLink > a { + text-decoration: none; + padding: 10px; + color: #96bc32; +} + +#licenseLink { + color: #5E6B8D; + text-align: left; +} + +#footer_text { + margin-top: 4px; + margin-bottom: 4px; + font-size: 16px; +} + +#myMsgLabel { + margin-top: 4px; + margin-bottom: 4px; + font-size: 18px; + margin-right: 10px; +} + +.refreshSection { + padding-top: 20px; + padding-left: 160px; + color: #3A73B4; +} + +label { + margin-right: 30px; +} + +#sendButton { + background-color:#abd155; + width:80px; + height:30px; + font-weight: 500; + font-size:16px; + transition: background-color .2s; + margin-left: 15px; +} + +#sendButton:hover { + background-color: #C7EE63; +} + +.agent-msg { + border: 1px solid black; + border-radius: 20px; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; +} + +.my-msg { + border-radius: 20px; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 20px; + padding-right: 10px; + margin-left: 60%; + background-color: #F0F0F0; + text-align: left; +} + +.thinking-msg { + border: 1px solid black; + border-radius: 20px; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; + margin-right: 60%; +} + +.footer_round_btn { + display: inline-block; + width: 20px; + height: 20px; + background-repeat: no-repeat; + background-size: cover; +} + +#footer_github_link { + background-image: url("/img/Footer_GitCat.svg"); + &:hover { + background-image: url("/img/Footer_GitCat_Hover.svg"); + } +} + +#footer_twitter_link { + background-image: url("/img/Footer_TwitterBird.svg"); + &:hover { + background-image: url("/img/Footer_TwitterBird_Hover.svg"); + } +} + +#footer_groupsio_link { + background-image: url("/img/Footer_GroupsIO.svg"); + &:hover { + background-image: url("/img/Footer_GroupsIO_Hover.svg"); + } +} + +#footer_gitter_link { + background-image: url("/img/footer_gitter.svg"); + &:hover { + background-image: url("/img/footer_gitter_hover.svg"); + } +} + +#footer_project_container { + overflow: hidden; + float: left; +} + +#footer_copyright { + font-size: 13px; + padding-left: 30px; + float: left; + clear: left; +} + +.footer_ol_io { + font-size: 15px; + padding-right: 220px; + float: right; +} + +.footer_ol_io_others { + font-size: 15px; + padding-right: 20px; + float: right; +} + +#footer_project { + font-weight: 500; + font-size: 16px; + color: #6A7070; + letter-spacing: 0; + float: left; + clear: left; +} + + +#footer_open_liberty { + width: 100px; + padding: 25px 25px 10px 25px; + background-image: url(/img/small_logo_dark_gray.svg); + background-repeat: no-repeat; + transition: background-image .2s; + float: left; + clear: left; +} diff --git a/examples/liberty-car-booking/src/main/webapp/fonts/BunueloCleanPro-Light.otf b/examples/liberty-car-booking/src/main/webapp/fonts/BunueloCleanPro-Light.otf new file mode 100644 index 0000000000000000000000000000000000000000..bcb8cfb50618a1f27568ca848c32213212520c50 GIT binary patch literal 113536 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
9mr_8QIUb$j+-RIQ2r5WjY}{~45_&w9=9pbJ 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 THA598yDCa{Q \ No newline at end of file diff --git a/examples/liberty-car-booking/src/main/webapp/img/sysProps.svg b/examples/liberty-car-booking/src/main/webapp/img/sysProps.svg new file mode 100644 index 0000000..3ba129f --- /dev/null +++ b/examples/liberty-car-booking/src/main/webapp/img/sysProps.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/examples/liberty-car-booking/src/main/webapp/index.html b/examples/liberty-car-booking/src/main/webapp/index.html new file mode 100644 index 0000000..4f190be --- /dev/null +++ b/examples/liberty-car-booking/src/main/webapp/index.html @@ -0,0 +1,47 @@ + + + + + Chat Room + + + + + +
+
+

Chat Room implemented with OpenLiberty.

+
+ +
+
+
+
+ +
+
+

Conversation with an AI Agent

+
+
+
+ + + + + + + + +
Chat MessagesTime
+
+
+
+
+ + + +
+ + + diff --git a/examples/pom.xml b/examples/pom.xml index c1af84f..b6fb2da 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -17,5 +17,6 @@ helidon-car-booking-portable-ext quarkus-car-booking glassfish-car-booking + liberty-car-booking diff --git a/examples/quarkus-car-booking/README.md b/examples/quarkus-car-booking/README.md index a012fa3..797c361 100644 --- a/examples/quarkus-car-booking/README.md +++ b/examples/quarkus-car-booking/README.md @@ -32,27 +32,27 @@ During my tests, GPT 3.5 has proved to be faster but less precise en consistent Quarkus provides a deep integration with LangChain4j thanks to a specific [extension](https://docs.quarkiverse.io/quarkus-langchain4j/dev/index.html). -In particular, it provides a powerful @RegisterAiService annotation and network interractions with LLMs are managed with its own RestClient. +In particular, it provides a powerful `@RegisterAiService` annotation and network interactions with LLMs are managed with its own RestClient. This example is based on a standard usage of LangChain4j with Helidon. There is no such deep integration. -I've added 3 technical classes to manage "the glue" (more or less the equivallent of @RegisterAiService): +I've added 3 technical classes to manage "the glue" (more or less the equivalent of `@RegisterAiService`): * ModelFactory: generates an OpenAI Chat model * ChatAiServiceFactory: generates a Chat assistant -* FraudAiServiceFactrory: generates a Fraud assistant. +* FraudAiServiceFactory: generates a Fraud assistant. I've been obliged to turn FraudResponse in a POJO. It seems that Google GSON, used to deserialize OpenAI responses does not support Java Record. -In contrast with Quarkus, network interractions with LLMs are based on standard LangChain4j. For instance, the Azure SDK is used with Azure OpenAI. +In contrast with Quarkus, network interactions with LLMs are based on standard LangChain4j. For instance, the Azure SDK is used with Azure OpenAI. ## Packaging the application -To package the application in JVM mode run: _mvn package_. +To package the application in JVM mode run: `mvn package`. ## Configuration -All configuration is centralized in microprofile-config.properties and can be redefined using environment variables. +All configuration is centralized in `microprofile-config.properties` and can be redefined using environment variables. ## Running the application @@ -66,14 +66,14 @@ Note: native mode not yet tested. The application exposes a REST API documented with OpenAPI. -To interract with the application go to: http://localhost:8080/openapi/ui. +To interact with the application go to: [http://localhost:8080/openapi/ui](http://localhost:8080/openapi/ui). Typical questions you can ask in the Chat: * Hello, how can you help me? * What is your list of cars? -* What is your cancelation policy? +* What is your cancellation policy? * What is your fleet size? Be short please. * How many electric cars do you have? * My name is James Bond, please list my bookings diff --git a/pom.xml b/pom.xml index 53ccc37..88a3405 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,6 @@ https://smallrye.io - 11 3.1 2.4.0 9.7 @@ -42,17 +41,15 @@ UTF-8 UTF-8 UTF-8 - 4.0.1 + 4.1.0 4.0.0.Final - 5.1.0.Final + 5.1.3.Final 3.5.3.Final - 3.0.3 - 0.33.0 + 0.34.0 3.9.9 3.13.0 3.5.0 3.4.0 - 11 SmallRye LLM @@ -84,7 +81,6 @@ smallrye-llm-langchain4j-buildcompatible-extension smallrye-llm-langchain4j-core smallrye-llm-langchain4j-config-mpconfig - smallrye-llm-langchain4j-liberty-bundle @@ -114,6 +110,13 @@ jakarta.enterprise jakarta.enterprise.cdi-api ${jakarta.enterprise.cdi-api.version} + provided + + + org.jboss.weld + weld-core-impl + ${version.weld} + runtime org.jboss.weld @@ -137,11 +140,6 @@ langchain4j ${dev.langchain4j.version} - - org.eclipse.microprofile.config - microprofile-config-api - ${microprofile-config-api.version} - diff --git a/smallrye-llm-langchain4j-buildcompatible-extension/pom.xml b/smallrye-llm-langchain4j-buildcompatible-extension/pom.xml index bc57214..1d055e0 100644 --- a/smallrye-llm-langchain4j-buildcompatible-extension/pom.xml +++ b/smallrye-llm-langchain4j-buildcompatible-extension/pom.xml @@ -35,10 +35,10 @@ @{jacocoArgLine} - - org.apache.maven.plugins - maven-enforcer-plugin - + + org.apache.maven.plugins + maven-enforcer-plugin + @@ -51,6 +51,12 @@ jakarta.enterprise jakarta.enterprise.cdi-api + provided + + + jakarta.annotation + jakarta.annotation-api + provided org.jboss.logging diff --git a/smallrye-llm-langchain4j-config-mpconfig/pom.xml b/smallrye-llm-langchain4j-config-mpconfig/pom.xml index f943aee..84680df 100644 --- a/smallrye-llm-langchain4j-config-mpconfig/pom.xml +++ b/smallrye-llm-langchain4j-config-mpconfig/pom.xml @@ -20,6 +20,7 @@ org.eclipse.microprofile.config microprofile-config-api + provided io.smallrye.llm diff --git a/smallrye-llm-langchain4j-config-mpconfig/src/main/java/io/smallrye/llm/core/langchain4j/mpconfig/LLMConfigMPConfig.java b/smallrye-llm-langchain4j-config-mpconfig/src/main/java/io/smallrye/llm/core/langchain4j/mpconfig/LLMConfigMPConfig.java index 6063229..7c69571 100644 --- a/smallrye-llm-langchain4j-config-mpconfig/src/main/java/io/smallrye/llm/core/langchain4j/mpconfig/LLMConfigMPConfig.java +++ b/smallrye-llm-langchain4j-config-mpconfig/src/main/java/io/smallrye/llm/core/langchain4j/mpconfig/LLMConfigMPConfig.java @@ -1,5 +1,7 @@ package io.smallrye.llm.core.langchain4j.mpconfig; +import static io.smallrye.llm.core.langchain4j.core.config.spi.LLMConfig.getBeanPropertyName; + import java.util.Set; import java.util.Spliterator; import java.util.Spliterators; @@ -39,8 +41,10 @@ public Set getBeanNames() { @Override public T getBeanPropertyValue(String beanName, String propertyName, Class type) { - T value = config.getOptionalValue(PREFIX + "." + beanName + "." + propertyName, type).orElse(null); - + if (VALUE.equals(propertyName)) { + return null; + } + T value = config.getOptionalValue(getBeanPropertyName(beanName, propertyName), type).orElse(null); return value; } diff --git a/smallrye-llm-langchain4j-core/pom.xml b/smallrye-llm-langchain4j-core/pom.xml index 9f58454..014aadc 100644 --- a/smallrye-llm-langchain4j-core/pom.xml +++ b/smallrye-llm-langchain4j-core/pom.xml @@ -18,6 +18,7 @@ jakarta.enterprise jakarta.enterprise.cdi-api + provided dev.langchain4j diff --git a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/aiservice/CommonAIServiceCreator.java b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/aiservice/CommonAIServiceCreator.java index 1ecf476..35546d9 100644 --- a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/aiservice/CommonAIServiceCreator.java +++ b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/aiservice/CommonAIServiceCreator.java @@ -1,66 +1,66 @@ package io.smallrye.llm.aiservice; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.literal.NamedLiteral; import org.jboss.logging.Logger; -import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.rag.content.retriever.ContentRetriever; import dev.langchain4j.service.AiServices; +import io.smallrye.llm.core.langchain4j.core.config.spi.ChatMemoryFactoryProvider; import io.smallrye.llm.spi.RegisterAIService; public class CommonAIServiceCreator { + private static final Logger LOGGER = Logger.getLogger(CommonAIServiceCreator.class); + @SuppressWarnings("unchecked") public static Object create(Instance lookup, Class interfaceClass) { RegisterAIService annotation = interfaceClass.getAnnotation(RegisterAIService.class); - ChatLanguageModel chatLanguageModel = getChatLanguageModel(lookup, annotation); - ContentRetriever contentRetriever = getContentRetriever(lookup, annotation); + Instance chatLanguageModel = getInstance(lookup, ChatLanguageModel.class, + annotation.chatLanguageModelName()); + Instance contentRetriever = getInstance(lookup, ContentRetriever.class, + annotation.contentRetrieverName()); try { - AiServices aiServices = AiServices.builder(interfaceClass) - .chatLanguageModel(chatLanguageModel); - - //This causes HuggingFace to throw an exception beceause tools return an empty collection. + AiServices aiServices = AiServices.builder(interfaceClass); + if (chatLanguageModel.isResolvable()) { + LOGGER.info("ChatLanguageModel " + chatLanguageModel.get()); + aiServices.chatLanguageModel(chatLanguageModel.get()); + } + if (contentRetriever.isResolvable()) { + LOGGER.info("ContentRetriever " + contentRetriever.get()); + aiServices.contentRetriever(contentRetriever.get()); + } if (annotation.tools() != null && annotation.tools().length > 0) { - aiServices.tools(Stream.of(annotation.tools()) - .map(c -> lookup.select(c).get()) - .collect(Collectors.toList())); + List tools = new ArrayList<>(annotation.tools().length); + for (Class toolClass : annotation.tools()) { + try { + tools.add(toolClass.getConstructor(null).newInstance(null)); + } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException ex) { + } + } + aiServices.tools(tools); } - - aiServices.chatMemory(MessageWindowChatMemory.withMaxMessages(annotation.chatMemoryMaxMessages())); - if (contentRetriever != null) - aiServices.contentRetriever(contentRetriever); - + aiServices.chatMemory( + ChatMemoryFactoryProvider.getChatMemoryFactory().getChatMemory(lookup, annotation.chatMemoryMaxMessages())); return aiServices.build(); } catch (Exception e) { throw new RuntimeException(e); } } - private static ChatLanguageModel getChatLanguageModel(Instance lookup, RegisterAIService annotation) { - LOGGER.info("getChatLanguageModel '" + annotation.chatLanguageModelName() + "'"); - if (annotation.chatLanguageModelName().isBlank()) - return lookup.select(ChatLanguageModel.class).get(); - return lookup.select(ChatLanguageModel.class, NamedLiteral.of(annotation.chatLanguageModelName())).get(); - } - - private static ContentRetriever getContentRetriever(Instance lookup, RegisterAIService annotation) { - if (annotation.contentRetrieverModelName().isBlank()) { - Instance contentRetrievers = lookup.select(ContentRetriever.class); - - if (contentRetrievers.isResolvable()) - return contentRetrievers.get(); + private static Instance getInstance(Instance lookup, Class type, String name) { + LOGGER.info("Getinstance of '" + type + "' with name '" + name + "'"); + if (name == null || name.isBlank()) { + return lookup.select(type); } - - Instance contentRetrievers = lookup.select(ContentRetriever.class, - NamedLiteral.of(annotation.contentRetrieverModelName())); - if (contentRetrievers.isResolvable()) - return contentRetrievers.get(); - return null; + return lookup.select(type, NamedLiteral.of(name)); } + } diff --git a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/ChatMemoryFactory.java b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/ChatMemoryFactory.java new file mode 100644 index 0000000..3441ce1 --- /dev/null +++ b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/ChatMemoryFactory.java @@ -0,0 +1,10 @@ +package io.smallrye.llm.core.langchain4j.core.config.spi; + +import jakarta.enterprise.inject.Instance; + +import dev.langchain4j.memory.ChatMemory; + +public interface ChatMemoryFactory { + + ChatMemory getChatMemory(Instance lookup, int size) throws Exception; +} diff --git a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/ChatMemoryFactoryProvider.java b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/ChatMemoryFactoryProvider.java new file mode 100644 index 0000000..a9d1af1 --- /dev/null +++ b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/ChatMemoryFactoryProvider.java @@ -0,0 +1,29 @@ +package io.smallrye.llm.core.langchain4j.core.config.spi; + +import java.util.Optional; +import java.util.ServiceLoader; + +import jakarta.enterprise.inject.Instance; + +import dev.langchain4j.memory.chat.MessageWindowChatMemory; + +public class ChatMemoryFactoryProvider { + private static final ChatMemoryFactory factory; + static { + ServiceLoader loader = ServiceLoader.load(ChatMemoryFactory.class, + ChatMemoryFactoryProvider.class.getClassLoader()); + Optional instance = loader.findFirst(); + if (instance.isEmpty()) { + factory = (Instance lookup, int size) -> MessageWindowChatMemory.withMaxMessages(size); + } else { + factory = instance.get(); + } + } + + /** + * @return the ChatMemoryFactory. + */ + public static ChatMemoryFactory getChatMemoryFactory() { + return factory; + } +} diff --git a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfig.java b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfig.java index c91509d..4a90597 100644 --- a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfig.java +++ b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfig.java @@ -15,6 +15,7 @@ public interface LLMConfig { String PREFIX = "smallrye.llm.plugin"; + String VALUE = "defined_bean_value"; void init(); @@ -29,6 +30,10 @@ public interface LLMConfig { Set getPropertyNamesForBean(String beanName); + static String getBeanPropertyName(String beanName, String propertyName) { + return PREFIX + "." + beanName + "." + propertyName; + } + static String dashToCamel(String property) { String fixed; fixed = Arrays.stream(property.split("-")) diff --git a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfigProvider.java b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfigProvider.java index afb9408..e825565 100644 --- a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfigProvider.java +++ b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/core/langchain4j/core/config/spi/LLMConfigProvider.java @@ -5,7 +5,6 @@ import java.util.List; import java.util.ServiceLoader; -@SuppressWarnings("unchecked") public class LLMConfigProvider { private static LLMConfig llmConfig; @@ -19,7 +18,7 @@ public class LLMConfigProvider { if (factories.isEmpty()) { throw new RuntimeException("No service Found for LLMConfig interface"); } else { - llmConfig = loader.findFirst().orElse(null); + llmConfig = factories.iterator().next(); //loader.findFirst().orElse(null); } } @@ -31,5 +30,4 @@ public static LLMConfig getLlmConfig() { return llmConfig; } - } diff --git a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/plugin/CommonLLMPluginCreator.java b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/plugin/CommonLLMPluginCreator.java index b24b740..6d5525b 100644 --- a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/plugin/CommonLLMPluginCreator.java +++ b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/plugin/CommonLLMPluginCreator.java @@ -1,5 +1,7 @@ package io.smallrye.llm.plugin; +import static io.smallrye.llm.core.langchain4j.core.config.spi.LLMConfig.VALUE; + import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -7,11 +9,13 @@ import java.util.List; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.literal.NamedLiteral; +import jakarta.enterprise.inject.spi.CDI; import org.jboss.logging.Logger; @@ -27,6 +31,7 @@ smallrye.llm.plugin.content-retriever.config.embedding-model=lookup:my-model */ public class CommonLLMPluginCreator { + public static final Logger LOGGER = Logger.getLogger(CommonLLMPluginCreator.class); @SuppressWarnings("unchecked") @@ -37,35 +42,50 @@ public static void createAllLLMBeans(LLMConfig llmConfig, Consumer bea for (String beanName : beanNameToCreate) { String className = llmConfig.getBeanPropertyValue(beanName, "class", String.class); String scopeClassName = llmConfig.getBeanPropertyValue(beanName, "scope", String.class); - if (scopeClassName == null || scopeClassName.isBlank()) + if (scopeClassName == null || scopeClassName.isBlank()) { scopeClassName = ApplicationScoped.class.getName(); + } Class scopeClass = (Class) loadClass(scopeClassName); Class targetClass = loadClass(className); - - // test if there is an inneer static class Builder - Class builderCLass = Arrays.stream(targetClass.getDeclaredClasses()) - .filter(declClass -> declClass.getName().endsWith("Builder")).findFirst().orElse(null); - LOGGER.info("Builder class : " + builderCLass); - if (builderCLass == null) { - LOGGER.warn("No builder class found, cancel " + beanName); - return; + Object bean = llmConfig.getBeanPropertyValue(beanName, VALUE, targetClass); + if (bean != null) { + beanBuilder.accept( + new BeanData(targetClass, null, scopeClass, beanName, (Instance creationalContext) -> bean)); + } else { + // test if there is an inner static class Builder + Class builderCLass = Arrays.stream(targetClass.getDeclaredClasses()) + .filter(declClass -> declClass.getName().endsWith("Builder")).findFirst().orElse(null); + LOGGER.info("Builder class : " + builderCLass); + if (builderCLass == null) { + LOGGER.warn("No builder class found, cancel " + beanName); + return; + } + beanBuilder.accept( + new BeanData(targetClass, builderCLass, scopeClass, beanName, + (Instance creationalContext) -> CommonLLMPluginCreator.create( + creationalContext, + beanName, + targetClass, + builderCLass))); } - beanBuilder.accept( - new BeanData(targetClass, builderCLass, scopeClass, beanName)); } } public static class BeanData { + private final Class targetClass; private final Class builderClass; private final Class scopeClass; private final String beanName; + private final Function, Object> callback; - public BeanData(Class targetClass, Class builderClass, Class scopeClass, String beanName) { + public BeanData(Class targetClass, Class builderClass, Class scopeClass, + String beanName, Function, Object> callback) { this.targetClass = targetClass; this.builderClass = builderClass; this.scopeClass = scopeClass; this.beanName = beanName; + this.callback = callback; } public Class getTargetClass() { @@ -83,6 +103,10 @@ public Class getScopeClass() { public String getBeanName() { return beanName; } + + public Function, Object> getCallback() { + return callback; + } } public static Object create(Instance lookup, String beanName, Class targetClass, Class builderClass) { @@ -112,6 +136,9 @@ public static Object create(Instance lookup, String beanName, Class t Instance inst; if ("default".equals(lookupableBean)) { inst = lookup.select(parameterType); + if (!inst.isResolvable()) { + inst = CDI.current().select(parameterType); + } } else { inst = lookup.select(parameterType, NamedLiteral.of(lookupableBean)); } diff --git a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/spi/RegisterAIService.java b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/spi/RegisterAIService.java index be6fae9..b48605f 100644 --- a/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/spi/RegisterAIService.java +++ b/smallrye-llm-langchain4j-core/src/main/java/io/smallrye/llm/spi/RegisterAIService.java @@ -24,4 +24,10 @@ String contentRetrieverModelName() default ""; int chatMemoryMaxMessages() default 10; + + String embeddingModelName() default ""; + + String embeddingStoreName() default ""; + + String contentRetrieverName() default ""; } diff --git a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml deleted file mode 100644 index e0eda69..0000000 --- a/smallrye-llm-langchain4j-liberty-bundle/BOM/pom.xml +++ /dev/null @@ -1,26 +0,0 @@ - - 4.0.0 - - io.smallrye.llm.liberty-bundle - smallrye-llm-langchain4j-liberty-bundle - 1.0.0-SNAPSHOT - ../pom.xml - - ${bom.artifact.id} - pom - https://openliberty.io/ - - - - - io.smallrye.llm.liberty-bundle - ${esa.artifact.id} - ${project.version} - runtime - esa - - - - \ No newline at end of file diff --git a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml deleted file mode 100644 index c2b7e40..0000000 --- a/smallrye-llm-langchain4j-liberty-bundle/ESA/pom.xml +++ /dev/null @@ -1,70 +0,0 @@ - - 4.0.0 - - io.smallrye.llm.liberty-bundle - smallrye-llm-langchain4j-liberty-bundle - 1.0.0-SNAPSHOT - ../pom.xml - - ${esa.artifact.id} - esa - - - - io.smallrye.llm.liberty-bundle - ${integration.artifact.id} - ${project.version} - - - - io.openliberty.features - microProfile-6.1 - 24.0.0.8 - esa - provided - - - - - - - - org.apache.aries - esa-maven-plugin - 1.0.2 - true - - true - all - - IBM - 2 - ${liberty.feature.name} - - - ${integration.artifact.id};visibility:=public - - osgi.subsystem.feature - 1.0.0 - - smallrye-llm-langchain4j-core;version="${project.version}" - - jakarta.enterprise; type="spec",io.smallrye.llm.spi; type="third-party" - - - - - - - - - org.apache.aries - esa-maven-plugin - - - - \ No newline at end of file diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml deleted file mode 100644 index d41e6ed..0000000 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/pom.xml +++ /dev/null @@ -1,107 +0,0 @@ - - - 4.0.0 - - io.smallrye.llm.liberty-bundle - smallrye-llm-langchain4j-liberty-bundle - 1.0.0-SNAPSHOT - ../pom.xml - - ${integration.artifact.id} - bundle - - - org.osgi - osgi.core - 8.0.0 - provided - - - org.osgi - org.osgi.service.component.annotations - 1.5.1 - provided - - - org.osgi - org.osgi.service.cm - 1.6.1 - provided - - - - io.openliberty.spi - io.openliberty.cdi.spi - 1.1.92 - provided - - - org.eclipse.microprofile - microprofile - ${microprofile-api.version} - pom - provided - - - io.smallrye.llm - smallrye-llm-langchain4j-core - ${project.version} - - - io.smallrye.llm - smallrye-llm-langchain4j-portable-extension - ${project.version} - - - - - - - org.apache.felix - maven-bundle-plugin - 5.1.9 - true - - - ${project.artifactId} - ${project.artifactId}; singleton:=true - 1.0.0 - SmallRye LLM LangChain4J Liberty Bundle - *;scope=compile|runtime - true - - ${new.integration.code.private.package};version="1.0.0" - - - - - - - - org.apache.felix - org.apache.felix.dependencymanager.annotation - 5.0.2 - - - - - - org.apache.felix - maven-bundle-plugin - - - org.apache.felix - org.apache.felix.dependencymanager.annotation - - - - diff --git a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java b/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java deleted file mode 100644 index 6a4d72a..0000000 --- a/smallrye-llm-langchain4j-liberty-bundle/integration/src/main/java/io/smallrye/llm/core/langchain4j/spi/liberty/cdi/internal/Langchain4JAIServiceCDIExtensionMetadata.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.smallrye.llm.core.langchain4j.spi.liberty.cdi.internal; - -import java.lang.annotation.Annotation; -import java.util.Set; - -import jakarta.enterprise.inject.spi.Extension; - -import org.osgi.service.component.annotations.Component; -import org.osgi.service.component.annotations.ConfigurationPolicy; - -import io.openliberty.cdi.spi.CDIExtensionMetadata; -import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JAIServicePortableExtension; -import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JPluginsPortableExtension; -import io.smallrye.llm.spi.RegisterAIService; - -/** - * @author Buhake Sindi - * @since 26 August 2024 - */ -@Component(service = CDIExtensionMetadata.class, configurationPolicy = ConfigurationPolicy.IGNORE, immediate = true) -public class Langchain4JAIServiceCDIExtensionMetadata implements CDIExtensionMetadata { - - /* - * (non-Javadoc) - * - * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getBeanDefiningAnnotationClasses() - */ - @Override - public Set> getBeanDefiningAnnotationClasses() { - // TODO Auto-generated method stub - return Set.of(RegisterAIService.class); - } - - /* - * (non-Javadoc) - * - * @see io.openliberty.cdi.spi.CDIExtensionMetadata#getExtensions() - */ - @Override - public Set> getExtensions() { - // TODO Auto-generated method stub - return Set.of(LangChain4JAIServicePortableExtension.class, LangChain4JPluginsPortableExtension.class); - } -} diff --git a/smallrye-llm-langchain4j-liberty-bundle/pom.xml b/smallrye-llm-langchain4j-liberty-bundle/pom.xml deleted file mode 100644 index 11b58df..0000000 --- a/smallrye-llm-langchain4j-liberty-bundle/pom.xml +++ /dev/null @@ -1,29 +0,0 @@ - - 4.0.0 - - io.smallrye.llm - smallrye-llm-parent - 1.0.0-SNAPSHOT - ../pom.xml - - io.smallrye.llm.liberty-bundle - smallrye-llm-langchain4j-liberty-bundle - pom - - - 6.1 - mpLLM-1.0 - smallrye-llm-langchain4j-features-bom - smallrye-llm-langchain4j-feature - smallrye-llm-langchain4j-bundle - io.smallrye.llm.core.langchain4j.spi.liberty.cdi.internal - - - - BOM - ESA - integration - - \ No newline at end of file diff --git a/smallrye-llm-langchain4j-portable-extension/effective-pom.xml b/smallrye-llm-langchain4j-portable-extension/effective-pom.xml new file mode 100644 index 0000000..d27aa22 --- /dev/null +++ b/smallrye-llm-langchain4j-portable-extension/effective-pom.xml @@ -0,0 +1,1156 @@ + + + + + + + + + + + + + + + 4.0.0 + + io.smallrye.llm + smallrye-llm-parent + 1.0.0-SNAPSHOT + + io.smallrye.llm + smallrye-llm-langchain4j-portable-extension + 1.0.0-SNAPSHOT + SmallRye Parent POM + http://maven.apache.org + 2018 + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + + + + + radcortez + Roberto Cortez + radcortez@redhat.com + Red Hat + https://www.redhat.com/en + + + + scm:git:git@github.com:smallrye/smallrye-llm.git + scm:git:git@github.com:smallrye/smallrye-llm.git + https://github.com/smallrye/smallrye-llm/ + + + GitHub + https://github.com/smallrye/smallrye-config/issues + + + + oss.sonatype + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + + + oss.sonatype + https://s01.oss.sonatype.org/content/repositories/snapshots/ + + + + 0.34.0 + false + 4.0.1 + 3.13.0 + 3.5.0 + 3.4.0 + 11 + 11 + 11 + 11 + 11 + 3.9.9 + 3.0.3 + UTF-8 + UTF-8 + https://sonarcloud.io + smallrye + SmallRye LLM + 9.7 + 3.25.3 + 4.2.1 + 3.2.0 + 3.3.2 + 3.13.0 + 3.1.1 + 3.1 + 3.2.5 + 2.22.0 + 3.2.2 + 1.9.0 + 3.1.1 + 0.8.12 + 2.1.1 + 10.0.0 + 4.0.1 + 3.0.0 + 2.1.1 + 3.1.0 + 3.3.0 + 3.6.3 + 3.Final + 5.10.2 + 1.6.13 + 3.0.5 + 3.5.3.Final + 2.2.1.Final + 3.0.1 + 3.3.1 + 4.5.1 + 3.12.1 + 2 + 2.4.0 + 1.0.0 + 2.3.0 + 3.3.0 + 3.2.5 + 3.2.5 + 1.19.7 + 2.16.2 + 5.1.3.Final + 4.0.0.Final + + + + + org.eclipse.microprofile.config + microprofile-config-api + 3.0.3 + + + jakarta.enterprise + jakarta.enterprise.cdi-api + 4.0.1 + + + org.jboss.weld + weld-junit5 + 4.0.0.Final + test + + + org.jboss.weld.se + weld-se-core + 5.1.3.Final + runtime + + + org.jboss.logging + jboss-logging + 3.5.3.Final + + + dev.langchain4j + langchain4j + 0.34.0 + + + jakarta.platform + jakarta.jakartaee-core-api + 10.0.0 + + + jakarta.ws.rs + jakarta.ws.rs-api + 3.1.0 + + + jakarta.annotation + jakarta.annotation-api + 2.1.1 + + + jakarta.json.bind + jakarta.json.bind-api + 3.0.0 + + + jakarta.json + jakarta.json-api + 2.1.1 + + + org.jboss.logging + jboss-logging-annotations + 2.2.1.Final + provided + + + org.jboss.logging + jboss-logging-processor + 2.2.1.Final + provided + + + org.jboss + jandex + 3.0.5 + + + org.junit.jupiter + junit-jupiter + 5.10.2 + test + + + org.junit.vintage + junit-vintage-engine + 5.10.2 + test + + + org.assertj + assertj-core + 3.25.3 + test + + + org.awaitility + awaitility + 4.2.1 + test + + + io.rest-assured + rest-assured + 4.5.1 + test + + + org.testcontainers + testcontainers + 1.19.7 + test + + + io.smallrye.common + smallrye-common-annotation + 2.4.0 + + + io.smallrye.common + smallrye-common-classloader + 2.4.0 + + + io.smallrye.common + smallrye-common-constraint + 2.4.0 + + + io.smallrye.common + smallrye-common-cpu + 2.4.0 + + + io.smallrye.common + smallrye-common-function + 2.4.0 + + + io.smallrye.common + smallrye-common-expression + 2.4.0 + + + io.smallrye.common + smallrye-common-io + 2.4.0 + + + io.smallrye.common + smallrye-common-net + 2.4.0 + + + io.smallrye.common + smallrye-common-os + 2.4.0 + + + io.smallrye.common + smallrye-common-ref + 2.4.0 + + + io.smallrye.common + smallrye-common-version + 2.4.0 + + + io.smallrye.common + smallrye-common-vertx-context + 2.4.0 + + + io.smallrye.testing + smallrye-testing-utilities + 2.3.0 + test + + + org.testng + testng + 7.6.0 + test + + + org.jboss.weld + weld-spi + 5.0.SP3 + + + org.jboss.weld + weld-api + 5.0.SP3 + + + org.jboss.weld + weld-core-impl + 5.1.0.Final + + + org.jboss.logmanager + jboss-logmanager + 2.1.19.Final + test + + + org.junit.jupiter + junit-jupiter-api + 5.9.2 + + + org.junit.jupiter + junit-jupiter-engine + 5.9.2 + + + org.junit.jupiter + junit-jupiter-migrationsupport + 5.9.2 + + + org.junit.jupiter + junit-jupiter-params + 5.9.2 + + + org.junit.platform + junit-platform-commons + 1.9.2 + + + org.junit.platform + junit-platform-console + 1.9.2 + + + org.junit.platform + junit-platform-engine + 1.9.2 + + + org.junit.platform + junit-platform-jfr + 1.9.2 + + + org.junit.platform + junit-platform-launcher + 1.9.2 + + + org.junit.platform + junit-platform-reporting + 1.9.2 + + + org.junit.platform + junit-platform-runner + 1.9.2 + + + org.junit.platform + junit-platform-suite + 1.9.2 + + + org.junit.platform + junit-platform-suite-api + 1.9.2 + + + org.junit.platform + junit-platform-suite-commons + 1.9.2 + + + org.junit.platform + junit-platform-suite-engine + 1.9.2 + + + org.junit.platform + junit-platform-testkit + 1.9.2 + + + + + + io.smallrye.llm + smallrye-llm-langchain4j-core + 1.0.0-SNAPSHOT + compile + + + dev.langchain4j + langchain4j + 0.34.0 + compile + + + jakarta.enterprise + jakarta.enterprise.cdi-api + 4.0.1 + provided + + + org.jboss.logging + jboss-logging + 3.5.3.Final + compile + + + io.smallrye.llm + smallrye-llm-langchain4j-config-mpconfig + 1.0.0-SNAPSHOT + test + + + io.smallrye.config + smallrye-config + 3.8.1 + test + + + org.jboss.weld + weld-junit5 + 4.0.0.Final + test + + + + + + true + + + false + + amq-cr-repo + AMQ-7.12.1-CR1 Brew maven Repository + https://download.hosts.prod.upshift.rdu2.redhat.com/devel/candidates/amq/amq-broker-7.12.1.CR1/maven-repository/maven-repository/ + + + + true + + + false + + redhat-qa + Red Hat QA Maven Repository + https://repository.engineering.redhat.com/nexus/content/repositories/jboss-qa/ + + + + true + + + true + + redhat-qa-snapshots + Red Hat QA Maven Repository + https://repository.engineering.redhat.com/nexus/content/repositories/jboss-qa-snapshots/ + + + + true + + redhat-maven-repo-ga + Red Hat Maven Repository + https://maven.repository.redhat.com/ga/all + + + + true + + + true + + eap8-internal-repository + https://download.eng.bos.redhat.com/brewroot/repos/jb-eap-8.0-maven-build/latest/maven/ + + + + true + + + true + + eap74-internal-repository + https://download.eng.bos.redhat.com/brewroot/repos/jb-eap-7.4-maven-build/latest/maven/ + + + + true + + + false + + jboss-community-repository + https://repository.jboss.org/nexus/content/groups/public/ + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + + + true + + + false + + redhat-qa + Red Hat QA Maven Repository + https://repository.engineering.redhat.com/nexus/content/repositories/jboss-qa/ + + + + true + + + true + + redhat-qa-snapshots + Red Hat QA Maven Repository + https://repository.engineering.redhat.com/nexus/content/repositories/jboss-qa-snapshots/ + + + + true + + + true + + eap8-internal-repository + https://download.eng.bos.redhat.com/brewroot/repos/jb-eap-8.0-maven-build/latest/maven/ + + + + true + + + true + + eap74-internal-repository + https://download.eng.bos.redhat.com/brewroot/repos/jb-eap-7.4-maven-build/latest/maven/ + + + + true + + + false + + jboss-community-repository + https://repository.jboss.org/nexus/content/groups/public/ + + + + false + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/src/main/java + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/src/main/scripts + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/src/test/java + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/target/classes + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/target/test-classes + + + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/src/main/resources + + + + + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/src/test/resources + + + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/target + smallrye-llm-langchain4j-portable-extension-1.0.0-SNAPSHOT + + + + maven-antrun-plugin + 3.1.0 + + + maven-assembly-plugin + 3.7.1 + + + maven-dependency-plugin + 3.7.0 + + + maven-release-plugin + 3.0.1 + + + maven-clean-plugin + 3.3.2 + + + maven-compiler-plugin + 3.13.0 + + 11 + 11 + + + + maven-resources-plugin + 3.3.1 + + + maven-gpg-plugin + 3.2.2 + + + --pinentry-mode + loopback + + + + + maven-install-plugin + 3.1.1 + + + maven-jar-plugin + 3.3.0 + + + true + + true + true + + + http://maven.apache.org + 17.0.12 + https://github.com/smallrye/smallrye-llm/ + scm:git:git@github.com:smallrye/smallrye-llm.git + ${buildNumber} + + + + + + maven-javadoc-plugin + 3.6.3 + + + + apiNote + a + API Note: + + + implSpec + a + Implementation Requirements: + + + implNote + a + Implementation Note: + + + param + + + return + + + throws + + + since + + + version + + + serialData + + + see + + + + + + maven-source-plugin + 3.3.0 + + + maven-site-plugin + 3.12.1 + + + maven-surefire-plugin + 3.4.0 + + + maven-surefire-report-plugin + 3.2.5 + + + maven-failsafe-plugin + 3.2.5 + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.13 + + 15 + + + + maven-deploy-plugin + 3.1.1 + + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + prepare-agent + generate-test-resources + + prepare-agent + + + jacocoArgLine + true + + META-INF/** + + + + + + jacocoArgLine + true + + META-INF/** + + + + + io.smallrye + smallrye-maven-plugin + 1.0.0 + + + maven-enforcer-plugin + 3.5.0 + + + enforce-maven + + enforce + + + + + [3.9.9,) + Check for Maven version >=3.9.9 failed. + Update your Maven install. + + + + + + + + + + + maven-compiler-plugin + 3.13.0 + + + default-compile + compile + + compile + + + + -Aorg.jboss.logging.tools.addGeneratedAnnotation=false + + 11 + 11 + + + + default-testCompile + test-compile + + testCompile + + + 11 + 11 + + + + + 11 + 11 + + + + maven-release-plugin + 3.0.1 + + true + @{project.version} + verify + false + true + false + -DskipTests ${release.arguments} + + + + maven-source-plugin + 3.3.0 + + + attach-sources + + jar-no-fork + + + + + + net.revelc.code.formatter + formatter-maven-plugin + 2.22.0 + + + format-sources + process-sources + + format + + + io/smallrye/coderules/eclipse-format.xml + false + + + + + + io.smallrye + smallrye-code-rules + 2 + compile + + + + io/smallrye/coderules/eclipse-format.xml + false + + + + net.revelc.code + impsort-maven-plugin + 1.9.0 + + + sort-imports + + sort + + + java.,javax.,jakarta.,org.,com. + * + false + true + + + + + java.,javax.,jakarta.,org.,com. + * + false + true + + + + org.codehaus.mojo + buildnumber-maven-plugin + 3.2.0 + + + get-scm-revision + initialize + + create + + + false + false + UNKNOWN + true + + + + + + org.codehaus.mojo + versions-maven-plugin + 2.16.2 + + false + + + + maven-surefire-plugin + 3.4.0 + + + default-test + test + + test + + + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/target/classes/META-INF/versions/17 + + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/target/classes + + + + + + + maven-enforcer-plugin + 3.5.0 + + + enforce-maven + + enforce + + + + + [3.9.9,) + Check for Maven version >=3.9.9 failed. + Update your Maven install. + + + + + + + + maven-clean-plugin + 3.3.2 + + + default-clean + clean + + clean + + + + + + maven-resources-plugin + 3.3.1 + + + default-testResources + process-test-resources + + testResources + + + + default-resources + process-resources + + resources + + + + + + maven-jar-plugin + 3.3.0 + + + default-jar + package + + jar + + + + true + + true + true + + + http://maven.apache.org + 17.0.12 + https://github.com/smallrye/smallrye-llm/ + scm:git:git@github.com:smallrye/smallrye-llm.git + ${buildNumber} + + + + + + + + true + + true + true + + + http://maven.apache.org + 17.0.12 + https://github.com/smallrye/smallrye-llm/ + scm:git:git@github.com:smallrye/smallrye-llm.git + ${buildNumber} + + + + + + maven-install-plugin + 3.1.1 + + + default-install + install + + install + + + + + + maven-deploy-plugin + 3.1.1 + + + default-deploy + deploy + + deploy + + + + + + maven-site-plugin + 3.12.1 + + + default-site + site + + site + + + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/target/site + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + + + default-deploy + site-deploy + + deploy + + + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/target/site + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + + + + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/target/site + + + org.apache.maven.plugins + maven-project-info-reports-plugin + + + + + + + + /home/ehugonne/dev/AI/smallrye-llm/smallrye-llm-langchain4j-portable-extension/target/site + + diff --git a/smallrye-llm-langchain4j-portable-extension/pom.xml b/smallrye-llm-langchain4j-portable-extension/pom.xml index 3da1ede..4690195 100644 --- a/smallrye-llm-langchain4j-portable-extension/pom.xml +++ b/smallrye-llm-langchain4j-portable-extension/pom.xml @@ -50,6 +50,10 @@ weld-junit5 test - + + org.jboss.weld + weld-core-impl + provided + diff --git a/smallrye-llm-langchain4j-portable-extension/src/main/java/io/smallrye/llm/core/langchain4j/portableextension/LangChain4JPluginsPortableExtension.java b/smallrye-llm-langchain4j-portable-extension/src/main/java/io/smallrye/llm/core/langchain4j/portableextension/LangChain4JPluginsPortableExtension.java index d128e26..6b37305 100644 --- a/smallrye-llm-langchain4j-portable-extension/src/main/java/io/smallrye/llm/core/langchain4j/portableextension/LangChain4JPluginsPortableExtension.java +++ b/smallrye-llm-langchain4j-portable-extension/src/main/java/io/smallrye/llm/core/langchain4j/portableextension/LangChain4JPluginsPortableExtension.java @@ -36,11 +36,7 @@ void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanMan .scope(beanData.getScopeClass()) .name(beanData.getBeanName()) .qualifiers(NamedLiteral.of(beanData.getBeanName())) - .produceWith(creationalContext -> CommonLLMPluginCreator.create( - creationalContext, - beanData.getBeanName(), - beanData.getTargetClass(), - beanData.getBuilderClass())); + .produceWith(beanData.getCallback()); LOGGER.info("Types: " + beanData.getTargetClass() + "," + Arrays.stream(beanData.getTargetClass().getInterfaces()).map(Class::getName) diff --git a/smallrye-llm-langchain4j-portable-extension/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension b/smallrye-llm-langchain4j-portable-extension/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension index baac51a..4f3c31b 100644 --- a/smallrye-llm-langchain4j-portable-extension/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension +++ b/smallrye-llm-langchain4j-portable-extension/src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension @@ -1,2 +1,2 @@ -io.smallrye.llm.core.langchain4j.portableextension.LangChain4JAIServicePortableExtension io.smallrye.llm.core.langchain4j.portableextension.LangChain4JPluginsPortableExtension +io.smallrye.llm.core.langchain4j.portableextension.LangChain4JAIServicePortableExtension diff --git a/smallrye-llm-langchain4j-portable-extension/src/test/java/io/smallrye/llm/core/PortableExtensionInstanceTest.java b/smallrye-llm-langchain4j-portable-extension/src/test/java/io/smallrye/llm/core/PortableExtensionInstanceTest.java index 782fca3..189d8ef 100644 --- a/smallrye-llm-langchain4j-portable-extension/src/test/java/io/smallrye/llm/core/PortableExtensionInstanceTest.java +++ b/smallrye-llm-langchain4j-portable-extension/src/test/java/io/smallrye/llm/core/PortableExtensionInstanceTest.java @@ -1,15 +1,16 @@ package io.smallrye.llm.core; -import dev.langchain4j.model.chat.ChatLanguageModel; -import io.smallrye.config.inject.ConfigExtension; -import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JAIServicePortableExtension; -import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JPluginsPortableExtension; +import java.lang.annotation.Annotation; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.concurrent.Callable; + import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.RequestScoped; import jakarta.enterprise.context.control.ActivateRequestContext; import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.spi.BeanManager; import jakarta.inject.Inject; + import org.jboss.weld.junit5.WeldInitiator; import org.jboss.weld.junit5.WeldJunit5Extension; import org.jboss.weld.junit5.WeldSetup; @@ -17,9 +18,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import java.lang.annotation.Annotation; -import java.lang.reflect.UndeclaredThrowableException; -import java.util.concurrent.Callable; +import dev.langchain4j.model.chat.ChatLanguageModel; +import io.smallrye.config.inject.ConfigExtension; +import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JAIServicePortableExtension; +import io.smallrye.llm.core.langchain4j.portableextension.LangChain4JPluginsPortableExtension; @ExtendWith(WeldJunit5Extension.class) public class PortableExtensionInstanceTest { @@ -73,8 +75,8 @@ void detectAIServiceInterface() { @Test void ensureInjectAndScope() { - MyDummyAIService myDummyAIService=myDummyAIServiceInstance.get(); - MyDummyApplicationScopedAIService myDummyApplicationScopedAIService=myDummyApplicationScopedAIServiceInstance.get(); + MyDummyAIService myDummyAIService = myDummyAIServiceInstance.get(); + MyDummyApplicationScopedAIService myDummyApplicationScopedAIService = myDummyApplicationScopedAIServiceInstance.get(); Assertions.assertNotNull(myDummyAIService); Assertions.assertNotNull(myDummyApplicationScopedAIService); assertBeanScope(MyDummyAIService.class, RequestScoped.class); @@ -83,7 +85,7 @@ void ensureInjectAndScope() { @Test void callEffectiveCreation() { - MyDummyAIService myDummyAIService=myDummyAIServiceInstance.get(); + MyDummyAIService myDummyAIService = myDummyAIServiceInstance.get(); Assertions.assertNotNull(requestContextCaller.run(() -> myDummyAIService.toString())); }