From d276d17c1f546ec4a2be933e40c786ded603a6c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Wed, 19 Jun 2024 20:18:25 -0500 Subject: [PATCH] Add AWS OpenSearch AutoConfiguration Currently, in order to use an OpenSearch instance provided by AWS, additional steps are needed. This commit introduces the required configuration. Add new starter and update docs --- pom.xml | 2 + spring-ai-bom/pom.xml | 6 + .../ROOT/pages/api/vectordbs/opensearch.adoc | 31 +--- spring-ai-spring-boot-autoconfigure/pom.xml | 13 ++ ...penSearchVectorStoreAutoConfiguration.java | 107 ++++++++---- .../OpenSearchVectorStoreProperties.java | 64 +++++++ ...nSearchVectorStoreAutoConfigurationIT.java | 159 ++++++++++++++++++ ...nSearchVectorStoreAutoConfigurationIT.java | 4 + .../pom.xml | 48 ++++++ 9 files changed, 379 insertions(+), 55 deletions(-) create mode 100644 spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/AwsOpenSearchVectorStoreAutoConfigurationIT.java create mode 100644 spring-ai-spring-boot-starters/spring-ai-starter-aws-opensearch-store/pom.xml diff --git a/pom.xml b/pom.xml index 077de7aed..a70d22acd 100644 --- a/pom.xml +++ b/pom.xml @@ -77,6 +77,7 @@ models/spring-ai-zhipuai models/spring-ai-moonshot spring-ai-spring-boot-starters/spring-ai-starter-anthropic + spring-ai-spring-boot-starters/spring-ai-starter-aws-opensearch-store spring-ai-spring-boot-starters/spring-ai-starter-azure-openai spring-ai-spring-boot-starters/spring-ai-starter-bedrock-ai spring-ai-spring-boot-starters/spring-ai-starter-huggingface @@ -177,6 +178,7 @@ 1.9.1 0.5.0 2.10.1 + 2.20.161 5.3.1 diff --git a/spring-ai-bom/pom.xml b/spring-ai-bom/pom.xml index eec22e618..1614278c3 100644 --- a/spring-ai-bom/pom.xml +++ b/spring-ai-bom/pom.xml @@ -307,6 +307,12 @@ ${project.version} + + org.springframework.ai + spring-ai-aws-opensearch-store-spring-boot-starter + ${project.version} + + org.springframework.ai spring-ai-opensearch-store-spring-boot-starter diff --git a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/opensearch.adoc b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/opensearch.adoc index 83ef08983..c514188b3 100644 --- a/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/opensearch.adoc +++ b/spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/opensearch.adoc @@ -114,7 +114,7 @@ List results = vectorStore.similaritySearch(SearchRequest.query("Sprin === Configuration properties -You can use the following properties in your Spring Boot configuration to customize the PGVector vector store. +You can use the following properties in your Spring Boot configuration to customize the OpenSearch vector store. [cols="2,5,1"] |=== @@ -134,6 +134,11 @@ fields are stored and indexed. | } } } +|`spring.opensearch.aws.host`| Hostname of the OpenSearch instance. | - +|`spring.opensearch.aws.service-name`| AWS service name for the OpenSearch instance. | - +|`spring.opensearch.aws.access-key`| AWS access key for the OpenSearch instance. | - +|`spring.opensearch.aws.secret-key`| AWS secret key for the OpenSearch instance. | - +|`spring.opensearch.aws.region`| AWS region for the OpenSearch instance. | - |=== === Customizing OpenSearch Client Configuration @@ -148,9 +153,8 @@ To enable it, add the following dependency to your project's Maven `pom.xml` fil [source,xml] ---- - software.amazon.awssdk - apache-client - 2.25.40 + org.springframework.ai + spring-ai-aws-opensearch-store-spring-boot-starter ---- @@ -159,24 +163,7 @@ or to your Gradle `build.gradle` build file. [source,groovy] ---- dependencies { - implementation 'software.amazon.awssdk:apache-client:2.25.40' -} ----- - -Here is an example of the needed bean: - -[source,java] ----- -@Bean -public OpenSearchClient openSearchClient() { - return new OpenSearchClient( - new AwsSdk2Transport( - ApacheHttpClient.builder().build(), - "search-...us-west-2.es.amazonaws.com", // OpenSearch endpoint, without https:// - "es", - Region.US_WEST_2, // signing service region - AwsSdk2TransportOptions.builder().build()) - ); + implementation 'org.springframework.ai:spring-ai-aws-opensearch-store-spring-boot-starter' } ---- diff --git a/spring-ai-spring-boot-autoconfigure/pom.xml b/spring-ai-spring-boot-autoconfigure/pom.xml index 9a59492fe..9c7688b49 100644 --- a/spring-ai-spring-boot-autoconfigure/pom.xml +++ b/spring-ai-spring-boot-autoconfigure/pom.xml @@ -342,6 +342,13 @@ true + + software.amazon.awssdk + apache-client + ${awssdk.version} + true + + @@ -435,6 +442,12 @@ test + + org.testcontainers + localstack + test + + org.testcontainers milvus diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfiguration.java index 1763aabc0..97c8a2055 100644 --- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfiguration.java +++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfiguration.java @@ -20,14 +20,24 @@ import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; import org.apache.hc.core5.http.HttpHost; import org.opensearch.client.opensearch.OpenSearchClient; +import org.opensearch.client.transport.OpenSearchTransport; +import org.opensearch.client.transport.aws.AwsSdk2Transport; +import org.opensearch.client.transport.aws.AwsSdk2TransportOptions; import org.opensearch.client.transport.httpclient5.ApacheHttpClient5TransportBuilder; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.vectorstore.OpenSearchVectorStore; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.http.SdkHttpClient; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.regions.Region; import java.net.URISyntaxException; import java.util.List; @@ -48,45 +58,76 @@ public class OpenSearchVectorStoreAutoConfiguration { @ConditionalOnMissingBean OpenSearchVectorStore vectorStore(OpenSearchVectorStoreProperties properties, OpenSearchClient openSearchClient, EmbeddingModel embeddingModel) { - return new OpenSearchVectorStore( - Optional.ofNullable(properties.getIndexName()).orElse(OpenSearchVectorStore.DEFAULT_INDEX_NAME), - openSearchClient, embeddingModel, Optional.ofNullable(properties.getMappingJson()) - .orElse(OpenSearchVectorStore.DEFAULT_MAPPING_EMBEDDING_TYPE_KNN_VECTOR_DIMENSION_1536)); + var indexName = Optional.ofNullable(properties.getIndexName()).orElse(OpenSearchVectorStore.DEFAULT_INDEX_NAME); + var mappingJson = Optional.ofNullable(properties.getMappingJson()) + .orElse(OpenSearchVectorStore.DEFAULT_MAPPING_EMBEDDING_TYPE_KNN_VECTOR_DIMENSION_1536); + return new OpenSearchVectorStore(indexName, openSearchClient, embeddingModel, mappingJson); } - @Bean - @ConditionalOnMissingBean - OpenSearchClient openSearchClient(OpenSearchConnectionDetails connectionDetails) { - HttpHost[] httpHosts = connectionDetails.getUris() - .stream() - .map(s -> createHttpHost(s)) - .toArray(HttpHost[]::new); - ApacheHttpClient5TransportBuilder transportBuilder = ApacheHttpClient5TransportBuilder.builder(httpHosts); + @Configuration(proxyBeanMethods = false) + @ConditionalOnMissingClass({ "software.amazon.awssdk.regions.Region", + "software.amazon.awssdk.http.apache.ApacheHttpClient" }) + static class OpenSearchConfiguration { - Optional.ofNullable(connectionDetails.getUsername()) - .map(username -> createBasicCredentialsProvider(httpHosts[0], username, connectionDetails.getPassword())) - .ifPresent(basicCredentialsProvider -> transportBuilder - .setHttpClientConfigCallback(httpAsyncClientBuilder -> httpAsyncClientBuilder - .setDefaultCredentialsProvider(basicCredentialsProvider))); + @Bean + @ConditionalOnMissingBean + OpenSearchClient openSearchClient(OpenSearchVectorStoreProperties properties) { + HttpHost[] httpHosts = properties.getUris().stream().map(s -> createHttpHost(s)).toArray(HttpHost[]::new); + ApacheHttpClient5TransportBuilder transportBuilder = ApacheHttpClient5TransportBuilder.builder(httpHosts); - return new OpenSearchClient(transportBuilder.build()); - } - - private BasicCredentialsProvider createBasicCredentialsProvider(HttpHost httpHost, String username, - String password) { - BasicCredentialsProvider basicCredentialsProvider = new BasicCredentialsProvider(); - basicCredentialsProvider.setCredentials(new AuthScope(httpHost), - new UsernamePasswordCredentials(username, password.toCharArray())); - return basicCredentialsProvider; - } - - private HttpHost createHttpHost(String s) { - try { - return HttpHost.create(s); + Optional.ofNullable(properties.getUsername()) + .map(username -> createBasicCredentialsProvider(httpHosts[0], username, properties.getPassword())) + .ifPresent(basicCredentialsProvider -> transportBuilder + .setHttpClientConfigCallback(httpAsyncClientBuilder -> httpAsyncClientBuilder + .setDefaultCredentialsProvider(basicCredentialsProvider))); + return new OpenSearchClient(transportBuilder.build()); } - catch (URISyntaxException e) { - throw new RuntimeException(e); + + private BasicCredentialsProvider createBasicCredentialsProvider(HttpHost httpHost, String username, + String password) { + BasicCredentialsProvider basicCredentialsProvider = new BasicCredentialsProvider(); + basicCredentialsProvider.setCredentials(new AuthScope(httpHost), + new UsernamePasswordCredentials(username, password.toCharArray())); + return basicCredentialsProvider; } + + private HttpHost createHttpHost(String s) { + try { + return HttpHost.create(s); + } + catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass({ Region.class, ApacheHttpClient.class }) + static class AwsOpenSearchConfiguration { + + @Bean + @ConditionalOnMissingBean + OpenSearchClient openSearchClient(OpenSearchVectorStoreProperties properties, AwsSdk2TransportOptions options) { + OpenSearchVectorStoreProperties.Aws aws = properties.getAws(); + Region region = Region.of(aws.getRegion()); + + SdkHttpClient httpClient = ApacheHttpClient.builder().build(); + OpenSearchTransport transport = new AwsSdk2Transport(httpClient, aws.getHost(), aws.getServiceName(), + region, options); + return new OpenSearchClient(transport); + } + + @Bean + @ConditionalOnMissingBean + AwsSdk2TransportOptions options(OpenSearchVectorStoreProperties properties) { + OpenSearchVectorStoreProperties.Aws aws = properties.getAws(); + return AwsSdk2TransportOptions.builder() + .setCredentials(StaticCredentialsProvider + .create(AwsBasicCredentials.create(aws.getAccessKey(), aws.getSecretKey()))) + .build(); + } + } static class PropertiesOpenSearchConnectionDetails implements OpenSearchConnectionDetails { diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreProperties.java index 723e0b388..6650500e6 100644 --- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreProperties.java +++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreProperties.java @@ -37,6 +37,8 @@ public class OpenSearchVectorStoreProperties { private String mappingJson; + private Aws aws = new Aws(); + public List getUris() { return uris; } @@ -77,4 +79,66 @@ public class OpenSearchVectorStoreProperties { this.mappingJson = mappingJson; } + public Aws getAws() { + return this.aws; + } + + public void setAws(Aws aws) { + this.aws = aws; + } + + static class Aws { + + private String host; + + private String serviceName; + + private String accessKey; + + private String secretKey; + + private String region; + + public String getHost() { + return this.host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getServiceName() { + return this.serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getAccessKey() { + return this.accessKey; + } + + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + public String getSecretKey() { + return this.secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public String getRegion() { + return this.region; + } + + public void setRegion(String region) { + this.region = region; + } + + } + } diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/AwsOpenSearchVectorStoreAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/AwsOpenSearchVectorStoreAutoConfigurationIT.java new file mode 100644 index 000000000..87a0ee2bf --- /dev/null +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/AwsOpenSearchVectorStoreAutoConfigurationIT.java @@ -0,0 +1,159 @@ +/* + * Copyright 2023 - 2024 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.vectorstore.opensearch; + +import com.jayway.jsonpath.JsonPath; +import net.minidev.json.JSONArray; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration; +import org.springframework.ai.document.Document; +import org.springframework.ai.embedding.EmbeddingModel; +import org.springframework.ai.transformers.TransformersEmbeddingModel; +import org.springframework.ai.vectorstore.OpenSearchVectorStore; +import org.springframework.ai.vectorstore.SearchRequest; +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.testcontainers.containers.localstack.LocalStackContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.hasSize; + +@Testcontainers +class AwsOpenSearchVectorStoreAutoConfigurationIT { + + @Container + private static final LocalStackContainer localstack = new LocalStackContainer( + DockerImageName.parse("localstack/localstack:3.5.0")) + .withEnv("LOCALSTACK_HOST", "localhost.localstack.cloud"); + + private static final String DOCUMENT_INDEX = "auto-spring-ai-document-index"; + + private List documents = List.of( + new Document("1", getText("classpath:/test/data/spring.ai.txt"), Map.of("meta1", "meta1")), + new Document("2", getText("classpath:/test/data/time.shelter.txt"), Map.of()), + new Document("3", getText("classpath:/test/data/great.depression.txt"), Map.of("meta2", "meta2"))); + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(OpenSearchVectorStoreAutoConfiguration.class, + SpringAiRetryAutoConfiguration.class)) + .withUserConfiguration(Config.class) + .withPropertyValues( + OpenSearchVectorStoreProperties.CONFIG_PREFIX + ".aws.host=" + + String.format("testcontainers-domain.%s.opensearch.localhost.localstack.cloud:%s", + localstack.getRegion(), localstack.getMappedPort(4566)), + OpenSearchVectorStoreProperties.CONFIG_PREFIX + ".aws.service-name=opensearch", + OpenSearchVectorStoreProperties.CONFIG_PREFIX + ".aws.region=" + localstack.getRegion(), + OpenSearchVectorStoreProperties.CONFIG_PREFIX + ".aws.access-key=" + localstack.getAccessKey(), + OpenSearchVectorStoreProperties.CONFIG_PREFIX + ".aws.secret-key=" + localstack.getSecretKey(), + OpenSearchVectorStoreProperties.CONFIG_PREFIX + ".indexName=" + DOCUMENT_INDEX, + OpenSearchVectorStoreProperties.CONFIG_PREFIX + ".mappingJson=" + """ + { + "properties":{ + "embedding":{ + "type":"knn_vector", + "dimension":384 + } + } + } + """); + + @BeforeAll + static void beforeAll() throws IOException, InterruptedException { + String[] createDomainCmd = { "awslocal", "opensearch", "create-domain", "--domain-name", + "testcontainers-domain", "--region", localstack.getRegion() }; + localstack.execInContainer(createDomainCmd); + + String[] describeDomainCmd = { "awslocal", "opensearch", "describe-domain", "--domain-name", + "testcontainers-domain", "--region", localstack.getRegion() }; + await().pollInterval(Duration.ofSeconds(30)).atMost(Duration.ofSeconds(300)).untilAsserted(() -> { + org.testcontainers.containers.Container.ExecResult execResult = localstack + .execInContainer(describeDomainCmd); + String response = execResult.getStdout(); + JSONArray processed = JsonPath.read(response, "$.DomainStatus[?(@.Processing == false)]"); + assertThat(processed).isNotEmpty(); + }); + } + + @Test + public void addAndSearchTest() { + + this.contextRunner.run(context -> { + OpenSearchVectorStore vectorStore = context.getBean(OpenSearchVectorStore.class); + + vectorStore.add(documents); + + Awaitility.await() + .until(() -> vectorStore + .similaritySearch(SearchRequest.query("Great Depression").withTopK(1).withSimilarityThreshold(0)), + hasSize(1)); + + List results = vectorStore + .similaritySearch(SearchRequest.query("Great Depression").withTopK(1).withSimilarityThreshold(0)); + + assertThat(results).hasSize(1); + Document resultDoc = results.get(0); + assertThat(resultDoc.getId()).isEqualTo(documents.get(2).getId()); + assertThat(resultDoc.getContent()).contains("The Great Depression (1929–1939) was an economic shock"); + assertThat(resultDoc.getMetadata()).hasSize(2); + assertThat(resultDoc.getMetadata()).containsKey("meta2"); + assertThat(resultDoc.getMetadata()).containsKey("distance"); + + // Remove all documents from the store + vectorStore.delete(documents.stream().map(Document::getId).toList()); + + Awaitility.await() + .until(() -> vectorStore + .similaritySearch(SearchRequest.query("Great Depression").withTopK(1).withSimilarityThreshold(0)), + hasSize(0)); + }); + } + + private String getText(String uri) { + var resource = new DefaultResourceLoader().getResource(uri); + try { + return resource.getContentAsString(StandardCharsets.UTF_8); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Configuration(proxyBeanMethods = false) + static class Config { + + @Bean + public EmbeddingModel embeddingModel() { + return new TransformersEmbeddingModel(); + } + + } + +} diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfigurationIT.java index bfcf986fb..f9d5a3493 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfigurationIT.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfigurationIT.java @@ -25,6 +25,7 @@ import org.springframework.ai.transformers.TransformersEmbeddingModel; import org.springframework.ai.vectorstore.OpenSearchVectorStore; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -32,6 +33,8 @@ import org.springframework.core.io.DefaultResourceLoader; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.regions.Region; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -58,6 +61,7 @@ class OpenSearchVectorStoreAutoConfigurationIT { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(OpenSearchVectorStoreAutoConfiguration.class, SpringAiRetryAutoConfiguration.class)) + .withClassLoader(new FilteredClassLoader(Region.class, ApacheHttpClient.class)) .withUserConfiguration(Config.class) .withPropertyValues( OpenSearchVectorStoreProperties.CONFIG_PREFIX + ".uris=" + opensearchContainer.getHttpHostAddress(), diff --git a/spring-ai-spring-boot-starters/spring-ai-starter-aws-opensearch-store/pom.xml b/spring-ai-spring-boot-starters/spring-ai-starter-aws-opensearch-store/pom.xml new file mode 100644 index 000000000..dc5d637b9 --- /dev/null +++ b/spring-ai-spring-boot-starters/spring-ai-starter-aws-opensearch-store/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + org.springframework.ai + spring-ai + 1.0.0-SNAPSHOT + ../../pom.xml + + spring-ai-aws-opensearch-store-spring-boot-starter + jar + Spring AI Starter - AWS OpenSearch Store + Spring AI AWS OpenSearch Store Auto Configuration + https://github.com/spring-projects/spring-ai + + + https://github.com/spring-projects/spring-ai + git://github.com/spring-projects/spring-ai.git + git@github.com:spring-projects/spring-ai.git + + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.ai + spring-ai-spring-boot-autoconfigure + ${project.parent.version} + + + + org.springframework.ai + spring-ai-opensearch-store + ${project.parent.version} + + + + software.amazon.awssdk + apache-client + ${awssdk.version} + + + +