@@ -35,6 +35,13 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-postgresml</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-azure-openai</artifactId>
|
||||
@@ -212,6 +219,12 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-testcontainers</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
@@ -219,6 +232,12 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 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.postgresml;
|
||||
|
||||
import org.springframework.ai.embedding.EmbeddingClient;
|
||||
import org.springframework.ai.postgresml.PostgresMlEmbeddingClient;
|
||||
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.jdbc.JdbcTemplateAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
/**
|
||||
* Auto-configuration class for PostgresMlEmbeddingClient.
|
||||
*/
|
||||
@AutoConfiguration(after = JdbcTemplateAutoConfiguration.class)
|
||||
@ConditionalOnClass(PostgresMlEmbeddingClient.class)
|
||||
@EnableConfigurationProperties(PostgresMlProperties.class)
|
||||
public class PostgresMlAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EmbeddingClient postgresMlEmbeddingClient(JdbcTemplate jdbcTemplate,
|
||||
PostgresMlProperties postgresMlProperties) {
|
||||
return new PostgresMlEmbeddingClient(jdbcTemplate, postgresMlProperties.getEmbedding().getTransformer(),
|
||||
postgresMlProperties.getEmbedding().getVectorType(), postgresMlProperties.getEmbedding().getKwargs(),
|
||||
postgresMlProperties.getEmbedding().getMetadataMode());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 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.postgresml;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
import org.springframework.ai.postgresml.PostgresMlEmbeddingClient;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Configuration properties for Postgres ML.
|
||||
*/
|
||||
@ConfigurationProperties(PostgresMlProperties.CONFIG_PREFIX)
|
||||
public class PostgresMlProperties {
|
||||
|
||||
public static final String CONFIG_PREFIX = "spring.ai.postgresml";
|
||||
|
||||
private final PostgresMlProperties.Embedding embedding = new PostgresMlProperties.Embedding(this);
|
||||
|
||||
private String transformer = "distilbert-base-uncased";
|
||||
|
||||
private PostgresMlEmbeddingClient.VectorType vectorType = PostgresMlEmbeddingClient.VectorType.PG_ARRAY;
|
||||
|
||||
private Map<String, Object> kwargs = Collections.emptyMap();
|
||||
|
||||
private MetadataMode metadataMode = MetadataMode.EMBED;
|
||||
|
||||
public PostgresMlProperties.Embedding getEmbedding() {
|
||||
return this.embedding;
|
||||
}
|
||||
|
||||
public String getTransformer() {
|
||||
return transformer;
|
||||
}
|
||||
|
||||
public void setTransformer(String transformer) {
|
||||
this.transformer = transformer;
|
||||
}
|
||||
|
||||
public PostgresMlEmbeddingClient.VectorType getVectorType() {
|
||||
return vectorType;
|
||||
}
|
||||
|
||||
public void setVectorType(PostgresMlEmbeddingClient.VectorType vectorType) {
|
||||
this.vectorType = vectorType;
|
||||
}
|
||||
|
||||
public Map<String, Object> getKwargs() {
|
||||
return kwargs;
|
||||
}
|
||||
|
||||
public void setKwargs(Map<String, Object> kwargs) {
|
||||
this.kwargs = kwargs;
|
||||
}
|
||||
|
||||
public MetadataMode getMetadataMode() {
|
||||
return metadataMode;
|
||||
}
|
||||
|
||||
public void setMetadataMode(MetadataMode metadataMode) {
|
||||
this.metadataMode = metadataMode;
|
||||
}
|
||||
|
||||
public static class Embedding {
|
||||
|
||||
private PostgresMlProperties postgresMlProperties;
|
||||
|
||||
private String transformer;
|
||||
|
||||
private PostgresMlEmbeddingClient.VectorType vectorType;
|
||||
|
||||
private Map<String, Object> kwargs;
|
||||
|
||||
private MetadataMode metadataMode;
|
||||
|
||||
protected Embedding(PostgresMlProperties postgresMlProperties) {
|
||||
Assert.notNull(postgresMlProperties, "PostgresMlProperties must not be null");
|
||||
this.postgresMlProperties = postgresMlProperties;
|
||||
}
|
||||
|
||||
public PostgresMlProperties getPostgresMlProperties() {
|
||||
return postgresMlProperties;
|
||||
}
|
||||
|
||||
public String getTransformer() {
|
||||
return StringUtils.hasText(this.transformer) ? this.transformer
|
||||
: getPostgresMlProperties().getTransformer();
|
||||
}
|
||||
|
||||
public void setTransformer(String transformer) {
|
||||
this.transformer = transformer;
|
||||
}
|
||||
|
||||
public PostgresMlEmbeddingClient.VectorType getVectorType() {
|
||||
return this.vectorType != null ? this.vectorType : getPostgresMlProperties().getVectorType();
|
||||
}
|
||||
|
||||
public void setVectorType(PostgresMlEmbeddingClient.VectorType vectorType) {
|
||||
this.vectorType = vectorType;
|
||||
}
|
||||
|
||||
public Map<String, Object> getKwargs() {
|
||||
return this.kwargs != null ? this.kwargs : getPostgresMlProperties().getKwargs();
|
||||
}
|
||||
|
||||
public void setKwargs(Map<String, Object> kwargs) {
|
||||
this.kwargs = kwargs;
|
||||
}
|
||||
|
||||
public MetadataMode getMetadataMode() {
|
||||
return this.metadataMode != null ? this.metadataMode : getPostgresMlProperties().getMetadataMode();
|
||||
}
|
||||
|
||||
public void setMetadataMode(MetadataMode metadataMode) {
|
||||
this.metadataMode = metadataMode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Metadata {
|
||||
|
||||
private Boolean rateLimitMetricsEnabled;
|
||||
|
||||
public boolean isRateLimitMetricsEnabled() {
|
||||
return Boolean.TRUE.equals(getRateLimitMetricsEnabled());
|
||||
}
|
||||
|
||||
public Boolean getRateLimitMetricsEnabled() {
|
||||
return this.rateLimitMetricsEnabled;
|
||||
}
|
||||
|
||||
public void setRateLimitMetricsEnabled(Boolean rateLimitMetricsEnabled) {
|
||||
this.rateLimitMetricsEnabled = rateLimitMetricsEnabled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 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.postgresml;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
import org.springframework.ai.embedding.EmbeddingResponse;
|
||||
import org.springframework.ai.postgresml.PostgresMlEmbeddingClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
|
||||
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Utkarsh Srivastava
|
||||
*/
|
||||
@JdbcTest(properties = "logging.level.sql=TRACE")
|
||||
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
|
||||
@Testcontainers
|
||||
@Disabled("Disabled from automatic execution, as it requires an excessive amount of memory (over 9GB)!")
|
||||
public class PostgresMlAutoConfigurationIT {
|
||||
|
||||
@Container
|
||||
@ServiceConnection
|
||||
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(
|
||||
DockerImageName.parse("ghcr.io/postgresml/postgresml:2.7.3").asCompatibleSubstituteFor("postgres"))
|
||||
.withCommand("sleep", "infinity")
|
||||
.withLabel("org.springframework.boot.service-connection", "postgres")
|
||||
.withUsername("postgresml")
|
||||
.withPassword("postgresml")
|
||||
.withDatabaseName("postgresml")
|
||||
.waitingFor(new LogMessageWaitStrategy().withRegEx(".*Starting dashboard.*\\s")
|
||||
.withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS)));
|
||||
|
||||
@Autowired
|
||||
JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Test
|
||||
void embedding() {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withBean(JdbcTemplate.class, () -> jdbcTemplate)
|
||||
.withConfiguration(AutoConfigurations.of(PostgresMlAutoConfiguration.class));
|
||||
contextRunner.run(context -> {
|
||||
PostgresMlEmbeddingClient embeddingClient = context.getBean(PostgresMlEmbeddingClient.class);
|
||||
|
||||
EmbeddingResponse embeddingResponse = embeddingClient
|
||||
.embedForResponse(List.of("Hello World", "World is big and salvation is near"));
|
||||
assertThat(embeddingResponse.getResults()).hasSize(2);
|
||||
assertThat(embeddingResponse.getResults().get(0).getOutput()).isNotEmpty();
|
||||
assertThat(embeddingResponse.getResults().get(0).getIndex()).isZero();
|
||||
assertThat(embeddingResponse.getResults().get(1).getOutput()).isNotEmpty();
|
||||
assertThat(embeddingResponse.getResults().get(1).getIndex()).isEqualTo(1);
|
||||
|
||||
assertThat(embeddingClient.dimensions()).isEqualTo(768);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 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.postgresml;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
import org.springframework.ai.postgresml.PostgresMlEmbeddingClient;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Unit Tests for {@link PostgresMlProperties}.
|
||||
*
|
||||
* @author Utkarsh Srivastava
|
||||
*/
|
||||
@SpringBootTest(properties = { "spring.ai.postgresml.metadata-mode=all", "spring.ai.postgresml.kwargs.key1=value1",
|
||||
"spring.ai.postgresml.kwargs.key2=value2", "spring.ai.postgresml.embedding.transformer=abc123" })
|
||||
class PostgresMlPropertiesTests {
|
||||
|
||||
@Autowired
|
||||
private PostgresMlProperties postgresMlProperties;
|
||||
|
||||
@Test
|
||||
void postgresMlPropertiesAreCorrect() {
|
||||
assertThat(this.postgresMlProperties).isNotNull();
|
||||
assertThat(this.postgresMlProperties.getTransformer()).isEqualTo("distilbert-base-uncased");
|
||||
assertThat(this.postgresMlProperties.getVectorType()).isEqualTo(PostgresMlEmbeddingClient.VectorType.PG_ARRAY);
|
||||
assertThat(this.postgresMlProperties.getKwargs()).isEqualTo(Map.of("key1", "value1", "key2", "value2"));
|
||||
assertThat(this.postgresMlProperties.getMetadataMode()).isEqualTo(MetadataMode.ALL);
|
||||
|
||||
PostgresMlProperties.Embedding embedding = this.postgresMlProperties.getEmbedding();
|
||||
|
||||
assertThat(embedding).isNotNull();
|
||||
assertThat(embedding.getTransformer()).isEqualTo("abc123");
|
||||
assertThat(embedding.getVectorType()).isEqualTo(PostgresMlEmbeddingClient.VectorType.PG_ARRAY);
|
||||
assertThat(embedding.getKwargs()).isEqualTo(Map.of("key1", "value1", "key2", "value2"));
|
||||
assertThat(embedding.getMetadataMode()).isEqualTo(MetadataMode.ALL);
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableConfigurationProperties(PostgresMlProperties.class)
|
||||
static class TestConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user