StabilityAI Autoconfiguration improvements

* Add IT Test
* Add starter to Spring AI bom
This commit is contained in:
Mark Pollack
2024-02-21 13:47:11 -05:00
parent 1c222a695c
commit 14e4a6cccf
7 changed files with 153 additions and 15 deletions

View File

@@ -37,6 +37,7 @@
<module>spring-ai-spring-boot-starters/spring-ai-starter-azure-store</module>
<module>spring-ai-spring-boot-starters/spring-ai-starter-weaviate-store</module>
<module>spring-ai-spring-boot-starters/spring-ai-starter-redis</module>
<module>spring-ai-spring-boot-starters/spring-ai-starter-stability-ai</module>
<module>spring-ai-spring-boot-starters/spring-ai-starter-neo4j-store</module>
<module>spring-ai-spring-boot-starters/spring-ai-starter-postgresml-embedding</module>
<module>spring-ai-docs</module>

View File

@@ -163,7 +163,7 @@
<version>${project.version}</version>
</dependency>
<!-- Spring Boog Starters -->
<!-- Spring Boot Starters -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId>
@@ -236,6 +236,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-stability-ai-spring-boot-starter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-transformers-spring-boot-starter</artifactId>

View File

@@ -17,27 +17,40 @@ package org.springframework.ai.autoconfigure.stabilityai;
import org.springframework.ai.stabilityai.StabilityAiImageClient;
import org.springframework.ai.stabilityai.api.StabilityAiApi;
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.web.client.RestClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestClient;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* @author Mark Pollack
* @since 0.8.0
*/
@AutoConfiguration(after = { RestClientAutoConfiguration.class })
@ConditionalOnClass(StabilityAiApi.class)
@EnableConfigurationProperties({ StabilityAiImageProperties.class })
@EnableConfigurationProperties({ StabilityAiConnectionProperties.class, StabilityAiImageProperties.class })
public class StabilityAiImageAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public StabilityAiApi stabilityAiApi(StabilityAiImageProperties stabilityAiImageProperties,
RestClient.Builder restClientBuilder) {
return new StabilityAiApi(stabilityAiImageProperties.getApiKey(), stabilityAiImageProperties.getBaseUrl(),
stabilityAiImageProperties.getOptions().getModel(), restClientBuilder);
public StabilityAiApi stabilityAiApi(StabilityAiConnectionProperties commonProperties,
StabilityAiImageProperties imageProperties) {
String apiKey = StringUtils.hasText(imageProperties.getApiKey()) ? imageProperties.getApiKey()
: commonProperties.getApiKey();
String baseUrl = StringUtils.hasText(imageProperties.getBaseUrl()) ? imageProperties.getBaseUrl()
: commonProperties.getBaseUrl();
Assert.hasText(apiKey, "StabilityAI API key must be set");
Assert.hasText(baseUrl, "StabilityAI base URL must be set");
return new StabilityAiApi(apiKey, imageProperties.getOptions().getModel(), baseUrl);
}
@Bean

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.ai.autoconfigure.stabilityai;
import org.springframework.ai.stabilityai.api.StabilityAiApi;
import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
@@ -30,7 +29,10 @@ public class StabilityAiImageProperties extends StabilityAiParentProperties {
public static final String CONFIG_PREFIX = "spring.ai.stabilityai.image";
@NestedConfigurationProperty
private StabilityAiImageOptions options;
private StabilityAiImageOptions options = StabilityAiImageOptions.builder().build(); // stable-diffusion-v1-6
// is
// default
// model
public StabilityAiImageOptions getOptions() {
return this.options;

View File

@@ -16,15 +16,9 @@
package org.springframework.ai.autoconfigure.bedrock.cohere;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import software.amazon.awssdk.regions.Region;
import org.springframework.ai.autoconfigure.bedrock.BedrockAwsConnectionProperties;
import org.springframework.ai.autoconfigure.openai.OpenAiConnectionProperties;
import org.springframework.ai.autoconfigure.openai.OpenAiEmbeddingProperties;
import org.springframework.ai.bedrock.cohere.BedrockCohereEmbeddingClient;
import org.springframework.ai.bedrock.cohere.api.CohereEmbeddingBedrockApi.CohereEmbeddingModel;
import org.springframework.ai.bedrock.cohere.api.CohereEmbeddingBedrockApi.CohereEmbeddingRequest;
@@ -32,6 +26,9 @@ import org.springframework.ai.bedrock.cohere.api.CohereEmbeddingBedrockApi.Coher
import org.springframework.ai.embedding.EmbeddingResponse;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import software.amazon.awssdk.regions.Region;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -0,0 +1,42 @@
package org.springframework.ai.autoconfigure.stabilityai;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.springframework.ai.image.*;
import org.springframework.ai.stabilityai.StyleEnum;
import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
@EnabledIfEnvironmentVariable(named = "STABILITYAI_API_KEY", matches = ".*")
public class StabilityAiAutoConfigurationIT {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withPropertyValues("spring.ai.stabilityai.image.api-key=" + System.getenv("STABILITYAI_API_KEY"))
.withConfiguration(AutoConfigurations.of(StabilityAiImageAutoConfiguration.class));
@Test
void generate() {
contextRunner.run(context -> {
ImageClient imageClient = context.getBean(ImageClient.class);
StabilityAiImageOptions imageOptions = StabilityAiImageOptions.builder()
.withStylePreset(StyleEnum.PHOTOGRAPHIC)
.build();
var instructions = """
A light cream colored mini golden doodle.
""";
ImagePrompt imagePrompt = new ImagePrompt(instructions, imageOptions);
ImageResponse imageResponse = imageClient.call(imagePrompt);
ImageGeneration imageGeneration = imageResponse.getResult();
Image image = imageGeneration.getOutput();
assertThat(image.getB64Json()).isNotEmpty();
});
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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.stabilityai;
import org.junit.jupiter.api.Test;
import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration;
import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties;
import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiConnectionProperties;
import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiEmbeddingProperties;
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
* @since 0.8.0
*/
public class StabilityAiImagePropertiesTests {
@Test
public void chatPropertiesTest() {
new ApplicationContextRunner().withPropertyValues(
// @formatter:off
"spring.ai.stabilityai.image.api-key=API_KEY",
"spring.ai.stabilityai.image.base-url=ENDPOINT",
"spring.ai.stabilityai.image.options.n=10",
"spring.ai.stabilityai.image.options.model=MODEL_XYZ",
"spring.ai.stabilityai.image.options.width=512",
"spring.ai.stabilityai.image.options.height=256",
"spring.ai.stabilityai.image.options.response-format=application/json",
"spring.ai.stabilityai.image.options.n=4",
"spring.ai.stabilityai.image.options.cfg-scale=7",
"spring.ai.stabilityai.image.options.clip-guidance-preset=SIMPLE",
"spring.ai.stabilityai.image.options.sampler=K_EULER",
"spring.ai.stabilityai.image.options.seed=0",
"spring.ai.stabilityai.image.options.steps=30",
"spring.ai.stabilityai.image.options.style-preset=neon-punk"
)
// @formatter:on
.withConfiguration(AutoConfigurations.of(StabilityAiImageAutoConfiguration.class))
.run(context -> {
var chatProperties = context.getBean(StabilityAiImageProperties.class);
assertThat(chatProperties.getBaseUrl()).isEqualTo("ENDPOINT");
assertThat(chatProperties.getApiKey()).isEqualTo("API_KEY");
assertThat(chatProperties.getOptions().getModel()).isEqualTo("MODEL_XYZ");
assertThat(chatProperties.getOptions().getWidth()).isEqualTo(512);
assertThat(chatProperties.getOptions().getHeight()).isEqualTo(256);
assertThat(chatProperties.getOptions().getResponseFormat()).isEqualTo("application/json");
assertThat(chatProperties.getOptions().getN()).isEqualTo(4);
assertThat(chatProperties.getOptions().getCfgScale()).isEqualTo(7);
assertThat(chatProperties.getOptions().getClipGuidancePreset()).isEqualTo("SIMPLE");
assertThat(chatProperties.getOptions().getSampler()).isEqualTo("K_EULER");
assertThat(chatProperties.getOptions().getSeed()).isEqualTo(0);
assertThat(chatProperties.getOptions().getSteps()).isEqualTo(30);
assertThat(chatProperties.getOptions().getStylePreset()).isEqualTo("neon-punk");
});
}
}