Add transformers-embedding boot auto-configuraiton and starter
This commit is contained in:
@@ -34,9 +34,9 @@ Add the `transformers-embedding` project to your maven dependencies:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>transformers-embedding</artifactId>
|
||||
<version>0.7.1-SNAPSHOT</version>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>transformers-embedding</artifactId>
|
||||
<version>0.7.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
@@ -51,8 +51,7 @@ If the model is not explicitly set, `TransformersEmbeddingClient` defaults to [s
|
||||
| Speed | 14200 sentences/sec |
|
||||
| Size | 80MB |
|
||||
|
||||
|
||||
Following snippet illustrates how to use the `TransformersEmbeddingClient`:
|
||||
Following snippet illustrates how to use the `TransformersEmbeddingClient` manually:
|
||||
|
||||
```java
|
||||
TransformersEmbeddingClient embeddingClient = new TransformersEmbeddingClient();
|
||||
@@ -73,14 +72,53 @@ List<List<Double>> embeddings = embeddingClient.embed(List.of("Hello world", "Wo
|
||||
|
||||
```
|
||||
|
||||
Note that when created manually you have to call the `afterPropertiesSet()` after setting the properties and before using the client.
|
||||
|
||||
The first `embed()` call downloads the the large ONNX model and caches it on the local file system.
|
||||
Therefore the first call might take longer than usual.
|
||||
Use the `#setResourceCacheDirectory(<path>)` to set the local folder where the ONNX models as stored.
|
||||
The default cache folder is `${java.io.tmpdir}/spring-ai-onnx-model`.
|
||||
|
||||
It is more convenient (and preferred) to create the TransformersEmbeddingClient as a `Bean`.
|
||||
Then you don't have to call the `afterPropertiesSet()` manually.
|
||||
|
||||
```java
|
||||
@Bean
|
||||
public EmbeddingClient embeddingClient() {
|
||||
return new TransformersEmbeddingClient();
|
||||
}
|
||||
```
|
||||
|
||||
## Transformers Embedding Spring Boot Starter.
|
||||
|
||||
You can bootstrap and auto-wire the `TransformersEmbeddingClient` with following boot starer:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-transformers-embedding-spring-boot-starter</artifactId>
|
||||
<version>0.7.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
and use the `spring.ai.embedding.transformer.*` properties to configure it.
|
||||
|
||||
For example add this to your application.properties to configure with the [intfloat/e5-small-v2](https://huggingface.co/intfloat/e5-small-v2) text embedding model:
|
||||
|
||||
```
|
||||
spring.ai.embedding.transformer.onnx.modelUri=https://huggingface.co/intfloat/e5-small-v2/resolve/main/model.onnx
|
||||
spring.ai.embedding.transformer.tokenizer.uri=https://huggingface.co/intfloat/e5-small-v2/raw/main/tokenizer.json
|
||||
```
|
||||
|
||||
The complete list of supported properties are:
|
||||
|
||||
| Property | Description | Default |
|
||||
| -------- | ------- | ------- |
|
||||
| spring.ai.embedding.transformer.tokenizer.uri | URI of a pre-trained HuggingFaceTokenizer created by the ONNX engine (e.g. tokenizer.json). | onnx/all-MiniLM-L6-v2/tokenizer.json |
|
||||
| spring.ai.embedding.transformer.tokenizer.options | HuggingFaceTokenizer options such as '`addSpecialTokens`', '`modelMaxLength`', '`truncation`', '`padding`', '`maxLength`', '`stride`' and '`padToMultipleOf`'. Leave empty to fallback to the defaults. | empty |
|
||||
| spring.ai.embedding.transformer.cache.enabled | Enable remote Resource caching. | true |
|
||||
| spring.ai.embedding.transformer.cache.directory | Directory path to cache remote resources, such as the ONNX models | ${java.io.tmpdir}/spring-ai-onnx-model |
|
||||
| spring.ai.embedding.transformer.onnx.modelUri | Existing, pre-trained ONNX model. | onnx/all-MiniLM-L6-v2/model.onnx |
|
||||
| spring.ai.embedding.transformer.onnx.gpuDeviceId | The GPU device ID to execute on. Only applicable if >= 0. Ignored otherwise. | -1 |
|
||||
| spring.ai.embedding.transformer.metadataMode | Specifies what parts of the Documents content and metadata will be used for computing the embeddings. | NONE |
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
</parent>
|
||||
<artifactId>transformers-embedding</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring AI Embedding Client - Sentence Transormers Embeddings </name>
|
||||
<description>Spring AI Sentence Transformers Embedding Client</description>
|
||||
<name>Spring AI Transormers Embedding Client</name>
|
||||
<description>Spring AI Transformers Embedding Client</description>
|
||||
<url>https://github.com/spring-projects-experimental/spring-ai</url>
|
||||
|
||||
<scm>
|
||||
@@ -21,8 +21,8 @@
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<djl.version>0.24.0</djl.version>
|
||||
<onnxruntime.version>1.16.1</onnxruntime.version>
|
||||
<djl.version>0.25.0</djl.version>
|
||||
<onnxruntime.version>1.16.2</onnxruntime.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ai.djl.huggingface.tokenizers.Encoding;
|
||||
import ai.djl.huggingface.tokenizers.HuggingFaceTokenizer;
|
||||
@@ -17,6 +18,8 @@ import ai.onnxruntime.OnnxValue;
|
||||
import ai.onnxruntime.OrtEnvironment;
|
||||
import ai.onnxruntime.OrtException;
|
||||
import ai.onnxruntime.OrtSession;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
@@ -33,12 +36,14 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class TransformersEmbeddingClient implements EmbeddingClient, InitializingBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TransformersEmbeddingClient.class);
|
||||
|
||||
// ONNX tokenizer for the all-MiniLM-L6-v2 model
|
||||
private final static String DEFAULT_ONNX_TOKENIZER_URI = "https://raw.githubusercontent.com/spring-projects-experimental/spring-ai/main/embedding-clients/transformers-embedding/src/main/resources/onnx/all-MiniLM-L6-v2/tokenizer.json";
|
||||
public final static String DEFAULT_ONNX_TOKENIZER_URI = "https://raw.githubusercontent.com/spring-projects-experimental/spring-ai/main/embedding-clients/transformers-embedding/src/main/resources/onnx/all-MiniLM-L6-v2/tokenizer.json";
|
||||
|
||||
// ONNX model for all-MiniLM-L6-v2 pre-trained transformer:
|
||||
// https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2
|
||||
private final static String DEFAULT_ONNX_MODEL_URI = "https://github.com/spring-projects-experimental/spring-ai/raw/main/embedding-clients/transformers-embedding/src/main/resources/onnx/all-MiniLM-L6-v2/model.onnx";
|
||||
public final static String DEFAULT_ONNX_MODEL_URI = "https://github.com/spring-projects-experimental/spring-ai/raw/main/embedding-clients/transformers-embedding/src/main/resources/onnx/all-MiniLM-L6-v2/model.onnx";
|
||||
|
||||
private final static int EMBEDDING_AXIS = 1;
|
||||
|
||||
@@ -59,10 +64,19 @@ public class TransformersEmbeddingClient implements EmbeddingClient, Initializin
|
||||
*/
|
||||
private OrtEnvironment environment;
|
||||
|
||||
/**
|
||||
* Runtime session that wraps the ONNX model and enables inference calls.
|
||||
*/
|
||||
private OrtSession session;
|
||||
|
||||
private final AtomicInteger embeddingDimensions = new AtomicInteger(-1);
|
||||
|
||||
/**
|
||||
* Specifies what parts of the {@link Document}'s content and metadata will be used
|
||||
* for computing the embeddings. Applicable for the {@link #embed(Document)} method
|
||||
* only. Has no effect on the {@link #embed(String)} or {@link #embed(List)}. Defaults
|
||||
* to {@link MetadataMode#NONE}.
|
||||
*/
|
||||
private final MetadataMode metadataMode;
|
||||
|
||||
/**
|
||||
@@ -76,7 +90,15 @@ public class TransformersEmbeddingClient implements EmbeddingClient, Initializin
|
||||
*/
|
||||
private boolean disableCaching = false;
|
||||
|
||||
private ResourceCacheService cache;
|
||||
/**
|
||||
* Cache service for caching large {@link Resource} contents, such as the
|
||||
* tokenizerResource and modelResource, on the local file system. Can be
|
||||
* enabled/disabled with the {@link #disableCaching} property and uses the
|
||||
* {@link #resourceCacheDirectory} for local storage.
|
||||
*/
|
||||
private ResourceCacheService cacheService;
|
||||
|
||||
public Map<String, String> tokenizerOptions = Map.of();
|
||||
|
||||
public TransformersEmbeddingClient() {
|
||||
this(MetadataMode.NONE);
|
||||
@@ -87,6 +109,10 @@ public class TransformersEmbeddingClient implements EmbeddingClient, Initializin
|
||||
this.metadataMode = metadataMode;
|
||||
}
|
||||
|
||||
public void setTokenizerOptions(Map<String, String> tokenizerOptions) {
|
||||
this.tokenizerOptions = tokenizerOptions;
|
||||
}
|
||||
|
||||
public void setDisableCaching(boolean disableCaching) {
|
||||
this.disableCaching = disableCaching;
|
||||
}
|
||||
@@ -121,23 +147,32 @@ public class TransformersEmbeddingClient implements EmbeddingClient, Initializin
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.cache = StringUtils.hasText(this.resourceCacheDirectory)
|
||||
|
||||
this.cacheService = StringUtils.hasText(this.resourceCacheDirectory)
|
||||
? new ResourceCacheService(this.resourceCacheDirectory) : new ResourceCacheService();
|
||||
|
||||
// Create a pre-trained HuggingFaceTokenizer instance from tokenizerResource
|
||||
// InputStream.
|
||||
this.tokenizer = HuggingFaceTokenizer.newInstance(getCachedResource(this.tokenizerResource).getInputStream(),
|
||||
Map.of());
|
||||
this.tokenizerOptions);
|
||||
|
||||
// onnxruntime
|
||||
this.environment = OrtEnvironment.getEnvironment();
|
||||
|
||||
var sessionOptions = new OrtSession.SessionOptions();
|
||||
if (this.gpuDeviceId >= 0) {
|
||||
// Run on a GPU or with another provider
|
||||
sessionOptions.addCUDA(this.gpuDeviceId);
|
||||
sessionOptions.addCUDA(this.gpuDeviceId); // Run on a GPU or with another
|
||||
// provider
|
||||
}
|
||||
this.session = this.environment.createSession(getCachedResource(this.modelResource).getContentAsByteArray(),
|
||||
sessionOptions);
|
||||
|
||||
logger.info("Model input names: " + this.session.getInputNames().stream().collect(Collectors.joining(", ")));
|
||||
logger.info("Model output names: " + this.session.getOutputNames().stream().collect(Collectors.joining(", ")));
|
||||
}
|
||||
|
||||
private Resource getCachedResource(Resource resource) {
|
||||
return this.disableCaching ? resource : this.cache.getCachedResource(resource);
|
||||
return this.disableCaching ? resource : this.cacheService.getCachedResource(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -186,6 +221,9 @@ public class TransformersEmbeddingClient implements EmbeddingClient, Initializin
|
||||
Map<String, OnnxTensor> modelInputs = Map.of("input_ids", inputIds, "attention_mask", attentionMask,
|
||||
"token_type_ids", tokenTypeIds);
|
||||
|
||||
// The Run result object is AutoCloseable to prevent references from leaking
|
||||
// out. Once the Result object is
|
||||
// closed, all it’s child OnnxValues are closed too.
|
||||
try (OrtSession.Result results = this.session.run(modelInputs)) {
|
||||
|
||||
// OnnxValue lastHiddenState = results.get(0);
|
||||
|
||||
@@ -16,11 +16,9 @@
|
||||
|
||||
package org.springframework.ai.embedding;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import org.springframework.ai.document.Document;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Add the `spring-ai-huggingface` dependency:
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-huggingface</artifactId>
|
||||
<version>0.7.0-SNAPSHOT</version>
|
||||
<version>0.7.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
@@ -49,6 +49,14 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Transformers Embedding Client -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>transformers-embedding</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Pinecone Vector Store-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
@@ -87,7 +95,6 @@
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
@@ -125,13 +132,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>transformers-embedding</artifactId>
|
||||
<version>${parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.autoconfigure.embedding.transformer;
|
||||
|
||||
import ai.djl.huggingface.tokenizers.HuggingFaceTokenizer;
|
||||
import ai.onnxruntime.OrtSession;
|
||||
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.embedding.TransformersEmbeddingClient;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties({ TransformersEmbeddingClientProperties.class })
|
||||
@ConditionalOnClass({ OrtSession.class, HuggingFaceTokenizer.class })
|
||||
public class TransformersEmbeddingClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EmbeddingClient embeddingClient(TransformersEmbeddingClientProperties properties) {
|
||||
|
||||
TransformersEmbeddingClient embeddingClient = new TransformersEmbeddingClient(properties.getMetadataMode());
|
||||
|
||||
embeddingClient.setDisableCaching(!properties.getCache().isEnabled());
|
||||
embeddingClient.setResourceCacheDirectory(properties.getCache().getDirectory());
|
||||
|
||||
embeddingClient.setTokenizerResource(properties.getTokenizer().getUri());
|
||||
embeddingClient.setTokenizerOptions(properties.getTokenizer().getOptions());
|
||||
|
||||
embeddingClient.setModelResource(properties.getOnnx().getModelUri());
|
||||
|
||||
embeddingClient.setGpuDeviceId(properties.getOnnx().getGpuDeviceId());
|
||||
|
||||
return embeddingClient;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.autoconfigure.embedding.transformer;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import ai.djl.huggingface.tokenizers.HuggingFaceTokenizer;
|
||||
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
import org.springframework.ai.embedding.TransformersEmbeddingClient;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import static org.springframework.ai.autoconfigure.embedding.transformer.TransformersEmbeddingClientProperties.CONFIG_PREFIX;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@ConfigurationProperties(CONFIG_PREFIX)
|
||||
public class TransformersEmbeddingClientProperties {
|
||||
|
||||
public static final String CONFIG_PREFIX = "spring.ai.embedding.transformer";
|
||||
|
||||
public static final String DEFAULT_CACHE_DIRECTORY = new File(System.getProperty("java.io.tmpdir"),
|
||||
"spring-ai-onnx-model")
|
||||
.getAbsolutePath();
|
||||
|
||||
/**
|
||||
* Configurations for the {@link HuggingFaceTokenizer} used to convert sentences into
|
||||
* tokens.
|
||||
*/
|
||||
public static class Tokenizer {
|
||||
|
||||
/**
|
||||
* URI of a pre-trained HuggingFaceTokenizer created by the ONNX engine (e.g.
|
||||
* tokenizer.json).
|
||||
*/
|
||||
private String uri = TransformersEmbeddingClient.DEFAULT_ONNX_TOKENIZER_URI;
|
||||
|
||||
/**
|
||||
* HuggingFaceTokenizer options such as 'addSpecialTokens', 'modelMaxLength',
|
||||
* 'truncation', 'padding', 'maxLength', 'stride' and 'padToMultipleOf'. Leave
|
||||
* empty to fallback to the defaults.
|
||||
*/
|
||||
private Map<String, String> options = new HashMap<>();
|
||||
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public void setUri(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public Map<String, String> getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public void setOptions(Map<String, String> options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final Tokenizer tokenizer = new Tokenizer();
|
||||
|
||||
public static class Cache {
|
||||
|
||||
/**
|
||||
* Enable the {@link Resource} caching.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* Resource cache directory. Used to cache remote resources, such as the ONNX
|
||||
* models, to the local file system. Applicable only for cache.enabled == true.
|
||||
* Defaults to {java.io.tmpdir}/spring-ai-onnx-model.
|
||||
*/
|
||||
private String directory = DEFAULT_CACHE_DIRECTORY;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getDirectory() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
public void setDirectory(String directory) {
|
||||
this.directory = directory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls caching of remote, large resources to local file system.
|
||||
*/
|
||||
private final Cache cache = new Cache();
|
||||
|
||||
public Cache getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public static class Onnx {
|
||||
|
||||
/**
|
||||
* Existing, pre-trained ONNX model. Commonly exported from
|
||||
* https://sbert.net/docs/pretrained_models.html. Defaults to
|
||||
* sentence-transformers/all-MiniLM-L6-v2.
|
||||
*/
|
||||
private String modelUri = TransformersEmbeddingClient.DEFAULT_ONNX_MODEL_URI;
|
||||
|
||||
/**
|
||||
* Run on a GPU or with another provider (optional).
|
||||
* https://onnxruntime.ai/docs/get-started/with-java.html#run-on-a-gpu-or-with-another-provider-optional
|
||||
*
|
||||
* The GPU device ID to execute on. Only applicable if >= 0. Ignored otherwise.
|
||||
*/
|
||||
private int gpuDeviceId = -1;
|
||||
|
||||
public String getModelUri() {
|
||||
return modelUri;
|
||||
}
|
||||
|
||||
public void setModelUri(String modelUri) {
|
||||
this.modelUri = modelUri;
|
||||
}
|
||||
|
||||
public int getGpuDeviceId() {
|
||||
return gpuDeviceId;
|
||||
}
|
||||
|
||||
public void setGpuDeviceId(int gpuDeviceId) {
|
||||
this.gpuDeviceId = gpuDeviceId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final Onnx onnx = new Onnx();
|
||||
|
||||
public Onnx getOnnx() {
|
||||
return onnx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies what parts of the {@link Document}'s content and metadata will be used
|
||||
* for computing the embeddings. Applicable for the
|
||||
* {@link TransformersEmbeddingClient#embed(Document)} method only. Has no effect on
|
||||
* the {@link TransformersEmbeddingClient#embed(String)} or
|
||||
* {@link TransformersEmbeddingClient#embed(List)}. Defaults to
|
||||
* {@link MetadataMode#NONE}.
|
||||
*/
|
||||
private MetadataMode metadataMode = MetadataMode.NONE;
|
||||
|
||||
public Tokenizer getTokenizer() {
|
||||
return tokenizer;
|
||||
}
|
||||
|
||||
public MetadataMode getMetadataMode() {
|
||||
return metadataMode;
|
||||
}
|
||||
|
||||
public void setMetadataMode(MetadataMode metadataMode) {
|
||||
this.metadataMode = metadataMode;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2023-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.ai.autoconfigure.embedding.transformer;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.embedding.TransformersEmbeddingClient;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
public class TransformersEmbeddingClientAutoConfigurationIT {
|
||||
|
||||
@TempDir
|
||||
File tempDir;
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(TransformersEmbeddingClientAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void embedding() {
|
||||
contextRunner.run(context -> {
|
||||
var properties = context.getBean(TransformersEmbeddingClientProperties.class);
|
||||
assertThat(properties.getCache().isEnabled()).isTrue();
|
||||
assertThat(properties.getCache().getDirectory())
|
||||
.isEqualTo(new File(System.getProperty("java.io.tmpdir"), "spring-ai-onnx-model").getAbsolutePath());
|
||||
|
||||
EmbeddingClient embeddingClient = context.getBean(EmbeddingClient.class);
|
||||
assertThat(embeddingClient).isInstanceOf(TransformersEmbeddingClient.class);
|
||||
|
||||
List<List<Double>> embeddings = embeddingClient.embed(List.of("Spring Framework", "Spring AI"));
|
||||
|
||||
assertThat(embeddings.size()).isEqualTo(2); // batch size
|
||||
assertThat(embeddings.get(0).size()).isEqualTo(embeddingClient.dimensions()); // dimensions
|
||||
// size
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remoteOnnxModel() {
|
||||
// https://huggingface.co/intfloat/e5-small-v2
|
||||
contextRunner.withPropertyValues("spring.ai.embedding.transformer.cache.directory=" + tempDir.getAbsolutePath(),
|
||||
"spring.ai.embedding.transformer.onnx.modelUri=https://huggingface.co/intfloat/e5-small-v2/resolve/main/model.onnx",
|
||||
"spring.ai.embedding.transformer.tokenizer.uri=https://huggingface.co/intfloat/e5-small-v2/raw/main/tokenizer.json")
|
||||
.run(context -> {
|
||||
var properties = context.getBean(TransformersEmbeddingClientProperties.class);
|
||||
assertThat(properties.getOnnx().getModelUri())
|
||||
.isEqualTo("https://huggingface.co/intfloat/e5-small-v2/resolve/main/model.onnx");
|
||||
assertThat(properties.getTokenizer().getUri())
|
||||
.isEqualTo("https://huggingface.co/intfloat/e5-small-v2/raw/main/tokenizer.json");
|
||||
|
||||
assertThat(properties.getCache().isEnabled()).isTrue();
|
||||
assertThat(properties.getCache().getDirectory()).isEqualTo(tempDir.getAbsolutePath());
|
||||
assertThat(tempDir.listFiles()).hasSize(2);
|
||||
|
||||
EmbeddingClient embeddingClient = context.getBean(EmbeddingClient.class);
|
||||
assertThat(embeddingClient).isInstanceOf(TransformersEmbeddingClient.class);
|
||||
|
||||
assertThat(embeddingClient.dimensions()).isEqualTo(384);
|
||||
|
||||
List<List<Double>> embeddings = embeddingClient.embed(List.of("Spring Framework", "Spring AI"));
|
||||
|
||||
assertThat(embeddings.size()).isEqualTo(2); // batch size
|
||||
assertThat(embeddings.get(0).size()).isEqualTo(embeddingClient.dimensions()); // dimensions
|
||||
// size
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,12 +27,10 @@ import java.util.UUID;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.testcontainers.containers.DockerComposeContainer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
import org.springframework.ai.ResourceUtils;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.embedding.TransformersEmbeddingClient;
|
||||
@@ -42,6 +40,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai</artifactId>
|
||||
<version>0.7.1-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-ai-transformers-embedding-spring-boot-starter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring AI Starter - Transformers Embedding</name>
|
||||
<description>Spring Transformers Embedding Auto Configuration</description>
|
||||
<url>https://github.com/spring-projects-experimental/spring-ai</url>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/spring-projects-experimental/spring-ai</url>
|
||||
<connection>git://github.com/spring-projects-experimental/spring-ai.git</connection>
|
||||
<developerConnection>git@github.com:spring-projects-experimental/spring-ai.git</developerConnection>
|
||||
</scm>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- production dependencies -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>spring-ai-spring-boot-autoconfigure</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.experimental.ai</groupId>
|
||||
<artifactId>transformers-embedding</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -310,11 +310,6 @@ public class MilvusVectorStoreIT {
|
||||
return new OpenAiEmbeddingClient(new OpenAiService(api), "text-embedding-ada-002");
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// public EmbeddingClient embeddingClient() {
|
||||
// return new TransformersEmbeddingClient();
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user