Skip to content

Commit

Permalink
Merge pull request #2 from Martin7-1/redis-spring-boot-starter
Browse files Browse the repository at this point in the history
[Feature] Redis embedding store spring boot starter
  • Loading branch information
langchain4j authored Jul 2, 2024
2 parents cd5d1c8 + 02e8718 commit ec2fc73
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .sdkmanrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
# See https://sdkman.io/usage#config
# A summary is to add the following to ~/.sdkman/etc/config
# sdkman_auto_env=true
java=17.0.11-tem
java=17.0.11-tem
2 changes: 1 addition & 1 deletion langchain4j-qianfan-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring</artifactId>
<version>0.30.0</version>
<version>0.32.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
91 changes: 91 additions & 0 deletions langchain4j-redis-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring</artifactId>
<version>0.32.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>langchain4j-redis-spring-boot-starter</artifactId>
<name>LangChain4j Spring Boot starter for Redis</name>
<packaging>jar</packaging>

<dependencies>

<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-redis</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>

<!-- should be listed before spring-boot-configuration-processor -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>

<!-- needed to generate automatic metadata about available config properties -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-embeddings-all-minilm-l6-v2-q</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-tests</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<type>test-jar</type>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.redis.testcontainers</groupId>
<artifactId>testcontainers-redis</artifactId>
<version>1.6.4</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.tinylog</groupId>
<artifactId>tinylog-impl</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.tinylog</groupId>
<artifactId>slf4j-tinylog</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package dev.langchain4j.store.embedding.redis.spring;

import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.store.embedding.redis.RedisEmbeddingStore;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.lang.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static dev.langchain4j.store.embedding.redis.spring.RedisEmbeddingStoreProperties.PREFIX;

@AutoConfiguration
@EnableConfigurationProperties(RedisEmbeddingStoreProperties.class)
@ConditionalOnProperty(prefix = PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
public class RedisEmbeddingStoreAutoConfiguration {

private static final String DEFAULT_HOST = "localhost";
private static final int DEFAULT_PORT = 6379;
private static final String DEFAULT_INDEX_NAME = "langchain4j-index";

@Bean
@ConditionalOnMissingBean
public RedisEmbeddingStore redisEmbeddingStore(RedisEmbeddingStoreProperties properties,
@Nullable EmbeddingModel embeddingModel) {
String host = Optional.ofNullable(properties.getHost()).orElse(DEFAULT_HOST);
int port = Optional.ofNullable(properties.getPort()).orElse(DEFAULT_PORT);
String indexName = Optional.ofNullable(properties.getIndexName()).orElse(DEFAULT_INDEX_NAME);
Integer dimension = Optional.ofNullable(properties.getDimension()).orElseGet(() -> embeddingModel == null ? null : embeddingModel.dimension());
List<String> metadataKeys = Optional.ofNullable(properties.getMetadataKeys()).orElse(new ArrayList<>());

return RedisEmbeddingStore.builder()
.host(host)
.port(port)
.user(properties.getUser())
.password(properties.getPassword())
.indexName(indexName)
.dimension(dimension)
.metadataKeys(metadataKeys)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.langchain4j.store.embedding.redis.spring;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.ArrayList;
import java.util.List;

@ConfigurationProperties(prefix = RedisEmbeddingStoreProperties.PREFIX)
@Getter
@Setter
public class RedisEmbeddingStoreProperties {

static final String PREFIX = "langchain4j.redis";

private String host;
private Integer port;
private String user;
private String password;
private String indexName;
private Integer dimension;
private List<String> metadataKeys;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=dev.langchain4j.store.embedding.redis.spring.RedisEmbeddingStoreAutoConfiguration
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package dev.langchain4j.store.embedding.redis.spring;

import com.redis.testcontainers.RedisContainer;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.redis.RedisEmbeddingStore;
import dev.langchain4j.store.embedding.spring.EmbeddingStoreAutoConfigurationIT;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.testcontainers.containers.wait.strategy.Wait;
import redis.clients.jedis.JedisPooled;

import static com.redis.testcontainers.RedisStackContainer.DEFAULT_IMAGE_NAME;
import static com.redis.testcontainers.RedisStackContainer.DEFAULT_TAG;

class RedisEmbeddingStoreAutoConfigurationIT extends EmbeddingStoreAutoConfigurationIT {

static RedisContainer redis = new RedisContainer(DEFAULT_IMAGE_NAME.withTag(DEFAULT_TAG))
.waitingFor(Wait.defaultWaitStrategy());

@BeforeAll
static void beforeAll() {
redis.start();
}

@AfterAll
static void afterAll() {
redis.stop();
}

@BeforeEach
void beforeEach() {
try (JedisPooled jedis = new JedisPooled(redis.getHost(), redis.getFirstMappedPort())) {
jedis.flushDB(); // TODO fix: why redis returns embeddings from different indexes?
}
}

@Override
protected Class<?> autoConfigurationClass() {
return RedisEmbeddingStoreAutoConfiguration.class;
}

@Override
protected Class<? extends EmbeddingStore<TextSegment>> embeddingStoreClass() {
return RedisEmbeddingStore.class;
}

@Override
protected String[] properties() {
return new String[]{
"langchain4j.redis.host=" + redis.getHost(),
"langchain4j.redis.port=" + redis.getFirstMappedPort()
};
}

@Override
protected String dimensionPropertyKey() {
return "langchain4j.redis.dimension";
}
}
2 changes: 2 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@
<module>langchain4j-azure-open-ai-spring-boot-starter</module>
<module>langchain4j-vertex-ai-gemini-spring-boot-starter</module>
<module>langchian4j-elasticsearch-spring-boot-starter</module>
<module>langchain4j-redis-spring-boot-starter</module>
<module>langchain4j-qianfan-spring-boot-starter</module>
</modules>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<tinylog.version>2.6.2</tinylog.version>
<spring.boot.version>3.2.6</spring.boot.version>
<testcontainers.version>1.19.8</testcontainers.version>
<tinylog.version>2.6.2</tinylog.version>
Expand Down

0 comments on commit ec2fc73

Please sign in to comment.