diff --git a/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/BeansProcessor.java b/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/BeansProcessor.java index ccc3de86c..f76887830 100644 --- a/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/BeansProcessor.java +++ b/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/BeansProcessor.java @@ -9,18 +9,13 @@ import java.util.Optional; import java.util.stream.Collectors; +import org.apache.poi.ss.formula.functions.T; import org.jboss.jandex.DotName; import com.fasterxml.jackson.databind.ObjectMapper; import io.quarkiverse.langchain4j.deployment.config.LangChain4jBuildConfig; -import io.quarkiverse.langchain4j.deployment.items.ChatModelProviderCandidateBuildItem; -import io.quarkiverse.langchain4j.deployment.items.EmbeddingModelProviderCandidateBuildItem; -import io.quarkiverse.langchain4j.deployment.items.ModerationModelProviderCandidateBuildItem; -import io.quarkiverse.langchain4j.deployment.items.ProviderHolder; -import io.quarkiverse.langchain4j.deployment.items.SelectedChatModelProviderBuildItem; -import io.quarkiverse.langchain4j.deployment.items.SelectedEmbeddingModelCandidateBuildItem; -import io.quarkiverse.langchain4j.deployment.items.SelectedModerationModelProviderBuildItem; +import io.quarkiverse.langchain4j.deployment.items.*; import io.quarkiverse.langchain4j.runtime.Langchain4jRecorder; import io.quarkus.arc.deployment.BeanDiscoveryFinishedBuildItem; import io.quarkus.arc.deployment.UnremovableBeanBuildItem; @@ -57,7 +52,8 @@ public void handleProviders(BeanDiscoveryFinishedBuildItem beanDiscoveryFinished LangChain4jBuildConfig buildConfig, BuildProducer selectedChatProducer, BuildProducer selectedEmbeddingProducer, - BuildProducer selectedModerationProducer) { + BuildProducer selectedModerationProducer, + List inProcessEmbeddingBuildItems) { boolean chatModelBeanRequested = false; boolean streamingChatModelBeanRequested = false; @@ -91,7 +87,8 @@ public void handleProviders(BeanDiscoveryFinishedBuildItem beanDiscoveryFinished if (embeddingModelBeanRequested) { selectedEmbeddingProducer.produce( new SelectedEmbeddingModelCandidateBuildItem( - selectProvider( + selectEmbeddingModelProvider( + inProcessEmbeddingBuildItems, embeddingCandidateItems, buildConfig.embeddingModel().provider(), "EmbeddingModel", @@ -110,7 +107,8 @@ public void handleProviders(BeanDiscoveryFinishedBuildItem beanDiscoveryFinished } @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - private String selectProvider(List chatCandidateItems, + private String selectProvider( + List chatCandidateItems, Optional userSelectedProvider, String requestedBeanName, String configNamespace) { @@ -139,6 +137,41 @@ private String selectProvider(List chatCandidateIt requestedBeanName, configNamespace, String.join(",", availableProviders))); } + private String selectEmbeddingModelProvider( + List inProcessEmbeddingBuildItems, + List chatCandidateItems, + Optional userSelectedProvider, + String requestedBeanName, + String configNamespace) { + List availableProviders = chatCandidateItems.stream().map(ProviderHolder::getProvider) + .collect(Collectors.toList()); + availableProviders.addAll(inProcessEmbeddingBuildItems.stream().map(InProcessEmbeddingBuildItem::getProvider) + .toList()); + if (availableProviders.isEmpty()) { + throw new ConfigurationException(String.format( + "A %s bean was requested, but no langchain4j providers were configured and no in-process embedding model were found on the classpath. " + + + "Consider adding an extension like 'quarkus-langchain4j-openai' or one of the in-process embedding model.", + requestedBeanName)); + } + if (availableProviders.size() == 1) { + return availableProviders.get(0); + } + // multiple providers exist, so we now need the configuration to select the proper one + if (userSelectedProvider.isEmpty()) { + throw new ConfigurationException(String.format( + "A %s bean was requested, but since there are multiple available providers, the 'quarkus.langchain4j.%s.provider' needs to be set to one of the available options (%s).", + requestedBeanName, configNamespace, String.join(",", availableProviders))); + } + boolean matches = availableProviders.stream().anyMatch(ap -> ap.equals(userSelectedProvider.get())); + if (matches) { + return userSelectedProvider.get(); + } + throw new ConfigurationException(String.format( + "A %s bean was requested, but the value of 'quarkus.langchain4j.%s.provider' does not match any of the available options (%s).", + requestedBeanName, configNamespace, String.join(",", availableProviders))); + } + @BuildStep @Record(ExecutionTime.RUNTIME_INIT) public void cleanUp(Langchain4jRecorder recorder, ShutdownContextBuildItem shutdown) { diff --git a/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/DocumentNativeSupportProcessor.java b/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/DocumentNativeSupportProcessor.java index 468be8357..07c0aa199 100644 --- a/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/DocumentNativeSupportProcessor.java +++ b/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/DocumentNativeSupportProcessor.java @@ -10,14 +10,11 @@ import java.util.zip.ZipEntry; import dev.langchain4j.data.document.splitter.DocumentBySentenceSplitter; +import io.quarkiverse.langchain4j.deployment.items.InProcessEmbeddingBuildItem; import io.quarkus.bootstrap.classloading.QuarkusClassLoader; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; -import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem; -import io.quarkus.deployment.builditem.nativeimage.NativeImageResourcePatternsBuildItem; -import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; -import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; -import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedPackageBuildItem; +import io.quarkus.deployment.builditem.nativeimage.*; /** * TODO: we might want to make this more granular so all these document related dependencies don't always end up in the @@ -26,24 +23,33 @@ public class DocumentNativeSupportProcessor { @BuildStep - void onnxJni(BuildProducer nativePatternProducer, + void onnxJni( + List inProcessEmbeddingBuildItems, + BuildProducer nativePatternProducer, BuildProducer reflectionProducer) { - // TODO: we can do better here and only include the target architecture's libs - nativePatternProducer - .produce(NativeImageResourcePatternsBuildItem.builder().includeGlobs("ai/onnxruntime/native/**").build()); - reflectionProducer - .produce(ReflectiveClassBuildItem.builder("opennlp.tools.sentdetect.SentenceDetectorFactory").build()); - reflectionProducer.produce(ReflectiveClassBuildItem.builder("ai.onnxruntime.OnnxTensor").methods(true).build()); + if (!inProcessEmbeddingBuildItems.isEmpty()) { + // TODO: we can do better here and only include the target architecture's libs + nativePatternProducer + .produce(NativeImageResourcePatternsBuildItem.builder().includeGlobs("ai/onnxruntime/native/**").build()); + reflectionProducer + .produce(ReflectiveClassBuildItem.builder("opennlp.tools.sentdetect.SentenceDetectorFactory").build()); + reflectionProducer.produce(ReflectiveClassBuildItem.builder("ai.onnxruntime.OnnxTensor").methods(true).build()); + } } @BuildStep - void apachePoiRuntimeClasses(BuildProducer classProducer, + void apachePoiRuntimeClasses( + List inProcessEmbeddingBuildItems, + BuildProducer classProducer, BuildProducer packageProducer) { Stream.of( - "dev.langchain4j.model.embedding.AllMiniLmL6V2EmbeddingModel", "dev.langchain4j.model.embedding.OnnxBertBiEncoder", "ai.onnxruntime.OrtEnvironment", "ai.onnxruntime.OnnxRuntime", + "ai.onnxruntime.OnnxTensorLike", + "ai.onnxruntime.OrtAllocator", + "ai.onnxruntime.OrtSession$SessionOptions", + "ai.onnxruntime.OrtSession", "org.apache.fontbox.ttf.RAFDataStream", "org.apache.fontbox.ttf.TTFParser", "org.apache.pdfbox.pdmodel.encrypetion.PublicKeySecurityHandler", @@ -61,15 +67,33 @@ void apachePoiRuntimeClasses(BuildProducer cla .filter(QuarkusClassLoader::isClassPresentAtRuntime) .map(RuntimeInitializedClassBuildItem::new).forEach(classProducer::produce); + for (InProcessEmbeddingBuildItem inProcessEmbeddingBuildItem : inProcessEmbeddingBuildItems) { + classProducer.produce(new RuntimeInitializedClassBuildItem(inProcessEmbeddingBuildItem.className())); + } + packageProducer.produce(new RuntimeInitializedPackageBuildItem("com.microsoft.schemas.office")); } @BuildStep - void openNLPResources(BuildProducer producer) { + void includeInProcessEmbeddingModels( + List inProcessEmbeddingBuildItems, + BuildProducer resources, + BuildProducer reflection) { + for (InProcessEmbeddingBuildItem inProcessEmbeddingBuildItem : inProcessEmbeddingBuildItems) { + resources.produce(new NativeImageResourceBuildItem(inProcessEmbeddingBuildItem.onnxModelPath())); + resources.produce(new NativeImageResourceBuildItem(inProcessEmbeddingBuildItem.vocabularyPath())); + reflection.produce(ReflectiveClassBuildItem.builder(inProcessEmbeddingBuildItem.className()) + .constructors(true) + .fields(true) + .methods(true) + .build()); + } + } + + @BuildStep + void openNLPResources( + BuildProducer producer) { registerCustomOpenNLPResources(producer); - // TODO: maybe we should also be opening jars and getting these? - producer.produce(new NativeImageResourceBuildItem("bert-vocabulary-en.txt")); - producer.produce(new NativeImageResourceBuildItem("all-minilm-l6-v2.onnx")); } private void registerCustomOpenNLPResources(BuildProducer resourcesProducer) { diff --git a/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/InProcessEmbeddingProcessor.java b/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/InProcessEmbeddingProcessor.java new file mode 100644 index 000000000..9c8a0b8ac --- /dev/null +++ b/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/InProcessEmbeddingProcessor.java @@ -0,0 +1,138 @@ +package io.quarkiverse.langchain4j.deployment; + +import java.util.List; + +import jakarta.enterprise.context.ApplicationScoped; + +import org.jboss.jandex.DotName; + +import dev.langchain4j.model.embedding.EmbeddingModel; +import io.quarkiverse.langchain4j.deployment.items.InProcessEmbeddingBuildItem; +import io.quarkiverse.langchain4j.runtime.InProcessEmbeddingRecorder; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.bootstrap.classloading.QuarkusClassLoader; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; + +/** + * Generate a local embedding build item for each local embedding model available in the classpath. + * Note that the user must have the dependency for the model in their pom.xml/build.gradle. + */ +public class InProcessEmbeddingProcessor { + + // https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2 + @BuildStep + InProcessEmbeddingBuildItem all_minilm_l6_v2_q() { + if (QuarkusClassLoader + .isClassPresentAtRuntime("dev.langchain4j.model.embedding.AllMiniLmL6V2QuantizedEmbeddingModel")) { + return new InProcessEmbeddingBuildItem("all-minilm-l6-v2-q", + "dev.langchain4j.model.embedding.AllMiniLmL6V2QuantizedEmbeddingModel", + "all-minilm-l6-v2-q.onnx", "bert-vocabulary-en.txt"); + } else { + return null; + } + } + + // https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2 + @BuildStep + InProcessEmbeddingBuildItem all_minilm_l6_v2() { + if (QuarkusClassLoader.isClassPresentAtRuntime("dev.langchain4j.model.embedding.AllMiniLmL6V2EmbeddingModel")) { + return new InProcessEmbeddingBuildItem("all-minilm-l6-v2", + "dev.langchain4j.model.embedding.AllMiniLmL6V2EmbeddingModel", + "all-minilm-l6-v2.onnx", "bert-vocabulary-en.txt"); + } else { + return null; + } + } + + // https://huggingface.co/neuralmagic/bge-small-en-v1.5-quant + @BuildStep + InProcessEmbeddingBuildItem bge_small_en_q() { + if (QuarkusClassLoader.isClassPresentAtRuntime("dev.langchain4j.model.embedding.BgeSmallEnQuantizedEmbeddingModel")) { + return new InProcessEmbeddingBuildItem("bge-small-en-q", + "dev.langchain4j.model.embedding.BgeSmallEnQuantizedEmbeddingModel", + "bge-small-en-q.onnx", "bert-vocabulary-en.txt"); + } else { + return null; + } + } + + // https://huggingface.co/BAAI/bge-small-en-v1.5 + @BuildStep + InProcessEmbeddingBuildItem bge_small_en() { + if (QuarkusClassLoader.isClassPresentAtRuntime("dev.langchain4j.model.embedding.BgeSmallEnEmbeddingModel")) { + return new InProcessEmbeddingBuildItem("bge-small-en", "dev.langchain4j.model.embedding.BgeSmallEnEmbeddingModel", + "bge-small-en.onnx", "bert-vocabulary-en.txt"); + } else { + return null; + } + } + + @BuildStep + InProcessEmbeddingBuildItem bge_small_zh_q() { + if (QuarkusClassLoader.isClassPresentAtRuntime("dev.langchain4j.model.embedding.BgeSmallZhQuantizedEmbeddingModel")) { + return new InProcessEmbeddingBuildItem("bge-small-zh-q", + "dev.langchain4j.model.embedding.BgeSmallZhQuantizedEmbeddingModel", + "bge-small-zh-q.onnx", "bge-small-zh-vocabulary.txt"); + } else { + return null; + } + } + + // https://huggingface.co/BAAI/bge-small-zh + @BuildStep + InProcessEmbeddingBuildItem bge_small_zh() { + if (QuarkusClassLoader.isClassPresentAtRuntime("dev.langchain4j.model.embedding.BgeSmallZhEmbeddingModel")) { + return new InProcessEmbeddingBuildItem("bge-small-zh", "dev.langchain4j.model.embedding.BgeSmallZhEmbeddingModel", + "bge-small-zh.onnx", "bge-small-zh-vocabulary.txt"); + } else { + return null; + } + } + + // https://huggingface.co/intfloat/e5-small-v2 + @BuildStep + InProcessEmbeddingBuildItem e5_small_v2_q() { + if (QuarkusClassLoader.isClassPresentAtRuntime("dev.langchain4j.model.embedding.E5SmallV2QuantizedEmbeddingModel")) { + return new InProcessEmbeddingBuildItem("e5-small-v2-q", + "dev.langchain4j.model.embedding.E5SmallV2QuantizedEmbeddingModel", + "e5-small-v2-q.onnx", "bert-vocabulary-en.txt"); + } else { + return null; + } + } + + // https://huggingface.co/intfloat/e5-small-v2 + @BuildStep + InProcessEmbeddingBuildItem e5_small_v2() { + if (QuarkusClassLoader.isClassPresentAtRuntime("dev.langchain4j.model.embedding.E5SmallV2EmbeddingModel")) { + return new InProcessEmbeddingBuildItem("e5-small-v2", "dev.langchain4j.model.embedding.E5SmallV2EmbeddingModel", + "e5-small-v2.onnx", "bert-vocabulary-en.txt"); + } else { + return null; + } + } + + // Expose a bean for each in process embedding model + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + void exposeInProcessEmbeddingBeans(InProcessEmbeddingRecorder recorder, + List embeddings, + BuildProducer beanProducer) { + + for (InProcessEmbeddingBuildItem embedding : embeddings) { + beanProducer.produce(SyntheticBeanBuildItem + .configure(DotName.createSimple(embedding.className())) + .types(EmbeddingModel.class) + .defaultBean() + .setRuntimeInit() + .scope(ApplicationScoped.class) + .supplier(recorder.instantiate(embedding.className())) + .done()); + } + + } + +} diff --git a/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/items/InProcessEmbeddingBuildItem.java b/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/items/InProcessEmbeddingBuildItem.java new file mode 100644 index 000000000..091ca8641 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkiverse/langchain4j/deployment/items/InProcessEmbeddingBuildItem.java @@ -0,0 +1,40 @@ +package io.quarkiverse.langchain4j.deployment.items; + +import io.quarkus.builder.item.MultiBuildItem; + +public final class InProcessEmbeddingBuildItem extends MultiBuildItem implements ProviderHolder { + + private final String modelName; + private final String onnxModelPath; + private final String vocabularyPath; + + private final String className; + + public InProcessEmbeddingBuildItem(String modelName, String className, String onnxModelPath, String vocabularyPath) { + this.modelName = modelName; + this.className = className; + this.onnxModelPath = onnxModelPath; + this.vocabularyPath = vocabularyPath; + } + + public String modelName() { + return modelName; + } + + public String onnxModelPath() { + return onnxModelPath; + } + + public String vocabularyPath() { + return vocabularyPath; + } + + public String className() { + return className; + } + + @Override + public String getProvider() { + return className; + } +} diff --git a/core/runtime/pom.xml b/core/runtime/pom.xml index 9975b2602..4701f4d64 100644 --- a/core/runtime/pom.xml +++ b/core/runtime/pom.xml @@ -66,12 +66,57 @@ ${quarkus-poi.version} + + dev.langchain4j langchain4j-embeddings-all-minilm-l6-v2-q ${langchain4j.version} true + + dev.langchain4j + langchain4j-embeddings-all-minilm-l6-v2 + ${langchain4j.version} + true + + + dev.langchain4j + langchain4j-embeddings-bge-small-en-q + ${langchain4j.version} + true + + + dev.langchain4j + langchain4j-embeddings-bge-small-en + ${langchain4j.version} + true + + + dev.langchain4j + langchain4j-embeddings-bge-small-zh-q + ${langchain4j.version} + true + + + dev.langchain4j + langchain4j-embeddings-bge-small-zh + ${langchain4j.version} + true + + + dev.langchain4j + langchain4j-embeddings-e5-small-v2-q + ${langchain4j.version} + true + + + dev.langchain4j + langchain4j-embeddings-e5-small-v2 + ${langchain4j.version} + true + + org.graalvm.sdk @@ -92,7 +137,8 @@ extension-descriptor - ${project.groupId}:${project.artifactId}-deployment:${project.version} + ${project.groupId}:${project.artifactId}-deployment:${project.version} + @@ -131,5 +177,5 @@ - + diff --git a/core/runtime/src/main/java/io/quarkiverse/langchain4j/runtime/InProcessEmbeddingRecorder.java b/core/runtime/src/main/java/io/quarkiverse/langchain4j/runtime/InProcessEmbeddingRecorder.java new file mode 100644 index 000000000..98d459ca0 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkiverse/langchain4j/runtime/InProcessEmbeddingRecorder.java @@ -0,0 +1,26 @@ +package io.quarkiverse.langchain4j.runtime; + +import java.util.function.Supplier; + +import org.jboss.logging.Logger; + +import dev.langchain4j.model.embedding.AbstractInProcessEmbeddingModel; +import io.quarkus.runtime.annotations.Recorder; + +@Recorder +public class InProcessEmbeddingRecorder { + @SuppressWarnings("unchecked") + public Supplier instantiate(String className) { + return () -> { + try { + Class loaded = (Class) InProcessEmbeddingRecorder.class + .getClassLoader().loadClass(className); + return loaded.getConstructor().newInstance(); + } catch (Exception e) { + Logger.getLogger(InProcessEmbeddingRecorder.class) + .errorf("Failed to instantiate in-process embedding model %s", className, e); + throw new RuntimeException(e); + } + }; + } +} diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index b444fb9a9..44e342554 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -14,6 +14,7 @@ * Document Stores ** xref:redis-store.adoc[Redis Store] ** xref:chroma-store.adoc[Chroma Store] +** xref:in-process-embedding.adoc[In-Process Embeddings] * Advanced topics ** xref:fault-tolerance.adoc[Fault Tolerance] \ No newline at end of file diff --git a/docs/modules/ROOT/pages/in-process-embedding.adoc b/docs/modules/ROOT/pages/in-process-embedding.adoc new file mode 100644 index 000000000..f29bf7a7f --- /dev/null +++ b/docs/modules/ROOT/pages/in-process-embedding.adoc @@ -0,0 +1,87 @@ += In-Process Embedding Models + +include::./includes/attributes.adoc[] + +When ingesting document or implementing the RAG pattern, you need an _embedding model_. +This is a model that takes a document and returns a vector representation of that document. +The vector representation is stored in a vector database, and is used to find similar documents. + +When using LLMs like OpenAI or HuggingFace, it provides _remote_ embedding models. +To compute the _embedding_ of a document, you need to send the document to the remote model. + +In-process models avoids this overhead by running the model in the same process as the application. +This is generally faster, but requires more memory. + +== Supported in-process models + +The Quarkus LangChain4j extension provides supports for a set of in-process embedding models. +They are **not** included by default, and you need to add the explicit dependency to your project. + +The following table lists the supported models, and the corresponding dependency to add to your project. + +[cols="2,1,1,1",options="header"] +|=== +| Model Name | Dependency | Vector Dimension | Injected type + +| https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2[all-minlm-l6-v2-q] +|`dev.langchain4j:langchain4j-embeddings-all-minilm-l6-v2-q:{langchain4j-version}` +| 384 +| `dev.langchain4j.model.embedding.AllMiniLmL6V2QuantizedEmbeddingModel` + +| https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2[all-minlm-l6-v2] +|`dev.langchain4j:langchain4j-embeddings-all-minilm-l6-v2:{langchain4j-version}` +| 384 +| `dev.langchain4j.model.embedding.AllMiniLmL6V2EmbeddingModel` + +| https://huggingface.co/neuralmagic/bge-small-en-v1.5-quant[bge-small-en-q] +|`dev.langchain4j:langchain4j-embeddings-bge-small-en-q:{langchain4j-version}` +| 384 +| `dev.langchain4j.model.embedding.BgeSmallEnQuantizedEmbeddingModel` + +| https://huggingface.co/neuralmagic/bge-small-en-v1.5-quant[bge-small-en] +|`dev.langchain4j:langchain4j-embeddings-bge-small-en:{langchain4j-version}` +| 384 +| `dev.langchain4j.model.embedding.BgeSmallEnEmbeddingModel` + +| https://huggingface.co/BAAI/bge-small-zh[bge-small-zh-q] +|`dev.langchain4j:langchain4j-embeddings-bge-small-zh-q:{langchain4j-version}` +| 384 +| `dev.langchain4j.model.embedding.BgeSmallZhQuantizedEmbeddingModel` + + +| https://huggingface.co/BAAI/bge-small-zh[bge-small-zh] +|`dev.langchain4j:langchain4j-embeddings-bge-small-zh:{langchain4j-version}` +| 384 +| `dev.langchain4j.model.embedding.BgeSmallZhEmbeddingModel` + + +| https://huggingface.co/intfloat/e5-small-v2[e5-small-v2-q] +|`dev.langchain4j:langchain4j-embeddings-e5-small-v2-q:{langchain4j-version}` +| 384 +| `dev.langchain4j.model.embedding.E5SmallV2QuantizedEmbeddingModel` + +| https://huggingface.co/intfloat/e5-small-v2[e5-small-v2-q] +|`dev.langchain4j:langchain4j-embeddings-e5-small-v2-q:{langchain4j-version}` +| 384 +| `dev.langchain4j.model.embedding.E5SmallV2EmbeddingModel` + +|=== + +== Injecting an embedding model + +You can inject the model in your application using: + +[source,java] +---- +@Inject E5SmallV2QuantizedEmbeddingModel model; +---- + +Use the corresponding model type for the model you want to use, and make sure you have added the corresponding dependency to your project. + +Note that if you do not have any other embedding model in your project, you can inject the `EmbeddingModel` interface, and it will be automatically injected with the available model: + +[source,java] +---- +@Inject EmbeddingModel model; +---- + diff --git a/docs/modules/ROOT/pages/includes/attributes.adoc b/docs/modules/ROOT/pages/includes/attributes.adoc index 75050ea2f..a58b96ae5 100644 --- a/docs/modules/ROOT/pages/includes/attributes.adoc +++ b/docs/modules/ROOT/pages/includes/attributes.adoc @@ -1,3 +1,3 @@ :project-version: 0.1.0 - +:langchain4j-version: 0.24.0 :examples-dir: ./../examples/ \ No newline at end of file diff --git a/docs/modules/ROOT/pages/redis-store.adoc b/docs/modules/ROOT/pages/redis-store.adoc index 7e52da56d..04f989624 100644 --- a/docs/modules/ROOT/pages/redis-store.adoc +++ b/docs/modules/ROOT/pages/redis-store.adoc @@ -21,6 +21,9 @@ This extension relies on the Quarkus Redis client. Ensure the default Redis data IMPORTANT: The Redis document store's functionality is built on the Redis JSON and Redis Search modules. Ensure these modules are installed, or consider using the Redis Stack. If you want to use the Quarkus Dev Services for Redis, make sure to configure `quarkus.redis.devservices.image-name=redis/redis-stack:latest` in your `application.properties` file. +IMPORTANT: The Redis document store requires the dimension of the vector to be set. Add the `quarkus.langchain4j.redis.dimension` property to your `application.properties` file and set it to the dimension of the vector. The dimension depends on the embedding model you use. +For example, `AllMiniLmL6V2QuantizedEmbeddingModel` produces vectors of dimension 384. OpenAI’s `text-embedding-ada-002` produces vectors of dimension 1536. + Upon installing the extension, you can utilize the Redis document store using the following code: [source,java] diff --git a/docs/templates/includes/attributes.adoc b/docs/templates/includes/attributes.adoc index e1a28816e..34e193281 100644 --- a/docs/templates/includes/attributes.adoc +++ b/docs/templates/includes/attributes.adoc @@ -1,3 +1,3 @@ :project-version: ${release.current-version} - +:langchain4j-version: ${langchain4j.version} :examples-dir: ./../examples/ \ No newline at end of file diff --git a/integration-tests/in-process-embedding-all-minilm-l6-v2-q/pom.xml b/integration-tests/in-process-embedding-all-minilm-l6-v2-q/pom.xml new file mode 100644 index 000000000..dad6070ec --- /dev/null +++ b/integration-tests/in-process-embedding-all-minilm-l6-v2-q/pom.xml @@ -0,0 +1,112 @@ + + + 4.0.0 + + + io.quarkiverse.langchain4j + quarkus-langchain4j-integration-tests-parent + 999-SNAPSHOT + + + in-process-embedding-all-minilm-l6-v2-q + + + + io.quarkiverse.langchain4j + quarkus-langchain4j-core + ${project.version} + + + + dev.langchain4j + langchain4j-embeddings-all-minilm-l6-v2-q + ${langchain4j.version} + + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + io.quarkus + quarkus-devtools-testing + test + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + + + false + native + + + + \ No newline at end of file diff --git a/integration-tests/in-process-embedding-all-minilm-l6-v2-q/src/main/java/org/acme/test/InProcessEmbeddingResource.java b/integration-tests/in-process-embedding-all-minilm-l6-v2-q/src/main/java/org/acme/test/InProcessEmbeddingResource.java new file mode 100644 index 000000000..94baf23b8 --- /dev/null +++ b/integration-tests/in-process-embedding-all-minilm-l6-v2-q/src/main/java/org/acme/test/InProcessEmbeddingResource.java @@ -0,0 +1,28 @@ +package org.acme.test; + +import jakarta.inject.Inject; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import dev.langchain4j.model.embedding.AllMiniLmL6V2QuantizedEmbeddingModel; +import dev.langchain4j.model.embedding.EmbeddingModel; + +@Path("/in-process-embedding") +public class InProcessEmbeddingResource { + + @Inject + AllMiniLmL6V2QuantizedEmbeddingModel allMiniLmL6V2QuantizedEmbeddingModel; + + @Inject + EmbeddingModel embeddingModel; + + @POST + public String computeEmbedding(String sentence) { + var r1 = allMiniLmL6V2QuantizedEmbeddingModel.embed(sentence); + var r2 = embeddingModel.embed(sentence); + + return "allMiniLmL6V2QuantizedEmbeddingModel: " + r1.content().dimensions() + "\n" + "embeddingModel: " + + r2.content().dimensions(); + } + +} diff --git a/integration-tests/in-process-embedding-all-minilm-l6-v2-q/src/main/resources/application.properties b/integration-tests/in-process-embedding-all-minilm-l6-v2-q/src/main/resources/application.properties new file mode 100644 index 000000000..6bdf5ee4b --- /dev/null +++ b/integration-tests/in-process-embedding-all-minilm-l6-v2-q/src/main/resources/application.properties @@ -0,0 +1 @@ +#quarkus.native.additional-build-args=--trace-class-initialization=ai.onnxruntime.OnnxRuntime \ No newline at end of file diff --git a/integration-tests/in-process-embedding-all-minilm-l6-v2-q/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java b/integration-tests/in-process-embedding-all-minilm-l6-v2-q/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java new file mode 100644 index 000000000..6016bed7a --- /dev/null +++ b/integration-tests/in-process-embedding-all-minilm-l6-v2-q/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java @@ -0,0 +1,8 @@ +package org.acme.test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +class InProcessEmbeddingResourceIT extends InProcessEmbeddingResourceTest { + +} \ No newline at end of file diff --git a/integration-tests/in-process-embedding-all-minilm-l6-v2-q/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java b/integration-tests/in-process-embedding-all-minilm-l6-v2-q/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java new file mode 100644 index 000000000..c73c5c94a --- /dev/null +++ b/integration-tests/in-process-embedding-all-minilm-l6-v2-q/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java @@ -0,0 +1,22 @@ +package org.acme.test; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; + +@QuarkusTest +class InProcessEmbeddingResourceTest { + + @Test + void test() { + var s = RestAssured.given() + .body("This is a sentence.") + .post("/in-process-embedding") + .andReturn().asString(); + Assertions.assertThat(s) + .contains("allMiniLmL6V2QuantizedEmbeddingModel: 384\n" + "embeddingModel: 384"); + } + +} diff --git a/integration-tests/in-process-embedding-all-minilm-l6-v2/pom.xml b/integration-tests/in-process-embedding-all-minilm-l6-v2/pom.xml new file mode 100644 index 000000000..27eaf3fa9 --- /dev/null +++ b/integration-tests/in-process-embedding-all-minilm-l6-v2/pom.xml @@ -0,0 +1,112 @@ + + + 4.0.0 + + + io.quarkiverse.langchain4j + quarkus-langchain4j-integration-tests-parent + 999-SNAPSHOT + + + in-process-embedding-all-minilm-l6-v2 + + + + io.quarkiverse.langchain4j + quarkus-langchain4j-core + ${project.version} + + + + dev.langchain4j + langchain4j-embeddings-all-minilm-l6-v2 + ${langchain4j.version} + + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + io.quarkus + quarkus-devtools-testing + test + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + + + false + native + + + + \ No newline at end of file diff --git a/integration-tests/in-process-embedding-all-minilm-l6-v2/src/main/java/org/acme/test/InProcessEmbeddingResource.java b/integration-tests/in-process-embedding-all-minilm-l6-v2/src/main/java/org/acme/test/InProcessEmbeddingResource.java new file mode 100644 index 000000000..ee3ac97d6 --- /dev/null +++ b/integration-tests/in-process-embedding-all-minilm-l6-v2/src/main/java/org/acme/test/InProcessEmbeddingResource.java @@ -0,0 +1,28 @@ +package org.acme.test; + +import jakarta.inject.Inject; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import dev.langchain4j.model.embedding.AllMiniLmL6V2EmbeddingModel; +import dev.langchain4j.model.embedding.EmbeddingModel; + +@Path("/in-process-embedding") +public class InProcessEmbeddingResource { + + @Inject + AllMiniLmL6V2EmbeddingModel allMiniLmL6V2QuantizedEmbeddingModel; + + @Inject + EmbeddingModel embeddingModel; + + @POST + public String computeEmbedding(String sentence) { + var r1 = allMiniLmL6V2QuantizedEmbeddingModel.embed(sentence); + var r2 = embeddingModel.embed(sentence); + + return "AllMiniLmL6V2EmbeddingModel: " + r1.content().dimensions() + "\n" + "embeddingModel: " + + r2.content().dimensions(); + } + +} diff --git a/integration-tests/in-process-embedding-all-minilm-l6-v2/src/main/resources/application.properties b/integration-tests/in-process-embedding-all-minilm-l6-v2/src/main/resources/application.properties new file mode 100644 index 000000000..6bdf5ee4b --- /dev/null +++ b/integration-tests/in-process-embedding-all-minilm-l6-v2/src/main/resources/application.properties @@ -0,0 +1 @@ +#quarkus.native.additional-build-args=--trace-class-initialization=ai.onnxruntime.OnnxRuntime \ No newline at end of file diff --git a/integration-tests/in-process-embedding-all-minilm-l6-v2/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java b/integration-tests/in-process-embedding-all-minilm-l6-v2/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java new file mode 100644 index 000000000..6016bed7a --- /dev/null +++ b/integration-tests/in-process-embedding-all-minilm-l6-v2/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java @@ -0,0 +1,8 @@ +package org.acme.test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +class InProcessEmbeddingResourceIT extends InProcessEmbeddingResourceTest { + +} \ No newline at end of file diff --git a/integration-tests/in-process-embedding-all-minilm-l6-v2/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java b/integration-tests/in-process-embedding-all-minilm-l6-v2/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java new file mode 100644 index 000000000..e18c3ed4f --- /dev/null +++ b/integration-tests/in-process-embedding-all-minilm-l6-v2/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java @@ -0,0 +1,22 @@ +package org.acme.test; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; + +@QuarkusTest +class InProcessEmbeddingResourceTest { + + @Test + void test() { + var s = RestAssured.given() + .body("This is a sentence.") + .post("/in-process-embedding") + .andReturn().asString(); + Assertions.assertThat(s) + .contains("AllMiniLmL6V2EmbeddingModel: 384\n" + "embeddingModel: 384"); + } + +} diff --git a/integration-tests/in-process-embedding-bge-small-en-q/pom.xml b/integration-tests/in-process-embedding-bge-small-en-q/pom.xml new file mode 100644 index 000000000..04e4f7932 --- /dev/null +++ b/integration-tests/in-process-embedding-bge-small-en-q/pom.xml @@ -0,0 +1,112 @@ + + + 4.0.0 + + + io.quarkiverse.langchain4j + quarkus-langchain4j-integration-tests-parent + 999-SNAPSHOT + + + in-process-embedding-bge-small-en-q + + + + io.quarkiverse.langchain4j + quarkus-langchain4j-core + ${project.version} + + + + dev.langchain4j + langchain4j-embeddings-bge-small-en-q + ${langchain4j.version} + + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + io.quarkus + quarkus-devtools-testing + test + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + + + false + native + + + + \ No newline at end of file diff --git a/integration-tests/in-process-embedding-bge-small-en-q/src/main/java/org/acme/test/InProcessEmbeddingResource.java b/integration-tests/in-process-embedding-bge-small-en-q/src/main/java/org/acme/test/InProcessEmbeddingResource.java new file mode 100644 index 000000000..77abb0b55 --- /dev/null +++ b/integration-tests/in-process-embedding-bge-small-en-q/src/main/java/org/acme/test/InProcessEmbeddingResource.java @@ -0,0 +1,28 @@ +package org.acme.test; + +import jakarta.inject.Inject; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import dev.langchain4j.model.embedding.BgeSmallEnQuantizedEmbeddingModel; +import dev.langchain4j.model.embedding.EmbeddingModel; + +@Path("/in-process-embedding") +public class InProcessEmbeddingResource { + + @Inject + BgeSmallEnQuantizedEmbeddingModel typedModel; + + @Inject + EmbeddingModel embeddingModel; + + @POST + public String computeEmbedding(String sentence) { + var r1 = typedModel.embed(sentence); + var r2 = embeddingModel.embed(sentence); + + return "bgeSmallEnQuantizedEmbeddingModel: " + r1.content().dimensions() + "\n" + "embeddingModel: " + + r2.content().dimensions(); + } + +} diff --git a/integration-tests/in-process-embedding-bge-small-en-q/src/main/resources/application.properties b/integration-tests/in-process-embedding-bge-small-en-q/src/main/resources/application.properties new file mode 100644 index 000000000..6bdf5ee4b --- /dev/null +++ b/integration-tests/in-process-embedding-bge-small-en-q/src/main/resources/application.properties @@ -0,0 +1 @@ +#quarkus.native.additional-build-args=--trace-class-initialization=ai.onnxruntime.OnnxRuntime \ No newline at end of file diff --git a/integration-tests/in-process-embedding-bge-small-en-q/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java b/integration-tests/in-process-embedding-bge-small-en-q/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java new file mode 100644 index 000000000..6016bed7a --- /dev/null +++ b/integration-tests/in-process-embedding-bge-small-en-q/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java @@ -0,0 +1,8 @@ +package org.acme.test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +class InProcessEmbeddingResourceIT extends InProcessEmbeddingResourceTest { + +} \ No newline at end of file diff --git a/integration-tests/in-process-embedding-bge-small-en-q/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java b/integration-tests/in-process-embedding-bge-small-en-q/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java new file mode 100644 index 000000000..42c2ad116 --- /dev/null +++ b/integration-tests/in-process-embedding-bge-small-en-q/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java @@ -0,0 +1,22 @@ +package org.acme.test; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; + +@QuarkusTest +class InProcessEmbeddingResourceTest { + + @Test + void test() { + var s = RestAssured.given() + .body("This is a sentence.") + .post("/in-process-embedding") + .andReturn().asString(); + Assertions.assertThat(s) + .contains("bgeSmallEnQuantizedEmbeddingModel: 384\n" + "embeddingModel: 384"); + } + +} diff --git a/integration-tests/in-process-embedding-bge-small-en/pom.xml b/integration-tests/in-process-embedding-bge-small-en/pom.xml new file mode 100644 index 000000000..f9132b769 --- /dev/null +++ b/integration-tests/in-process-embedding-bge-small-en/pom.xml @@ -0,0 +1,112 @@ + + + 4.0.0 + + + io.quarkiverse.langchain4j + quarkus-langchain4j-integration-tests-parent + 999-SNAPSHOT + + + in-process-embedding-bge-small-en + + + + io.quarkiverse.langchain4j + quarkus-langchain4j-core + ${project.version} + + + + dev.langchain4j + langchain4j-embeddings-bge-small-en + ${langchain4j.version} + + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + io.quarkus + quarkus-devtools-testing + test + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + + + false + native + + + + \ No newline at end of file diff --git a/integration-tests/in-process-embedding-bge-small-en/src/main/java/org/acme/test/InProcessEmbeddingResource.java b/integration-tests/in-process-embedding-bge-small-en/src/main/java/org/acme/test/InProcessEmbeddingResource.java new file mode 100644 index 000000000..6236b4096 --- /dev/null +++ b/integration-tests/in-process-embedding-bge-small-en/src/main/java/org/acme/test/InProcessEmbeddingResource.java @@ -0,0 +1,28 @@ +package org.acme.test; + +import jakarta.inject.Inject; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import dev.langchain4j.model.embedding.BgeSmallEnEmbeddingModel; +import dev.langchain4j.model.embedding.EmbeddingModel; + +@Path("/in-process-embedding") +public class InProcessEmbeddingResource { + + @Inject + BgeSmallEnEmbeddingModel typedModel; + + @Inject + EmbeddingModel embeddingModel; + + @POST + public String computeEmbedding(String sentence) { + var r1 = typedModel.embed(sentence); + var r2 = embeddingModel.embed(sentence); + + return "bgeSmallEnEmbeddingModel: " + r1.content().dimensions() + "\n" + "embeddingModel: " + + r2.content().dimensions(); + } + +} diff --git a/integration-tests/in-process-embedding-bge-small-en/src/main/resources/application.properties b/integration-tests/in-process-embedding-bge-small-en/src/main/resources/application.properties new file mode 100644 index 000000000..6bdf5ee4b --- /dev/null +++ b/integration-tests/in-process-embedding-bge-small-en/src/main/resources/application.properties @@ -0,0 +1 @@ +#quarkus.native.additional-build-args=--trace-class-initialization=ai.onnxruntime.OnnxRuntime \ No newline at end of file diff --git a/integration-tests/in-process-embedding-bge-small-en/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java b/integration-tests/in-process-embedding-bge-small-en/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java new file mode 100644 index 000000000..6016bed7a --- /dev/null +++ b/integration-tests/in-process-embedding-bge-small-en/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java @@ -0,0 +1,8 @@ +package org.acme.test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +class InProcessEmbeddingResourceIT extends InProcessEmbeddingResourceTest { + +} \ No newline at end of file diff --git a/integration-tests/in-process-embedding-bge-small-en/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java b/integration-tests/in-process-embedding-bge-small-en/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java new file mode 100644 index 000000000..6ff16c911 --- /dev/null +++ b/integration-tests/in-process-embedding-bge-small-en/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java @@ -0,0 +1,22 @@ +package org.acme.test; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; + +@QuarkusTest +class InProcessEmbeddingResourceTest { + + @Test + void test() { + var s = RestAssured.given() + .body("This is a sentence.") + .post("/in-process-embedding") + .andReturn().asString(); + Assertions.assertThat(s) + .contains("bgeSmallEnEmbeddingModel: 384\n" + "embeddingModel: 384"); + } + +} diff --git a/integration-tests/in-process-embedding-e5-small-v2-q/pom.xml b/integration-tests/in-process-embedding-e5-small-v2-q/pom.xml new file mode 100644 index 000000000..3206fdc9b --- /dev/null +++ b/integration-tests/in-process-embedding-e5-small-v2-q/pom.xml @@ -0,0 +1,112 @@ + + + 4.0.0 + + + io.quarkiverse.langchain4j + quarkus-langchain4j-integration-tests-parent + 999-SNAPSHOT + + + in-process-embedding-e5-small-v2-q + + + + io.quarkiverse.langchain4j + quarkus-langchain4j-core + ${project.version} + + + + dev.langchain4j + langchain4j-embeddings-e5-small-v2-q + ${langchain4j.version} + + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + io.quarkus + quarkus-devtools-testing + test + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + + + false + native + + + + \ No newline at end of file diff --git a/integration-tests/in-process-embedding-e5-small-v2-q/src/main/java/org/acme/test/InProcessEmbeddingResource.java b/integration-tests/in-process-embedding-e5-small-v2-q/src/main/java/org/acme/test/InProcessEmbeddingResource.java new file mode 100644 index 000000000..77a540cb4 --- /dev/null +++ b/integration-tests/in-process-embedding-e5-small-v2-q/src/main/java/org/acme/test/InProcessEmbeddingResource.java @@ -0,0 +1,28 @@ +package org.acme.test; + +import jakarta.inject.Inject; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import dev.langchain4j.model.embedding.E5SmallV2QuantizedEmbeddingModel; +import dev.langchain4j.model.embedding.EmbeddingModel; + +@Path("/in-process-embedding") +public class InProcessEmbeddingResource { + + @Inject + E5SmallV2QuantizedEmbeddingModel typedModel; + + @Inject + EmbeddingModel embeddingModel; + + @POST + public String computeEmbedding(String sentence) { + var r1 = typedModel.embed(sentence); + var r2 = embeddingModel.embed(sentence); + + return "e5SmallV2QuantizedEmbeddingModel: " + r1.content().dimensions() + "\n" + "embeddingModel: " + + r2.content().dimensions(); + } + +} diff --git a/integration-tests/in-process-embedding-e5-small-v2-q/src/main/resources/application.properties b/integration-tests/in-process-embedding-e5-small-v2-q/src/main/resources/application.properties new file mode 100644 index 000000000..6bdf5ee4b --- /dev/null +++ b/integration-tests/in-process-embedding-e5-small-v2-q/src/main/resources/application.properties @@ -0,0 +1 @@ +#quarkus.native.additional-build-args=--trace-class-initialization=ai.onnxruntime.OnnxRuntime \ No newline at end of file diff --git a/integration-tests/in-process-embedding-e5-small-v2-q/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java b/integration-tests/in-process-embedding-e5-small-v2-q/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java new file mode 100644 index 000000000..6016bed7a --- /dev/null +++ b/integration-tests/in-process-embedding-e5-small-v2-q/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java @@ -0,0 +1,8 @@ +package org.acme.test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +class InProcessEmbeddingResourceIT extends InProcessEmbeddingResourceTest { + +} \ No newline at end of file diff --git a/integration-tests/in-process-embedding-e5-small-v2-q/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java b/integration-tests/in-process-embedding-e5-small-v2-q/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java new file mode 100644 index 000000000..237b285e8 --- /dev/null +++ b/integration-tests/in-process-embedding-e5-small-v2-q/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java @@ -0,0 +1,22 @@ +package org.acme.test; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; + +@QuarkusTest +class InProcessEmbeddingResourceTest { + + @Test + void test() { + var s = RestAssured.given() + .body("This is a sentence.") + .post("/in-process-embedding") + .andReturn().asString(); + Assertions.assertThat(s) + .contains("e5SmallV2QuantizedEmbeddingModel: 384\n" + "embeddingModel: 384"); + } + +} diff --git a/integration-tests/in-process-embedding-e5-small-v2/pom.xml b/integration-tests/in-process-embedding-e5-small-v2/pom.xml new file mode 100644 index 000000000..4a7d314be --- /dev/null +++ b/integration-tests/in-process-embedding-e5-small-v2/pom.xml @@ -0,0 +1,112 @@ + + + 4.0.0 + + + io.quarkiverse.langchain4j + quarkus-langchain4j-integration-tests-parent + 999-SNAPSHOT + + + in-process-embedding-e5-small-v2 + + + + io.quarkiverse.langchain4j + quarkus-langchain4j-core + ${project.version} + + + + dev.langchain4j + langchain4j-embeddings-e5-small-v2 + ${langchain4j.version} + + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + io.quarkus + quarkus-devtools-testing + test + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + + + false + native + + + + \ No newline at end of file diff --git a/integration-tests/in-process-embedding-e5-small-v2/src/main/java/org/acme/test/InProcessEmbeddingResource.java b/integration-tests/in-process-embedding-e5-small-v2/src/main/java/org/acme/test/InProcessEmbeddingResource.java new file mode 100644 index 000000000..a251b20c9 --- /dev/null +++ b/integration-tests/in-process-embedding-e5-small-v2/src/main/java/org/acme/test/InProcessEmbeddingResource.java @@ -0,0 +1,28 @@ +package org.acme.test; + +import jakarta.inject.Inject; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; + +import dev.langchain4j.model.embedding.E5SmallV2EmbeddingModel; +import dev.langchain4j.model.embedding.EmbeddingModel; + +@Path("/in-process-embedding") +public class InProcessEmbeddingResource { + + @Inject + E5SmallV2EmbeddingModel typedModel; + + @Inject + EmbeddingModel embeddingModel; + + @POST + public String computeEmbedding(String sentence) { + var r1 = typedModel.embed(sentence); + var r2 = embeddingModel.embed(sentence); + + return "e5SmallV2EmbeddingModel: " + r1.content().dimensions() + "\n" + "embeddingModel: " + + r2.content().dimensions(); + } + +} diff --git a/integration-tests/in-process-embedding-e5-small-v2/src/main/resources/application.properties b/integration-tests/in-process-embedding-e5-small-v2/src/main/resources/application.properties new file mode 100644 index 000000000..6bdf5ee4b --- /dev/null +++ b/integration-tests/in-process-embedding-e5-small-v2/src/main/resources/application.properties @@ -0,0 +1 @@ +#quarkus.native.additional-build-args=--trace-class-initialization=ai.onnxruntime.OnnxRuntime \ No newline at end of file diff --git a/integration-tests/in-process-embedding-e5-small-v2/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java b/integration-tests/in-process-embedding-e5-small-v2/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java new file mode 100644 index 000000000..6016bed7a --- /dev/null +++ b/integration-tests/in-process-embedding-e5-small-v2/src/test/java/org/acme/test/InProcessEmbeddingResourceIT.java @@ -0,0 +1,8 @@ +package org.acme.test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +class InProcessEmbeddingResourceIT extends InProcessEmbeddingResourceTest { + +} \ No newline at end of file diff --git a/integration-tests/in-process-embedding-e5-small-v2/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java b/integration-tests/in-process-embedding-e5-small-v2/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java new file mode 100644 index 000000000..0b58f5469 --- /dev/null +++ b/integration-tests/in-process-embedding-e5-small-v2/src/test/java/org/acme/test/InProcessEmbeddingResourceTest.java @@ -0,0 +1,22 @@ +package org.acme.test; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; + +@QuarkusTest +class InProcessEmbeddingResourceTest { + + @Test + void test() { + var s = RestAssured.given() + .body("This is a sentence.") + .post("/in-process-embedding") + .andReturn().asString(); + Assertions.assertThat(s) + .contains("e5SmallV2EmbeddingModel: 384\n" + "embeddingModel: 384"); + } + +} diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index b7d4ea9b0..428b2beea 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -16,5 +16,11 @@ hugging-face ollama azure-openai + in-process-embedding-all-minilm-l6-v2-q + in-process-embedding-all-minilm-l6-v2 + in-process-embedding-bge-small-en-q + in-process-embedding-bge-small-en + in-process-embedding-e5-small-v2-q + in-process-embedding-e5-small-v2