Skip to content

Commit 5a1cf4a

Browse files
committed
Introduce S3 Vector Store Support
Signed-off-by: matejnedic <[email protected]>
1 parent 9d1e1b5 commit 5a1cf4a

File tree

16 files changed

+1081
-1
lines changed

16 files changed

+1081
-1
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Copyright 2023-2025 the original author or authors.
4+
~
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~
9+
~ https://www.apache.org/licenses/LICENSE-2.0
10+
~
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19+
xmlns="http://maven.apache.org/POM/4.0.0"
20+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
21+
<modelVersion>4.0.0</modelVersion>
22+
<parent>
23+
<groupId>org.springframework.ai</groupId>
24+
<artifactId>spring-ai-parent</artifactId>
25+
<version>1.1.0-SNAPSHOT</version>
26+
<relativePath>../../../pom.xml</relativePath>
27+
</parent>
28+
<artifactId>spring-ai-autoconfigure-vector-store-s3</artifactId>
29+
<packaging>jar</packaging>
30+
<name>Spring AI Auto Configuration for S3 vector store</name>
31+
<description>Spring AI Auto Configuration for S3 vector store</description>
32+
<url>https://github.com/spring-projects/spring-ai</url>
33+
34+
<scm>
35+
<url>https://github.com/spring-projects/spring-ai</url>
36+
<connection>git://github.com/spring-projects/spring-ai.git</connection>
37+
<developerConnection>[email protected]:spring-projects/spring-ai.git</developerConnection>
38+
</scm>
39+
40+
<dependencies>
41+
<!-- production dependencies -->
42+
<dependency>
43+
<groupId>org.springframework.ai</groupId>
44+
<artifactId>spring-ai-s3-vector-store</artifactId>
45+
<version>${project.parent.version}</version>
46+
</dependency>
47+
<dependency>
48+
<groupId>org.springframework.boot</groupId>
49+
<artifactId>spring-boot-starter</artifactId>
50+
</dependency>
51+
<dependency>
52+
<groupId>org.springframework.boot</groupId>
53+
<artifactId>spring-boot-configuration-processor</artifactId>
54+
<optional>true</optional>
55+
</dependency>
56+
<dependency>
57+
<groupId>org.springframework.boot</groupId>
58+
<artifactId>spring-boot-autoconfigure-processor</artifactId>
59+
<optional>true</optional>
60+
</dependency>
61+
<!-- test dependencies -->
62+
<dependency>
63+
<groupId>org.springframework.ai</groupId>
64+
<artifactId>spring-ai-test</artifactId>
65+
<version>${project.parent.version}</version>
66+
<scope>test</scope>
67+
</dependency>
68+
<dependency>
69+
<groupId>org.springframework.boot</groupId>
70+
<artifactId>spring-boot-starter-test</artifactId>
71+
<scope>test</scope>
72+
</dependency>
73+
<dependency>
74+
<groupId>org.awaitility</groupId>
75+
<artifactId>awaitility</artifactId>
76+
<scope>test</scope>
77+
</dependency>
78+
<dependency>
79+
<groupId>org.springframework.ai</groupId>
80+
<artifactId>spring-ai-transformers</artifactId>
81+
<version>${project.parent.version}</version>
82+
<scope>test</scope>
83+
</dependency>
84+
</dependencies>
85+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.springframework.ai.vectorstore.s3.autoconfigure;
2+
3+
4+
import org.springframework.ai.embedding.EmbeddingModel;
5+
import org.springframework.ai.vectorstore.SpringAIVectorStoreTypes;
6+
import org.springframework.ai.vectorstore.s3.S3VectorStore;
7+
import org.springframework.boot.autoconfigure.AutoConfiguration;
8+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
9+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
10+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
11+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
12+
import org.springframework.context.annotation.Bean;
13+
import org.springframework.util.Assert;
14+
import software.amazon.awssdk.services.s3vectors.S3VectorsClient;
15+
16+
/**
17+
* {@link AutoConfiguration Auto-configuration} for S3 Vector Store.
18+
*
19+
* @author Matej Nedic
20+
*/
21+
@AutoConfiguration
22+
@ConditionalOnClass({S3VectorsClient.class, EmbeddingModel.class})
23+
@EnableConfigurationProperties(S3VectorStoreProperties.class)
24+
@ConditionalOnProperty(name = SpringAIVectorStoreTypes.TYPE, havingValue = SpringAIVectorStoreTypes.S3,
25+
matchIfMissing = true)
26+
public class S3VectorStoreAutoConfiguration {
27+
28+
private final S3VectorStoreProperties properties;
29+
30+
S3VectorStoreAutoConfiguration(S3VectorStoreProperties p) {
31+
Assert.notNull(p.getIndexName(), "Index name cannot be null!");
32+
Assert.notNull(p.getVectorBucketName(), "Bucket name cannot be null");
33+
this.properties = p;
34+
}
35+
@Bean
36+
@ConditionalOnMissingBean
37+
S3VectorStore s3VectorStore(S3VectorsClient s3VectorsClient, EmbeddingModel embeddingModel) {
38+
S3VectorStore.Builder builder = new S3VectorStore.Builder(s3VectorsClient, embeddingModel);
39+
builder.indexName(properties.getIndexName()).vectorBucketName(properties.getVectorBucketName());
40+
return builder.build();
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.springframework.ai.vectorstore.s3.autoconfigure;
2+
3+
4+
import org.springframework.boot.context.properties.ConfigurationProperties;
5+
6+
7+
/**
8+
* @author Matej Nedic
9+
*/
10+
@ConfigurationProperties(prefix = S3VectorStoreProperties.CONFIG_PREFIX)
11+
public class S3VectorStoreProperties {
12+
13+
public static final String CONFIG_PREFIX = "spring.ai.vectorstore.s3";
14+
15+
private String indexName;
16+
17+
private String vectorBucketName;
18+
19+
public String getIndexName() {
20+
return indexName;
21+
}
22+
23+
public void setIndexName(String indexName) {
24+
this.indexName = indexName;
25+
}
26+
27+
public String getVectorBucketName() {
28+
return vectorBucketName;
29+
}
30+
31+
public void setVectorBucketName(String vectorBucketName) {
32+
this.vectorBucketName = vectorBucketName;
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#
2+
# Copyright 2025-2025 the original author or authors.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# https://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
org.springframework.ai.vectorstore.s3.autoconfigure.S3VectorStoreAutoConfiguration
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package org.springframework.ai.vectorstore.azure.autoconfigure;
2+
3+
4+
import io.micrometer.observation.tck.TestObservationRegistry;
5+
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.api.extension.ExtendWith;
7+
import org.springframework.ai.embedding.EmbeddingModel;
8+
import org.springframework.ai.transformers.TransformersEmbeddingModel;
9+
import org.springframework.ai.vectorstore.VectorStore;
10+
import org.springframework.ai.vectorstore.s3.S3VectorStore;
11+
import org.springframework.ai.vectorstore.s3.autoconfigure.S3VectorStoreAutoConfiguration;
12+
import org.springframework.boot.autoconfigure.AutoConfigurations;
13+
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
14+
import org.springframework.boot.test.system.OutputCaptureExtension;
15+
import org.springframework.context.annotation.Bean;
16+
import org.springframework.context.annotation.Configuration;
17+
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
18+
import software.amazon.awssdk.regions.Region;
19+
import software.amazon.awssdk.services.s3vectors.S3VectorsClient;
20+
21+
import static org.assertj.core.api.Assertions.assertThat;
22+
23+
/**
24+
* @author Matej Nedic
25+
*/
26+
@ExtendWith(OutputCaptureExtension.class)
27+
public class S3VectorStoreAutoConfigurationTest {
28+
29+
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
30+
.withConfiguration(AutoConfigurations.of( S3VectorStoreAutoConfiguration.class))
31+
.withUserConfiguration(Config.class)
32+
.withPropertyValues("spring.ai.vectorstore.s3.vectorBucketName=testBucket")
33+
.withPropertyValues("spring.ai.vectorstore.s3.indexName=testIndex");
34+
35+
@Test
36+
public void autoConfigurationDisabledWhenTypeIsNone() {
37+
this.contextRunner.withPropertyValues("spring.ai.vectorstore.type=none").run(context -> {
38+
assertThat(context.getBeansOfType(S3VectorStore.class)).isEmpty();
39+
assertThat(context.getBeansOfType(VectorStore.class)).isEmpty();
40+
});
41+
}
42+
43+
@Test
44+
public void autoConfigurationEnabledByDefault() {
45+
this.contextRunner.run(context -> {
46+
assertThat(context.getBeansOfType(S3VectorStore.class)).isNotEmpty();
47+
assertThat(context.getBeansOfType(VectorStore.class)).isNotEmpty();
48+
assertThat(context.getBean(VectorStore.class)).isInstanceOf(S3VectorStore.class);
49+
});
50+
}
51+
52+
@Test
53+
public void autoConfigurationEnabledWhenTypeIsS3() {
54+
this.contextRunner.withPropertyValues("spring.ai.vectorstore.type=S3").run(context -> {
55+
assertThat(context.getBeansOfType(S3VectorStore.class)).isNotEmpty();
56+
assertThat(context.getBeansOfType(VectorStore.class)).isNotEmpty();
57+
assertThat(context.getBean(VectorStore.class)).isInstanceOf(S3VectorStore.class);
58+
});
59+
}
60+
61+
@Configuration(proxyBeanMethods = false)
62+
static class Config {
63+
64+
@Bean
65+
public S3VectorsClient s3VectorsClient() {
66+
return S3VectorsClient.builder().region(Region.US_EAST_1).credentialsProvider(DefaultCredentialsProvider.builder().build()).build();
67+
}
68+
69+
@Bean
70+
public EmbeddingModel embeddingModel() {
71+
return new TransformersEmbeddingModel();
72+
}
73+
74+
}
75+
76+
77+
}

pom.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
<module>vector-stores/spring-ai-redis-store</module>
7979
<module>vector-stores/spring-ai-typesense-store</module>
8080
<module>vector-stores/spring-ai-weaviate-store</module>
81-
81+
<module>vector-stores/spring-ai-s3-vector-store</module>
8282

8383
<module>auto-configurations/common/spring-ai-autoconfigure-retry</module>
8484

@@ -140,6 +140,7 @@
140140
<module>auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-typesense</module>
141141
<module>auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-weaviate</module>
142142
<module>auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-pgvector</module>
143+
<module>auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-s3</module>
143144

144145
<module>spring-ai-spring-boot-starters/spring-ai-starter-vector-store-aws-opensearch</module>
145146
<module>spring-ai-spring-boot-starters/spring-ai-starter-vector-store-azure</module>
@@ -161,6 +162,7 @@
161162
<module>spring-ai-spring-boot-starters/spring-ai-starter-vector-store-redis</module>
162163
<module>spring-ai-spring-boot-starters/spring-ai-starter-vector-store-typesense</module>
163164
<module>spring-ai-spring-boot-starters/spring-ai-starter-vector-store-weaviate</module>
165+
<module>spring-ai-spring-boot-starters/spring-ai-starter-vector-store-s3</module>
164166

165167
<module>models/spring-ai-anthropic</module>
166168
<module>models/spring-ai-azure-openai</module>

spring-ai-commons/src/main/java/org/springframework/ai/observation/conventions/VectorStoreProvider.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ public enum VectorStoreProvider {
120120
*/
121121
REDIS("redis"),
122122

123+
/**
124+
* Vector store provided by simple.
125+
*/
126+
S3_VECTOR("s3_vector"),
127+
123128
/**
124129
* Vector store provided by simple.
125130
*/
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<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">
2+
<modelVersion>4.0.0</modelVersion>
3+
<parent>
4+
<groupId>org.springframework.ai</groupId>
5+
<artifactId>spring-ai-parent</artifactId>
6+
<version>1.1.0-SNAPSHOT</version>
7+
<relativePath>../../pom.xml</relativePath>
8+
</parent>
9+
<artifactId>spring-ai-starter-vector-store-s3</artifactId>
10+
<packaging>jar</packaging>
11+
<name>Spring AI Starter - S3 Vector Store</name>
12+
<description>Spring AI S3 Vector Store Starter</description>
13+
<url>https://github.com/spring-projects/spring-ai</url>
14+
15+
<scm>
16+
<url>https://github.com/spring-projects/spring-ai</url>
17+
<connection>git://github.com/spring-projects/spring-ai.git</connection>
18+
<developerConnection>[email protected]:spring-projects/spring-ai.git</developerConnection>
19+
</scm>
20+
21+
22+
<dependencies>
23+
<dependency>
24+
<groupId>org.springframework.boot</groupId>
25+
<artifactId>spring-boot-starter</artifactId>
26+
</dependency>
27+
<dependency>
28+
<groupId>org.springframework.ai</groupId>
29+
<artifactId>spring-ai-autoconfigure-vector-store-s3</artifactId>
30+
<version>${project.parent.version}</version>
31+
</dependency>
32+
<dependency>
33+
<groupId>org.springframework.ai</groupId>
34+
<artifactId>spring-ai-autoconfigure-vector-store-observation</artifactId>
35+
<version>${project.parent.version}</version>
36+
</dependency>
37+
<dependency>
38+
<groupId>org.springframework.ai</groupId>
39+
<artifactId>spring-ai-s3-vector-store</artifactId>
40+
<version>${project.parent.version}</version>
41+
</dependency>
42+
</dependencies>
43+
44+
45+
46+
</project>

spring-ai-vector-store/src/main/java/org/springframework/ai/vectorstore/SpringAIVectorStoreTypes.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,6 @@ private SpringAIVectorStoreTypes() {
6464

6565
public static final String WEAVIATE = "weaviate";
6666

67+
public static final String S3 = "S3";
68+
6769
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
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">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<groupId>org.springframework.ai</groupId>
6+
<artifactId>spring-ai-parent</artifactId>
7+
<version>1.1.0-SNAPSHOT</version>
8+
<relativePath>../../pom.xml</relativePath>
9+
</parent>
10+
11+
<artifactId>spring-ai-s3-vector-store</artifactId>
12+
<dependencies>
13+
<dependency>
14+
<groupId>org.springframework.ai</groupId>
15+
<artifactId>spring-ai-vector-store</artifactId>
16+
<version>${project.parent.version}</version>
17+
</dependency>
18+
19+
<dependency>
20+
<groupId>software.amazon.awssdk</groupId>
21+
<artifactId>s3vectors</artifactId>
22+
<version>2.32.2</version>
23+
</dependency>
24+
25+
26+
<!-- TESTING -->
27+
<dependency>
28+
<groupId>org.springframework.ai</groupId>
29+
<artifactId>spring-ai-transformers</artifactId>
30+
<version>${project.parent.version}</version>
31+
<scope>test</scope>
32+
</dependency>
33+
34+
<dependency>
35+
<groupId>org.springframework.ai</groupId>
36+
<artifactId>spring-ai-test</artifactId>
37+
<version>${project.parent.version}</version>
38+
<scope>test</scope>
39+
</dependency>
40+
41+
<dependency>
42+
<groupId>org.springframework.boot</groupId>
43+
<artifactId>spring-boot-starter-test</artifactId>
44+
<scope>test</scope>
45+
</dependency>
46+
</dependencies>
47+
</project>

0 commit comments

Comments
 (0)